Skip to content

Global blocks: translation, preview, depth and search (gaps from #210) - #212

Merged
dogfrogfog merged 2 commits into
fix/cms-global-block-integrityfrom
claude/magical-hawking-x1zdxg
Oct 27, 2026
Merged

dogfrogfog merged 2 commits into
fix/cms-global-block-integrityfrom
claude/magical-hawking-x1zdxg

Conversation

@dogfrogfog

Copy link
Copy Markdown
Member

Addresses #210 — the remaining global-blocks gaps (parts 1, 2, 4, 5, 6).

Stacked on #211. #210 is an explicit follow-up to the correctness-fix PR #211 (revalidatePagesUsingGlobalBlock, resolveGlobalBlocks, upsertPageEmbedding, the extractPageText slot case). Those live only on #211's branch, so this PR is based on fix/cms-global-block-integrity and targets it — the diff shows only the follow-up work. Merge #211 first, then this (or retarget to main once #211 lands).

What changed

1. Translation, scheduled publishing and comments for global blocks (src/lib/plugins/index.ts)
globalBlock is now in the translatorPlugin, schedulePublicationPlugin and commentsPlugin collection lists, so a shared block can be AI-translated, scheduled, and commented on — not just the pages that embed it. No migration: scheduling rides on the existing payload_jobs collection, comments injects only admin components, and translator provenance is not enabled.

2. Standalone preview for a global block (GlobalBlock config + new route)
New route /[locale]/preview/global-block/[id] renders the single block, wired to admin.livePreview / admin.preview. The locale prefix follows the as-needed routing policy (default locale unprefixed). Lets an editor see a block before publishing it to every page that embeds it.

4. Inner relationships populated at full depth (GlobalBlockRenderer)
The page query spends one depth level on the slot relationship, so a global block's inner relationships were populated one level shallower than the same block placed directly on a page. The renderer now re-fetches the referenced block at the page render depth (PAGE_RENDER_DEPTH = 3), respecting draft + locale. Extracted into a shared GlobalBlockRenderer used by both the slot block and the preview route.

5. rawHtml indexed for semantic search (RawHtml/extractText.ts + extractPageText)
New rawHtml case strips tags and drops <script> / <style> content (the opening-tag match tolerates > inside quoted attributes) before indexing.

6. Re-index every locale on page and global-block saves (indexEmbedding + revalidatePagesUsingGlobalBlock)
Decision for the open question in part 6: yes, cover every locale, for both page saves and global-block saves — locale fallback means an en edit changes the rendered (and therefore indexable) text of es pages that fall back to en. Both re-index paths now extract at the same depth (PAGE_EMBEDDING_DEPTH = 2, threaded into resolveGlobalBlocks) so neither overwrites the other's embedding with less text, and so a block that reads a relationship (e.g. testimonialsList) is indexed.

Not included — part 3 ("make this block global" action)

Left for a dedicated follow-up. It's an interactive admin-form mutation (create a globalBlock from a page block's data, then swap the block for a globalSectionSlot via Payload's form replaceFieldRow API) that needs live iteration in the admin panel to get right — not something to land unverified in a batch PR. This PR does not close #210 for that reason.

How it was verified

  • bun run check-types — passes across the whole repo.
  • bun run lint (ultracite check) — 0 warnings / 0 errors on changed files.
  • bun run build — compiles successfully (✓ Compiled successfully); the build only stops at "Collecting page data" because this sandbox can't reach the Neon database. CI / the Vercel preview run the full build + migrations.
  • Self-review: /code-review and /simplify on the diff; findings addressed (locale-prefix in the preview path, consistent embedding depth across re-index paths, hidden-block parity between render and index, hardened script/style stripping).
  • Adversarial unit check of the HTML stripper (incl. > inside a script attribute) — script/style content fully dropped.

Design note for the reviewer (part 4)

The slot renderer re-fetches the referenced block at PAGE_RENDER_DEPTH so inner relationships reach parity with page blocks. An alternative is to resolve slots in the data layer (getPageBySlugQuery → resolveGlobalBlocks with a draft option), which would batch the fetch and ride the page cache. I kept the renderer-side fetch here because resolveGlobalBlocks is request-scoped (req.payload) while the render DAL is payload-client/cache-scoped, so unifying would mean reworking that shared resolver and its draft handling — more surface than I could runtime-verify without a DB. Happy to switch to the data-layer approach if you'd prefer it.

Please test manually (needs the admin + a DB)

  • Admin Preview / Live Preview on a Global Block renders the block (draft before publish; both en default-unprefixed and es prefixed URLs).
  • Translate / schedule / comment controls appear on a Global Block.
  • Editing a global block re-indexes search for every affected page and locale; searching finds rawHtml text and global-block content.

Generated by Claude Code

Follow-up to the global blocks correctness fixes (#211). Addresses the
remaining gaps from #210, parts 1, 2, 4, 5 and 6:

- Wire `globalBlock` into the translator, scheduling and comments plugins
  so a shared block can be AI-translated, scheduled and commented on.
- Add a standalone preview route (`/[locale]/preview/global-block/[id]`)
  and wire `admin.livePreview` / `admin.preview`, so an editor can see a
  global block before publishing it to every page that embeds it.
- Re-populate the slot reference at the page render depth so a global
  block's inner relationships get the same population as page blocks.
- Index `rawHtml` block text for semantic search, stripping tags and
  dropping `<script>` / `<style>` content.
- Re-index every locale on page and global block saves, because locale
  fallback can change the rendered text of locales other than the edited
  one. Both re-index paths now extract at the same depth so neither
  overwrites the other's embedding with less text.

Part 3 (a "make this block global" admin action) is intentionally left
for a dedicated follow-up — it is an interactive admin-form mutation that
needs live iteration in the panel to get right.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0166RzePhuVEPA8r65QFJV5J
@vercel

vercel Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ideal-cms Ready Ready Preview Oct 27, 2026 1:17pm UTC

Request Review

Resolve overlap with the new upstream commit (shared `isPublishedGlobalBlock`
/ `collectGlobalBlockIds`, the draft-only-save skip, and the resolveGlobalBlocks
refactor):
- Slot renderer keeps the GlobalBlockRenderer re-fetch (depth parity) but now
  gates on the shared `isPublishedGlobalBlock`.
- `extractPageText` indexes the published block's first *visible* inner block.
- `resolveGlobalBlocks` keeps the upstream refactor plus the `depth` param.
- The global-block revalidation hook keeps upstream's publish-change guard and
  uses `PAGE_EMBEDDING_DEPTH` when loading pages for re-indexing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0166RzePhuVEPA8r65QFJV5J
@dogfrogfog
dogfrogfog merged commit 8e611dc into fix/cms-global-block-integrity Oct 27, 2026
2 checks passed
@dogfrogfog

Copy link
Copy Markdown
Member Author

Consolidated into #211 (commit 8e611dc), which is why GitHub marks this as merged. Kept: plugins, preview route, render depth, rawHtml search, page-save re-index in every locale. Changed: global block text stays out of page search vectors, so the global block re-index path and resolveGlobalBlocks are dropped; embedding depth is 1; stripHtml also handles quoted > in normal tags and plain </> text; the renderer uses disableErrors instead of .catch(() => null).

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.1.1 🎉

The release is available on npm package (@latest dist-tag)

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 0.18.1 🎉

The release is available on npm package (@latest dist-tag)

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.4.0 🎉

The release is available on npm package (@latest dist-tag)

Your semantic-release bot 📦🚀

This branch was successfully deployed

1 active deployment
Preview — 3fb90981 Deployed Oct 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants