Deep Linking

Send a user to a view inside a devframe (from another dock, a coding agent, or a copied URL) two ways: the hub relays a dock activation to focus a dock in place; a standalone SPA reads its URL hash to restore the view.

Send a user to a view inside a devframe (from another dock, a coding agent, or a copied URL) two ways: the hub relays a dock activation to focus a dock in place; a standalone SPA reads its URL hash to restore the view.

Focusing a dock inside a hub

The active dock is state local to the host page. A mounted devframe, in its own iframe and RPC client, reaches it via the hub: hub:docks:activate switches the active dock, carrying an opaque params bag the target reads:

await rpc.call('hub:docks:activate', {
  dockId: 'devframes:plugin:data-inspector',
  params: { sourceId: 'my-plugin:store' },
})

The hub broadcasts the request and mirrors it into the devframe:docks:active slot, so a dock mounting because of the switch converges on it. The target subscribes, filters on its dockId, and reads params; see Cross-iframe dock activation.

Focus is one-shot: the terminals dock reads params.sessionId, the Data Inspector params.sourceId; a target naming something unregistered waits, then fires once. An id that never arrives is a no-op; an unknown dockId warns (DF8107).

Standalone, a devframe SPA owns its own URL. Encode the shareable view in the hash (#…): it round-trips a copied link, survives reload, and stays clear of the handshake query string (?devframe_auth_token=). Parse with URLSearchParams:

// read on load
const params = new URLSearchParams(location.hash.replace(/^#/, ''))
const sourceId = params.get('source')

// write back, without stacking history entries
history.replaceState(history.state, '', `#${params.toString()}`)

// react to back/forward and manual edits
window.addEventListener('hashchange', applyState)

The terminals dock keys a selection as #id=<sessionId>; the Data Inspector encodes its workbench (#source=…&query=… plus filter/auto-rerun flags). replaceState writes never fire hashchange, so boot read, live listener, and write-back don't loop.

Keep credentials out of anything shareable: a handshake token belongs in the query string, scrubbed once read (as the Data Inspector does), never in a copyable hash.