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
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,40 @@ auto-generated per-PR notes; this file is the curated, human-readable history.

## [Unreleased]

### Added
- **The desktop left navigation's layout core and preferences** (#487, phase 1 of
4). A new pure `src/core/left-nav-layout.ts` owns every layout decision the
foldable left navigation needs: the named constants and thresholds, the
explicit `'wide' | 'rail'` mode, the hysteresis reducer that maps a proposed
width onto the next mode, the keyboard separator resolver, rail activation, the
separator's ARIA range, and the mobile projection that makes a phone ignore
rail mode without discarding the preference. Two new browser preferences back
it (`asb:leftNavMode`, `asb:leftNavDrawerPx`); the wide sidebar's width
deliberately stays on the existing `asb:sidebarPx` rather than gaining a second
owner, and the focused section is session state that does not reopen after a
reload. Hysteresis comes from the threshold *pair* — folding needs a proposal
below 140px and restoring one above 260px — so no single pointer pixel can
oscillate the mode, and the keyboard path routes through the same reducer as
the pointer path rather than reimplementing it. Every function takes the
navigation's proposed *total* width, which is what keeps a drag continuous
across a mode change: handing the reducer a mode-relative width instead made a
monotone rightward drag snap the navigation 108px backwards on the frame a
drawer converted to the sidebar. Arrow keys resize within a band and perform
the band edge's semantic transition, because an arrow's *relative* step cannot
cross a dead zone wider than itself — leaving that relative stranded a bare
rail's `ArrowRight` and a floored sidebar's `ArrowLeft` permanently, while
`aria-valuemin: 48` was advertised throughout. Opening a section is a separate
operation from the click toggle, since #428's bounded drag-hover re-asserts
intent repeatedly and a toggle would flap the drawer shut. Every reducer
normalizes its input, so the "a focused drawer exists only in rail mode"
invariant is an unconditional postcondition rather than a precondition the
caller has to honour. **No user-visible change yet**: the rail, the docked
focused drawer and the resize separator arrive in phase 3.
- The sidebar's `'col'` drag axis now clamps through the same
`LEFT_PANEL_MIN_PX`/`LEFT_PANEL_MAX_PX` constants as the load path, instead of
repeating `180`/`420` as literals (#487). Behaviour is unchanged; it removes
the second owner of a range whose whole point is having one.

### Changed
- **`VariableBarApp`'s shared activation port is now caller-neutral** (#478).
`state.filterActive`/`params.saveFilterActive` — named after Workbench
Expand All @@ -25,6 +59,20 @@ auto-generated per-PR notes; this file is the curated, human-readable history.
adapter refactor — no user-visible behavior changes.

### Fixed
- **A corrupt `asb:sidebarPx` decodes to the default width instead of `NaN`**
(#487). The width decoded through a bare `clamp(parseInt(stored), 180, 420)`,
and `clamp` is not NaN-safe (`Math.max(180, NaN)` is `NaN`), so a non-numeric
stored value would reach the DOM as `width: NaNpx` — which the browser drops,
collapsing the sidebar with nothing to explain why, and the bad value would
persist across reloads. It now falls back to the documented 248px default, and
decoding requires the *whole* stored string to be a finite number — `parseInt`
accepted a numeric prefix, so a truncated write or a hand-edited `"200px"`
decoded to a plausible-looking width while the contract promised the default.
Hardening rather than a reproducible user-visible bug: no code path in the app
writes a malformed value (a real drag always carries a finite `clientX`), so
reaching it takes a hand-edited or foreign-origin `localStorage` entry. The
same NaN hole in `editorPct`/`sideSplitPct`/`cellDrawerPx`/`docPanePx` is
tracked separately in #570.
- **The Dashboard tree no longer reveals two rows' pencil/trash actions at
once, and its `· N` count now sits inline after the label** (#568). The
hover/focus reveal rule (`.dash-tree-row:focus-within .dash-tree-act`)
Expand Down
6 changes: 5 additions & 1 deletion src/application/app-preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ export type PreferenceKey =
| 'sidePanel' | 'resultRowLimit'
// #313 — the documentation pane's own persisted resize width, a sibling of
// cellDrawerPx (never shared with it — see splitters.ts's 'docPane' axis).
| 'docPanePx';
| 'docPanePx'
// #487 — the desktop left navigation's semantic mode and its focused drawer's
// width. The WIDE sidebar width stays `sidebarPx` above: `core/left-nav-layout.ts`
// reuses that preference rather than introducing a second owner of one width.
| 'leftNavMode' | 'leftNavDrawerPx';

/** The one state field this service reads/writes (`toggleTheme` only) — a
* plain settable property, not a signal (matches `AppState.theme`). */
Expand Down
Loading