// teardown
Content as code
Decks with a type system.
Content · typed and versioned
Access · data, not code
what gets read shapes the next revision
// overview
A deck changes constantly and goes out one person at a time. So the content gets what code gets: a type, a diff, a version, and edits an agent can make safely. Who can see it, through which link and for how long lives in the database, where it changes without a deploy. I have built that split three times, and in its furthest form the content is a typed tree an in-product agent patches.
// how it works
01 · author
The content has a type
Every deck starts from a type contract that lives in the repo. On this site and at Ryse, a deck is a TypeScript file exporting a typed deck object, registered by slug, so a slide that breaks its type breaks the build. In a client's deck studio the same idea becomes data: decks, one-pagers and whitepapers are trees of 17 typed primitives stored in Convex, styled only through design tokens.
02 · patch
Edits are patches, not rewrites
In the code builds, an edit is a git diff, reviewed like any other change whether a person or a coding agent wrote it. In the studio, the in-product agent edits through five typed operations: patch a node, or replace, insert, delete and reorder slides. A person's inline text fix uses the same node patch and the same reducer, and the live preview follows the agent slide by slide. Anything outside the content, like a rename, arrives as a proposal a human applies.
03 · version
Validated, then versioned
The agent learns the schema from a declarations file generated from the Convex validators, and the validator it can call as a tool is a generated copy of the one the server runs. Applying a session re-validates the entire tree, confirms every referenced image exists, and writes a new numbered version. Restoring an old version opens a fresh session from it instead of rewriting history. In the code builds, the commit is the version and the type check is the gate.
04 · access
Access is a row
Who can see a deck is data, never code. On this site a deck stays private until a database flag makes it public, and share links can be revoked, can expire, and can ask for an email first. At Ryse, a deck is open to the admin team until someone restricts it; that person becomes the owner and adds viewers one at a time, each grant stamped with who made it and when. The studio's links are public, email-gated or invite-only.
05 · present
Private decks are never prerendered
Public deck pages are prerendered only for slugs that exist in the registry and are marked public in the database, and flipping the flag triggers on-demand revalidation. Share links resolve on every request. An unknown, revoked or expired token gets the same notice, so a dead link reveals nothing. Clearing an email gate turns the viewer into a person record, in the same table the contact form and whitepaper downloads write to.
06 · measure
Dwell counts only visible time
The viewer sends four events: opened, slide view, slide dwell and completed. Dwell comes from a pure timer that accrues only while the tab is visible, reports increments rather than totals, and flushes on a 15-second heartbeat, on every slide change, and by beacon when the tab hides or the page closes. Glances under 250 ms are dropped. The admin view rolls it up into completion rate, median session length and where viewers drop off.
// decisions
The parts that were a call, not a default — and what the alternative would have cost.
Content in code, access in the database
One system holding both, whether a CMS or a hardcoded allowlist
Content wants what code has: types, diffs, review, and agents that already know how to edit files. Access wants the opposite. It changes per recipient, has to take effect in minutes, and should leave a record. Splitting them makes sending a deck to one more person a row, not a release.
Storage follows the editor
One content store for every build
When the editors are developers and coding agents, files in git are the cheapest review loop available. A client team revising collateral constantly can't wait on a deploy, so there the same contract becomes a typed tree in the database that an in-product agent patches. The types stay in code either way.
A composable tree of typed primitives
A fixed set of slide templates, or free-form React
Templates hit their ceiling by the second deck, and every new layout becomes a code change. Free-form React puts every layout choice, and every way to break the build, in the model's hands. Composable primitives with token-only styling express new layouts without a deploy, validate mechanically on every apply, and keep the brand in the tokens. A registry component is the escape hatch.
Narrow, named tools for the agent
A shell and a validation script
A shell is the widest tool an agent can hold, and it can read the environment its credentials live in. The editing agent gets file tools inside an isolated workspace plus named tools: patch operations, a validator generated from the server's own, and proposals for anything with side effects. Every capability it has is one someone chose.
Share links pin a version
Links that always serve the latest edit
A recipient should see what was sent. Pinning means publishing a revision can't quietly change a deck someone is halfway through. The admin view counts links that have drifted behind the current version, and moving a link forward is one explicit, audited step.
// guarantees
Access without a deploy
Visibility, share links and per-person grants live in the database. Publishing, restricting or revoking a deck is a row change, and every link or grant records when it was made.
What you sent stays sent
Studio links pin an immutable version. A revision never changes a deck someone is reading; drifted links are counted, and moving one forward is explicit and audited.
Dwell means on screen
Per-slide time accrues only while the slide is visible and is sent by beacon when the tab hides. Tracking fails quietly: it can lose an event, never break the viewer.