Repository navigation
feat: synced section references - #546
scottbuscemi wants to merge 2 commits into
Conversation
…ry paths `new URL(...).pathname` returns a URL-encoded path (spaces become `%20`), which the filesystem can't resolve. Replacing with `fileURLToPath()` decodes the path correctly, fixing ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL for users whose project root contains spaces (e.g. "Local Sites").
Editors can now insert a section as a live reference rather than a copy. A synced reference stores an `emdash-section-ref` Portable Text block that resolves to the section's current content at render time — edits to the section propagate automatically everywhere it is referenced. - New `SectionRef.astro` renderer resolves sectionId at render time - New `getSectionById(id)` public API (was internal-only, required a db arg) - `emdash-section-ref` registered in `emdashComponents` type map - New `SectionRefExtension` TipTap node with drag, select, delete support - Section picker modal gains Insert copy / Synced reference mode toggle - PT ↔ ProseMirror serialization handles `emdash-section-ref` ↔ `sectionRef` - Docs updated with insertion mode comparison and getSectionById reference
🦋 Changeset detectedLatest commit: 7eae81f The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/blocks
@emdash-cms/cloudflare
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
Pull request overview
Adds a new “synced reference” mode for inserting Sections into Portable Text, where content stores only a section ID and is resolved at render time—allowing section edits to propagate everywhere they’re referenced.
Changes:
- Core: introduce
emdash-section-refrendering via newSectionRef.astro, and promotegetSectionById(id)to a public API (withgetSectionByIdWithDbfor admin/server usage). - Admin: add a TipTap
sectionRefnode + section picker mode toggle (“Insert copy” vs “Synced reference”) and PT↔PM conversion support. - Docs/infra: document the two modes + PT block format; fix Vite alias path resolution in blocks playground; add changeset.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/core/src/sections/index.ts | Public getSectionById() and internal getSectionByIdWithDb() split. |
| packages/core/src/index.ts | Re-export getSectionById from the package entrypoint. |
| packages/core/src/components/index.ts | Register and export SectionRef; map emdash-section-ref in emdashComponents. |
| packages/core/src/components/SectionRef.astro | New renderer that resolves sectionId at render time and renders current section content. |
| packages/core/src/api/handlers/sections.ts | Update admin handlers to call getSectionByIdWithDb. |
| packages/blocks/playground/vite.config.ts | Use fileURLToPath for a stable Vite alias path. |
| packages/admin/src/components/editor/SectionRefNode.tsx | New TipTap atom node + node view for synced section references. |
| packages/admin/src/components/SectionPickerModal.tsx | Add insertion mode toggle and update onSelect signature to include mode. |
| packages/admin/src/components/PortableTextEditor.tsx | Register extension; PT↔PM conversions; insert copy/ref behavior. |
| docs/src/content/docs/guides/sections.mdx | Document insertion modes, PT format, and getSectionById. |
| .changeset/rich-dancers-repair.md | Version bump notes for the new feature. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const { node } = Astro.props; | ||
| const section = node.sectionId ? await getSectionById(node.sectionId) : null; | ||
| --- | ||
|
|
||
| {section ? ( | ||
| <div class="emdash-section-ref" data-section-id={node.sectionId} data-section-slug={section.slug}> | ||
| <PortableTextComponent value={section.content} /> | ||
| </div> |
| export async function getSectionById(id: string): Promise<Section | null> { | ||
| const db = await getDb(); | ||
| return getSectionByIdWithDb(id, db); | ||
| } |
| In the editor, synced references appear as a labelled block with a **Synced section** badge. They cannot be edited inline; to change the content, open the Sections library. | ||
|
|
||
| <Aside type="caution"> | ||
| Deleting a section that is referenced in published content will cause those references to render nothing. The admin editor shows a warning, but there is currently no broken-reference check before deletion. |
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
|
This PR has been inactive for 14 days. It will be closed automatically in 7 days if there is no further activity. If you're still working on this, please push an update or leave a comment. |
What does this PR do?
Adds support for inserting sections as synced references rather than copies.
Currently, inserting a section pastes its Portable Text blocks directly into the post — edits to the section don't affect existing content. This PR adds a second mode: a synced reference stores only the section's ID in the Portable Text content. The section's current content is resolved at render time, so editing the section automatically updates every post that references it.
This is a common pattern in content systems.
Closes #
What changed
packages/core (
emdash)SectionRef.astrocomponent: resolvessectionIdat render time viagetSectionById, renders current content with<PortableText>; renders nothing in prod if section is deleted, dev placeholder otherwiseemdash-section-refregistered inemdashComponentstype map so<PortableText>handles it automaticallygetSectionById(id)promoted to public API (was@internalwith requireddbarg); internal variant renamedgetSectionByIdWithDbpackages/admin (
@emdash-cms/admin)SectionRefExtensionTipTap node: renders a labelled block with a Synced section badge, drag handle, delete button, and link to the Sections libraryPortableTextEditor: imports and registersSectionRefExtension;convertPTBlockhandlesemdash-section-ref→sectionRefPM node;convertPMNodehandlessectionRef→ PT block;handleSectionSelectacceptsmode: "copy" | "ref"SectionPickerModal: new Insert copy / Synced reference toggle at the top;onSelectsignature updated to(section, mode)docs
guides/sections.mdx: explains the two insertion modes with a comparison table, documents the PT block format, caveat on orphaned references, andgetSectionByIdAPI referenceType of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm formathas been runAI-generated code disclosure
todo