feat(stammbaum): sync view to shareable ?cx&cy&z URL (#692)

A view-keyed effect mirrors pan/zoom into the URL via replaceState (URL read
untracked to avoid a feedback loop). State survives panel open/close
(US-PANEL-002 AC1) and a shared link reproduces the view (AC2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-29 17:02:47 +02:00
parent 8d29bb10e2
commit 289c3bbfb5
3 changed files with 42 additions and 1 deletions

View File

@@ -1,6 +1,8 @@
<script lang="ts">
import { untrack } from 'svelte';
import { m } from '$lib/paraglide/messages.js';
import { page } from '$app/state';
import { replaceState } from '$app/navigation';
import StammbaumTree from '$lib/person/genealogy/StammbaumTree.svelte';
import StammbaumSidePanel from '$lib/person/genealogy/StammbaumSidePanel.svelte';
import StammbaumControls from '$lib/person/genealogy/StammbaumControls.svelte';
@@ -8,6 +10,7 @@ import {
type PanZoomState,
DEFAULT_VIEW,
clampZoom,
serializePanZoomParams,
ZOOM_STEP_KB
} from '$lib/person/genealogy/panZoom';
import { animateView, prefersReducedMotion } from '$lib/person/genealogy/animateView';
@@ -45,6 +48,21 @@ function fitToScreen() {
reducedMotion: prefersReducedMotion()
});
}
// Mirror the view into shareable ?cx&cy&z params (OQ-003). Only `view` is a
// tracked dependency; the current URL is read untracked so the replaceState
// write does not retrigger the effect. The state thus survives panel open/close
// (US-PANEL-002 AC1) and a shared link reproduces it (AC2).
$effect(() => {
const { cx, cy, z } = serializePanZoomParams(view);
untrack(() => {
const url = new URL(window.location.href);
url.searchParams.set('cx', cx);
url.searchParams.set('cy', cy);
url.searchParams.set('z', z);
replaceState(url, page.state);
});
});
</script>
<!-- 4.25rem = 4rem navbar (h-16) + 0.25rem accent strip (h-1).