Skip to content

Render block-move pending state - #77979

Closed
adamsilverstein wants to merge 14 commits into
suggest-mode-6c-move-mechanismfrom
suggest-mode-6c-move-ui
Closed

adamsilverstein wants to merge 14 commits into
suggest-mode-6c-move-mechanismfrom
suggest-mode-6c-move-ui

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented May 5, 2026 •

Copy link
Copy Markdown
Member

What

Adds the visual treatment for block-move suggestions (#77434, task 6c). Pairs with the mechanism PR (#77978).

  • A block flagged with metadata.suggestion = { type: 'pending-move' } renders at 70% opacity with a dotted green outline at its new position plus a "Suggested move" label tab. Three distinct outline styles communicate three distinct kinds of structural change: solid bracket (attribute edit), dashed (insert), dotted (move).
  • The sidebar shows a "Move block: paragraph" summary and a "Moved from to " descriptor sentence.

How

  • style.scss: pending-move styling (70% opacity + dotted green outline + label tab).
  • suggestion-summary.js: handles block-move ops with a "Move block: " line.
  • suggestion-diff.js: BlockMoveDiff renders a from→to descriptor.

Cross-cutting polish (applies to all three marker types)

The last few commits on this branch are integration-test polish that emerged after all three structural marker types (pending-remove, pending-insert, pending-move) were stacked together. They live on this PR rather than being split across 6a / 6b / 6c because the canvas treatment is shared infrastructure — splitting them by marker type would be artificial:

  • Move canvas styles into the block-editor content bundle — the is-suggestion-pending-* rules previously lived in the editor-package stylesheet (wp-edit-blocks), which is not loaded into the canvas iframe. Only wp-block-editor-content (compiled from block-editor/src/content.scss) is. The marker class landed on the DOM but no rule matched. Fix: move the canvas-targeted rules into a new block-editor/src/components/block-list/content-suggestion.scss partial.
  • Strike through every line of pending-remove and switch to Google Docs green (#188038) — the previous ::after line-through only struck the midline of multi-line blocks; text-decoration: line-through on the wrapper covers every line. Applies the same green Google Docs uses for suggestions.
  • Show pending-insert content on the reviewer's canvas — pending-insert blocks bypass the suggestion overlay so the suggester's typed content syncs through CRDT (instead of being trapped in the suggester's local overlay), and the inserted text renders in suggestion green.
  • Tint pending-suggestion previews with the suggester's avatar color — each pending-* marker captures the suggester's user id; the canvas HOC pipes it through getAvatarBorderColor into a --suggestion-author-color CSS variable so individual suggestions read as that suggester's color (the same palette live cursors use). Falls through to the green default when the id isn't available.

Testing

  • 153 unit tests pass. Lint and stylelint clean.
  • For full-stack testing instructions, see the foundations PR (#77967).

This is the final PR in the stack — once merged, structural suggestions for insert / remove / move are fully shipped under issue #77434. Phase 6d (Yjs AttributionManager-backed) tracks separately.

Stack

  1. #77967 — Schema v2 bump (foundations)
  2. #77968 — Capture block-remove (mechanism)
  3. #77970 — Render block-remove preview (UI)
  4. #77971 — Capture block-insert-after (mechanism)
  5. #77973 — Render block-insert-after preview (UI)
  6. #77978 — Capture block-move (mechanism)
  7. #77979 — Render block-move preview (UI)

Refs #77434, #73411.

@github-actions

github-actions Bot commented May 5, 2026 •

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>
Co-authored-by: danluu <danluu@git.wordpress.org>
Co-authored-by: t-hamano <wildworks@git.wordpress.org>
Co-authored-by: saroshaga <saroshaga@git.wordpress.org>

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

@github-actions

github-actions Bot commented May 5, 2026 •

Copy link
Copy Markdown

Size Change: +534 B (+0.01%)

Total Size: 7.52 MB

📦 View Changed
Filename Size Change
build/scripts/editor/index.min.js 486 kB +125 B (+0.03%)
build/styles/block-editor/content-rtl.css 5.82 kB +262 B (+4.71%) 🔍
build/styles/block-editor/content-rtl.min.css 4.38 kB +249 B (+6.02%) 🔍
build/styles/block-editor/content.css 5.82 kB +260 B (+4.67%) 🔍
build/styles/block-editor/content.min.css 4.38 kB +255 B (+6.18%) 🔍
build/styles/editor/style-rtl.css 31 kB -152 B (-0.49%)
build/styles/editor/style-rtl.min.css 26.4 kB -153 B (-0.58%)
build/styles/editor/style.css 31 kB -156 B (-0.5%)
build/styles/editor/style.min.css 26.3 kB -156 B (-0.59%)

compressed-size-action

@github-actions

github-actions Bot commented May 5, 2026 •

Copy link
Copy Markdown

Flaky tests detected in 0cb3aee.
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/28139684210
📝 Reported issues:

@adamsilverstein
adamsilverstein requested a review from ellatrix as a code owner May 6, 2026 14:09
@github-actions github-actions Bot added the [Package] Block editor /packages/block-editor label May 6, 2026
@adamsilverstein

Copy link
Copy Markdown
Member Author

Current approach: uniform Google Docs green + decoration-driven preview

The pending-suggestion canvas treatment was just simplified to a single suggestion color (#188038, the green Google Docs uses for suggestions) applied uniformly across all three structural marker classes:

  • is-suggestion-pending-remove → green text + text-decoration: line-through so every line of a multi-line block is struck through (the previous ::after horizontal rule only crossed the midline).
  • is-suggestion-pending-insert → dashed green outline + reduced opacity.
  • is-suggestion-pending-move → dotted green outline + a green "Suggested move" label tab pinned to the top-left corner.

These rules now live in packages/block-editor/src/components/block-list/content-suggestion.scss (compiled into wp-block-editor-content, which is what the canvas iframe actually loads). They previously lived in packages/editor/src/components/suggestion-mode/style.scss, which compiles into wp-edit-blocks — the editor chrome stylesheet — and is never loaded inside the iframe. That's why the marker classes were landing on the DOM but nothing was rendering for a while.

Why this is enough for most cases

The combination of (a) a saturated, semantically loaded color and (b) a decoration that's specific to the op type (strikethrough / dashed / dotted + label) gives the reviewer two independent signals: "this is a suggestion" and "this is the kind of suggestion it is." Both are visible at a glance without resolving any author identity, which means it works correctly even when the suggester is offline or anonymous.

Possible follow-up: per-author tinting

A nicer refinement (closer to actual Google Docs behavior) would tint each suggestion in the suggester's own color, the same color their cursor and avatar already use during live collaboration. That gives the reviewer a third signal — who suggested this — without an extra UI element to read.

The infrastructure already exists: the collab presence system writes --collaborator-outline-color onto each block based on who's actively editing it, and the same per-user color resolver could be reused for suggestion tinting. Roughly:

  1. Capture author at marker time. withSuggestionMarker in packages/editor/src/components/suggestion-mode/store-interceptor.js currently writes { type, commentId }. Extend the marker to { type, commentId, authorId }, sourced from getCurrentUser() (or, more precisely, from the suggesting peer's auto-save context — that path already knows who the suggester is).
  2. Render an author-color custom property on the block. In withSuggestionBlockClassName (packages/editor/src/components/suggestion-mode/with-suggestion-overlay.js), when a structural marker is present, also write style={ { ...props.style, '--suggestion-author-color': resolveAuthorColor(authorId) } }. The collab system already has the resolver — we'd be plugging into it rather than reinventing.
  3. Have the canvas SCSS consume the variable. Replace every $suggestion-color reference in content-suggestion.scss with var(--suggestion-author-color, #{$suggestion-color}). The current #188038 stays as the fallback for solo-edit and pre-collab cases.

Why it might be worth it

  • Matches Google Docs' actual behavior more closely.
  • Reviewers can immediately see who suggested what, which scales better than a single uniform color when several collaborators are suggesting in the same document.
  • Maintenance cost is low because the per-user color is already a solved problem in the collab layer — we'd be consuming an existing variable, not introducing a new color-management surface.

Holding off on this for now; flagging here so we can pick it up after the structural-suggestion stack lands.

@saroshaga saroshaga left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for working on this and all the other PRs, @adamsilverstein!

I tested this in Playground with ?gutenberg-pr=77979.

The structural suggestion flows worked for me:

  • Block move creates a dotted outline + “Suggested move” tab.
  • Block insert creates a dashed outline.
  • Block remove creates colored text with line-through.
  • The Notes sidebar exposes Accept/Reject controls, and the states update after disposition.

Structural block suggestions:

image

One usability confusion I hit: “remove block” and “delete selected text” look/behave very differently.

When I remove an entire block in Suggest mode, I see the expected structural marker: the block remains visible with colored line-through text. But when I select text inside a paragraph and delete it while in Suggest mode, the deleted text disappears from the canvas and the block only gets the generic suggestion bracket.

Inline text deletion suggestion:

image

As a tester, I initially expected deleted inline text to remain visible with strikethrough, similar to Google Docs/Word suggestions. If inline deleted-text visualization is intentionally out of scope for this PR, I think it would help to call that out in the testing notes, e.g.:

  • “Remove block” tests the structural block-remove marker.
  • “Delete selected text inside a block” is a text/attribute suggestion and does not show the same block-remove strikethrough marker.

A couple of smaller notes from testing this PR:

  • The move suggestion card in the sidebar showed Move block: paragraph, but I did not see an obvious from/to descriptor in the visible card. I had to inspect the canvas to understand where the block moved.
  • The floating block toolbar can obscure the block-remove line-through marker, which makes the removed content harder to inspect while the block is selected.

@adamsilverstein

Copy link
Copy Markdown
Member Author

Thanks for testing @saroshaga !

As a tester, I initially expected deleted inline text to remain visible with strikethrough, similar to Google Docs/Word suggestions. If inline deleted-text visualization is intentionally out of scope for this PR, I think it would help to call that out in the testing notes, e.g.:

Yes, that makes sense! I worked on the previewing of these inline changes separately in #77869 - I realized that was confusing so I left a comment explaining how to test the complete feature and how I have broken it up here: #73411 (comment) - in short gh pr checkout 77979 should get you the complete stack - I will also double check that works and verify the inline previews are also working, I've iterated on that several times and may have broken it in a recent change, I will test.

A couple of smaller notes from testing this PR:

The move suggestion card in the sidebar showed Move block: paragraph, but I did not see an obvious from/to descriptor in the visible card. I had to inspect the canvas to understand where the block moved.

What do you think this could say? "Moved up three positions" or "moved from below" or just "moved up". I'm a little unsure what context we could really provide here that would help users. Maybe a visual treatment on the block? eg. a moved block could show the old position as a strike out/removed version and connect the two with an arrow.

The floating block toolbar can obscure the block-remove line-through marker, which makes the removed content harder to inspect while the block is selected.

We don't control the toolbar directly in floating mode, but maybe something in the styling could improve the placement.

@adamsilverstein

Copy link
Copy Markdown
Member Author

Yes, that makes sense! I worked on the previewing of these inline changes separately in #77869 - I realized that was confusing so I left a comment explaining how to test the complete feature and how I have broken it up here: #73411 (comment) - in short gh pr checkout 77979 should get you the complete stack - I will also double check that works and verify the inline previews are also working, I've iterated on that several times and may have broken it in a recent change, I will test.

Hmm, those changes should be in suggest-mode-6c-move-ui, as well, checking...

@adamsilverstein

adamsilverstein commented May 12, 2026 •

Copy link
Copy Markdown
Member Author

I created a new feature branch for testing the complete feature with all the parts...

Testing the complete feature

To exercise the complete Suggest-mode feature (this structural stack + inline previews #77869) in one shot, check out the try/suggest-mode-combined integration branch:

git fetch origin try/suggest-mode-combined && git checkout try/suggest-mode-combined
npm install && npm run build
npm run wp-env start

Testing-only - code review still happens on the individual PRs.

Testing with Playground

The complete feature is testable with the combined PR in Playground: https://playground.wordpress.net/gutenberg.html?pr=78994 (test pr is #78994)

@saroshaga

Copy link
Copy Markdown

Yes, that makes sense! I worked on the previewing of these inline changes separately in #77869

awesome, thanks!

What do you think this could say? "Moved up three positions" or "moved from below" or just "moved up". I'm a little unsure what context we could really provide here that would help users. Maybe a visual treatment on the block? eg. a moved block could show the old position as a strike out/removed version and connect the two with an arrow.

A ghost placeholder block! I like it :D

Moved up / down was something that I felt could be useful. If we can positions, that's also neat.

I should've added that this is minor.

@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-ui branch from 3bb3ba7 to 2ca8de9 Compare May 16, 2026 01:11
@adamsilverstein
adamsilverstein removed the request for review from spacedmonkey May 18, 2026 16:19
@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-ui branch from 2ca8de9 to f2d1c3a Compare June 17, 2026 16:43
@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-mechanism branch from 43d6215 to 7d1657c Compare June 17, 2026 20:19
@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-ui branch from f2d1c3a to 688d72e Compare June 17, 2026 20:19
@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-mechanism branch from 7d1657c to 75a6a73 Compare June 17, 2026 21:06
@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-ui branch from 688d72e to 8b90242 Compare June 17, 2026 21:06
@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-mechanism branch from 75a6a73 to 0828f5c Compare June 17, 2026 21:13
@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-ui branch from 8b90242 to b5e52e8 Compare June 17, 2026 21:13
@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-mechanism branch from 0828f5c to 9fec5ed Compare June 17, 2026 21:20
@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-ui branch from b5e52e8 to ce3c626 Compare June 17, 2026 21:20
adamsilverstein and others added 9 commits June 18, 2026 08:33
Brings the visual treatment for block-move suggestions:

- style.scss adds the pending-move treatment: 70% opacity plus a
  dotted green outline. The dotted style distinguishes it from the
  dashed pending-insert (the block existed before — only its position
  is suggested) and the solid bracket used by pending attribute edits.

- suggestion-summary.js adds a "Move block: <name>" line via the same
  friendlyBlockName helper used by the other structural ops.

- suggestion-diff.js adds BlockMoveDiff, which renders a "Moved <name>
  from <position> to <position>" sentence so reviewers can verify the
  proposed motion without the canvas open. Block content stays the
  same — only its position changed — so a content diff would just be
  noise.

Refs #77434.
The dotted outline plus 70% opacity alone was not strong enough to
unambiguously communicate "this is a preview" on the reviewer's screen.
On a fresh load the moved block can read like a regular drag-and-drop
result, especially in intents that don't tint the canvas with Suggest-
mode chrome.

Add a small green tab pinned to the top-left corner that reads
"Suggested move" so the preview state is obvious from the canvas
without having to open the sidebar.
The structural-marker class HOC reads metadata.suggestion.type from the
block-editor store and is intentionally not gated on isSuggestMode — a
reviewer in Edit intent must still see the pending-remove strikethrough,
the pending-insert dashed outline, and the pending-move dotted outline,
since the marker is the only visual signal that a structural change is
proposed but not yet applied.

The existing test coverage stopped at the marker→class mapping table,
so a regression in the gating could have shipped silently. Lift the
HOC to a named export (was previously only registered as a filter) and
add cases for each marker type in both Edit and Suggest intents.
… bundle

The suggestion-mode visual treatment (`is-suggestion-pending`,
`is-suggestion-pending-remove`, `is-suggestion-pending-insert`,
`is-suggestion-pending-move`) targets `.block-editor-block-list__block`
inside the editor canvas iframe. Previously these rules lived in the
editor-package stylesheet (`wp-edit-blocks`), which is not loaded into
the iframe — only `wp-block-editor-content` (compiled from
`block-editor/src/content.scss`) is. As a result the marker class
landed on the DOM but no rule matched, so the preview chrome was
invisible.

Move the canvas-targeted rules to a new partial under
`block-editor/src/components/block-list/content-suggestion.scss` and
include it from `block-editor/src/content.scss`. The class names
describe a block visual state, so block-editor styling them does not
violate layering: it doesn't need to know how the marker arrived. The
sidebar/diff/header rules stay in the editor package since they target
chrome surrounding the iframe rather than blocks within it.
…o Google Docs green

The previous treatment dimmed the block to 50% opacity (which read as
"greyed out" rather than "proposed for deletion") and drew a single
horizontal `::after` line at vertical center, which only struck through
whichever line of text happened to fall on the midline.

Apply `text-decoration: line-through` directly so every line of a
multi-line block gets struck through, and switch the suggestion color to
the same green Google Docs uses (`#188038`) for a clearer, consistent
"this is a suggestion" cue across remove/insert/move treatments.
When a suggester adds a new block in Suggest mode, the block-insert-after
detector tags the live block with `metadata.suggestion = pending-insert`,
but any subsequent `setAttributes` calls (the suggester typing into the
block) get diverted into the per-peer suggestion overlay. The reviewer
therefore sees the inserted block as empty — the typed content is
trapped on the suggester's peer.

A pending-insert block has no \"before\" state worth preserving — the
block itself is the suggestion, and the suggester is the only author
of its attributes. Bypass the overlay for any block whose marker is
`pending-insert` so edits flow through to the real attributes and sync
via CRDT, then color the inserted block's text in suggestion green so
the reviewer sees a clear preview of what's being added (matching
Google Docs' suggesting-mode insert treatment).

The overlay test helper was registering `blockEditorStore` for every
test, which silently activated the provider's orphan-prune effect and
deleted overlay entries whose synthetic `clientId` had no matching
block. Register `blockEditorStore` only when a test explicitly passes
`blocks`, so the existing `clientId=\"a\"` tests keep working.
…atar color

Capture the suggester's user id when writing each pending-* marker, and
have the canvas HOC pipe that id through `getAvatarBorderColor` into a
`--suggestion-author-color` CSS variable on the wrapper. The
`is-suggestion-pending-*` rules consume the variable with the previous
green as the fallback, so individual suggestions now read as the
suggester's own color (the same palette live cursors use) — Google
Docs-style — and reviewers can tell two suggesters apart at a glance
without changing the green-only default for anonymous edits.
…te log (#77675)

When two edit sessions create a “room” for the same document, they can encounter a race where WordPress creates two copies of the sync post meta for that document and the editors can work on two different histories of the document, leading to data loss.

This change introduces a process to merge updates into a canonical sync post meta to avoid this race. When duplicates are detected, the newest sync’d meta is chosen as the new canonical copy and it replaces the other copies.

During this process, additional database activity occurs to resolve the duplicates, though that should normatively present itself when duplicates already exist. Due to a lack of a broadly-supported way to perform database locking, there remains a secondary race when resolving the duplicates; this condition should be rarer than the one which motivated this in the first place. That means that while risk still exists, the overall risk should be much lower. An attempt was made to cover this secondary risk but the solution was complicated and itself unclear.

Co-authored-by: danluu <danluu@git.wordpress.org>
Co-authored-by: dmsnell <dmsnell@git.wordpress.org>
Co-authored-by: peterwilsoncc <peterwilsoncc@git.wordpress.org>
* RTC: Fix find_canonical_storage_post_id() always returning null

get_posts() with 'fields' => 'ids' returns an array of IDs, so the
previous is_numeric( $post_id ) check was always false. As a result,
find_canonical_storage_post_id() always returned null and the storage
layer kept promoting the suffixed post to the canonical slug, leaving
two posts sharing the same slug. This caused
test_first_access_race_does_not_split_room_storage to fail on
WordPress 6.8/6.9 where the Gutenberg shim is active (WP 7.0+ skips
the test because core ships its own class).

Read the first element from the result array and return it as the
canonical post ID instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* CI: Temporarily run previous-major WP PHP tests on pull_request

The race-condition regression test for WP_Sync_Post_Meta_Storage only
runs against the Gutenberg shim, which is bypassed on the latest WP.
PRs normally exclude the previous-major WP job, so the fix cannot be
verified by CI on a PR. Comment out that exclude with a TODO to revert
before merging.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Revert "CI: Temporarily run previous-major WP PHP tests on pull_request"

CI confirmed the fix on the previous WP major. Restore the original
pull_request exclude.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Backport changelog: Add PR 78053 to 7.0/11660 entry

The find_canonical_storage_post_id() fix needs to ride along with the
existing 11660 backport so Core gets the corrected race-resolution
logic in one piece.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: t-hamano <wildworks@git.wordpress.org>
Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>
@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-mechanism branch from 9fec5ed to b0d4b37 Compare June 18, 2026 06:33
@adamsilverstein
adamsilverstein force-pushed the suggest-mode-6c-move-ui branch from ce3c626 to 55be6bc Compare June 18, 2026 06:35
@adamsilverstein adamsilverstein changed the title Suggest mode 6c: Render block-move pending state in canvas and sidebar Suggest mode: Render block-move pending state Jun 30, 2026
@adamsilverstein adamsilverstein changed the title Suggest mode: Render block-move pending state Render block-move pending state Jun 30, 2026
@adamsilverstein

Copy link
Copy Markdown
Member Author

Superseded by the fresh-stack restructure of Suggest mode (#73411).

The feature has been re-sliced from this 16-deep stack onto current trunk as a clean 2-PR stack:

The whole feature now sits behind a Suggestion Mode experiment (Settings → Experiments → Collaboration). Closing in favour of the new stack — details: #73411 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Notes Phase 3 of the Gutenberg roadmap around block commenting [Package] Block editor /packages/block-editor [Package] Editor /packages/editor [Status] In Progress Tracking issues with work in progress [Type] Feature New feature to highlight in changelogs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants