/** * The public site (SPEC.md §57). * * One renderer draws a page, and it is this one. Studio's builder canvas loads this * very bundle inside its iframe, so a preview cannot drift from what a visitor sees — * there is no second implementation for it to drift from (SPEC.md §59). * * `preview.tsx` is the document that mounts what is here and adds the editor's half * of the conversation. Everything the page *looks like* is in this file and in * `app/blocks/`. */ import { AssemoraPage, type BlockViewProps, createBlockRegistry } from '@assemora/react' import type { BlockTree } from '@assemora/schema' import { HeroView } from './blocks/hero.tsx' import { RichTextView } from './blocks/rich-text.tsx' /** * Drawn where a block type has no view here. * * A stored page outlives the code that renders it: a block dropped from this project * is still in every tree that used it, and a visitor should never be shown a silent * gap where one used to be. */ const Missing = ({ block }: BlockViewProps) => (
No view is registered for a “{block.type}” block.
) /** * Where a block declaration meets its component. * * The key is the block's type from `src/blocks/`, and this map is the only thing * joining the two. Add a block there, add its view here, and the builder can place * it. */ export const blocks = createBlockRegistry( { hero: HeroView, richText: RichTextView }, { fallback: Missing }, ) /** * What a visitor gets: the published tree, by slug, with no session at all. * * `src/routes.ts` is the other half, and it says why this is a route rather than a * policy. Nothing here is authenticated, so `/preview` and `/preview?slug=about` are * pages anybody can open. */ export const readPublished = async (slug: string): Promise