Add global (saved) embed components

Add global (saved) embed components

#57 in Riparion/riparion-cms — merged 2026-06-03

Summary

Extends the existing embed system so any embed type can have saved, centrally-managed instances, referenced from post/page bodies by id — [[/name id=N]]. The id prop is mutually exclusive with all other props (props come from the stored definition), so editing a saved component updates every reference at once. Adds no new widget types.

What's included

  • Data + resolution: new global_embeds table (sqlite + postgres) storing the frozen prop bag as JSON; resolve_global_embeds is a batched async post-parse pass wired into get_post, assemble_page_detail, and highlight_block. Unknown ids fall back to a graceful missing_global notice; bodies with no id do zero DB work.
  • Authoring: a typed PropSpec/PropKind schema on every SMART_TAGS entry powers a per-prop admin form with a live preview at /admin/components, gated by a new components:write token (added to ALL_TOKENS, nav, breadcrumbs, audit vocabulary).
  • Delete = convert to inline: deleting a component rewrites every reference into an equivalent inline embed with the frozen props baked in, then removes the row — all in one transaction, and stamps pages.updated_at so sitemap lastmod stays correct. No page breaks.
  • Discoverability: saved components appear in the editor's [[/ autocomplete and the "+ Smart tag" menu, inserting [[/name id=N]]; Live preview resolves them server-side.
  • Help: new /help/components article + admin deep-link.

Review hardening (applied)

  • highlight_block now requires an author token (posts:write/pages:write) — it reads admin-managed config when resolving references, so it can't be an unauthenticated read.
  • Prop values reject "/newlines so they round-trip safely when inlined on delete.
  • Inline-on-delete is transactional; missing_global id bound to a local before rsx.

Verification

  • cargo fmt --check; clippy -D warnings on server (sqlite + postgres) and wasm/web — all clean.
  • cargo test --features server,sqlite — 47 pass (incl. new mdx round-trip tests).
  • Manual end-to-end steps appended to TODO_VERIFICATION.md.

🤖 Generated with Claude Code

Last updated 2026-06-04