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 @@ - @@ -423,16 +433,13 @@ border-color: var(--accent); } - /* Phone: the floating transport pill (SidebarRail) owns the bottom-centre and already carries an - "add environment" button, so the canvas's own Add pill stands down and the zoom cluster lifts - clear of the transport pill instead of sharing the bottom edge with it. */ + /* Phone: the floating canvas controls stand down. The transport pill (SidebarRail) already carries + an "add environment" button and now a recenter button, and pinch handles zoom in/out - so both the + Add pill and the whole zoom cluster (which the owner found ate too much space) are hidden here. */ @media (max-width: 900px) { - .add { - display: none; - } - + .add, .controls { - bottom: calc(env(safe-area-inset-bottom, 0px) + 76px); + display: none; } } diff --git a/src/lib/components/shell/Sidebar.svelte b/src/lib/components/shell/Sidebar.svelte index 80a8274..f3c9dd9 100644 --- a/src/lib/components/shell/Sidebar.svelte +++ b/src/lib/components/shell/Sidebar.svelte @@ -40,9 +40,11 @@ interface Props { onaddworld: () => void; onplaystory: () => void; + /** Passed through to the rail's phone pill: "fit the tree" when the lineage canvas is showing. */ + onrecenter?: () => void; } - let { onaddworld, onplaystory }: Props = $props(); + let { onaddworld, onplaystory, onrecenter }: Props = $props(); const target = $derived(trainTarget(bench.generationsEvolved, bench.maxGenerations)); const label = $derived(trainLabel(bench.maxGenerations)); @@ -87,7 +89,7 @@ every time the panel opened. --> {#if !shell.open || shell.narrow} - + {/if} {#if shell.open} diff --git a/src/lib/components/shell/SidebarRail.svelte b/src/lib/components/shell/SidebarRail.svelte index d7e3ca9..0620f9d 100644 --- a/src/lib/components/shell/SidebarRail.svelte +++ b/src/lib/components/shell/SidebarRail.svelte @@ -22,9 +22,12 @@ interface Props { onaddworld: () => void; onplaystory: () => void; + /** "Fit the tree", when the lineage canvas is showing. On a phone its own zoom cluster is hidden, + so this rides in the pill; absent (the focus view, or desktop) the button is not offered. */ + onrecenter?: () => void; } - let { onaddworld, onplaystory }: Props = $props(); + let { onaddworld, onplaystory, onrecenter }: Props = $props(); const speed = $derived(SPEEDS.find((option) => option.value === bench.speed) ?? SPEEDS[1]); const train = $derived(trainLabel(bench.maxGenerations)); @@ -104,6 +107,14 @@ > + + + {#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}