feat(hub): shared-iframe docks with postMessage soft navigation#128
Merged
Conversation
Add a design proposal for hosting a foreign multi-tab devtool (e.g. Nuxt DevTools) as first-class hub docks that share one live iframe and switch views via client-side soft navigation. Splits the design into a hub-owned data model (a frameId shared-iframe axis orthogonal to groupId, plus client-only dock registration) and a versioned, origin-locked host<->iframe postMessage protocol for the live nav loop. The embedded app ships only a small postMessage shim and takes no hub/RPC dependency. Includes the viewer UI-behavior contract for Vite DevTools, the adapter behavior, fallback semantics, message tables, sequences, and TS type sketches.
✅ Deploy Preview for devfra ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Implement the hub side of the shared-iframe soft-nav design: extend DevframeViewIframe with navTarget + a subTabs anchor flag (and NavTarget / FrameSubTabsConfig types), and ship a viewer-side frame-nav adapter (attachFrameNavClient) that the client host auto-attaches to a subTabs anchor iframe. The adapter runs a versioned, origin-locked host<->iframe postMessage handshake, materializes one client-only member dock per reported tab (via the client docks.register from #129, keyed <frameId>:<tabId>), and drives a bidirectional nav loop: selecting a member posts navigate; the app's navigated report moves the dock highlight. An idempotent guard breaks the echo, a declarative manifest snapshot reconciles add/update/remove (clearing selection when the active tab vanishes), and a handshake timeout degrades to no-shim (anchor stays a single plain iframe dock). Grouping stays orthogonal to frameId and follows the #130 category rule. Covered by client host + adapter tests; hub API snapshots updated.
The mutable string[] in NavTarget.query and the unknown-typed state field
broke DevframeViewIframe's round-trip through shared-state's Immutable<T>
(Immutable<unknown> collapses to {}), so downstream type guards over
devframe:docks (e.g. storybook-hub's isIframeDock) stopped narrowing and
CI typecheck failed. Make query arrays readonly and drop the history-state
field (path + query cover routers; a recursive serializable state trips
TS2589). Narrow query values with typeof, not Array.isArray.
… hubs Add a 'Tabbed Tool' to both minimal hub examples: a single SPA (Overview / Components / Timeline / Settings) mounted as a subTabs anchor. Each SPA ships the ~40-line devframe:frame-nav postMessage shim (announces a tab manifest, answers navigate, reports navigated); the hub's client host attaches the frame-nav adapter, materializing one client-only dock per tab that all share one iframe. To satisfy the viewer contract, both shells now keep one iframe alive per frameId (shown/hidden instead of re-src'd), drive selection through docks.switchEntry so the adapter hears entry:activated, set state.domElements.iframe + emit dom:iframe:mounted on mount, and re-render when the adapter reconciles member docks. Verified end-to-end in a browser: opening the anchor reveals its tabs, selecting a tab soft-navigates the shared iframe with no reload, and navigating inside the frame moves the hub's active dock (bidirectional).
…ft-nav # Conflicts: # examples/minimal-next-devframe-hub/src/client/app/page.tsx # examples/minimal-vite-devframe-hub/src/client/main.ts
Add a 'Shared-iframe soft navigation' section to the Client Context guide (the anchor's subTabs opt-in, the devframe:frame-nav postMessage protocol, the client-only member docks, and the viewer's dom:iframe:mounted seam), a frameId note + demo pointer in the Hub grouping guide, and a mention in the examples list. Refocus plans/shared-iframe-soft-nav.md now that the hub side has shipped: replace the status banner with a concise 'what shipped + where' list and turn the implementation checklist into the remaining downstream work (the Vite DevTools UI contract and the Nuxt shim).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Lets a foreign multi-tab devtool (e.g. Nuxt DevTools) be hosted as first-class hub docks that share one live iframe and switch views via client-side (soft) navigation. Ships the hub side plus the design spec (
plans/shared-iframe-soft-nav.md) that hands the UI contract to Vite DevTools.Builds on two already-merged foundations: #129 (client-only
docks.register()/update()) and #130 (grouped-entrycategoryas an in-group sub-category).Design (see
plans/shared-iframe-soft-nav.md)Two cleanly separated layers, mirroring the a11y precedent (durable data model on the hub; live loop on a direct channel):
frameIdis a shared-iframe axis orthogonal togroupId; per-tab members are client-only docks materialized from a manifest.postMessageprotocol (ready/manifest/navigate/navigated) with an idempotent echo guard and hard-nav fallback. The embedded app ships only a smallpostMessageshim and takes no hub/RPC dependency.Implementation (hub side)
navTarget+subTabsonDevframeViewIframe, plusNavTarget/FrameSubTabsConfig(packages/hub/src/types/docks.ts).attachFrameNavClient(packages/hub/src/client/frame-nav.ts): runs the handshake, reconciles the declarative manifest snapshot into client-only member docks (<frameId>:<tabId>), drives the bidirectional nav loop, degrades to no-shim on timeout.frameIdwhen asubTabsanchor iframe mounts (dom:iframe:mounted), and tears it down on removal/dispose (packages/hub/src/client/host.ts).Gates run locally on the hub package:
tsc --noEmit,eslint, and the full hub vitest suite (97 passing) — all green.Remaining (out of this PR)
frameId, emitdom:iframe:mounted, render members).postMessagenav shim.This PR was created with the help of an agent.