diff --git a/.claude/hooks b/.claude/hooks deleted file mode 100644 index e69de29..0000000 diff --git a/.claude/launch.json b/.claude/launch.json deleted file mode 100644 index e69de29..0000000 diff --git a/.claude/routines b/.claude/routines deleted file mode 100644 index e69de29..0000000 diff --git a/.claude/scheduled_tasks.json b/.claude/scheduled_tasks.json deleted file mode 100644 index e69de29..0000000 diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index e69de29..0000000 diff --git a/.claude/skills b/.claude/skills deleted file mode 100644 index e69de29..0000000 diff --git a/.claude/workflows b/.claude/workflows deleted file mode 100644 index e69de29..0000000 diff --git a/.mcp.json b/.mcp.json deleted file mode 100644 index e69de29..0000000 diff --git a/src/app.html b/src/app.html index f273cc5..5ec0a50 100644 --- a/src/app.html +++ b/src/app.html @@ -3,6 +3,20 @@ + %sveltekit.head% diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg index cc5dc66..e88d727 100644 --- a/src/lib/assets/favicon.svg +++ b/src/lib/assets/favicon.svg @@ -1 +1,46 @@ -svelte-logo \ No newline at end of file + + OMbrella + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lib/charts/CanvasChart.svelte b/src/lib/charts/CanvasChart.svelte index 535a5c2..84d306f 100644 --- a/src/lib/charts/CanvasChart.svelte +++ b/src/lib/charts/CanvasChart.svelte @@ -4,7 +4,7 @@ Renders line and bar series over a shared hourly time axis on a devicePixelRatio-aware canvas. Supports daylight background bands, timezone-aware axis labels with emphasized day boundaries, a DOM tooltip - with crosshair, wheel/pinch zoom, drag panning, and cross-chart + with crosshair, Ctrl+wheel / pinch zoom, drag panning, and cross-chart synchronization via the `group` prop. Usage: @@ -161,6 +161,16 @@ let themeVersion = $state(0); const legendHidden = new SvelteSet(); + // Briefly shown when the user scrolls over the chart without holding Ctrl + let zoomHintVisible = $state(false); + let zoomHintTimeout: ReturnType | undefined; + + function showZoomHint(): void { + zoomHintVisible = true; + clearTimeout(zoomHintTimeout); + zoomHintTimeout = setTimeout(() => (zoomHintVisible = false), 1200); + } + // Zoom range and crosshair: either group-shared or local to this chart. // The group is acquired once at component init (the prop is treated as fixed). const groupName = untrack(() => group); @@ -170,6 +180,7 @@ onDestroy(() => { if (groupName) releaseGroup(groupName); + clearTimeout(zoomHintTimeout); }); // ─── Derived: view window & scales ────────────────────────────────────────── @@ -737,6 +748,12 @@ }; const onWheel = (e: WheelEvent): void => { + // Zoom only while Ctrl (or ⌘) is held — a plain scroll should keep + // scrolling the page. Trackpad pinch also arrives as ctrlKey wheel. + if (!e.ctrlKey && !e.metaKey) { + showZoomHint(); + return; + } e.preventDefault(); const t = pixToTime(localX(e)); zoomAt(t, e.deltaY < 0 ? 1 / 1.3 : 1.3); @@ -803,6 +820,19 @@ style:touch-action="pan-y" > + {#if zoomHintVisible} +
+ + Hold Ctrl + and scroll to zoom + +
+ {/if} + {#if tooltipVisible}
= { + system: 'Theme: follow system', + light: 'Theme: light', + dark: 'Theme: dark' + }; + + function cycleTheme() { + storedTheme.update( + (current) => themeCycle[(themeCycle.indexOf(current) + 1) % themeCycle.length] + ); + } + function navigateToLocation(newLocation: GeoLocation) { storedLocation.set(newLocation); const locationRoute = buildLocationRoute(newLocation); @@ -54,17 +67,19 @@ {#if location} -