Skip to content

Add Suggestion mode with Notes #73411

Open
2 of 2 issues completed
Open

Description

@jeffpaul

Start here

Two ways in, depending on why you're here:

What problem does this address?

As Notes evolve beyond simple block-level comments, this enhancement adds support for suggested edits that can be applied directly to the content. This is similar to the "Suggesting" workflow in Google Docs, where reviewers propose changes and authors can accept or reject them.

While this started as a Notes concept, it is more broadly an editorial workflow / content editing concept that overlaps with how Notes work.

What is the solution?

In Suggest mode, edits are not applied to the content directly. Instead they are captured as suggestions rendered in context, which the post author can accept or reject. There are two kinds of suggestion:

  • Inline text suggestions - typing, deletion, cut, paste, and formatting (bold / italic / link) toggles within rich text.
  • Structural suggestions - block-level changes such as inserting, removing, or moving blocks, and attribute changes like heading level or alignment.

Design: inline suggestions are markers, by construction

Every inline edit becomes a <mark class="wp-suggestion"> marker by construction, so no edit falls back to a separate overlay diff render. This follows the Google Docs model - the text shows once and is styled in place, rather than showing paired <del>/<ins> runs.

  • Text edits - typing, word/line deletion, cut, and single-line paste are caught on beforeinput / cut / paste by the suggestion keyboards, which write markers directly. The remaining onChange seams (committed IME composition, autocorrect, drag-drop, multi-line paste) are diffed into markers by a singleton content reconciler.
  • Formatting - bold / italic / link toggles become a single format-type marker wrapping the run.

The result is a single accept/reject path and a single serialization. An attribute-only revert shim on the overlay remains only for structural attribute changes (heading level, alignment); it never touches inline text or formatting.

A safety-net e2e asserts the invariant directly: a block never carries both an inline marker and an overlay diff.

Testing

#78994 bundles all nine layers into one branch and builds it in Playground, so there is nothing to install. It is also the one place the feature is enabled by default - everywhere else it sits behind an experiment, off until you turn it on:

Test in WordPress Playground

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

Setup: none. Open a post with a few paragraphs and look for Options (⋮) > Mode. A multi-paragraph post matters - some of the input handling only shows up once there is more than one block.

(If you are testing one of the stack PRs instead of #78994, enable Settings > Experiments > Collaboration > Suggestion Mode first. #78994 is the only branch that skips that step.)

1. Switching modes

  • Options (⋮) > Mode offers Editing, Suggesting, and Viewing. Switching announces itself with a snackbar ("You're suggesting").
  • Viewing is read-only: you can select and scroll, but not type.
  • Keyboard shortcuts follow the Google Docs convention: ⇧⌥⌘Z / ⇧⌥⌘X / ⇧⌥⌘C on Mac, Ctrl+Shift+Alt with the same letters elsewhere. They register wherever Suggest mode is available.
  • Reload the editor - the mode returns to Editing. It is session-scoped on purpose and never persisted.

Switch to Suggesting for everything below.

2. Inline text suggestions

  • Type in the middle of a paragraph → the new text appears as an underlined add marker, and one "Add: …" note appears in the sidebar (not "Format:").
  • Delete three ways: select text and press Delete; press Backspace at a caret (repeating grows one marker rather than one per keystroke); use word-delete (Option/Ctrl+Backspace). Each becomes a struck-through del marker - the text stays until accepted.
  • Cut (Cmd/Ctrl+X) a selection → del marker, and the clipboard still receives the cut text.
  • Type over a selection → a del + add pair (two notes).
  • Paste a single line → one add marker. Paste multiple lines → captured as a whole-attribute suggestion, never a raw commit.
  • Autocorrect / spellcheck replace / IME commit → reconciled into add/del markers.
  • Grapheme safety: backspace over emoji/ZWJ sequences (👨‍👩‍👧, flags) marks whole graphemes.
  • Edits that straddle an existing marker are left alone rather than corrupting another suggestion.

3. Formatting suggestions

  • Select a run and toggle bold / italic / link → the run is wrapped in a single format marker (dotted underline, no duplicated text). The note records the original so Reject can restore it.

4. Structural suggestions

  • Insert a block and type into it → ONE "Insert block" note (no extra "Add" note); the block carries the pending-insert treatment. An empty inserted block does not suggest anything until it has content.
  • Delete a block → it stays visible with a struck-through pending-remove treatment.
  • Move a block up/down → it lands at the proposed position with a pending-move treatment and a non-interactive ghost marks its origin. Moving the same block twice keeps a single note.
  • Change an attribute (heading level via the block switcher, alignment) → captured as an attribute suggestion.

5. Notes sidebar

  • Every suggestion appears as a note with a Docs-style summary ("Add:", "Delete:", "Format:", "Insert block:", "Move block:", "Remove block:").
  • If a non-notes sidebar (eg. post/block settings) is open when a suggestion is created, it switches to All notes automatically so the note is immediately visible; a closed sidebar stays closed.
  • With multiple suggesters, markers and pending treatments are tinted per author, taken from their avatar color.

6. Undo

  • Undo a just-typed addition → the marker and its note both go away.
  • Same for a deletion, a type-over (both notes go), a format change, and each structural type.
  • Make several suggestions and undo repeatedly → they withdraw newest-first. Redo must not crash.

7. Persistence and saving

  • Save draft stays available while suggestions are pending (structural pending state saves into content and is hidden from the front end at render time).
  • After saving, there is no "unsaved changes" browser warning.
  • Reload the post → inline markers, pending structural treatments (including the move ghost), and their notes all survive, and can still be accepted or rejected.

8. Review cycle (as the author, in Editing intent)

  • Accept one of each kind: addition (text kept, marker unwrapped), deletion (text removed), format (proposed formatting kept), insert (block kept), remove (block removed), move (new order kept, ghost cleared), attribute (change lands in content).
  • Reject one of each kind: the content reverts exactly (a rejected move restores the original order, including after a reload).
  • Accepting or rejecting shows a "Suggestion applied." / "Suggestion rejected." snackbar.
  • Front end: publish/preview with pending suggestions → proposed additions and inserted blocks never render until accepted; pending deletions and format runs still render their real text.

If you hit something that does not match the above, please note which step and which mode you were in - that narrows it down a lot.

Reviewing the stack

Suggest mode review stack

How to review this

Start at the bottom and work up. Each PR targets the one below it rather than trunk, so its diff shows only its own layer. Reading #80427 first and moving up means you never see the same code twice, and each layer arrives with its dependencies already in place.

Review each PR against its own base. GitHub's diff already does this - resist comparing against trunk, which will show you the whole feature every time.

Leave comments on the PR that owns the code. If something in #80431 looks wrong because of a decision made in #80429, the comment belongs on #80429.

A few files are deliberately split across layers, so they look incomplete when you first meet them:

Everything else is whole in exactly one PR.

Follow up fixes live in the layer that owns the code. The review round that produced #81655-#81695 originally merged everything onto the top of the stack, which left #80433 at 115 files and put fixes to List View, post status and the intent switcher three layers above the code they change. Those now sit in their own layer, and the documentation and the end-to-end suite have their own PRs, so each layer reviews as one kind of thing.

If you only have time for a slice: #80429 (block-level capture) and #80431 (inline suggestion operations) hold most of the interesting logic. #80427 is the smallest and a good place to sanity-check the gating approach.

Test while you review - #78994 builds the tip of the stack, so the Testing walkthrough above exercises exactly the code you are reading. Its only departure is that it turns the feature on by default; the experiment gate itself is reviewed on #80427.

Architecture write-up: docs/explanations/architecture/suggestions.md.

Automated coverage

  • Unit: the suggestion-mode, inline-suggestions (diff/plan engines), inline-markers, and attribute-suggestions suites.
  • E2E: 64 tests - suggestion-mode.spec.js golden paths (22), suggestion-mode-review.spec.js review cycle (19), suggestion-mode-undo.spec.js (9), suggestion-mode-overlay-retirement.spec.js safety net (8: the invariant plus the word-delete / cut / multi-line-paste / autocorrect / IME seams), and suggestion-mode-persistence.spec.js (6: save/reload round-trips and the front-end strip).
  • PHP: the front-end strips are covered by the strip-inline-suggestion-markers and strip-pending-structural-suggestions phpunit tests.
Superseded earlier stacks

The feature was originally prototyped across a larger multi-workstream stack (#77403, #77404, #77405, #77406, #78351, #78352, #78353, #78308, #77967, #77968, #77970, #77971, #77973, #77978, #77979, #78412, #79282) plus the combined testing branch (#78994). A visual stack diagram of the original layout is in this comment.

That work was then consolidated into a manually stacked two-PR series - #79713 (core subsystem + experiment gate) and #79714 (inline markers; #79752, overlay retirement, was folded into it). Those two PRs are now closed, reworked into the finer-grained seven-PR stack above with identical end-state code.

Open questions

  1. Should suggestions be tied to a specific version or revision of the post?
  2. How should conflicts be handled if content changes before a suggestion is applied (concurrent / multi-author suggestions)?

Activity

  1. saroshaga commented on Dec 11, 2025

    @saroshaga

    Hi @jeffpaul! Thanks for opening this exploratory issue! I'd like to suggest extending this feature to also support REPLY block notes being able to display, apply, and reject suggestions; not just top-level notes.

    The specific scenario I'm envisioning is enabling collaborators to respond to block notes with actionable suggestions:

    1. An author creates a block note (e.g., asking for help improving a paragraph or suggesting alternative phrasing)
    2. A collaborator (whether a human or an AI assistant), replies with a suggestion
    3. The suggestion in the REPLY note could then be applied/rejected just like a top-level suggestion.

    This would allow for a conversational workflow where:

    • Note authors can address notes to collaborators requesting specific changes
    • Collaborator responds with suggested edits as reply notes
    • Note authors maintain control via accept/reject for each suggestion

    This could make Block Notes a more powerful collaboration interface that extends beyond human-to-human feedback to human-AI collaboration.

    Please let me know if I should open a separate issue for this!

  2. jasmussen commented on Mar 5, 2026

    @jasmussen
    Contributor

    Shared here some nascent (extremely subject to feedback) mockups for suggesting. The path/instinct from there that resonates the most for me personally, is this one:

    Image

    That is: seek to land #74546 which simplifies the kebab menu, and then add at least Edit and Suggest modes to the top of it.

    In the Suggest mode, any edit made will become a note with ✅ and ❌ buttons.

    It will likely be useful to also land inline text-selections before that, so the mechanisms to mark text are in.

  3. adamsilverstein commented on Apr 15, 2026

    @adamsilverstein
    Member

    See also this related issue: #73410

    Noting this comment from @chriszarate:

    In v14, Yjs introduces the concept of an AttributionManager (name may change) which allows changes to be attributed to specific clients. This is exactly the primitive we need for suggested edits...

    A stable version of v14 is expected in the next few months, but an investigation of what an upgrade looks like is useful for planning and de-risking.

    So we may want to plan the approach here with that in mind (ie. building off this PR #77005 and leveraging the new AttributionManager). Also see this previous discussion around using Yjs for suggestions: #65012 (reply in thread)

  4. 190 remaining items

  5. added 12 commits that reference this issue on Oct 10, 2026
  6. adamsilverstein commented on Oct 10, 2026

    @adamsilverstein
    Member

    Thanks @tyxla for the careful review, every item turned out to be real. All six are addressed now, along with the overlapping suggestions question @annezazu raised. Everything is pushed to the stack and to the combined testing branch #78994.

    Claude worked through the list with me, here is the summary:

    1. Pending text leaking out of post_content. On save, the server now moves pending additions, suggested blocks, block attribute changes, proposed formatting and pending moves out of post_content onto their note, leaving only an empty anchor. Users who can edit the post get the full content back in the editor (REST edit context, autosaves, revisions). Search, excerpts, feeds, headless clients and a deactivated plugin see only the published text. ffe53ab, 03cb463, 4fd4ef5, 7a46834, tests 55b8118

    Stored post_content before: pending text and block present, search finds it Stored post_content after: empty anchor and placeholder, search finds nothing

    2. Accept and Reject saving the note before the post. Decisions are now provisional ("accepted, not saved yet") until the post saves, and the same save pass finalizes them on the server. Leaving without saving brings the suggestion back as pending. Notes already in the old stuck state get Apply again / Reject again / Reopen. 57b47ba, 32215b2, 642a6a9, tests be52996

    Before: accepted, left without saving, note stuck with no buttons After: the suggestion is pending again with Accept and Reject

    3. Post-level edits applied for real. Excerpt, featured image, slug, terms (including new terms, created only on accept) and registered meta are now proposals with their own notes, sanitized by the server the way core sanitizes each field. Move to trash and the site settings rows are locked while Suggesting, and re-editing a field updates its pending note. getEditedPostAttribute returns the proposed value while Suggesting so every panel shows it; that is documented. e554e5e, 44cb2cf, 292eb7c, 50d2b6d, 582d768, 1639a3a

    Before: excerpt and featured image changed directly After: each field change is a suggestion note

    4. Anyone's note trashed by the cleanup. The cleanup now only trashes the current user's own notes. Another author's suggestion whose text is gone is marked "outdated" instead, keeping its history. 32215b2, cfd0260

    Before: another author's note trashed After: the note is kept and marked outdated

    5. Undo/Redo after Accept, and deleting a block with a pending move. Redo now restores the decision instead of trashing the note. Deleting a moved block now replaces the move with the removal, so rejecting the removal puts the block back where it started. @tyxla, does "the removal replaces the move" match what you would expect, or should the two stay separate suggestions? 84f465e, f977a26, 9102d24, tests 4551ea2

    Before: redo leaves the change applied, note trashed After: redo restores the decision

    Before: rejecting the removal leaves the block at its moved position After: rejecting the removal returns the block to its original position

    6. Regex <mark> stripping. The render strip now uses the HTML Tag Processor, modeled on the block notes strip, and fails closed on anything unbalanced. 6761095

    Overlapping suggestions. Additions, deletions and formatting are now separate marker types (core/suggestion-add, -del, -format), so deleting or formatting inside another author's addition, deleting over formatting and formatting over a deletion all work. Children are independent; rejecting a parent makes its children outdated, and accepting a parent leaves them pending. Same-kind overlap (an addition inside an addition, overlapping deletions, formatting on formatting) is still refused, and the notice now names the other author and offers a reply. All 37 accept/reject orders of the example from the earlier comment are covered end to end. c64a9a1, 4f30385, 9882fc6, ad1d69a, 18a063f, c8bc9d8, tests 42b5a86

    Formatting and a deletion nested inside another author's addition, with context lines in the sidebar After rejecting the parent addition, the nested suggestions are outdated

    Hardening. A security pass over the new server code found and fixed several cases where note data could be resolved against the wrong post or by a user without edit rights. All note reads now go through one gate. 1f76606

    Screencasts:

    s1-accept-leave-after.mp4
    accept-undo-redo-after.mp4
    delete-moved-block-after.mp4
    post-fields-after.mp4
    marker-types-reject-after.mp4
    s2-editor-after.mp4
    What full Google Docs style multi-id resolution would take

    Supporting same-kind nesting (an addition inside an addition, overlapping deletions by several authors, formatting on formatting) at any depth would need:

    • A different representation. Either id lists inside one format per kind (like Docs' suggestedInsertionIds), a rich-text API change allowing several formats of one type per character keyed by id, or Yjs AttributionManager once RTC syncs structured rich text instead of HTML strings.
    • A delta model for formatting. Two format suggestions on the same text cannot share one "before" snapshot; each needs its own added/removed styles so either can be rejected alone.
    • The per-character membership rule generalized to every combination, with the dependency graph derived (never stored) and used only for labels like "built on" and "includes".
    • Changes in nearly every module, plus per-character format reconstruction in PHP, an owning-note rule for the save pass, and a migration.
    • The biggest risk: with today's HTML-string RTC sync, two peers editing an id list at once can garble it, so it needs a canonicalizer and concurrency fuzzing.
    • Testing: property-based tests against a character oracle, since exhaustive orders stop being practical past depth 2.

    Rough cost is 55-90 extra engineer-days, against 23-26 for the distinct types above. The recommendation is to skip it for 7.2 and revisit if same-kind refusals turn out to be common in testing, or once RTC moves to structured rich text, building on AttributionManager rather than id lists.

    Full details are in the architecture doc on #82047. Every fix started from a failing e2e test; the full Suggestion mode suite passes 248/248.

    Please give it a spin on #78994 and let me know what breaks.

  7. tyxla commented on Oct 12, 2026

    @tyxla
    Member

    Thank you for documenting my feedback here @adamsilverstein, it's a much better place than Slack and I should have come here in the first place! 🙌

  8. ofadam commented on Oct 12, 2026

    @ofadam

    When a suggestion is rejected or approved, is there a username attached to that action? I see the user recorded who made the suggestion, obviously, but don't see that important info. A timestamp for rejecting or approving is also missing.

    Additionally, I see the "All Notes" action bar icon, but wish there was a way to quickly determine if notes have actually been left on a post without needing to click on it. Could a badge be used?

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

Metadata

Metadata

Labels

Needs Design FeedbackNeeds general design feedback.Needs DevReady for, and needs developer effortsNeeds TestingNeeds further testing to be confirmed.[Feature] NotesPhase 3 of the Gutenberg roadmap around block commenting[Type] EnhancementA suggestion for improvement.

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions