Repository navigation
v3.2.8
What's new
The editor gets a page outline, and the canvas gets a frame (#1250, #1251)
A long page is now navigable at a glance. The sidebar's Outline tab lists the page's sections in order — shared header and footer marked as such — each one clickable to select and scroll to, and draggable to reorder. Reordering previously worked only on a freshly created page: a drop needed a blank area of canvas to land on, so on a page that already had sections there was nowhere to drop. It now works on any page, and the panel keeps its selection and hover state in step with the canvas.
Around it, the canvas is framed: a 16px gutter and a rounded sheet with a soft shadow, so the page reads as a page rather than butting straight into the toolbar and sidebar. (Recovered from #1231, whose banner half was superseded by #1233.)
Editor and publishing fixes (#1252, #1253, #1256, #1257)
- Publishing a freshly created site no longer fails on the first attempt — the client retries once while the new site's data settles server-side, instead of reporting a publish error for a site that was healthy a moment later.
- The toolbar reads the editor's site context reactively. It pulled the context apart once at mount (
const { value: site } = …), so an in-place update to the cached record — notablyupdate_recordafter the domain endpoints returned — never re-rendered it, and the publish dialog kept showing a stale, empty host until a full reload. - The section editor no longer opens in a dirty state. It seeded its "original" baseline before the section's content had arrived, so the dialog claimed unsaved changes on a section you had never touched, and could block navigation behind that phantom edit.
fields.yamlmistakes now speak up. An unrecognised key on a field definition — or the legacyfields:shape — was silently ignored on import, so the field quietly didn't work. Imports now warn with the file, path and field; repeaters and groups that declare nosubfieldswarn too; and page types report nestedsubfieldsas unsupported rather than dropping the children in silence (implementing nested page-type fields is tracked in #1259).- A page type's Body Footer HTML renders again (via #1255) — it was being dropped when a page rendered from a page type's layout.
Browse mode is now an honest read-only inspector (#1233)
In primo dev with the default --author files, local files are the source of truth and the CMS is a projection of disk — but the editor still looked fully editable. Typing into a field and pressing Save silently discarded the edit (the old build posted a full-width apology banner explaining exactly that).
Files-author mode is now visibly read-only: fields, toolbars, drag-and-drop, symbol toggles, and the code editors render disabled instead of live-but-doomed, mod-S / mod-Enter no longer fire, and only the input types that actually honour readonly are allowlisted. The lock is held from the very first paint, is re-confirmed against dev-auth on refresh, and fails closed — if the check doesn't return a valid mode, the editor stays locked rather than guessing.
The editor no longer hangs on a stuck section (#1236)
The page spinner cleared only when every section reported a mount, and mounting requires an iframe — so a section whose content never resolved never compiled, and the canvas spun forever behind the spinner. The editor now stops waiting on a section that can't mount instead of hanging the whole page. (Resilience half of #1235.)
Domain-connect and dashboard fixes (#1230)
- Connecting a domain now refreshes the editor's cached site record, so a manual/external connection appears immediately rather than after a full reload (#1230, #1253).
- Mobile sidebar and tab-clipping fixes.
- Dashboard previews no longer show stale thumbnails.
Marketplace and editor polish (#1232)
Blocks/Starters are header tabs with scroll position preserved between them; the editor toolbar shares one button style with aria-labels on icon-only controls; dashboard catalog cards share a single stylesheet.
E2E regression suite, now running on every PR (#1237, #1240)
A Playwright suite covers the core promise — a developer builds a site, hands it to a client for visual editing, and later development doesn't silently destroy the client's content — against a disposable, isolated instance (seeded through the pinned primo-cli, no real data, no paid services).
It now runs in CI on every pull request, including Dependabot's — which auto-merge.yml had been merging with only static CodeQL analysis behind them, never actually exercising the code (including bumps to libraries the editor's drag interactions sit on).
Product analytics restored (#1238)
Telemetry was fully wired but hardcoded off since "disable telemetry" — the client never tracked product events at all, only unhandled exceptions. The existing PostHog integration is enabled again with a transparent, privacy-conscious default, and the client now reports product events.
Server: assigned group id, and ids for nested index pages (#1243, #1244)
- Bootstrap returns the authoritative
group_idin both branches (with and without an import). The store can regenerate a supplied id, so clients mirroringserver.yamldrifted from the database — the reported symptom wasserver.yamlsayingarchitecture1while the database held a generated id. Clients can now reconcile. created_idswrite-back covers nested index pages. The import keyed ids by"pages/" + path + ".yaml"whilepathhas any trailing/indexstripped for routing, sopages/services/index.yamlwas recorded aspages/services.yaml. Nested index pages were the one page kind left without an_id;primo addnow mints them too.
Dependencies
Note for operators: the image is built and deployed when the release is published, not when the tag is pushed (introduced in v3.2.7). This tag only produces a draft release with the platform binaries; publishing it is what rolls :latest and triggers the Railway deploy.


