You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Coordinate with: #487 so both docked sides share one centre-width policy and resize lifecycle.
Goal
Replace the current collection of unrelated desktop right-side overlays and panes with one foldable, shell-owned right inspector for contextual content:
cell detail;
rows/detail viewer content;
function, aggregate-function, data-type, and other Reference documentation;
future contextual diagnostic/detail tools that belong in the same right-side surface.
On desktop, the inspector is docked beside the Query/Dashboard surface, consumes normal layout width, and pushes the centre surface left. It never covers the editor or Dashboard with a backdrop.
Mobile keeps its existing mobile/full-screen/modal presentation where needed and is not redesigned by this issue.
Status
Architecture is decided — vanilla rendering per docs/ADR-0004-ui-shell.md (#577/#578 closed). Implementation is routed through #586, which already delivers most of this issue's shell skeleton: the docked inspectorHost slot in .main-row, the shared SurfaceLifecycle open/close/resize/Escape helper, and the rightInspectorPx width-preference migration. This issue is the product contract that host carries — tool registry, per-tool validity/generation rules, tool navigation, accessibility, and mobile behavior — which #586 deliberately does not define (see #586's non-goals: tool registry/tabs, contextual auto-open triggers, chevron UI).
Final desktop shell geometry
The application header remains full width above the shell.
On desktop: exactly one right-inspector host exists per application shell/document; no inspector tool creates a backdrop, covers the centre surface, or traps focus; only one inspector tool is visible at a time; valid inactive tool state may remain cached for the session.
Fold control
Use one narrow vertical chevron attached to the inspector's left boundary.
folded: chevron points left, meaning open
open: chevron points right, meaning fold closed
The chevron remains visible at the right edge when folded and moves with the inspector's left edge when open. Toggling it does not discard valid active-tool state; reopening restores the most recently active still-valid tool; contextual triggers still open and activate the appropriate tool automatically. Do not add redundant top-bar open controls solely for panel visibility, and remove redundant per-tool desktop X controls where the panel-level fold control replaces them. The accessible label reflects the current action, such as Open details panel or Close details panel.
The exact API may evolve as it lands on #586's SurfaceLifecycle + docked slot, but the ownership boundary remains:
Shell owns: the single inspector host (#586: inspectorHost); fold/open geometry (#586: hidden-state + resize handle); active-tool selection; tool selector chrome; width preference and resize separator (#586: rightInspectorPx + compatibility migration); interaction with left navigation and the centre minimum; high-level teardown on workspace replacement and sign-out.
Tool controllers own: typed payloads; content/domain rendering state; result or catalog validity; async generation tokens; tool-specific back stacks, embedded viewers, and actions; initiator metadata used for safe focus restoration.
Feature call sites request a tool through the shell-facing API. They must not append independent desktop panels/backdrops or write shell widths.
Each inspector subtree has one owner. Do not leave parallel independent inspector implementations active behind different triggers.
Tool navigation
When more than one valid tool has session content, show compact accessible tabs or an equivalent selector:
Cell | Rows | Reference
Rules: only tools with live/available content appear; opening a tool unfolds and activates it; a new Cell request replaces the Cell payload rather than adding another Cell tab; a new Reference target replaces the current target while preserving Reference's own valid back stack semantics; switching tools does not destroy valid inactive tool state; closing one tool activates the most recently used valid remaining tool; if no valid tool remains, fold the inspector; the fold chevron remains panel-level chrome, not a tool tab.
Cell tool
Clicking a supported result cell opens/unfolds Cell, preserving formatting, copy, and structured-value rendering. Bind validity to a stable result-generation token plus tab/query identity: result replacement, rerun, tab closure, workspace switch, or sign-out invalidates stale Cell state, and stale asynchronous formatting/render work cannot repaint a newer payload. No desktop backdrop or focus trap; opening Cell does not unnecessarily steal editor selection; folding/reopening preserves Cell only while its origin remains valid. A stale cell must never appear as though it belongs to the current result.
Rows/detail tool
Migrate passive rows/detail consumers that currently share cell-drawer chrome, preserving table/detail rendering, paging, copy, and result semantics. Bind validity to the originating result generation: result replacement, tab closure, workspace switch, or sign-out invalidates it. One Rows tool instance exists per shell and a new request replaces its payload. Keep truly modal decisions modal; move only passive inspection content into the inspector.
Reference tool
Preserve the existing mature behavior: loading, found, missing, unavailable, and Retry states; related/alias navigation and disambiguation; a bounded back stack; safe Markdown rendering; embedded code-viewer lifecycle; stale lookup generation protection; catalog capability semantics; contextual opening from F1, hover, completion, schema references, and future Reference actions.
Reference validity differs from result-bound tools: Query-tab switching does not invalidate it; Query/Dashboard switching may preserve it; workspace changes preserve it only when connection/catalog identity and target remain valid; connection change, catalog invalidation, or sign-out closes it and destroys embedded viewers; Reference back-stack state remains owned by the Reference controller, not by the shell.
Width and preference migration
#586 delivers this migration as shared shell substrate: the inspectorHost slot, its resize handle, and the canonical rightInspectorPx write.
Suggested bounds remain subject to real-browser validation:
Final clamping must account for viewport width, #487 left-navigation presentation, active centre surface, and the useful centre minimum. A temporary viewport clamp must not destroy the saved preferred inspector width.
Replace separate browser preferences (cellDrawerPx, docPanePx) with rightInspectorPx. Compatibility read order: valid rightInspectorPx, then valid docPanePx, then valid cellDrawerPx, then default. Write only rightInspectorPx after migration. Width is a browser preference and must not enter workspace storage, portable bundles, or share links. Fold state and active payload are session state.
The left-edge separator must support pointer and keyboard resizing and integrate with #586's shell geometry. Do not use arbitrary delayed resize events.
Surface, workspace, and teardown behavior
Query -> Dashboard: preserve valid Reference; invalidate or hide Cell/Rows according to result ownership; never destroy Query merely to show/hide the inspector; reflow Dashboard when the inspector opens, folds, or resizes.
Dashboard -> Query: preserve valid Reference; restore Cell/Rows only if the same originating result is still valid; reflow CodeMirror/results through normal layout hooks.
Workspace switch: invalidate all result-bound tools; clear initiators belonging to the old workspace/shell; preserve Reference only when explicitly safe; stale callbacks from the previous workspace cannot repaint the inspector.
Sign-out/auth loss: invalidate every tool; destroy embedded viewers and listeners; cancel active resize and pending structural transition work; remove/fold inspector content before Login; late callbacks cannot remount it.
Focus and Escape contract
The desktop inspector is non-modal.
focus moves normally between inspector, editor, and results; there is no focus trap;
Escape while focus is inside the inspector folds it, consumed so it does not also cancel a query; Escape outside keeps existing global semantics;
after a fold/unfold/tool-switch completes, focus settles on a visible, sensible control — the chevron when folding, the newly active tool's content/tab when unfolding or switching, or the initiator when a tool activated in response to one;
the chevron, tabs, and separator remain keyboard reachable with visible focus;
transient intermediate frames (mid-resize, mid-fold) are unspecified — no requirement to preserve focus mid-gesture; a transient focus on <body> during a drag is acceptable;
teardown (workspace switch, sign-out, unmount) must dispose pending focus/transition work — the only disposal rule.
The implementation builds on #586's SurfaceLifecycle (escapePolicy, returnFocusTo) rather than a bespoke per-tool mechanism.
Accessibility
Inspector host:
role="complementary"
aria-label="Details and reference"
Tool selector: proper tabs/tablist semantics when tabs are used; active state is announced; unavailable tools are absent rather than disabled placeholders.
Keys: Left/Right Arrow resize by a small step; Shift+Left/Right resize by a larger step; Home uses minimum width; End uses the current maximum width.
Chevron direction, visible state, and accessible label remain synchronized.
Mobile
At the existing mobile breakpoint: do not apply desktop docked geometry; retain existing mobile/full-screen/modal cell and detail behavior where required; Reference may use the established mobile pane pattern; reuse domain/tool controllers where practical without forcing one desktop/mobile renderer; preserve desktop width preference without narrowing mobile permanently.
Main-surface reflow
Opening, folding, tool replacement, or resizing must correctly reflow CodeMirror/editor measurement, result columns and virtualized rows, editor/results split layout, Dashboard grids/flow layouts/charts/sticky bars, and detached/fullscreen views where applicable. Presentation changes must not cancel a running query or Dashboard execution.
Implementation sequence
Building on #586's docked host and SurfaceLifecycle:
Move Reference into the host while preserving its mature behavior.
Move Cell and remove its desktop backdrop/focus trap.
Keep mobile-specific composition behind the existing breakpoint.
Verify simultaneous left/right docking and Query/Dashboard behavior.
Record production LOC removed/added and bundle delta in the PR description.
Tests
Shell and fold state: one inspector host per shell/document; folded state leaves only the chevron and consumes no panel width; open state reduces centre width; chevron direction and label are correct; folding preserves valid tool state; no desktop backdrop or focus trap; left and right panels coexist within centre-width limits.
Tool selection and validity: Cell/Rows/Reference open, unfold, and activate correctly; replacing one payload does not destroy other valid tool state; closing one activates the most recent valid remaining tool; no valid tools folds the inspector; duplicate independent panels cannot be created; result rerun/tab/workspace removal invalidates Cell/Rows; stale async work cannot repaint; Reference back stack, aliases, disambiguation, Retry, and embedded-viewer disposal remain correct.
Width and migration: rightInspectorPx restores and clamps; compatibility reads docPanePx, then cellDrawerPx; only the unified key is written; viewport clamping does not overwrite preferred width; pointer/keyboard resize behavior matches; cancellation, blur, remount, and sign-out leave no listeners or stale work.
Focus, accessibility, and reflow: Escape inside folds only the inspector and does not cancel a query; safe initiator restoration and fallbacks are proven in Chromium and WebKit (final settled state only); complementary, tabs, separator, and chevron semantics are correct; editor/results/Dashboard reflow correctly; opening/folding never cancels execution; Query state survives Dashboard round trips; no mobile, detached, or fullscreen regression.
Implementation notes: mid-resize/mid-fold focus racing is guidance from #586's SurfaceLifecycle, not an acceptance gate here.
Acceptance criteria
Desktop has one foldable right inspector for Cell, Rows/detail, and Reference.
The inspector is docked, consumes layout width, and never overlays the desktop editor/Dashboard.
One chevron controls the panel and redundant desktop panel controls are removed.
Contextual triggers automatically open the correct tool.
Result-bound tools cannot display stale content.
Reference retains navigation, async safety, and embedded-viewer lifecycle.
Width is unified into one browser preference with deterministic migration.
Left navigation, Query, and Dashboard share a coherent width/reflow policy.
Mobile behavior remains appropriate and is not permanently narrowed.
Focus and Escape behavior are safe, non-modal, and proven in Chromium and WebKit once a transition completes.
Workspace switch and sign-out cannot leak tools, listeners, initiators, or stale callbacks.
Non-goals
Moving unrelated modal confirmations into the inspector.
Showing several inspector tools side by side.
Persisting inspector payloads in workspace data.
Redesigning Cell or Reference content beyond host integration.
Replacing detached/fullscreen views serving a distinct purpose.
Redesigning mobile navigation.
Requirements on transient mid-gesture focus behavior.
Inherited from #586 (phase 1 of #593) — two deliberate deferrals
#586 delivered this issue's substrate (the SurfaceLifecycle primitive and the docked inspectorHost slot). Two focus/modality questions surfaced in review of PR #596 and were
deliberately deferred to this issue rather than settled there, because both need this issue's
fuller focus contract to resolve safely. Acceptance criteria 9 and 10 above already cover them;
this records the concrete starting state so it is not re-derived.
Docked Cell and Rows use escapePolicy: 'always' while non-modal. src/ui/results.ts:506 and :1305. Because refactor(shell): one SurfaceLifecycle primitive and a docked right-inspector slot #586 deliberately dropped keyboard-owner
acquisition on the docked path (the pre-refactor(shell): one SurfaceLifecycle primitive and a docked right-inspector slot #586 modal cell drawer disabled every app shortcut
while open — src/ui/shortcuts.ts bails when ownsKeyboard(app)), Escape is captured by the
inspector even when focus is in the editor or results, so it cannot reach the global
running-query cancel action. tests/unit/results.test.ts currently locks that in.
Reference already uses 'focus-inside' (src/ui/doc-pane.ts:204).
Switching Cell/Rows to 'focus-inside' is the likely right answer, but it requires
auto-focus-into-panel-on-open as well — otherwise "click a cell, immediately press Escape"
stops working, since focus is still outside the panel. That auto-focus behaviour is part of
this issue's contract, not refactor(shell): one SurfaceLifecycle primitive and a docked right-inspector slot #586's, which is why it was deferred.
Resolve either by restoring modal ownership for the full-screen presentation, or by making
the lifecycle presentation-aware (one policy for the desktop dock, another for the mobile
full-screen surface) — the latter is probably the cleaner primitive extension, and SurfaceLifecycle's escapePolicy parameter is the natural place for it.
Both were reviewed and consciously accepted as #586's shipped state by the repo owner at that
PR's merge gate; neither is an oversight to rediscover.
Depends on: #425
Coordinate with: #487 so both docked sides share one centre-width policy and resize lifecycle.
Goal
Replace the current collection of unrelated desktop right-side overlays and panes with one foldable, shell-owned right inspector for contextual content:
On desktop, the inspector is docked beside the Query/Dashboard surface, consumes normal layout width, and pushes the centre surface left. It never covers the editor or Dashboard with a backdrop.
Mobile keeps its existing mobile/full-screen/modal presentation where needed and is not redesigned by this issue.
Status
Architecture is decided — vanilla rendering per
docs/ADR-0004-ui-shell.md(#577/#578 closed). Implementation is routed through #586, which already delivers most of this issue's shell skeleton: the dockedinspectorHostslot in.main-row, the sharedSurfaceLifecycleopen/close/resize/Escape helper, and therightInspectorPxwidth-preference migration. This issue is the product contract that host carries — tool registry, per-tool validity/generation rules, tool navigation, accessibility, and mobile behavior — which #586 deliberately does not define (see #586's non-goals: tool registry/tabs, contextual auto-open triggers, chevron UI).Final desktop shell geometry
The application header remains full width above the shell.
On desktop: exactly one right-inspector host exists per application shell/document; no inspector tool creates a backdrop, covers the centre surface, or traps focus; only one inspector tool is visible at a time; valid inactive tool state may remain cached for the session.
Fold control
Use one narrow vertical chevron attached to the inspector's left boundary.
The chevron remains visible at the right edge when folded and moves with the inspector's left edge when open. Toggling it does not discard valid active-tool state; reopening restores the most recently active still-valid tool; contextual triggers still open and activate the appropriate tool automatically. Do not add redundant top-bar open controls solely for panel visibility, and remove redundant per-tool desktop
Xcontrols where the panel-level fold control replaces them. The accessible label reflects the current action, such asOpen details panelorClose details panel.Tool registry and ownership
Use a narrow typed registry, initially:
Conceptual shell-facing operations:
The exact API may evolve as it lands on #586's
SurfaceLifecycle+ docked slot, but the ownership boundary remains:Shell owns: the single inspector host (#586:
inspectorHost); fold/open geometry (#586: hidden-state + resize handle); active-tool selection; tool selector chrome; width preference and resize separator (#586:rightInspectorPx+ compatibility migration); interaction with left navigation and the centre minimum; high-level teardown on workspace replacement and sign-out.Tool controllers own: typed payloads; content/domain rendering state; result or catalog validity; async generation tokens; tool-specific back stacks, embedded viewers, and actions; initiator metadata used for safe focus restoration.
Feature call sites request a tool through the shell-facing API. They must not append independent desktop panels/backdrops or write shell widths.
Each inspector subtree has one owner. Do not leave parallel independent inspector implementations active behind different triggers.
Tool navigation
When more than one valid tool has session content, show compact accessible tabs or an equivalent selector:
Rules: only tools with live/available content appear; opening a tool unfolds and activates it; a new Cell request replaces the Cell payload rather than adding another Cell tab; a new Reference target replaces the current target while preserving Reference's own valid back stack semantics; switching tools does not destroy valid inactive tool state; closing one tool activates the most recently used valid remaining tool; if no valid tool remains, fold the inspector; the fold chevron remains panel-level chrome, not a tool tab.
Cell tool
Clicking a supported result cell opens/unfolds Cell, preserving formatting, copy, and structured-value rendering. Bind validity to a stable result-generation token plus tab/query identity: result replacement, rerun, tab closure, workspace switch, or sign-out invalidates stale Cell state, and stale asynchronous formatting/render work cannot repaint a newer payload. No desktop backdrop or focus trap; opening Cell does not unnecessarily steal editor selection; folding/reopening preserves Cell only while its origin remains valid. A stale cell must never appear as though it belongs to the current result.
Rows/detail tool
Migrate passive rows/detail consumers that currently share cell-drawer chrome, preserving table/detail rendering, paging, copy, and result semantics. Bind validity to the originating result generation: result replacement, tab closure, workspace switch, or sign-out invalidates it. One Rows tool instance exists per shell and a new request replaces its payload. Keep truly modal decisions modal; move only passive inspection content into the inspector.
Reference tool
Preserve the existing mature behavior: loading, found, missing, unavailable, and Retry states; related/alias navigation and disambiguation; a bounded back stack; safe Markdown rendering; embedded code-viewer lifecycle; stale lookup generation protection; catalog capability semantics; contextual opening from F1, hover, completion, schema references, and future Reference actions.
Reference validity differs from result-bound tools: Query-tab switching does not invalidate it; Query/Dashboard switching may preserve it; workspace changes preserve it only when connection/catalog identity and target remain valid; connection change, catalog invalidation, or sign-out closes it and destroys embedded viewers; Reference back-stack state remains owned by the Reference controller, not by the shell.
Width and preference migration
#586 delivers this migration as shared shell substrate: the
inspectorHostslot, its resize handle, and the canonicalrightInspectorPxwrite.Suggested bounds remain subject to real-browser validation:
Final clamping must account for viewport width, #487 left-navigation presentation, active centre surface, and the useful centre minimum. A temporary viewport clamp must not destroy the saved preferred inspector width.
Replace separate browser preferences (
cellDrawerPx,docPanePx) withrightInspectorPx. Compatibility read order: validrightInspectorPx, then validdocPanePx, then validcellDrawerPx, then default. Write onlyrightInspectorPxafter migration. Width is a browser preference and must not enter workspace storage, portable bundles, or share links. Fold state and active payload are session state.The left-edge separator must support pointer and keyboard resizing and integrate with #586's shell geometry. Do not use arbitrary delayed resize events.
Surface, workspace, and teardown behavior
Query -> Dashboard: preserve valid Reference; invalidate or hide Cell/Rows according to result ownership; never destroy Query merely to show/hide the inspector; reflow Dashboard when the inspector opens, folds, or resizes.
Dashboard -> Query: preserve valid Reference; restore Cell/Rows only if the same originating result is still valid; reflow CodeMirror/results through normal layout hooks.
Workspace switch: invalidate all result-bound tools; clear initiators belonging to the old workspace/shell; preserve Reference only when explicitly safe; stale callbacks from the previous workspace cannot repaint the inspector.
Sign-out/auth loss: invalidate every tool; destroy embedded viewers and listeners; cancel active resize and pending structural transition work; remove/fold inspector content before Login; late callbacks cannot remount it.
Focus and Escape contract
The desktop inspector is non-modal.
<body>during a drag is acceptable;The implementation builds on #586's
SurfaceLifecycle(escapePolicy,returnFocusTo) rather than a bespoke per-tool mechanism.Accessibility
Inspector host:
Tool selector: proper tabs/tablist semantics when tabs are used; active state is announced; unavailable tools are absent rather than disabled placeholders.
Resize separator:
Keys: Left/Right Arrow resize by a small step; Shift+Left/Right resize by a larger step; Home uses minimum width; End uses the current maximum width.
Chevron direction, visible state, and accessible label remain synchronized.
Mobile
At the existing mobile breakpoint: do not apply desktop docked geometry; retain existing mobile/full-screen/modal cell and detail behavior where required; Reference may use the established mobile pane pattern; reuse domain/tool controllers where practical without forcing one desktop/mobile renderer; preserve desktop width preference without narrowing mobile permanently.
Main-surface reflow
Opening, folding, tool replacement, or resizing must correctly reflow CodeMirror/editor measurement, result columns and virtualized rows, editor/results split layout, Dashboard grids/flow layouts/charts/sticky bars, and detached/fullscreen views where applicable. Presentation changes must not cancel a running query or Dashboard execution.
Implementation sequence
Building on #586's docked host and
SurfaceLifecycle:Tests
Shell and fold state: one inspector host per shell/document; folded state leaves only the chevron and consumes no panel width; open state reduces centre width; chevron direction and label are correct; folding preserves valid tool state; no desktop backdrop or focus trap; left and right panels coexist within centre-width limits.
Tool selection and validity: Cell/Rows/Reference open, unfold, and activate correctly; replacing one payload does not destroy other valid tool state; closing one activates the most recent valid remaining tool; no valid tools folds the inspector; duplicate independent panels cannot be created; result rerun/tab/workspace removal invalidates Cell/Rows; stale async work cannot repaint; Reference back stack, aliases, disambiguation, Retry, and embedded-viewer disposal remain correct.
Width and migration:
rightInspectorPxrestores and clamps; compatibility readsdocPanePx, thencellDrawerPx; only the unified key is written; viewport clamping does not overwrite preferred width; pointer/keyboard resize behavior matches; cancellation, blur, remount, and sign-out leave no listeners or stale work.Focus, accessibility, and reflow: Escape inside folds only the inspector and does not cancel a query; safe initiator restoration and fallbacks are proven in Chromium and WebKit (final settled state only); complementary, tabs, separator, and chevron semantics are correct; editor/results/Dashboard reflow correctly; opening/folding never cancels execution; Query state survives Dashboard round trips; no mobile, detached, or fullscreen regression.
Implementation notes: mid-resize/mid-fold focus racing is guidance from #586's
SurfaceLifecycle, not an acceptance gate here.Acceptance criteria
Non-goals
Inherited from #586 (phase 1 of #593) — two deliberate deferrals
#586 delivered this issue's substrate (the
SurfaceLifecycleprimitive and the dockedinspectorHostslot). Two focus/modality questions surfaced in review of PR #596 and weredeliberately deferred to this issue rather than settled there, because both need this issue's
fuller focus contract to resolve safely. Acceptance criteria 9 and 10 above already cover them;
this records the concrete starting state so it is not re-derived.
Docked Cell and Rows use
escapePolicy: 'always'while non-modal.src/ui/results.ts:506and:1305. Because refactor(shell): one SurfaceLifecycle primitive and a docked right-inspector slot #586 deliberately dropped keyboard-owneracquisition on the docked path (the pre-refactor(shell): one SurfaceLifecycle primitive and a docked right-inspector slot #586 modal cell drawer disabled every app shortcut
while open —
src/ui/shortcuts.tsbails whenownsKeyboard(app)), Escape is captured by theinspector even when focus is in the editor or results, so it cannot reach the global
running-query cancel action.
tests/unit/results.test.tscurrently locks that in.Reference already uses
'focus-inside'(src/ui/doc-pane.ts:204).Switching Cell/Rows to
'focus-inside'is the likely right answer, but it requiresauto-focus-into-panel-on-open as well — otherwise "click a cell, immediately press Escape"
stops working, since focus is still outside the panel. That auto-focus behaviour is part of
this issue's contract, not refactor(shell): one SurfaceLifecycle primitive and a docked right-inspector slot #586's, which is why it was deferred.
Mobile is full-screen but non-modal.
src/styles.css:3339turns.inspector-hostintoposition: fixed; inset: 0; z-index: 60; width: 100vwbelow the mobile breakpoint, while thesame docked code path acquires no keyboard owner. So a visually full-screen panel leaves
hardware-keyboard application shortcuts live behind it. Pre-refactor(shell): one SurfaceLifecycle primitive and a docked right-inspector slot #586 the mobile cell drawer was
full-screen and modal, so this is a real mobile behaviour change that shipped with refactor(shell): one SurfaceLifecycle primitive and a docked right-inspector slot #586
even though mobile behaviour was one of its explicit non-goals.
Resolve either by restoring modal ownership for the full-screen presentation, or by making
the lifecycle presentation-aware (one policy for the desktop dock, another for the mobile
full-screen surface) — the latter is probably the cleaner primitive extension, and
SurfaceLifecycle'sescapePolicyparameter is the natural place for it.Both were reviewed and consciously accepted as #586's shipped state by the repo owner at that
PR's merge gate; neither is an oversight to rediscover.
Related
SurfaceLifecycleprimitive and dockedinspectorHostslot this issue builds on.