fix(blog): rich-text toolbar + inline link editor, and VTEX product picker#4882
Merged
Conversation
…editor The TipTap v3 editor no longer re-renders on transactions by default, so reading `editor.isFocused`/`isActive()` during render went stale — the toolbar never appeared on mouse selection and mark highlights lagged. Select this state reactively via `useEditorState`. Replace the native `window.prompt()` link dialog with an inline, Figma-like popover (Enter to apply, Esc to cancel, remove-link button). Extract the shared `ToolbarButton` and new `RichTextLinkControl` into one module, add `normalizeLinkUrl` (bare domains default to https), and validate blog links with `isSafeLinkUrl` (previously unchecked). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The shelf/card editors only accepted raw SKU ids typed by hand. Add a "Browse products" dialog (VTEX-only for now, like the preview path-param picker) to discover products by free search, category, or collection/cluster id — invoking `vtex/loaders/intelligentSearch/productList.ts` through the mesh preview-invoke proxy. Toggling a product writes its SKU id via the existing writeProductListIds, so the section still receives Product[] and storage is unchanged. Thread the sandbox ref (org/vm/branch) from Post/Category editors down through BlockDocument → BlockRow → BlockEditor so the blocks can invoke loaders; the picker only shows when a running sandbox is available, with manual id entry as the fallback. Pure request-builders and payload mappers are unit-tested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the raw SKU-id inputs with resolved product cards (thumbnail + name + id): the stored ids are resolved back to products via a batched productList invoke (new `useProductsByIds` hook, sharing `invokeLoader` with the picker dialog). The shelf shows a responsive card grid with drag-to-reorder and hover-to-remove, plus a dashed "Add products" empty state; ProductCard shows a compact selected-product row. Manual id entry remains the fallback when no sandbox is running. Also fix the picker modal: swap the Radix ScrollArea (whose display:table wrapper defeated truncation and let long names overflow) for a width-bounded native scroll, add horizontal padding, and clamp product names to two lines. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Let the shelf title be Normal / H1 / H2 / H3. The level is stored inside the value itself — H1 saves `<h1>Text</h1>`, Normal saves the bare `Text` — so the section (which renders the title as HTML and already styles `.section-title h1/h2/h3`) picks up the heading with no site change. The editor parses the stored value back into (level, text), previews the chosen size, and re-wraps on edit. Pure parse/format helpers are unit-tested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A post autosaves on every change, so it can end up missing required fields. Add `missingPostFields` (Title, Slug, Category, Excerpt, Cover image) shared by the list and the editor. Incomplete posts render red in the list with a warning icon (tooltip listing what's missing), and the editor shows an "N issues" badge and disables "See preview" until the post is complete — mirroring the category preview gate, now for every required field. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # apps/mesh/src/web/components/sandbox/content/content-browser.tsx
CI knip flagged the export as dead — the field labels are inlined in missingPostFields, so the constant had no consumer.
The link popover now has a "Same tab / New tab" toggle that sets the anchor's `target` (and `rel` for new-tab links). The TipTap link extension forced `target="_blank"` + rel on every link via its default HTMLAttributes, so a same-tab link was impossible; clear those defaults in both editors (paragraph block + sections-editor field) and drive target from each link's own attribute. New links default to a new tab; editing an existing link reflects its current target. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
guitavano
approved these changes
Jul 21, 2026
decocms Bot
pushed a commit
that referenced
this pull request
Jul 21, 2026
PR: #4882 fix(blog): rich-text toolbar + inline link editor, and VTEX product picker Bump type: patch - decocms (apps/mesh/package.json): 4.82.19 -> 4.82.20 Deploy-Scope: web
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Blog CMS fixes, split across two commits.
1. Rich-text toolbar + inline link editor (
fix)The paragraph TipTap editor's formatting toolbar (bold/italic/underline/link) only appeared on keyboard interaction, not on mouse selection, and the link flow used a native
window.prompt.editor.isFocused/isActive()during render went stale — the toolbar never showed on mouse selection and mark highlights lagged. Now selected reactively viauseEditorState.window.promptlink dialog with an inline, Figma-like popover (Enter to apply, Esc to cancel, remove-link button). Extracted the sharedToolbarButton+ newRichTextLinkControl, addednormalizeLinkUrl(bare domains → https), and validated blog links withisSafeLinkUrl(previously unchecked).2. VTEX product picker for ProductShelf/ProductCard (
feat)The shelf/card editors only accepted raw SKU ids typed by hand.
vtex/loaders/intelligentSearch/productList.tsthrough the mesh preview-invoke proxy.writeProductListIds, so the section still receivesProduct[]and storage is unchanged.BlockDocument→BlockRow→BlockEditorso blocks can invoke loaders. The picker only shows when a running sandbox is available; manual id entry remains the fallback.Testing
bun teston the new pure logic (link normalization/validation, picker request-builders + payload mappers) — passing.bun run fmt, oxlint, and workspace typecheck clean.🤖 Generated with Claude Code
Summary by cubic
Fixes the blog rich-text toolbar and link flow (now with a same-tab/new-tab toggle), adds a VTEX product picker with rich product cards, adds a heading level selector for ProductShelf titles, and flags incomplete posts to block preview until required fields are filled.
New Features
vtex/loaders/intelligentSearch/productList.tsandvtex/loaders/categories/tree.tsvia the mesh preview-invoke proxy. Shows only with a running sandbox; manual ID input remains the fallback. Shelf shows a sortable product grid (drag to reorder, hover to remove) and ProductCard shows a compact selected-product row; still persists SKU IDs only via a newuseProductsByIdslookup.<h1>Title</h1>) and the editor previews the size.Bug Fixes
useEditorState, so it appears on mouse selection and mark highlights stay in sync.window.promptlink flow with an inline popover (Enter to apply, Esc to cancel, remove link) and added a same-tab/new-tab toggle that setstarget(andrelfor new-tab). Cleared TipTap’s default link HTMLAttributes so each link’stargetcontrols behavior. AddednormalizeLinkUrland validated URLs withisSafeLinkUrl.REQUIRED_POST_FIELDSexport.Written for commit 8acbdef. Summary will update on new commits.