Skip to content

feat(core): session-scoped dock UI state — restore open/selected dock, tab, and scroll across reloads - #527

Open
dvcolomban wants to merge 3 commits into
vitejs:mainfrom
dvcolomban:dvcol/dock-session-restore
Open

feat(core): session-scoped dock UI state — restore open/selected dock, tab, and scroll across reloads#527
dvcolomban wants to merge 3 commits into
vitejs:mainfrom
dvcolomban:dvcol/dock-session-restore

Conversation

@dvcolomban

@dvcolomban dvcolomban commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Why

Reloading the page (dev-server restart, an HMR full-reload, etc.) always dropped back to whatever dock is open by default, which gets annoying mid-debugging.

What changed

  • Restores the active spec tab and scroll position inside a ViewJsonRender dock — sessionStorage, keyed per dock and tab, since nothing server-side needs to see these and syncing them across tabs would be unwanted UX.
  • Restores which dock is open and selected — selectedId, folded into the existing localStorage-backed panelStore alongside open/mode/position/geometry, rather than a second store, since it's a browser-local singleton exactly like those already are.

Linked Issues

Additional context

selectedId living in the same object as open means it inherits #525's shared-state mirror for free, if that one lands — no separate wiring needed there.

Demo

Screen.Recording.2026-08-10.at.15.38.35.mov

…, tab, and scroll across reloads

Splits the dock panel's persisted state in two: config (geometry/position/
mode/inactiveTimeout) stays in localStorage (vite-devtools-dock-state,
cross-tab), while *state* (open, selected dock, active tab, scroll position)
moves to sessionStorage (vite-devtools-dock-session, per-tab) — a full page
reload no longer leaves the panel open-but-empty or resets scroll/tab.

- createDocksContext (context.ts) now backs open + selectedId with a single
  useSessionStorage store, merged with the geometry-only panelStore into the
  same DockPanelStorage shape every dock component already reads/writes
  through context.panel.store — only open's backing store changes.
- A restored selectedId is validated once at boot (once dock entries have
  actually loaded) against being a real selectable leaf — not a group, a
  subTabs anchor, or a stale id — clearing it without routing through
  switchEntry (which would force the panel back open). Past that one-time
  check, switchEntry may legitimately land on a group/anchor id itself (e.g.
  a subTabs anchor with no live member yet) — this doesn't re-litigate that.
- DockEmbedded now mirrors DockStandalone's own auto-select-first-entry
  boot guard, gated on the panel actually being open.
- ViewJsonRender provides its dock entry id for descendants to inject, and
  restores/persists its own scroll position per dock, debounced.
- New useUncontrolledValue composable: session-persisted fallback for a
  json-render element's own uncontrolled value, keyed by the injected dock
  id plus a signature of the element's static shape (works around
  FlatElement.key not being forwarded to registry components). Tabs now
  uses it instead of its hand-rolled ref; Select/TextInput/Switch — until
  now inert while unbound — get it too.

Two behavior changes ship with this, both intentional:
- Opening the docks in one tab no longer auto-opens them in another tab of
  the same page (open moved from localStorage to sessionStorage).
- The very first load after this ships, the panel starts closed even if a
  user's old vite-devtools-dock-state said open: true — that key's open is
  simply never read again.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
…ionStorage split

Folds selectedId into the existing localStorage-backed panelStore instead of a
separate sessionStorage-scoped store: it's a browser-local singleton exactly
like open/mode/position, so it belongs in the same object and gets mirrored
for free once that object is shared over RPC. Only the active spec tab and
scroll position stay genuinely per-tab (still sessionStorage, via each
ViewJsonRender's own independent key) since nothing server-side needs them
and syncing them across tabs would be unwanted UX.
@pkg-pr-new

pkg-pr-new Bot commented Aug 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@vitejs/devtools

npm i https://pkg.pr.new/@vitejs/devtools@527

@vitejs/devtools-kit

npm i https://pkg.pr.new/@vitejs/devtools-kit@527

@vitejs/devtools-oxc

npm i https://pkg.pr.new/@vitejs/devtools-oxc@527

@vitejs/devtools-rolldown

npm i https://pkg.pr.new/@vitejs/devtools-rolldown@527

@vitejs/devtools-vite

npm i https://pkg.pr.new/@vitejs/devtools-vite@527

@vitejs/devtools-vitest

npm i https://pkg.pr.new/@vitejs/devtools-vitest@527

commit: f3733c8

@dvcolomban
dvcolomban marked this pull request as ready for review August 10, 2026 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant