Skip to content

feat: synced section references - #546

Closed
scottbuscemi wants to merge 2 commits into
emdash-cms:mainfrom
scottbuscemi:feat/synced-section-references
Closed

scottbuscemi wants to merge 2 commits into
emdash-cms:mainfrom
scottbuscemi:feat/synced-section-references

Conversation

@scottbuscemi

@scottbuscemi scottbuscemi commented Apr 13, 2026 •

Copy link
Copy Markdown
Contributor

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.

Screenshot 2026-04-13 at 3 05 12 PM

Closes #

What changed

packages/core (emdash)

  • New SectionRef.astro component: resolves sectionId at render time via getSectionById, renders current content with <PortableText>; renders nothing in prod if section is deleted, dev placeholder otherwise
  • emdash-section-ref registered in emdashComponents type map so <PortableText> handles it automatically
  • getSectionById(id) promoted to public API (was @internal with required db arg); internal variant renamed getSectionByIdWithDb

packages/admin (@emdash-cms/admin)

  • New SectionRefExtension TipTap node: renders a labelled block with a Synced section badge, drag handle, delete button, and link to the Sections library
  • PortableTextEditor: imports and registers SectionRefExtension; convertPTBlock handles emdash-section-ref → sectionRef PM node; convertPMNode handles sectionRef → PT block; handleSectionSelect accepts mode: "copy" | "ref"
  • SectionPickerModal: new Insert copy / Synced reference toggle at the top; onSelect signature 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, and getSectionById API reference

Type of change

Checklist

AI-generated code disclosure

  • This PR includes AI-generated code

todo

  • Replace the "arrow" icon for going to Sections with a pencil icon
  • Ensure the edit icon goes directly to editing the related section

…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
Copilot AI review requested due to automatic review settings April 13, 2026 22:03
@changeset-bot

changeset-bot Bot commented Apr 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7eae81f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
emdash Minor
@emdash-cms/admin Minor
@emdash-cms/cloudflare Minor
@emdash-cms/auth Minor
@emdash-cms/blocks Minor
@emdash-cms/gutenberg-to-portable-text Minor
@emdash-cms/x402 Minor
create-emdash Minor
@emdash-cms/plugin-embeds Patch

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

@pkg-pr-new

pkg-pr-new Bot commented Apr 13, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@546

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@546

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@546

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@546

emdash

npm i https://pkg.pr.new/emdash@546

create-emdash

npm i https://pkg.pr.new/create-emdash@546

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@546

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@546

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@546

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@546

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@546

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@546

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@546

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@546

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@546

commit: 7eae81f

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-ref rendering via new SectionRef.astro, and promote getSectionById(id) to a public API (with getSectionByIdWithDb for admin/server usage).
  • Admin: add a TipTap sectionRef node + 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.

Comment on lines +24 to +31
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>
Comment on lines +78 to +81
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.
@github-actions

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

@github-actions

Copy link
Copy Markdown
Contributor

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.

@github-actions github-actions Bot added stale and removed stale labels Apr 29, 2026
@github-actions github-actions Bot added stale and removed stale labels May 14, 2026
@github-actions github-actions Bot added stale and removed stale labels May 28, 2026
@github-actions github-actions Bot added the review/awaiting-author Reviewed; waiting on the author to respond label May 30, 2026
@github-actions github-actions Bot added stale and removed stale labels Jun 13, 2026
@github-actions github-actions Bot added stale and removed stale labels Jun 27, 2026
@ascorbic ascorbic closed this Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants