Summary
The wireframe viewer (.specify/extensions/wireframe/viewer/viewer.html) supports being opened on a specific SVG via #<feature>/<file>.svg — the boot sequence reads window.location.hash. But two gaps make deep-linking unusable in practice:
- The hash is never written. Clicking a wireframe in the nav calls
updateView() but doesn't update location.hash, so the address bar never reflects the current SVG — you can't copy a shareable link by browsing.
- The Next.js
/wireframes route ignores the URL. src/app/wireframes/page.tsx hardcodes getAssetUrl('/wireframes/viewer.html') and forwards no param, so tortoisewolfe.github.io/<repo>/wireframes/... can't deep-link into the iframe at all.
Proposed fix (implemented downstream in RescueDogs, offered upstream)
viewer.html — in updateView(), after updating the active nav state:
if (wf.path && window.location.hash.replace(/^#/, '') !== wf.path) {
history.replaceState(null, '', '#' + wf.path);
}
Plus a hashchange listener so external hash changes (back/forward, a parent frame, a pasted link) select the matching wireframe. replaceState avoids piling a history entry on every click.
/wireframes route — make it an optional catch-all [[...slug]]:
generateStaticParams enumerates public/wireframes/wireframes-manifest.json so /wireframes/<feature>/<file> prerenders under static export.
- Forward the slug to the iframe as a hash.
- Gotcha: drop the
.svg extension from the URL slug — keeping it makes the route emit out/wireframes/<feature>/<file>.svg/index.html, which collides (EEXIST at export) with the real <file>.svg asset sync-wireframes.sh copies to the same path. Restore .svg when building the hash.
Why upstream
This lives in the vendored wireframe extension, so every fork (RescueDogs, HatCoatAndBoots, future template tracks) re-derives or loses the fix on an extension update. Landing it upstream makes deep-linkable wireframes a shared capability.
Reference
Downstream implementation: TortoiseWolfe/RescueDogs PR for feat/wireframe-deeplinks (viewer hash-write + [[...slug]] route + unit test; build/export verified with 63 deep-link pages, no collision).
Summary
The wireframe viewer (
.specify/extensions/wireframe/viewer/viewer.html) supports being opened on a specific SVG via#<feature>/<file>.svg— the boot sequence readswindow.location.hash. But two gaps make deep-linking unusable in practice:updateView()but doesn't updatelocation.hash, so the address bar never reflects the current SVG — you can't copy a shareable link by browsing./wireframesroute ignores the URL.src/app/wireframes/page.tsxhardcodesgetAssetUrl('/wireframes/viewer.html')and forwards no param, sotortoisewolfe.github.io/<repo>/wireframes/...can't deep-link into the iframe at all.Proposed fix (implemented downstream in RescueDogs, offered upstream)
viewer.html— inupdateView(), after updating the active nav state:Plus a
hashchangelistener so external hash changes (back/forward, a parent frame, a pasted link) select the matching wireframe.replaceStateavoids piling a history entry on every click./wireframesroute — make it an optional catch-all[[...slug]]:generateStaticParamsenumeratespublic/wireframes/wireframes-manifest.jsonso/wireframes/<feature>/<file>prerenders under static export..svgextension from the URL slug — keeping it makes the route emitout/wireframes/<feature>/<file>.svg/index.html, which collides (EEXISTat export) with the real<file>.svgassetsync-wireframes.shcopies to the same path. Restore.svgwhen building the hash.Why upstream
This lives in the vendored wireframe extension, so every fork (RescueDogs, HatCoatAndBoots, future template tracks) re-derives or loses the fix on an extension update. Landing it upstream makes deep-linkable wireframes a shared capability.
Reference
Downstream implementation: TortoiseWolfe/RescueDogs PR for
feat/wireframe-deeplinks(viewer hash-write +[[...slug]]route + unit test; build/export verified with 63 deep-link pages, no collision).