From 318dca342317c571aa99a597d1ce00dd153d7dbc Mon Sep 17 00:00:00 2001 From: Justin Visser Date: Thu, 18 Jun 2026 23:01:47 +0200 Subject: [PATCH] web: UI polish pass (layout, seeds, reroll, fit) - Graph layout: fix the reroll-vs-reload determinism bug (a reroll requested the layout before the new edges arrived, caching a link-less uniform circle; only cache once edges are present). Retune to a slightly-wide organic blob in a 340x270 viewBox so it fills the square-ish cells without flat ovals. - Reroll world: moved out of Advanced into a button in the WORLD card header; any seed reroll (header or per-seed dice) now replays the spread from round 0 (pendingReplay flag in the store, watched by playback). freshSeed extracted to lib/seed.ts. - Seeds: separated from the real quantities under a 'Seeds' header ('labels for a random world, not counts') with a # prefix so a value reads as an id. - Fit: grid rows subtract the container padding so the 2x2 fits exactly and the bottom card shadows are no longer clipped; the settings card absolutely fills the sidebar so the graph grid defines the height and the card scrolls internally instead of growing taller than the graphs. --- web/src/App.vue | 12 ++-- web/src/components/AdvancedFields.vue | 99 +++++++++++++++------------ web/src/components/ControlsCard.vue | 47 ++++++++++++- web/src/components/PanelGrid.vue | 14 ++-- web/src/composables/useLayout.ts | 30 ++++---- web/src/composables/usePlayback.ts | 16 +++++ web/src/composables/useSimStore.ts | 19 +++++ web/src/lib/seed.ts | 8 +++ 8 files changed, 180 insertions(+), 65 deletions(-) create mode 100644 web/src/lib/seed.ts diff --git a/web/src/App.vue b/web/src/App.vue index 675c0b6..c183b7e 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -163,15 +163,15 @@ onMounted(async () => { grid-column: 1; grid-row: 1; min-height: 0; - display: flex; - flex-direction: column; + position: relative; } - /* The settings card fills the sidebar so its height matches the two-row - graph grid; it scrolls internally only if its own content is taller. */ + /* The card absolutely fills the sidebar, so its own content never grows the + row: the graph grid defines the height, the sidebar matches it, and the + card scrolls internally if its content (e.g. Advanced expanded) is taller. */ .sidebar :deep(.card) { - flex: 1; - min-height: 0; + position: absolute; + inset: 0; overflow-y: auto; } diff --git a/web/src/components/AdvancedFields.vue b/web/src/components/AdvancedFields.vue index b0dc716..25e84ba 100644 --- a/web/src/components/AdvancedFields.vue +++ b/web/src/components/AdvancedFields.vue @@ -79,21 +79,6 @@ function setField(field: keyof Config, event: Event) { if (Number.isFinite(nextValue)) store.setBaseField(field, nextValue) } -// Seeds are just names for worlds; a small range keeps the fields and -// shared URLs readable while still giving plenty of distinct worlds. -function freshSeed(): number { - const buffer = new Uint32Array(1) - crypto.getRandomValues(buffer) - return (buffer[0] ?? 0) % 10000 -} - -function reroll(field: keyof Config) { - store.setBaseField(field, freshSeed()) -} - -function rerollWorld() { - for (const { field } of seedFields) store.setBaseField(field, freshSeed()) -} @@ -226,6 +220,44 @@ details[open] .chevron { min-width: 0; } +.seeds-head { + display: flex; + align-items: baseline; + flex-wrap: wrap; + gap: 8px; + margin-top: 18px; +} + +.seeds-title { + font-size: 12px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--ink-4); +} + +.seeds-note { + font-size: 11.5px; + font-weight: 400; + color: var(--ink-4); +} + +.seedval { + display: flex; + align-items: center; + gap: 3px; +} + +.hash { + color: var(--ink-4); + font-weight: 600; + font-size: 13px; +} + +.seedval input[type='number'] { + width: 52px; +} + input[type='number'] { width: 72px; font: 600 13px/1.4 inherit; @@ -258,23 +290,6 @@ input[type='number'] { height: 14px; } -.reroll-world { - margin-top: 12px; - font-size: 12.5px; - font-weight: 600; - color: var(--ink-3); - background: none; - border: 1px solid var(--border); - border-radius: 8px; - padding: 5px 12px; - cursor: pointer; -} - -.reroll-world:hover { - border-color: var(--ink-4); - color: var(--ink); -} - /* Single column when the controls container is narrow (sidebar / mobile). */ @container controls (max-width: 380px) { .grid { diff --git a/web/src/components/ControlsCard.vue b/web/src/components/ControlsCard.vue index 12885bf..9caeb38 100644 --- a/web/src/components/ControlsCard.vue +++ b/web/src/components/ControlsCard.vue @@ -33,7 +33,16 @@ const offendsProgramEffect = offends('programEffect')