Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions src/lib/components/bench/LineageCanvas.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -295,7 +305,7 @@
<Button variant="icon" size="sm" aria-label="zoom in" onclick={() => zoomFromCentre(1.2)}>
<Icon name="plus" size={15} />
</Button>
<Button variant="icon" size="sm" aria-label="recenter the tree" onclick={recenter}>
<Button variant="icon" size="sm" aria-label="recenter the tree" onclick={recenterTree}>
<Icon name="crosshair" size={15} />
</Button>
</div>
Expand Down Expand Up @@ -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;
}
}
</style>
6 changes: 4 additions & 2 deletions src/lib/components/shell/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -87,7 +89,7 @@
every time the panel opened.
-->
{#if !shell.open || shell.narrow}
<SidebarRail {onaddworld} {onplaystory} />
<SidebarRail {onaddworld} {onplaystory} {onrecenter} />
{/if}

{#if shell.open}
Expand Down
13 changes: 12 additions & 1 deletion src/lib/components/shell/SidebarRail.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -104,6 +107,14 @@
>
<TransportIcon playing={false} size={9} />
</Button>

<!-- Phone only: the lineage canvas hides its own zoom cluster there, so "fit the tree" rides in
the pill. Absent on the focus view (no tree to fit) and on desktop (the canvas keeps its own). -->
{#if shell.narrow && onrecenter}
<Button variant="icon" aria-label="recenter the tree" title="recenter" onclick={onrecenter}>
<Icon name="crosshair" />
</Button>
{/if}
{/if}
</aside>

Expand Down
12 changes: 10 additions & 2 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -144,7 +148,11 @@
<!-- The studio sidebar and its transport do not apply in Research; the instruments carry their
own controls (built in later phases). -->
{#if !app.research}
<Sidebar onaddworld={addWorld} onplaystory={() => bench.playStory()} />
<Sidebar
onaddworld={addWorld}
onplaystory={() => bench.playStory()}
onrecenter={focusing ? undefined : recenterTree}
/>
{/if}

<!-- The bench IS the canvas: the family tree (or a focused world's workbench) fills the height
Expand All @@ -156,7 +164,7 @@
{:else if focusing}
<FocusView onaddworld={addWorld} />
{:else}
<LineageCanvas onaddworld={addWorld} />
<LineageCanvas onaddworld={addWorld} onrecenterready={(fn) => (recenterTree = fn)} />
{/if}
</main>
</div>
Expand Down
Loading