Skip to content

Add a drag-foldable desktop left navigation with rail and focused drawers #487

Description

@BorisTyshkevich

Goal

Complete the drag-foldable desktop left navigation, preserving its proven layout/state model, and finish the remaining interaction, accessibility, reflow, and integration work on the decided vanilla UI shell.

Desktop presentations remain:

wide
  [two-pane sidebar] [main Query/Dashboard surface] [optional right inspector]

rail
  [48px rail] [main Query/Dashboard surface] [optional right inspector]

focused drawer
  [48px rail] [one docked navigation section] [main surface] [optional right inspector]

The left navigation participates in normal layout. It is never a desktop overlay or backdrop. Mobile keeps its segmented/bottom-navigation presentation and preserves desktop preferences for return to desktop.

Status

Architecture is decided — vanilla rendering per docs/ADR-0004-ui-shell.md (#577/#578 closed). Implementation routes through #587 (side-panel registry) and #586 (SurfaceLifecycle primitive + docked right-inspector slot, shared geometry with #488).

Phases 1–3 (PRs #571/#573/#574) were merged, then removed from main by the 2026-08-03 baseline reset. Restart from current main, salvaging proven work rather than re-deriving it: feat/left-nav-layout-core-487p1 (pure core/left-nav-layout.ts reducer, three-layout resize session), feat/nav-section-registry-487p2 (registry decisions — icon-as-factory, separate accessibleLabel, pane-scoped showSection, load-boundary key bridge — now being adapted by #587), feat/left-rail-focused-drawer-487p3 (rail/focused-drawer geometry, 180px drawer floor, per-section filters). The 2026-07-29 ship-log comment on this issue remains the historical record of the phase 1–3 implementation.

Proven foundations to salvage

Pull these from the branches above rather than re-deriving them:

  • Pure semantic layout coresrc/core/left-nav-layout.ts owns semantic mode, focused-section coherence, thresholds, pointer/keyboard reducers, viewport clamping, and preferred/proposed/effective resize-session rules; it stays DOM-free.
  • Persistent section ownership — Databases, Dashboards, Library, and History each keep one logical host/state owner; presentation changes must not recreate search, expansion, loaded data, scroll, edit, or selection state.
  • Gesture memory and preference safety — resize sessions keep persisted preference, raw proposal, and effective viewport-clamped layout separate; temporary viewport constraints and pointer sampling must not corrupt the saved preferred width.
  • One presentation ownerrefactor(shell): a side-panel registry so adding a navigation panel is a one-file change #587's side-panel registry (section ownership) and refactor(shell): one SurfaceLifecycle primitive and a docked right-inspector slot #586's docked layout (shared geometry with the right inspector) together own navigation geometry, visibility, pane exposure, separator state, and ARIA presentation; no second, competing owner for the same subtree.

Remaining product work

1. Focus settlement after structural transitions

After a transition that hides or structurally reflows the focused navigation source completes, focus must settle on a visible, semantically sensible destination:

Transition Destination
wide or focused drawer -> bare rail matching rail launcher; separator fallback
rail/focused drawer -> wide matching live wide-mode tab; separator fallback
focused drawer closed by Escape or launcher matching rail launcher
desktop navigation -> mobile, including Tables active mobile bottom-navigation button
mobile Tables -> Editor/Results selected bottom-navigation button
mobile Query -> Dashboard visible Editor route button
cancelled gesture or no semantic change preserve current user focus

Transient intermediate frames (mid-drag, mid-fold) are unspecified — no requirement to capture intent before a destructive frame, freeze focus during drag repaint, or resolve stale-vs-superseded transition races. A transient focus on <body> during a drag is acceptable.

Teardown — remount, workspace switch, sign-out, or shell unmount — must dispose any pending focus/transition work; that is the only disposal rule.

Keep: no focus traps on the navigation surfaces, full keyboard reachability, visible focus, and Escape semantics consumed by the surface it closes.

The implementation may be a small imperative service or scheduler; it must not become a global application focus manager.

2. Separator discoverability

Closing a focused drawer relocates the 7px separator from the drawer edge to the rail edge. Make the new drag location discoverable — a brief visual emphasis or geometry transition, honoring prefers-reduced-motion — without a separate collapse button, without stealing keyboard focus, and without making animation necessary for correctness. Keep the separator keyboard reachable with visible focus.

3. Centre and right-panel reflow

Reflow every active centre surface — CodeMirror/editor geometry, result table and virtualized rows, editor/results split layout, Dashboard grids/flow layouts/charts/sticky bars, and the optional right inspector docked via #586 — through established observers or explicit layout hooks. The width budget must include left navigation, the optional right inspector, and a documented useful centre minimum; viewport clamping must not overwrite the preferred left width.

4. #428 drag-and-drop integration

During a Library-query drag in rail mode: bounded hover on Dashboards opens the docked Dashboards drawer through the existing semantic controller seam; repeated hover notifications stay idempotent; drops work on Dashboard, Panels, and Filters targets; leaving/cancelling clears temporary hover state; semantic opening preempts an active resize session; the drawer never becomes an overlay.

5. Documentation and final verification

Document wide/rail/focused-drawer behavior and separator keyboard controls, and mobile preference preservation; reconcile roadmap/changelog references including #586/#587; verify simultaneous left/right docking with #488 in Chromium and WebKit.

Tests

Unit coverage

  • semantic origin/destination transition matrix (final settled state only);
  • disposal cancels pending focus/transition work on teardown;
  • preferred width survives effective viewport clamps;
  • separator keyboard and pointer behavior remain equivalent.

Real-browser coverage

Chromium and WebKit must cover:

  • wide search/tree focus -> fold -> matching rail launcher;
  • focused drawer -> fold -> matching launcher;
  • drawer/rail -> wide -> matching live tab;
  • desktop -> mobile Editor, Results, and Tables;
  • mobile Tables -> Editor/Results;
  • mobile Query -> Dashboard;
  • Escape/launcher close;
  • Home/End separator behavior;
  • a completed structural transition never ends on <body> when a semantic destination exists;
  • centre reflow with and without the right inspector.

Implementation notes: multi-frame drag/focus racing and stale-vs-current transition ordering are useful guidance if the pre-reset branches' approach is worth reusing, but they are not acceptance gates.

Acceptance criteria

  1. Wide, rail, and focused-drawer presentations follow the documented geometry and never use a desktop overlay.
  2. All four sections preserve their domain/UI state across presentation changes.
  3. Mode and width preferences decode safely, survive viewport clamps, and remain outside workspace/query/Dashboard documents.
  4. Pointer and keyboard separator behavior is deterministic and does not corrupt stored preference.
  5. Rail clicks, Escape, drag hover, and programmatic reveals cannot be overwritten by stale resize work.
  6. Mobile never renders the desktop rail/drawer and preserves desktop preference for return to desktop.
  7. Structural transitions settle focus on a visible semantic destination once complete.
  8. The relocated separator is discoverable without mandatory motion or focus stealing.
  9. Left/right docked panels and every centre surface reflow while retaining the documented centre minimum.
  10. Add Library drag assignment to Dashboard panels and variables #428 drag-hover/drop integration works in rail mode without overlays or state flapping.
  11. Chromium and WebKit prove CSS visibility, final-state focus settlement, mobile/surface transitions, and combined left/right layout.

Non-goals

  • A desktop overlay drawer or backdrop.
  • Duplicate navigation hosts or stores.
  • DOM focus knowledge in the pure layout reducer.
  • Arbitrary setTimeout focus or resize guesses.
  • Animation as a correctness dependency.
  • Automatically reopening the last focused drawer after reload.
  • Requirements on transient mid-gesture focus behavior.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions