Skip to content

Phase 2: Capture Suggest-mode edits as an in-memory overlay#77404

Open
adamsilverstein wants to merge 10 commits into
suggest-mode-phase-1from
suggest-mode-phase-2
Open

Phase 2: Capture Suggest-mode edits as an in-memory overlay#77404
adamsilverstein wants to merge 10 commits into
suggest-mode-phase-1from
suggest-mode-phase-2

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Apr 16, 2026

Copy link
Copy Markdown
Member

Overview

This is one of 5 stacked PRs implementing Suggest mode for the WordPress editor — a Google Docs–style workflow where reviewers can propose changes that the post author can Accept or Reject. Tracked in #73411, with design direction from #73410 and jasmussen's mockups.

This PR (Phase 2)

Builds the in-memory suggestion overlay:

  • SuggestionOverlayProvider — React context that stores pending attribute changes per clientId, plus a baseline snapshot captured on the first edit. The block-editor store is never written.
  • withSuggestionOverlay HOC — registered as an editor.BlockEdit filter. The HOC is split into an outer useSelect wrapper and an inner SuggestingBlockEdit component: non-Suggest intents run a single useSelect and return the wrapped BlockEdit untouched, so the overlay's context lookup, refs, and merge memo add no measurable overhead on timing-sensitive e2e specs.
  • Attribute merging — the block receives { ...realAttributes, ...overlayAttributes } for rendering, so the user sees their in-progress change live. Object-valued attributes (style, metadata) are one-level merged so untouched fields survive.
  • Orphan pruning — when a block is removed from the editor, its overlay entry is dropped from the reducer.
  • Re-capture after clear — the baseline-captured signal lives on the reducer's entry map (not a local ref), so Submit/Discard/orphan-prune cleanly resets it and a later edit captures a fresh baseline.

What's NOT in this PR

  • The _wp_suggestion comment meta and REST plumbing (Phase 3)
  • Accept/Reject controls in the sidebar (Phase 3)
  • Auto-save from the overlay to a note comment (Phase 5)
  • The green bracket / block marker (Phase 5)

Phase 2 test plan

  • Switch to Suggest mode; type into a Paragraph; confirm the block renders your change
  • In the DevTools console run wp.data.select('core/editor').getEditedPostContent() and confirm the serialized content does not include your suggested text
  • Switch back to Edit mode; confirm real edits once again write to the store
  • In a large post, toggle between Edit and Suggest; typing responsiveness should be unchanged in Edit mode
  • Unit: npm run test:unit -- packages/editor/src/components/suggestion-mode/test/overlay-context.js
  • Unit: npm run test:unit -- packages/editor/src/components/suggestion-mode/test/with-suggestion-overlay.js

🗺️ PR Stack Navigation

# PR Phase
1 #77403 Intent scaffolding Edit / Suggest / View mode
2 #77404 Overlay capture ← this PR In-memory suggestion overlay
3 #77405 Provider + Accept/Reject _wp_suggestion meta, provider, sidebar actions
4 #77406 Summary + docs + attribute tests Add/Delete/Formatting summary, architecture stub, conflict scoping
5a #78351 REST permissions and PHP coverage Permissions, payload cap, PHP tests
5b #78352 Summary + attribute conflict + docs Renderer, per-attribute staleness, architecture docs
5c #78353 Surface Apply/Reject in the collaboration sidebar Icon buttons + e2e + sidebar wiring
6 #78308 Auto-save subsystem Background debounced save (replaces commit-bar)

📋 Tracking issue: #73411

@github-actions

github-actions Bot commented Apr 16, 2026

Copy link
Copy Markdown

Size Change: +2.84 kB (+0.04%)

Total Size: 7.51 MB

📦 View Changed
Filename Size Change
build/scripts/editor/index.min.js 478 kB +2.84 kB (+0.6%)

compressed-size-action

@adamsilverstein adamsilverstein force-pushed the suggest-mode-phase-2 branch 3 times, most recently from 8043af8 to b1af20a Compare April 16, 2026 19:13
@adamsilverstein adamsilverstein force-pushed the suggest-mode-phase-2 branch 2 times, most recently from 6ae1fa6 to b09c96d Compare April 17, 2026 01:17
@github-actions

github-actions Bot commented Apr 17, 2026

Copy link
Copy Markdown

Flaky tests detected in 85c543b.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/28125974011
📝 Reported issues:

@adamsilverstein adamsilverstein added [Type] Enhancement A suggestion for improvement. [Feature] Notes Phase 3 of the Gutenberg roadmap around block commenting labels Apr 19, 2026
Comment thread lib/compat/wordpress-6.9/block-comments.php Outdated
@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: adamsilverstein <adamsilverstein@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@adamsilverstein adamsilverstein added the [Status] In Progress Tracking issues with work in progress label Apr 29, 2026
@adamsilverstein adamsilverstein requested review from Mamaduka and t-hamano and removed request for spacedmonkey April 29, 2026 17:20
Add the suggestion-overlay subsystem that powers the `suggest` editor
intent added in the previous step. An `editor.BlockEdit` filter wraps
every block; when the intent is `suggest` it diverts `setAttributes`
into an overlay reducer keyed by `clientId` and passes merged
`{ ...real, ...overlay }` attributes into the block for rendering.
The block-editor store is never touched, so autosave, undo, and RTC
sync stay at the real baseline.

A `SuggestionCommitBar` exposes Submit / Discard controls in the
block toolbar when the selected block has a pending overlay. Submit
creates a note comment carrying a versioned `_wp_suggestion` JSON
payload (`blockName`, `baseRevision`, `operations`) via REST; discard
clears the overlay. The `applySuggestion` / `rejectSuggestion` hooks
are stubbed behind the same provider interface so Phase 3 can plug in
without UI changes, and a future Yjs AttributionManager-backed
provider can drop in with the same shape.

PHP-side registers `_wp_suggestion` and `_wp_suggestion_status`
comment meta with REST exposure. Both require `edit_comment`; no new
routes are added.

Refs #73411
Move the baseline-captured signal from a local ref onto the reducer's
entry map so Submit/Discard/orphan-prune cleanly resets it — a second
edit after the overlay is cleared now reliably captures a new baseline
instead of silently no-op'ing on the stale ref.

Split the HOC into an outer Suggest-mode check and an inner
SuggestingBlockEdit component. The overlay's context lookup, refs, and
merge memo only run when intent === suggest; in Edit/View the wrapper
executes a single useSelect and returns the original BlockEdit
untouched, removing measurable per-render overhead on timing-sensitive
e2e specs running on shards 4/5.

Add unit coverage for View-intent pass-through and for re-capturing a
baseline after the overlay is cleared.
Adds a store-level interceptor for direct updateBlockAttributes calls
(block-switcher variation picker bypasses the BlockEdit setAttributes
HOC), replaces the JSON.stringify equality with a recursive structural
compare so reordered keys in 'style'/'metadata' don't produce spurious
'changed' detections, and rejects oversized suggestion payloads at the
sanitize_callback rather than truncating them into invalid JSON.

Also gates the orphan-prune subscription behind hasEntries, makes the
PRUNE_ORPHANS action serializable for Redux DevTools, mirrors a payload
size cap on the client, and adds tests for the new helpers.
…ceptor

The store interceptor reverts any direct `updateBlockAttributes` mutation
in Suggest mode so user-driven bypasses (e.g. the block-switcher variation
picker) can't sneak past the overlay. It was also reverting the
provider's programmatic `metadata.noteId` write that links a freshly-
created note comment back to its block, leaving every suggestion-derived
note orphaned in the sidebar with both "Original block deleted." and
"Target block has been deleted." notices.

Treat `metadata.noteId` as a system passthrough: fold it from the live
attributes into the snapshot before diffing so the value is invisible to
the diff and preserved in the revert payload, and strip it from the
overlay-bound payload so it never leaks into the user's pending edits.
Other metadata keys still go through the regular diff/revert path.

Add unit tests for the new helpers and integration tests that register
the actual block-editor store, mount the interceptor in Suggest mode,
and confirm a programmatic noteId write persists on the live block —
both alone and alongside an unrelated user-style content mutation.
The success snackbar was passing 'snackbar' as the first argument to
createNotice, but that argument is the notice status. Use 'success'
to match the valid status set; the 'snackbar' display style is still
applied via the options.type field.
…sign

Document the role and lifecycle of the in-memory overlay, the
store-interceptor's snapshot/diff/revert strategy, and the comment-meta
storage model so reviewers can understand the architecture without
reverse-engineering it from the implementation. Also document the
note-permission remap on the REST controller subclass.

Adds an eslint suppression entry for store-interceptor.js to match the
existing pre-existing react-hooks/refs violations now flagged by the
project's lint-suppression machinery.
Add lint suppressions for the recently introduced react-hooks/refs
violation in with-suggestion-overlay.js and react-hooks/globals
violation in its test, matching the pattern already used elsewhere
in the suggestion-mode directory.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backport from WordPress Core Pull request that needs to be backported to a Gutenberg release from WordPress Core [Feature] Notes Phase 3 of the Gutenberg roadmap around block commenting [Package] Editor /packages/editor [Type] Enhancement A suggestion for improvement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant