Files
zernonia 4a18271502 feat: typeset (#1923)
* feat(v4): port Typeset from shadcn-ui

Adds the Typeset designer at /typeset, the typeset.css stylesheet, and the
docs page, ported from upstream shadcn-ui.

Typeset is one stylesheet that styles rendered markdown: wrap content in a
`typeset` container and everything inside is styled, with three controls
(size, leading, flow) driving the rhythm.

- public/typeset.css is byte-identical to upstream. It is served verbatim at
  /typeset.css for download and imported from assets/css/main.css so the
  preview renders exactly what users get. The import has to be global and
  after Tailwind: CSS orders @layer by first appearance, so loading typeset
  as a <link> or a route-level chunk registers `components` ahead of `base`
  and preflight kills every typeset rule. That ordering differs between dev
  and the production build, so it must not be left to load order.
- URL state uses one query key per param via useRouteQuery, validated on read
  and default-stripped on write, so a shared link is readable and a
  hand-edited one degrades to defaults instead of breaking.
- Undo/redo keeps its own stack of URL snapshots, since every picker writes
  with `replace` and leaves nothing for the browser to pop.
- The preview iframe is driven over postMessage, never by rewriting its src:
  the URL only seeds the first paint. The iframe announces readiness rather
  than the designer posting on `load`, which fires before Vue hydrates and
  would drop the first message (leaving deep links showing defaults).
- The preview holds its params in local reactive state instead of writing
  them back through its own router — the iframe's first router.replace lands
  while Vue Router is still settling and is silently dropped.
- Hover previews stay out of the URL so browsing a picker doesn't record a
  phantom undo entry per hovered item.
- Fonts resolve through the existing useFontLoader (unifont/Bunny) and the
  Get Code panel emits @nuxt/fonts config for Nuxt and Fontsource for
  Vite/Laravel/Astro.

Verified end-to-end in Chrome against both the dev server and the production
build: typeset.css winning the cascade, postMessage restyling without reload,
hover previews, locks, shuffle, undo/redo, iframe shortcut forwarding,
specimen switching, and deep-link restore.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(v4): address review feedback on typeset

- Revalidate the specimen name through `definePageMeta({ validate })` instead
  of a setup-time throw. Vue Router reuses the page component when only
  `[name]` changes, so the setup check only ever covered the first render.
- Seed the preview iframe URL from committed params, not merged ones: a hover
  preview is uncommitted by definition and must not be baked into a real
  navigation.
- Reference `globalThis.document` in the keydown listeners rather than the
  bare global, matching the rest of the typeset code. The bundler already
  replaces `document` with `void 0` for the server build, so this was not a
  live SSR crash, but it left the composables relying on that transform.
- Document that `typeset-scroll` only works inside a `typeset` container,
  since the stylesheet scopes it as a descendant.

Verified in Chrome against dev and the production build (28/28 each),
including the new 404 handling: an unknown specimen returns 404 and renders
the error page with no specimen content leaking, and valid specimens still
load afterwards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 22:18:15 +08:00
..
2026-08-07 22:18:15 +08:00
2025-11-15 10:15:02 +08:00
2026-03-17 09:27:47 +08:00
2026-04-03 01:36:56 +08:00
2026-08-07 22:18:15 +08:00
2026-03-17 09:27:47 +08:00
2026-03-17 09:27:47 +08:00
2025-11-15 10:15:02 +08:00
2026-07-11 15:31:10 +08:00
2026-04-03 01:36:56 +08:00
2026-04-03 11:32:57 +08:00
2025-04-01 15:43:15 +08:00