import type { Manifest } from '../core/manifest' import type { ThemeSignal } from '../index' import type { Theme } from '../ui/shell-core' import { resolveThemeSignals, themeRootAttrs, themeSignalsSeedScript, } from './theme-signals' /** * Production HTML document templates for a published build. They mirror the dev * server's documents but drop every development-only inject — no live-reload SSE * client, no `process/Bun is not defined` error overlay — and reference the * content-hashed asset URLs the production bundle emits (so a host can cache them * indefinitely). The React trees themselves are produced by the *shared* * renderers (`ssr-shell`, `ssr-render`, `ssr-primer`); only the envelope here * differs from dev, by necessity (hashed assets vs. a fixed dev path). */ const FONT_LINKS = '' + '' + '' /** Content-hashed entry URLs the production build emitted (already base-prefixed). * `render` is a per-component map (componentId → bundle URL): each component is * built into its own bundle so the catalog is never built as one graph. */ export interface DocAssets { browser: string render: Record primer: string /** Resolves each externalized bare specifier (the shared runtime libraries) to * its one content-hashed vendor bundle — the `` } /** The browse shell document: pre-rendered chrome + inlined seed, hydrated by the * browser entry. No dev injects. */ export function shellDoc(opts: { title: string tokensCss: string globalCss: string vitrineCss: string theme: Theme /** The effective theme root signals to emit (see the `theme` config). */ signals: readonly ThemeSignal[] markup: string ssr: boolean manifest: Manifest a11y: boolean assets: DocAssets }): string { // `color-scheme` matches the theme so user-agent surfaces (scrollbars, default // control chrome) follow it rather than rendering in their light defaults. const reset = `html,body{margin:0;height:100%;background:var(--dc-bg)}html{color-scheme:${opts.theme}}` const rootAttrs = themeRootAttrs( resolveThemeSignals(opts.theme, opts.signals), ) const seed = JSON.stringify({ manifest: opts.manifest, theme: opts.theme, a11y: opts.a11y, }) return `${opts.title}${FONT_LINKS}${importMap(opts.assets.importmap)}
${opts.markup}
${themeSignalsSeedScript(opts.signals)}` } // The isolated case render document used to live here, alongside the shell and // primer documents. It now belongs to the DOM substrate (`substrate/dom.ts`): // the document envelope is where a medium's assumptions live — the theme signals // on the root, the body surface, the mount, the script tag — so it is the // substrate's to shape, and both the dev server and this production host render // it through `substrate.document()`. The shell and primer documents below stay: // they are the browse *chrome*, which remains a DOM application by design. /** The primer reading-page document. */ export function primerDoc(opts: { tokensCss: string globalCss: string vitrineCss: string theme: Theme /** The effective theme root signals to emit (see the `theme` config). */ signals: readonly ThemeSignal[] markup: string ssr: boolean /** Style-engine output, placed after the static ${opts.headStyles ?? ''}${importMap(opts.assets.importmap)}
${opts.markup}
${themeSignalsSeedScript(opts.signals)}` }