diff --git a/src/lib/components/bench/LineageCanvas.svelte b/src/lib/components/bench/LineageCanvas.svelte
index 6232fe8..8795998 100644
--- a/src/lib/components/bench/LineageCanvas.svelte
+++ b/src/lib/components/bench/LineageCanvas.svelte
@@ -22,9 +22,19 @@
interface Props {
/** Add a fresh root world - the canvas offers it in its own corner, not only the sidebar. */
onaddworld: () => void;
+ /**
+ * Handed the canvas's "fit the tree" action on mount (the same `register` pattern Canvas uses).
+ * On a phone the canvas hides its own zoom cluster - pinch handles zoom - so the recenter rides
+ * in the transport pill instead, which calls back through this. The parent only offers it to the
+ * pill while this canvas is the live view, so a handle to a torn-down canvas is never reached.
+ */
+ onrecenterready?: (recenter: () => void) => void;
}
- let { onaddworld }: Props = $props();
+ let { onaddworld, onrecenterready }: Props = $props();
+
+ // Hand our recenter up to the parent once mounted (recenterTree is a hoisted declaration below).
+ onMount(() => onrecenterready?.(recenterTree));
let container: HTMLDivElement;
/** Each node's rendered height, measured live - the edges need it to leave from a parent's foot. */
@@ -181,7 +191,7 @@
}
/** Frame the whole tree in the viewport - the "recenter" action. */
- function recenter() {
+ function recenterTree() {
const rect = container.getBoundingClientRect();
if (!bench.worlds.length) {
canvas.reset();
@@ -295,7 +305,7 @@
-
+
+
+ {#if shell.narrow && onrecenter}
+
+
+
+ {/if}
{/if}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index f55a6fc..e681ee9 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -93,6 +93,10 @@
// exists. A focusedId left pointing at a removed world falls straight back to the canvas.
const focusing = $derived(bench.focusedId ? bench.find(bench.focusedId) : undefined);
+ // The lineage canvas binds its "fit the tree" action here so the phone transport pill can offer it.
+ // It is only live while the canvas is mounted (not the focus view), so the pill hides the button then.
+ let recenterTree = $state<(() => void) | undefined>();
+
/**
* Space plays/pauses - but ONLY when it isn't already the focused control's key.
*
@@ -144,7 +148,11 @@
{#if !app.research}
- bench.playStory()} />
+ bench.playStory()}
+ onrecenter={focusing ? undefined : recenterTree}
+ />
{/if}