Skip to content

Suggest mode 6/9: review UI (Apply / Reject sidebar and summary) - #80432

Open
adamsilverstein wants to merge 89 commits into
suggest/inline-opsfrom
suggest/review-ui
Open

adamsilverstein wants to merge 89 commits into
suggest/inline-opsfrom
suggest/review-ui

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Jul 17, 2026 •

Copy link
Copy Markdown
Member

Part of #73411

What's in this PR

Adds the sidebar review surface: SuggestionActions renders the Accept /
Reject affordance in a note's header, resolves the marked text for inline
suggestions on read, and shows a Docs-style Add / Delete / Format summary
(SuggestionSummary + word-diff). The notes list hides the generic Resolve
button for suggestion notes so the checkmark isn't duplicated, and the note
toolbar's Add-note button stays active while a suggestion is being composed.

Screenshot

Reviewing as the post author. Each suggestion becomes a note with a plain-language summary and Accept / Reject actions, here an attribute change and an inline addition:

All notes sidebar listing a heading level format suggestion and an Add suggestion, each with accept and reject buttons

Testing

This is one layer of the stack. To exercise the whole feature, #78994 bundles every layer into one branch and builds it in Playground:

Test in WordPress Playground

👉 https://playground.wordpress.net/gutenberg.html?pr=78994

Enable Gutenberg > Experiments > Collaboration > Suggestion Mode, then follow the walkthrough in #73411, which also explains how to review the stack layer by layer.


Suggest mode stack

This rebuilds the manually-stacked Suggest mode work (#73411) as a GitHub Stack of 9 small, independently reviewable PRs, each building on the one below it:

  1. #80427 - editor intent (edit/suggest/view) + experiment gate
  2. #80428 - suggestion storage, REST controller, provider
  3. #80429 - block-level capture (attribute + structural)
  4. #80430 - inline marker primitive
  5. #80431 - inline suggestion operations
  6. #80432 - review UI (Apply/Reject sidebar + summary)
  7. #80433 - inline live wiring
  8. #82047 - architecture documentation
  9. #82048 - end-to-end test suite

Each follow up fix now sits in the layer that owns the code it changes, rather than piling onto the top of the stack. The whole feature can be exercised end-to-end via the combined testing branch #78994 (Playground). Behind the "Suggestion Mode" experiment (Gutenberg > Experiments).


AI Use

Code and description both written with 🤖 Claude Code. I will review and test.

Adds the sidebar review surface: SuggestionActions renders the Accept /
Reject affordance in a note's header, resolves the marked text for inline
suggestions on read, and shows a Docs-style Add / Delete / Format summary
(SuggestionSummary + word-diff). The notes list hides the generic Resolve
button for suggestion notes so the checkmark isn't duplicated, and the note
toolbar's Add-note button stays active while a suggestion is being composed.
@github-actions

github-actions Bot commented Jul 17, 2026 •

Copy link
Copy Markdown

Size Change: +5.08 kB (+0.06%)

Total Size: 7.93 MB

📦 View Changed
Filename Size Change
build/scripts/editor/index.min.js 596 kB +4.39 kB (+0.74%)
build/styles/editor/style-rtl.css 31.8 kB +193 B (+0.61%)
build/styles/editor/style-rtl.min.css 27.2 kB +155 B (+0.57%)
build/styles/editor/style.css 31.8 kB +184 B (+0.58%)
build/styles/editor/style.min.css 27.2 kB +154 B (+0.57%)

compressed-size-action

# Conflicts:
#	packages/editor/src/components/collab-sidebar/format.js
#	tools/eslint/suppressions.json
@adamsilverstein
adamsilverstein marked this pull request as ready for review August 5, 2026 22:24
@github-actions

github-actions Bot commented Aug 23, 2026 •

Copy link
Copy Markdown

Flaky tests detected in ec52201.
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/33117692745
📝 Reported tests:

refuses the drop and uploads nothing in /test/e2e/specs/editor/various/single-file-placeholder-drop.spec.js, passed after 1 failed attempt.
Error: expect(received).toHaveLength(expected)

Expected length: 0
Received length: 2
Received array:  [{"_links": {"about": [{"href": "http://localhost:8889/wp-json/wp/v2/types/attachment"}], "author": [{"embeddable": true, "href": "http://localhost:8889/wp-json/wp/v2/users/1"}], "collection": [{"href": "http://localhost:8889/wp-json/wp/v2/media"}], "curies": [{"href": "https://api.w.org/{rel}", "name": "wp", "templated": true}], "replies": [{"embeddable": true, "href": "http://localhost:8889/wp-json/wp/v2/comments?post=106"}], "self": [{"href": "http://localhost:8889/wp-json/wp/v2/media/106", "targetHints": {"allow": ["GET", "POST", "PUT", "PATCH", "DELETE"]}}], "wp:attached-to": [{"embeddable": true, "href": "http://localhost:8889/wp-json/wp/v2/posts/104", "id": 104, "post_type": "post"}]}, "alt_text": "", "author": 1, "caption": {"rendered": ""}, "class_list": ["post-106", "attachment", "type-attachment", "status-inherit", "hentry", "entry"], "comment_status": "open", "date": "2026-08-27T21:33:22", "date_gmt": "2026-08-27T21:33:22", "description": {"rendered": "<p class=\"attachment\"><a href='http://localhost:8889/wp-content/uploads/2026/08/10x10_e2e_test_image_z9T8jK-1.png'><img loading=\"lazy\" decoding=\"async\" width=\"10\" height=\"10\" src=\"http://localhost:8889/wp-content/uploads/2026/08/10x10_e2e_test_image_z9T8jK-1.png\" class=\"attachment-medium size-medium\" alt=\"\" style=\"width:100%;height:100%;max-width:10px;\" /></a></p>
"}, "featured_media": 0, "filename": "10x10_e2e_test_image_z9T8jK-1.png", "filesize": 80, "guid": {"rendered": "http://localhost:8889/wp-content/uploads/2026/08/10x10_e2e_test_image_z9T8jK-1.png"}, "id": 106, "link": "http://localhost:8889/?attachment_id=106", "media_details": {"file": "2026/08/10x10_e2e_test_image_z9T8jK-1.png", "filesize": 80, "height": 10, "image_meta": {"alt": "", "aperture": "0", "camera": "", "caption": "", "copyright": "", "created_timestamp": "0", "credit": "", "focal_length": "0", "iso": "0", "keywords": [], "orientation": "0", "shutter_speed": "0", "title": ""}, "sizes": {}, "width": 10}, "media_type": "image", "meta": [], "mime_type": "image/png", "modified": "2026-08-27T21:33:22", "modified_gmt": "2026-08-27T21:33:22", "ping_status": "closed", "post": 104, "slug": "10x10_e2e_test_image_z9t8jk-2", "source_url": "http://localhost:8889/wp-content/uploads/2026/08/10x10_e2e_test_image_z9T8jK-1.png", "status": "inherit", "template": "", "title": {"rendered": "10x10_e2e_test_image_z9T8jK"}, "type": "attachment"}, {"_links": {"about": [{"href": "http://localhost:8889/wp-json/wp/v2/types/attachment"}], "author": [{"embeddable": true, "href": "http://localhost:8889/wp-json/wp/v2/users/1"}], "collection": [{"href": "http://localhost:8889/wp-json/wp/v2/media"}], "curies": [{"href": "https://api.w.org/{rel}", "name": "wp", "templated": true}], "replies": [{"embeddable": true, "href": "http://localhost:8889/wp-json/wp/v2/comments?post=105"}], "self": [{"href": "http://localhost:8889/wp-json/wp/v2/media/105", "targetHints": {"allow": ["GET", "POST", "PUT", "PATCH", "DELETE"]}}], "wp:attached-to": [{"embeddable": true, "href": "http://localhost:8889/wp-json/wp/v2/posts/104", "id": 104, "post_type": "post"}]}, "alt_text": "", "author": 1, "caption": {"rendered": ""}, "class_list": ["post-105", "attachment", "type-attachment", "status-inherit", "hentry", "entry"], "comment_status": "open", "date": "2026-08-27T21:33:22", "date_gmt": "2026-08-27T21:33:22", "description": {"rendered": "<p class=\"attachment\"><a href='http://localhost:8889/wp-content/uploads/2026/08/10x10_e2e_test_image_z9T8jK.png'><img loading=\"lazy\" decoding=\"async\" width=\"10\" height=\"10\" src=\"http://localhost:8889/wp-content/uploads/2026/08/10x10_e2e_test_image_z9T8jK.png\" class=\"attachment-medium size-medium\" alt=\"\" style=\"width:100%;height:100%;max-width:10px;\" /></a></p>
"}, "featured_media": 0, "filename": "10x10_e2e_test_image_z9T8jK.png", "filesize": 80, "guid": {"rendered": "http://localhost:8889/wp-content/uploads/2026/08/10x10_e2e_test_image_z9T8jK.png"}, "id": 105, "link": "http://localhost:8889/?attachment_id=105", "media_details": {"file": "2026/08/10x10_e2e_test_image_z9T8jK.png", "filesize": 80, "height": 10, "image_meta": {"alt": "", "aperture": "0", "camera": "", "caption": "", "copyright": "", "created_timestamp": "0", "credit": "", "focal_length": "0", "iso": "0", "keywords": [], "orientation": "0", "shutter_speed": "0", "title": ""}, "sizes": {}, "width": 10}, "media_type": "image", "meta": [], "mime_type": "image/png", "modified": "2026-08-27T21:33:22", "modified_gmt": "2026-08-27T21:33:22", "ping_status": "closed", "post": 104, "slug": "10x10_e2e_test_image_z9t8jk", "source_url": "http://localhost:8889/wp-content/uploads/2026/08/10x10_e2e_test_image_z9T8jK.png", "status": "inherit", "template": "", "title": {"rendered": "10x10_e2e_test_image_z9T8jK"}, "type": "attachment"}]
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/various/single-file-placeholder-drop.spec.js:71:45

# Conflicts:
#	tools/eslint/suppressions.json
Rename the files this PR adds from .js to .ts/.tsx and add types so the
strict type check covers them: the word diff exports a WordDiffSegment
interface and the summary/actions components carry typed props. No behavior
changes; eslint suppression paths follow the renames.
# Conflicts:
#	packages/components/CHANGELOG.md
# Conflicts:
#	packages/editor/CHANGELOG.md
A structural op carries no text of its own, so "Insert block: paragraph"
never showed what was typed into the new block and "Remove block:
paragraph" read differently from deleting the same words as a text
selection. The sidebar now reads the anchored block's rich text (live
block first, op snapshot as fallback, markers stripped) and the summary
quotes it after the block label; blocks without text keep the bare label.

Scalar attribute changes now state both sides ("heading level 3 → 4",
an unset value reads "default"), since the bare attribute name left the
reviewer to guess the proposed value. Object values keep the bare name.
@adamsilverstein

Copy link
Copy Markdown
Member Author

Picked up one fix here from Seth's feedback on #73411.

Claude's note on what landed in this layer:

6201b69 - insert and remove summaries now quote the block's text (Insert block: paragraph “...”), and scalar attribute changes name both values (Change: heading level 3 → 4). The type-over part of the same fix lives in #80433, and the doc and e2e parts in #82047 and #82048.

adamsilverstein and others added 28 commits October 3, 2026 12:55
Phase 1 refuses every post-level edit while suggesting; the excerpt,
featured image and slug are fields a suggester should be able to
propose, the way the title already is.

Generalize the title proposal into post field proposals held in the
editor store rather than the session, so `editPost` can divert a
proposable edit into a proposal and `getEditedPostAttribute` can show
the proposed value while suggesting: every panel that reads the post
through the editor store shows the proposal with no change of its own.
The title moves onto the same path. Each proposal is saved as its own
note with a `post-attribute-set` op; accept applies it past the guard,
reject drops it, and undo withdraws the newest one. The note names the
field and the summary shows before and after (thumbnails for the
featured image).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011GLh3wdWJSgU3NLobNDk6x
Design System token fallbacks are injected at build time; a hand-written
one fails the stylesheet lint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011GLh3wdWJSgU3NLobNDk6x
…e post

When a note's proposals are over the size it can store, the save pass
leaves them in the post content and sets
`_wp_suggestion_extraction_skipped` on the note. Nothing told the
reviewer, who would expect the saved post to carry only the baseline.

Show a short notice on the pending suggestion while the flag is set,
noting that visitors still do not see it.

Layer: review-ui (the suggestion card lives there; the flag is only set
from inline-wiring, so it stays inert below).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011GLh3wdWJSgU3NLobNDk6x
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011GLh3wdWJSgU3NLobNDk6x
# Conflicts:
#	packages/editor/src/components/collab-sidebar/note.jsx
… actions

Trunk now publishes @wordpress/block-editor's declarations (#85036).

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

This branch has not been deployed

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

Labels

[Package] Editor /packages/editor [Type] Feature New feature to highlight in changelogs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants