CMS
The structured-content engine, file formats, and API.
The full spec of how collections work. Engine lives in console lib/cms.ts.
Files, per collection, inside each site repo
.sitecms.json: the registry. Per collection: id, name, page, version: 2, cmsFields (key, label, type), optional detail{dir, slugFrom}..cms/<id>/items.json: ordered array of items. System fields:_id(8-char),_status(“published” | “draft”),_slug(when detail pages are on). All other keys are field values as strings..cms/<id>/item.html: one item’s markup with{{fieldKey}}slots.{{_url}}resolves to the item’s detail URL..cms/<id>/detail.html: optional full-page template for detail pages.
Rendering
The listing page contains <!-- cms:<id>:start --> and <!-- cms:<id>:end --> anchors. Rendering joins published items through item.html and splices between the anchors. Escaping is type-aware: richtext values are raw HTML, everything else is escaped. Detail pages render to <dir>/<slug>/index.html for published items; stale pages are deleted. Every mutation writes items.json, the page, affected detail pages, and the registry in ONE commit (git trees API), so each operation is one deploy.
Field types
text, richtext, image, link, date, number, option. Types shape the editor (image preview + upload, number input, richtext toolbar) and inform AI generation. Values are strings; there is no hard validation beyond numbers.
Migration from v1
v1 defined collections as page + itemOpen tag with positional field extraction. Migration (automatic on first open): last item block becomes item.html with slots, all blocks extract to items.json, page gets anchors, first render is equivalent markup. The live site does not change.
API surface
POST /api/sites/<site>/collections actions: migrate, add, update, duplicate, remove (bulk via itemIds), status (bulk), reorder, slug, schema (labels/types), upload (base64 image to images/cms/), unregister. All item mutations accept a rev token from GET; a stale rev returns 409 with stale: true and the UI refreshes.
POST /api/sites/<site>/collections/ai actions: generate (items land as drafts), csv (AI column mapping, drafts), bulk (instruction across items, scope all/published/draft), inferSchema (names and types fields), addField / removeField (agent edits item.html, validated so no existing slot is lost), linkDetail (wraps items in links to their pages), enableDetail (agent designs detail.html from the site’s own look, assigns slugs, auto-links listing), createApply (create a collection from agent edits + migrate, one commit).
Guarantees
- Template edits are rejected if any existing
{{slot}}disappears. - Draft items never render anywhere.
- A collection needs at least one field and keeps at least one item (the template source in v1; in v2 empty collections are allowed but add uses the last item as default source).