Redesign the Settings pane: subtabs + readable rows (#69) - #85
Conversation
…descriptions (#69) ThemeRow moves from a single-line [label | control | reset] row to a vertical head-row + full-width-control + optional description stack (layout B), so labels stop truncating and stacked controls share one width. Toggle/Color fields stay inline on the head row since a full-width control would look odd for them. TierWidthsField and HueMapField adopt the shared ThemeRow instead of hand-rolled markup. Drops a stale controlsPane assertion that assumed tips were hover-only text; several field tips legitimately mention "Rebuild", which is now visible by design.
…-dashes (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… the rewrite (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#69) Add markAutosave/isAutosave to settingsSchema.ts. setDraft/stageReset now write through for autosave stores instead of staging a draft; stageResetAll/anyResettable skip them (Reset-all stays World-only). Marks LIVE_UPDATES and SYNTAX_THEME autosave, reintroducing the write-through behavior Phase-1 Task 3 removed, now as a named per-store flag. Flips the SYNTAX_THEME draft tests to assert instant apply, and fixes a pre-existing test-isolation leak in settingsDrafts.test.ts (disposable per-test FOO/BAR stores were never unregistered, leaking non-default values into later anyResettable() sweeps) via a new _unregisterForTests hook.
…orld only (#69) ControlsPane drops the Shortcuts/Debug subtabs (moving to header modals in Tasks 8/9); Settings is now World (draftable, 9-section accordion, keeps the Save/Discard/Reset-all footer) plus autosave Updates/Preview, each a single section rendered inline via a new SectionNode.inline flag (no <details> wrapper, no section reset — a one-item accordion is bad UX). onRunCollisionCheck/onRunStemDiagnostic stay wired through ControlsPaneProps for now; Task 9 removes them once Debug moves to its own modal.
Shortcuts is reference material, not a setting: replace the removed Controls-pane subtab with a `?` icon in the header that opens a self-hiding modal (dialog role, Esc/backdrop/close-button dismiss), reusing the SourcePicker modal chrome. ShortcutsSection is now dead and removed.
Debug's dev diagnostics (collision check, stem placement) move out of Settings into a DebugModal opened from a header bug icon, shown only when isDebugMode() (dev server, VITE_DEBUG, or ?debug) is on. Mirrors the Shortcuts modal (Task 8). Drops the now-dead onRunCollisionCheck/ onRunStemDiagnostic props threaded through ControlsPane/LeftSidebar.
…ip CSS; modal focus + comment (#69) Final integration review of the Phase-2 settings work found the scene's document keydown handler still firing Esc-deselect/R/F underneath open modals; the shortcuts-chip CSS stranded in ControlsPane.css after the shortcuts list moved into its own modal; a stale comment claiming HAS_ANY_NON_DEFAULT drives Reset-all's enabled state (it's anyResettable() now); and both new modals not moving focus in on open. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…aria-describedby (#69, #79) A wrapping <label> around head + control + description made every control's accessible name "label + full description" and let clicking the description text activate inline toggle/color controls. ThemeRow now wraps only head + control in the <label>; the description is a sibling, linked back to the control via aria-describedby (id generated per-field by Field/TierWidthsField/ HueMapField via useId).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…; drop two redundant hints (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Updates tab (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ve the footer) (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Depth-driven collapsibility: sections + one subgroup level collapse; deeper groups render as flat labeled clusters. Fixes a .map(renderChild) call that passed the array index as the depth arg, flattening every group past the 3rd. (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Single wrapper seeds the nesting depth (default level 2) and every path uses it, so no call site passes a literal depth and nothing maps renderChild directly. (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ch 12→16) (#69) Info Overview, Tree, Search, Readme, and Settings now share the same horizontal content inset so the panes line up when switching tabs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Section header pins at the scroll-body top; an open subgroup header stacks just below it (offset via --cc-controls-header-h). Opaque backgrounds + z-index so scrolled rows don't bleed through. (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ens and go stale) (#69)
…very pane Overview, Readme, Tree, and Search now frame their content with the same 16px inset as Settings (pane-body--padded) on every side, not just the left. (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e-list) .tree-list's padding shorthand (later, equal specificity) was zeroing the .tree-root top/bottom padding; move the vertical inset onto the two-class .tree-root.tree-list rule so it wins. Tree content now insets 16px on all sides. (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… top/bottom) (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Scroll body drops its top padding (the first child carries the 16px inset now) so a sticky header pins at the true top and rows stop peeking above it. - Zero the row--bleed margin-bottom on sticky summaries so stacked headers sit flush, and tighten the stack offset. (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… at every level Sticky headers no longer go translucent on hover (scrolled rows were showing through the nested one); hover is the same tinted gray composited opaquely. Focus ring is inset so it's fully visible instead of clipped to the bottom edge. (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
||
| // A modal (shortcuts/debug/source picker) owns keyboard input while open — | ||
| // don't let scene shortcuts (Esc-deselect, R, F) fire underneath it. | ||
| if (document.querySelector('[role="dialog"][aria-modal="true"]')) return; |
There was a problem hiding this comment.
Maybe there could be some global scene state value that this script uses? This feels pretty brittle.
There was a problem hiding this comment.
Agreed — the DOM query was brittle. Replaced it with a computed MODAL_OPEN signal in state/stores/ui.ts (SOURCE_PICKER.visible || SHORTCUTS_OPEN || DEBUG_OPEN); the handler now reads MODAL_OPEN.value. city/ already imports @/state freely, so this is idiomatic. Test updated to drive it via the signal (openShortcuts/closeShortcuts) instead of an injected DOM node. — 42ed71f
| describedBy={descId} | ||
| onCommit={(v) => commit({ ...map, [k]: v })} | ||
| /> | ||
| <span class="theme-hue-preview" style={{ background: `hsl(${value}, 80%, 55%)` }} /> |
There was a problem hiding this comment.
What's this harcoded preview value? why is it different from the file tag color value? Fix all of these please.
There was a problem hiding this comment.
Good catch. The swatch hardcoded hsl(hue, 80%, 55%) — unrelated to how files actually render. Files show the ExtensionBadge color hsl(hue, 60%, 35%), so the preview now uses a shared fileTagHsl() helper (with FILE_TAG_SATURATION/FILE_TAG_LIGHTNESS constants in utils/colors) and Badge.tsx derives its luminance-check values from the same constants — one source now. — 42ed71f
Replaces the brittle document.querySelector('[role=dialog]') check with a
computed MODAL_OPEN signal in the ui store (source picker / shortcuts / debug).
Review feedback. (#69)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The per-extension preview swatch hardcoded hsl(hue, 80%, 55%), different from the color files actually show. Now uses fileTagHsl() (hue, 60%, 35% — the same as the ExtensionBadge), sourced from one shared constant. Review feedback. (#69) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes #69
A focused redesign of the Settings pane and its surrounding chrome. Scope note: #69 as filed was "pure reorganization onto subtabs"; during design review the owner widened it to a full Settings interaction-model + IA improvement, plus sidebar-wide polish.
Settings pane → 3 tabs (World · Updates · Preview)
Control rows (layout B)
World accordions
MAX_COLLAPSE_DEPTH): sections + one subgroup level collapse; deeper groups render as flat labeled clusters, so the panel never becomes a maze of nested accordions.Shortcuts & Debug left Settings (they aren't settings)
?icon in the header top-right opens a Shortcuts modal.import.meta.env.DEVforjust dev, aVITE_DEBUGbuild arg, or?debugin the URL), opens a Debug modal.Both modals reuse the existing SourcePicker modal pattern (signal-driven via
state/stores/ui.ts, mounted fromApp).Interaction model
markAutosave) onLIVE_UPDATES+SYNTAX_THEME; the draft layer applies their writes immediately andReset allskips them. World stores stay draft/Save.Sidebar consistency (all panes)
Accessibility
aria-describedbyand kept OUT of the control's accessible name (screen readers announce "Max floors", then the description separately). Toggle/color rows no longer treat the description text as part of the click target.role="dialog"/aria-modal, close on Esc/backdrop, and focus their close button on open.Verification
just test(app + api) green — 2426 app tests, incl. guards for: stacked rows + inline descriptions, subtab structure + World-only footer, autosave write-through (instant apply, Reset-all skips), the accordion depth cap, the em-dash rule, both modals (open/close/Esc/backdrop),isDebugMode(), modal-open suppresses scene Esc-deselect, and description-out-of-name +aria-describedbywiring.just lintclean. layoutV4 snapshot untouched.🤖 Generated with Claude Code