feat(devtools): project each DevTools tab as its own Vite DevTools dock entry#1035
Conversation
…ck entry Replace the single Nuxt DevTools hub iframe with one iframe dock entry per tab, all members of the Nuxt dock group, each opening a chromeless deep link to that tab.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughDevTools now detects embedded mode from Estimated code review effort: 3 (Moderate) | ~25 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…dock tabs Fix the per-tab dock 404 by riding the embed flag on each tab's existing route (`?embed=1`) instead of a dedicated `/embed/*` route that the built client doesn't contain. Upgrade @vitejs/devtools + @vitejs/devtools-kit to 0.4.4 and @devframes/hub + devframe to 0.7.10, and set each grouped entry's `category` so it becomes an in-group sub-category of the Nuxt group. Defers the postMessage shared-iframe soft-navigation (devframe#128).
There was a problem hiding this comment.
🧹 Nitpick comments (1)
pnpm-workspace.yaml (1)
79-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRefresh the Vite compatibility rationale after this version bump.
The catalog now uses
@vitejs/devtools ^0.4.4, but the adjacent override comment still refers to@vitejs/devtools@0.4.2and its Vite 8.1 incompatibility. Update the rationale to reflect 0.4.4, or remove the Vite 8.0 pin if this release resolves the issue; verify against the package release notes and lockfile.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pnpm-workspace.yaml` at line 79, Update the adjacent override comment for `@vitejs/devtools` to reference the catalog’s ^0.4.4 version and its current Vite compatibility rationale; verify the package release notes and lockfile, and remove the Vite 8.0 pin if 0.4.4 resolves the incompatibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@pnpm-workspace.yaml`:
- Line 79: Update the adjacent override comment for `@vitejs/devtools` to
reference the catalog’s ^0.4.4 version and its current Vite compatibility
rationale; verify the package release notes and lockfile, and remove the Vite
8.0 pin if 0.4.4 resolves the incompatibility.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 730e7c28-966c-4602-a075-3e6d370dfe3e
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (3)
packages/devtools/client/composables/embed.tspackages/devtools/src/integrations/dock-tabs.tspnpm-workspace.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/devtools/src/integrations/dock-tabs.ts
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
The client is an ssr:false SPA served by a single sirv middleware; the old hub only ever hard-loaded the root `/`. Per-tab dock entries now also load the root (`?embed=1&to=<route>`) and a global route middleware soft-navigates to the target tab, instead of hard-loading a deep client URL. This keeps the feature on the one serving path proven to work and sidesteps any deep-path SPA-fallback concerns.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/devtools/src/integrations/dock-tabs.ts (1)
200-201: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy liftSerialize custom-tab refreshes and return the sync promise.
syncCustomTabs()is asynchronous but launched fire-and-forget. Concurrent refreshes can apply stale snapshots out of order, causing an older refresh to hide entries from a newer one; failures also become unhandled rejections. Queue the refreshes or guard application with a generation token, and return the promise from the hook.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/devtools/src/integrations/dock-tabs.ts` around lines 200 - 201, Update the devtools:customTabs:refresh hook to return syncCustomTabs() and serialize successive refreshes (or use a generation guard) so stale snapshots cannot overwrite newer results; ensure sync failures are propagated through the returned promise instead of becoming unhandled rejections.
🧹 Nitpick comments (2)
packages/devtools/src/integrations/dock-tabs.ts (2)
127-130: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winUse an explicit visible condition when re-showing tabs.
Removed tabs are hidden with
when: 'false', but re-registration clears that state withwhen: undefined. Sinceupdateis a partial patch, verify thatundefinedactually removes the previous condition; otherwise a removed and re-added tab may remain hidden. Preferwhen: 'true'or the dependency’s documented reset mechanism. (devtools.vite.dev)Proposed fix
- existing.update({ when: undefined, ...entry }) + existing.update({ when: 'true', ...entry })Also applies to: 187-190
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/devtools/src/integrations/dock-tabs.ts` around lines 127 - 130, Update the existing-tab re-registration path around existing.update so it explicitly restores visibility instead of relying on an undefined partial-field reset; set the documented visible condition (prefer when: 'true', or the dependency’s supported reset mechanism) while preserving the metadata refresh.Source: MCP tools
1-58: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftKeep built-in tab metadata single-sourced.
This Node-side catalog can drift from the client’s routes and tab definitions: a future client tab addition or rename could silently produce a missing or broken dock entry. Share the catalog or add a parity test before this becomes the release path.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/devtools/src/integrations/dock-tabs.ts` around lines 1 - 58, Keep BUILTIN_TABS synchronized with the client page definitions by introducing a parity test or shared catalog source. Validate that every client tab has a matching Node entry and that names and mirrored metadata such as title, icon, order, and category remain consistent, so additions or renames cannot silently produce invalid dock entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/devtools/src/integrations/dock-tabs.ts`:
- Around line 200-201: Update the devtools:customTabs:refresh hook to return
syncCustomTabs() and serialize successive refreshes (or use a generation guard)
so stale snapshots cannot overwrite newer results; ensure sync failures are
propagated through the returned promise instead of becoming unhandled
rejections.
---
Nitpick comments:
In `@packages/devtools/src/integrations/dock-tabs.ts`:
- Around line 127-130: Update the existing-tab re-registration path around
existing.update so it explicitly restores visibility instead of relying on an
undefined partial-field reset; set the documented visible condition (prefer
when: 'true', or the dependency’s supported reset mechanism) while preserving
the metadata refresh.
- Around line 1-58: Keep BUILTIN_TABS synchronized with the client page
definitions by introducing a parity test or shared catalog source. Validate that
every client tab has a matching Node entry and that names and mirrored metadata
such as title, icon, order, and category remain consistent, so additions or
renames cannot silently produce invalid dock entries.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 7ac1aaee-9287-44b4-8a2b-8bf33a060e91
📒 Files selected for processing (2)
packages/devtools/client/middleware/route.global.tspackages/devtools/src/integrations/dock-tabs.ts
Bring back the dedicated `/embed/<tab route>` sub-path for per-tab dock iframes. The `/embed/[...path]` page resolves the target tab's own route component and mounts it directly (like SplitScreen), so the tab renders without the SideNav or split pane and takes the full iframe. app.vue drops the split-pane wrapper while embedded; the route middleware skips its first-visit/overview redirects for `/embed/*`.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/devtools/src/integrations/dock-tabs.ts (1)
194-196: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winSerialize custom-tab refreshes before hiding tabs.
The refresh hook now triggers multiple async registrations (
devtools:customTabshandlers and this dock integration), butsyncCustomTabs()does not wait for those handlers before hiding disappeared entries. QueuesyncCustomTabs()so previous custom-tab refreshes settle first, and avoid swallowing promise rejections in the queue.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/devtools/src/integrations/dock-tabs.ts` around lines 194 - 196, Update the devtools:customTabs:refresh hook around syncCustomTabs() to serialize refresh operations through an async queue, ensuring each prior custom-tab registration settles before synchronization hides disappeared entries. Chain each new refresh onto the previous promise and preserve promise rejections rather than swallowing them.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/devtools/src/integrations/dock-tabs.ts`:
- Around line 194-196: Update the devtools:customTabs:refresh hook around
syncCustomTabs() to serialize refresh operations through an async queue,
ensuring each prior custom-tab registration settles before synchronization hides
disappeared entries. Chain each new refresh onto the previous promise and
preserve promise rejections rather than swallowing them.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 27e339cb-5e94-4ad4-b56b-31275756252c
📒 Files selected for processing (5)
packages/devtools/client/app.vuepackages/devtools/client/composables/embed.tspackages/devtools/client/middleware/route.global.tspackages/devtools/client/pages/embed/[...path].vuepackages/devtools/src/integrations/dock-tabs.ts
Replace the per-tab `/embed/` iframes (which deep-loaded client routes and 404'd) with the shared-iframe soft-navigation model from devframe#128 / vitejs/devtools#464. Register a single `subTabs` anchor iframe under the Nuxt group that loads the client root once; a client-side `devframe:frame-nav` postMessage shim announces one member dock per tab (from useAllTabs, carrying its category as an in-group sub-category) and soft-navigates within that one iframe. No deep-path loads, no Node-side tab list. Upgrades @vitejs/devtools + @vitejs/devtools-kit to ^0.4.5 and the devframe family to ^0.7.11.
Drive the shared-iframe member docks from useEnabledTabs() so a tab's show() condition, hidden/pinned settings and experimental gating decide whether its dock appears (mirroring the old SideNav), re-announcing the manifest when that set changes. Expose the Settings page as its own dock member. In embedded mode the SideNav is hidden, so lay the content out full-width.
…hared-iframe - module-main: serve the base-rewritten client index for the root document regardless of query, so the shared-frame anchor URL (`/?embed=1`) no longer gets a raw, un-rewritten `index.html` from sirv (broken base). - e2e: update iframe/nuxt-group specs for the shared-iframe model — the anchor renders chromelessly (SideNav hidden) and surfaces per-tab member docks via the frame-nav shim, instead of showing the in-iframe SideNav. - restore main's `unimport/auto-insert` eslint-disable comments that a merge in this branch had dropped (lint was failing on unrelated files).
… embedded settings - Upgrade @vitejs/devtools + @vitejs/devtools-kit to ^0.4.6 and @devframes/hub + devframe to ^0.7.12. - Set `categoryOrder` on the Nuxt dock group (vitejs/devtools#468) so the in-group sub-categories sort in Nuxt's own order (app, vue-devtools, analyze, server, modules, documentation, advanced) instead of the shared default table. Default uncategorised tabs to `app` in the frame-nav manifest to match. - Hide settings that are redundant when embedded (owned by the Vite DevTools dock): the tab visibility/order panel, the color-mode toggle, the SideNav options, and the close-on-outside-click toggle.
Intent
Surface every Nuxt DevTools tab as its own entry in the Vite DevTools dock, under a
Nuxtgroup, instead of the single hub iframe that hosted the whole app with in-app tab switching.Approach: shared-iframe soft navigation
Built on devframe's shared-iframe soft-navigation feature (
devframe#128/vitejs/devtools#464):module-main.tsregisters a singlesubTabsanchor iframe under theNuxtgroup. It loads the client root once (/__nuxt_devtools__/client/?embed=1) and stays alive; tabs switch by client-side navigation within it — no per-tab iframe, no reload.devframe:frame-navshim (composables/frame-nav.ts). Inside the anchor, the client announces a tab manifest fromuseEnabledTabs()(so a tab'sshow()condition, hidden/pinned settings and experimental gating decide whether its dock appears), answersnavigatewithrouter.push, and reportsnavigatedso the dock highlight follows in-app navigation. The dock materializes one member dock per reported tab (nuxt:devtools:<tab>), all sharing the one iframe. Settings is exposed as its own member.category(uncategorised →app), rendered as an in-group sub-category. TheNuxtgroup setscategoryOrder(vitejs/devtools#468) so sub-categories sort in Nuxt's own order — app, vue-devtools, analyze, server, modules, documentation, advanced — rather than the dock's shared default table.?embed=1puts the client in embedded mode (composables/embed.ts):app.vuehides the SideNav and split pane and lays the content out full-width. The Settings page also hides options that the dock now owns when embedded: the tab visibility/order panel, the color-mode toggle, the SideNav options, and close-on-outside-click.view/client.tsbinds the@vue/devtools-kitiframe channel to the shared anchor iframe, and retargets open/toggle/navigate at theNuxtgroup.Serving fix
The anchor loads
/__nuxt_devtools__/client/?embed=1; the dev middleware now serves the base-rewritten SPA index for the root document ignoring the query string (previously a query made sirv serve a raw, un-rewrittenindex.html, breaking the base — the root cause of the earlier iframe/404 failures).Dependencies
@vitejs/devtools+@vitejs/devtools-kit→^0.4.6;@devframes/hub+devframe→^0.7.12;@devframes/plugin-data-inspector+@devframes/plugin-code-serverstay^0.7.11.Notes
Drops the interim machinery from earlier iterations (Node-side hardcoded built-in tab list, per-tab dock registrations, the
/embed/*route). e2e specs updated for the shared-iframe model (anchor renders chromelessly; per-tab members registered via the shim). CI green: lint, build, typecheck, and the full e2e matrix pass.This PR was created with the help of an agent.