Skip to content

@W-22849705 feat(content): support Page Designer content blocks (fragments)#500

Open
clavery wants to merge 2 commits into
mainfrom
work/W-22849705
Open

@W-22849705 feat(content): support Page Designer content blocks (fragments)#500
clavery wants to merge 2 commits into
mainfrom
work/W-22849705

Conversation

@clavery

@clavery clavery commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds first-class support for Page Designer content blocks — reusable, shared fragment.*-typed content — across the SDK, CLI, and VS Code extension. Previously the SDK bucketed these as components, so they were invisible as a distinct, shared concept.

This work item was a title-only stub ("Support 'Content Blocks' in Content Topic and Page Designer tree views"); the data model was established by exploring a live instance (zzpq-023 MarketStreet site library).

What a content block is (verified against the instance)

  • A <content> element typed fragment.* (e.g. fragment.Content.contentCard, fragment.Layout.grid) with a <display-name>.
  • A shared singleton: one definition, N incoming <content-link>s. The same block — and its identical children — appears everywhere it's linked; there is no per-placement override.
  • A Layout block keeps its regions; its region children stay plain component.* (converting a block does not convert its children). Blocks can nest inside other blocks.
  • "Convert to Content Block" rewrites an element's <type> from component.* to fragment.* and adds a <display-name> on the same content object — same content-id, no link rewiring.
  • No XSD change needed: the instance's library.xsd (26.6) is byte-identical to the bundled copy and fragment.* already validates.

Changes

SDK (@salesforce/b2c-tooling-sdk)

  • Add FRAGMENT to LibraryNodeType; classify fragment.* content as FRAGMENT.
  • Parse and expose LibraryNode.displayName.
  • Add Library.getContentBlocks() — a deduplicated catalog of a library's blocks, including unlinked ones (scans the full content set, not just the linked tree).
  • Render blocks distinctly in getTreeString ((CONTENT BLOCK: <typeId>), magenta); promote and count fragments in exportContent (new fragmentCount).

CLI (@salesforce/b2c-cli)

  • content export/content list render blocks distinctly and count them separately.
  • content list --type fragment lists the deduplicated catalog.

VS Code extension (b2c-vs-extension)

  • Per-library Content Blocks group is the single source of truth for a block (full child subtree). Because blocks are shared singletons, every page/component that links a block shows a non-expanding reference (↗) that reveals the canonical block in the group — so a block is only ever edited in one place.
  • Right-click Convert to Content Block on a component (rewrites <type> + adds display-name, re-imports the element).
  • Extract a reusable importLibraryXML helper; remove a leftover debug temp-file write in content-fs-provider.ts.

Docs/skills — updated the CLI content reference, the VS Code extension overview, and the b2c-content skill.

Notes

  • The extension tree/convert UI builds and typechecks and reuses the proven siteArchiveImport path, but could not be exercised in the Extension Dev Host headlessly — confirm the tree rendering and convert round-trip interactively before release.
  • convertToBlock is a prototype write-action; it mirrors the platform's observed mutation.

…ments)

Content blocks are reusable, shared `fragment.*`-typed Page Designer content.
Previously the SDK bucketed them as components, so they were invisible as a
distinct, shared concept across the SDK, CLI, and VS Code extension.

SDK (@salesforce/b2c-tooling-sdk):
- Add `FRAGMENT` to `LibraryNodeType`; classify `fragment.*` content as FRAGMENT
- Parse and expose `LibraryNode.displayName` (content blocks always have one)
- Add `Library.getContentBlocks()`: deduplicated catalog of a library's blocks,
  including unlinked ones (scans full content set, not just the linked tree)
- Render blocks distinctly in `getTreeString` ("(CONTENT BLOCK: <typeId>)", magenta)
- Promote and count fragments in `exportContent` (new `fragmentCount`)

CLI (@salesforce/b2c-cli):
- `content export`/`list` render blocks distinctly and count them separately
- `content list --type fragment` lists the dedup catalog via getContentBlocks

VS Code extension (b2c-vs-extension):
- Per-library "Content Blocks" group is the single source of truth for a block
  (full child subtree). Because blocks are shared singletons, every page/component
  that links a block shows a non-expanding reference (↗) that reveals the canonical
  block in the group — so a block is only ever edited in one place
- Right-click "Convert to Content Block" rewrites a component's <type> to
  fragment.* + adds a display-name and re-imports the element
- Extract reusable importLibraryXML helper; remove leftover debug temp-file write

Verified end-to-end against the zzpq-023 MarketStreet site library (leaf, Layout,
shared, nested, and unlinked content blocks). The bundled library.xsd is
byte-identical to the instance's 26.6 schema (fragment.* already validates), so no
schema change was needed.
@clavery clavery requested a review from wei-liu-sf as a code owner June 15, 2026 00:08
…te+recreate+relink

The initial convert prototype rewrote an element's <type> and re-imported it,
but live testing against instances proved a site-archive MERGE import silently
ignores <type> changes on an existing content element (verified by importing a
real fragment from one instance into another — the element stayed a component).

Empirically characterized the platform's actual contract (zzpq-019/013/023):
- IMPEX can CREATE a net-new fragment element fine.
- IMPEX merge will NOT change an existing element's type.
- `mode="delete"` on a content element DEEP-deletes its whole subtree AND strips
  every incoming content-link.
- So a faithful in-place conversion (matching Page Designer's "Convert to Content
  Block") requires, in ONE archive: delete the target, recreate it as fragment.*,
  recreate all descendants (cascade-deleted), and re-import every referrer so
  incoming content-links survive.

Implements that as `Library.buildContentBlockConversionXML(contentId, displayName)`:
- delete marker + recreated fragment (display-name first for XSD order; own region
  content-link types flipped component.<base>.<region> -> fragment.<base>.<region>)
- all descendants recreated; all referrers re-imported
The extension convert command now builds this archive via the SDK and imports it.

Verified end-to-end: the SDK-built archive imported into zzpq-013 produces an
element BYTE-IDENTICAL to a manual BM/PD conversion, with the parent's
content-link preserved. Added SDK tests (archive structure: delete/recreate/
descendants/referrers, region-link rename) including xmllint validation against
the bundled library.xsd.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant