Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
de57d01
Try: add support for multiple note ids
adamsilverstein Feb 2, 2026
6deb7d5
Apply code improvements from Code Rabbit
adamsilverstein Feb 2, 2026
80e9da5
Fix "Add Note" to create new note thread instead of focusing reply field
adamsilverstein Feb 3, 2026
0de44c6
complete enabling multiple notes per block
adamsilverstein Feb 5, 2026
d3af188
ensure most recent selected when adding additional note
adamsilverstein Feb 5, 2026
63ba39d
Remove unnecessary useMemo around getNoteIdsFromMetadata
adamsilverstein Feb 6, 2026
f32b6dc
Simplify useSelect by removing intermediate metadata variable
adamsilverstein Feb 6, 2026
953c810
Add comment explaining isSubmittingRef guard in AddComment
adamsilverstein Feb 6, 2026
8e6e4e0
remove blur guard
adamsilverstein Feb 6, 2026
343333e
Revert "ensure most recent selected when adding additional note"
adamsilverstein Feb 6, 2026
71a473a
Fix prettier formatting in comments.js
adamsilverstein Feb 6, 2026
192c591
Fix race conditions when adding a new note
adamsilverstein Feb 13, 2026
7d19f06
Fix prettier formatting in collab-sidebar
adamsilverstein Feb 13, 2026
4ca28f1
Fix duplicate snackbar in multi-note E2E tests
adamsilverstein Feb 13, 2026
7c63215
Add tests for multiple notes per block feature
adamsilverstein Apr 7, 2026
11b8e1f
Fix toolbar intercept in auto-select E2E test
adamsilverstein Apr 7, 2026
4bb6ea7
Merge branch 'trunk' into add/enable-multiple-notes-per-block
adamsilverstein Apr 23, 2026
2890100
Merge branch 'trunk' into add/enable-multiple-notes-per-block
adamsilverstein Apr 29, 2026
5d67c99
Notes: dedupe noteId mixed-type comparison and add layout coverage
adamsilverstein Apr 29, 2026
7127977
Notes: add e2e coverage for legacy migration and reload persistence
adamsilverstein Apr 29, 2026
a325780
Notes: tidy multi-note e2e tests and document race condition
adamsilverstein Apr 29, 2026
b4f59c4
Notes: restore focus parity with trunk in openTheSidebar
adamsilverstein Apr 29, 2026
f39db20
REST: Allow post editors to apply/reject suggestions on their posts
adamsilverstein Apr 16, 2026
1f28717
Tests: Add PHP tests for suggestion meta and update permissions
adamsilverstein Apr 16, 2026
d7151a7
Editor: Auto-save suggestions and announce mode changes
adamsilverstein Apr 16, 2026
3653a62
Editor: Surface suggestion state in the block and sidebar
adamsilverstein Apr 16, 2026
15029c0
Editor: Record suggestion auto-save changes in the CHANGELOG
adamsilverstein Apr 16, 2026
e4d27b6
Editor: Summarize inline format changes as 'Formatting: bold'
adamsilverstein Apr 16, 2026
8bfb366
Editor: Move suggestion accept/reject into the note header
adamsilverstein Apr 16, 2026
c9cd0f8
Editor: Only prompt to apply when a suggestion actually conflicts
adamsilverstein Apr 16, 2026
100fd48
Editor: Make suggestion accept reliable and tighten the header controls
adamsilverstein Apr 16, 2026
8225350
Editor: Make editor intent session-scoped, not a persisted preference
adamsilverstein Apr 16, 2026
e524688
Tests: Fix PHP coverage for note suggestion meta
adamsilverstein Apr 17, 2026
c390c08
Editor: Allow suggestion meta on create; fix role in rewrite test
adamsilverstein Apr 17, 2026
bdf8992
Tests: Update suggestion e2e specs for session-scoped intent and snac…
adamsilverstein Apr 19, 2026
bf39791
Tests: Pin REST meta assertions and unit-test lifecycle helper directly
adamsilverstein Apr 19, 2026
2bb3c5e
Tests: Tighten suggestion-mode e2e locators and drop meta round-trip
adamsilverstein Apr 19, 2026
d4727d3
Suggestions: Refactor auto-save, harden controller, polish phase 5
adamsilverstein Apr 29, 2026
821099f
Notes: restore beforeEach/afterAll for Multiple notes per block tests
adamsilverstein Apr 29, 2026
ecb6bcf
Tests: Re-register comment meta in REST test set_up
adamsilverstein Apr 29, 2026
e006449
Fix Suggest-mode auto-save diff for RichTextData; tighten heading-lev…
adamsilverstein Apr 29, 2026
1f05e72
Merge branch 'suggest-mode-phase-4' into add-suggestion-mode
adamsilverstein Apr 29, 2026
28b16e0
Notes: restore selectBlock call and simplify metadata selector
adamsilverstein Apr 29, 2026
9bc4e62
Notes: restore window-focus guard on AddNote blur handler
adamsilverstein Apr 29, 2026
2709799
Notes: derive auto-select target outside the effect
adamsilverstein Apr 29, 2026
1433e64
Notes: nest 'Multiple notes per block' inside Block Notes describe
adamsilverstein Apr 29, 2026
0e78d09
Notes: keep List View entry point routing through the row's clientId
adamsilverstein Apr 29, 2026
694983c
Merge suggest-mode-phase-4 into phase-5 — keep header SuggestionActio…
adamsilverstein Apr 29, 2026
441fa5a
Restore SuggestionActionButtons in note header
adamsilverstein Apr 29, 2026
331a9e6
Merge suggest-mode-phase-4 into phase-5
adamsilverstein Apr 29, 2026
e11dd57
Add suggestion-summary.js to ESLint suppressions
adamsilverstein Apr 29, 2026
da95141
Merge branch 'suggest-mode-phase-4' into add-suggestion-mode
adamsilverstein Apr 30, 2026
83e6b20
Editor: Match accept/reject icon sizes in the note header
adamsilverstein Apr 30, 2026
d460eb6
Merge branch 'suggest-mode-phase-4' into add-suggestion-mode
adamsilverstein Apr 30, 2026
4138610
Fix Accept suggestion for content/text/formatting changes
adamsilverstein Apr 30, 2026
314131a
Merge branch 'suggest-mode-phase-4' into add-suggestion-mode
adamsilverstein Apr 30, 2026
dd1faa5
Merge branch 'add/enable-multiple-notes-per-block' into add-suggestio…
adamsilverstein May 1, 2026
ae2fd82
Suggestions: Append new note IDs instead of overwriting
adamsilverstein May 1, 2026
4b7f160
Suggestions: Skip resolved notes when syncing further edits
adamsilverstein May 1, 2026
1e08d38
Merge phase-4 to pick up connectors e2e capability fix (#77857)
adamsilverstein May 1, 2026
c898e40
Merge remote-tracking branch 'origin/suggest-mode-phase-4' into add-s…
adamsilverstein May 3, 2026
184f638
Lint: suppress existing react-hooks violations in suggestion-mode
adamsilverstein May 4, 2026
e71f710
Merge remote-tracking branch 'origin/suggest-mode-phase-4' into add-s…
adamsilverstein May 4, 2026
4ea2355
Merge phase-4 doc updates into add-suggestion-mode
adamsilverstein May 4, 2026
3812cbb
Suggestions: Document auto-save, summary renderer, multi-noteId helpers
adamsilverstein May 4, 2026
9acc5cb
Merge remote-tracking branch 'origin/suggest-mode-phase-4' into add-s…
adamsilverstein May 4, 2026
26a3702
Merge phase-4 ESLint suppressions into add-suggestion-mode
adamsilverstein May 4, 2026
1a1ad5a
Notes: stop auto-select effect from re-opening collapsed threads
adamsilverstein May 5, 2026
7998920
Suggestions: Extract auto-save subsystem to follow-up PR
adamsilverstein May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 36 additions & 23 deletions docs/explanations/architecture/suggestions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## Overview

Suggestions extend the Notes feature (block-level comments) to support proposed content changes. A reviewer switches to **Suggest** intent, edits a block, and the change is captured as a versioned suggestion payload stored on a note comment. The post author can then **Apply** (merge the change) or **Reject** (dismiss it) from the notes sidebar.
Suggestions extend the Notes feature (block-level comments) to support proposed content changes. A reviewer switches to **Suggest** intent and edits a block; the change is captured as a versioned suggestion payload on a note comment, auto-saved in the background after a short idle window. The post author then **Accepts** (merges the change) or **Rejects** (dismisses it) from the notes sidebar.

The feature is designed around a swappable provider interface so the storage backend can evolve from comment-meta (v1, current) to Yjs `AttributionManager` (v2, future) without changing the UI or apply/reject logic.
The feature is designed around a swappable provider interface so the storage backend can evolve from comment-meta (v1, current) to Yjs `AttributionManager` (v2, future) without changing the UI or accept/reject logic.

## End-to-end lifecycle

Expand All @@ -14,21 +14,22 @@ sequenceDiagram
participant U as Reviewer
participant B as Block
participant O as Overlay store
participant AS as AutoSave (debounced)
participant P as SuggestionsProvider
participant R as REST (/wp/v2/comments)
participant A as Post author

U->>B: Switch to Suggest intent, edit block
B->>O: setAttributes → overlay (store baseline on first edit)
B->>O: setAttributes → overlay (capture baseline on first edit)
Note right of O: Block-editor store is NEVER written
U->>B: Click "Submit suggestion"
B->>P: createSuggestion({ clientId, operations })
P->>R: POST note + _wp_suggestion meta
O->>AS: Overlay changed (debounce ~1.5s)
AS->>P: createSuggestion or updateSuggestion
P->>R: POST / PUT note + _wp_suggestion meta
R-->>P: Saved comment
P->>B: updateBlockAttributes(metadata.noteId)
P->>B: updateBlockAttributes(metadata.noteId) (on create)

A->>A: Open notes sidebar
A->>P: Apply (or Reject)
A->>P: Accept (or Reject)
alt baseRevision stale
P-->>A: Confirm dialog ("Apply anyway?")
end
Expand Down Expand Up @@ -56,7 +57,13 @@ When the intent is `suggest`, an `editor.BlockEdit` filter (`withSuggestionOverl
2. **Diversion** — `setAttributes` writes to a React-context-backed overlay (`SuggestionOverlayProvider`) keyed by `clientId`, not the block-editor store.
3. **Merge for render** — the block receives `{ ...realAttributes, ...overlayAttributes }` so the user sees their in-progress change live.

Because the store is never touched, autosave, undo/redo, and RTC sync stay at the real baseline. On commit, the overlay is serialized into a suggestion payload and sent to the server as comment meta; on discard, the overlay is cleared.
A companion `editor.BlockListBlock` filter tags each block that has a pending overlay with an `is-suggestion-pending` class, which renders the green bracket/outline treatment so edited blocks are discoverable without relying on the selected-block toolbar.

Because the store is never touched, autosave, undo/redo, and RTC sync stay at the real baseline.

### Auto-save

There is no manual "Submit" step — `SuggestionAutoSave` watches the overlay and, after ~1.5 s of idle time on a given block, persists the current operations as a note comment. The overlay entry tracks the resulting `commentId` and a fingerprint of the last synced operations, so subsequent edits update the same note rather than creating new ones. If an edit is undone back to baseline the auto-saver trashes the note instead.

### Store interceptor

Expand Down Expand Up @@ -90,8 +97,9 @@ The Suggest-mode subsystem lives in `packages/editor/src/components/suggestion-m
| `with-suggestion-overlay.js`| `editor.BlockEdit` HOC that diverts `setAttributes` into the overlay. |
| `store-interceptor.js` | Snapshot/diff/revert subscriber for store-level mutations; multi-peer accept logic. |
| `provider.js` | `useSuggestionsProvider` — the `createSuggestion` / `applySuggestion` / `rejectSuggestion` API. Owns `operationsFromOverlay`, `applyOperations`, `parseSuggestionPayload`, and the wrapper-aware equality check. |
| `suggestion-diff.js` | Sidebar diff preview (word-level for text attributes, label fallback otherwise). |
| `commit-bar.js` | "Submit suggestion" toolbar shown while editing in Suggest intent. |
| `suggestion-diff.js` | Inline diff preview rendered in a comment thread (word-level for text attributes, label fallback otherwise). |
| `suggestion-summary.js` | Compact sidebar summary ("Add: …", "Delete: …", "Format: …") used in collapsed thread lists. |
| `auto-save.js` | Debounced background persistence of pending overlays as note comments (replaces the explicit "Submit" affordance from earlier phases). |

REST/PHP surface lives in `lib/compat/wordpress-6.9/`:

Expand Down Expand Up @@ -145,25 +153,29 @@ When bumping the version, add a migration step in `parseSuggestionPayload` that

```
useSuggestionsProvider() → {
createSuggestion({ clientId, blockName, operations }) → Promise<comment>
applySuggestion({ commentId, clientId, payload }) → Promise<void>
rejectSuggestion({ commentId }) → Promise<void>
createSuggestion({ clientId, blockName, operations }) → Promise<comment>
updateSuggestion({ commentId, blockName, operations }) → Promise<comment>
deleteSuggestion({ commentId }) → Promise<void>
applySuggestion({ commentId, clientId, payload }) → Promise<void>
rejectSuggestion({ commentId }) → Promise<void>
}
```

The current implementation (`provider.js`) uses comment meta. A future Yjs-backed implementation would read from `AttributionManager` and write changes through the CRDT document, exposing the same three methods.
The current implementation (`provider.js`) uses comment meta. A future Yjs-backed implementation would read from `AttributionManager` and write changes through the CRDT document, exposing the same methods.

## Apply / Reject
## Accept / Reject

- **Apply**: runs `applyOperations(currentAttributes, payload.operations)` to produce new attributes, dispatches `updateBlockAttributes`, marks the note as resolved with `_wp_suggestion_status = 'applied'`.
- **Accept**: runs `applyOperations(currentAttributes, payload.operations)` to produce new attributes, dispatches `updateBlockAttributes`, marks the note as resolved with `_wp_suggestion_status = 'applied'`.
- **Reject**: marks the note as resolved with `_wp_suggestion_status = 'rejected'`. No content change.
- **Staleness**: if `baseRevision` differs from the current `post_modified_gmt`, a warning snackbar is shown but apply is not blocked (conservative approach — the user reviews and decides).
- **Conflict detection**: accept-time staleness is checked at the attribute level, not the post level. `hasAttributeConflict(currentAttributes, operations)` compares each operation's captured `before` to the block's current value; only a real divergence on a targeted attribute prompts the "apply anyway" confirmation. Post-level `baseRevision` is still stamped into the payload for provenance, but does not drive the prompt — every auto-save bumps `post_modified_gmt`, so a post-level compare would flag nearly every suggestion as stale.

## Review UI

## Diff Preview
In the notes sidebar, a suggestion thread renders:

The `SuggestionDiff` component renders operations in the notes sidebar:
- **Text attributes**: word-level LCS diff with green underlined insertions and red strikethrough deletions.
- **Non-text attributes**: `attribute: before → after` label.
- **`SuggestionSummary`** — a Docs-style "Add: …", "Delete: …", "Format: …" summary derived from the operations.
- **Accept / Reject icon buttons** — checkmark and close icons that trigger the provider's apply/reject flows.
- **`SuggestionDiff`** (still available) — the full word-level diff preview for when a more detailed view is needed.

## Yjs v2 Migration Path

Expand All @@ -189,5 +201,6 @@ These are non-obvious quirks reviewers should keep in mind when reading the code

- **Structural suggestions** (block insert, remove, move) are not yet supported. The `operations` array is designed to accept `block-insert-after` and `block-remove` types in the future.
- **Inline text selections** are not anchored — suggestions apply to the entire attribute, not a sub-range. Fragment-level suggestions depend on inline annotation infrastructure tracked separately.
- **Permissions**: the Gutenberg REST comment controller overrides `update_item_permissions_check` so users with `edit_post` on the parent can update note comments (and their suggestion meta). This lets post editors apply or reject suggestions authored by other users. The `_wp_suggestion` and `_wp_suggestion_status` meta `auth_callback`s follow the same pattern.
- **Permissions**: the Gutenberg REST comment controller overrides `update_item_permissions_check` so users with `edit_post` on the parent can update note comments — **but only for suggestion-lifecycle fields** (`status` limited to `approved`/`hold`, plus `meta._wp_suggestion_status`). Any other field in the update body falls back to core's `edit_comment` check, preventing post editors from rewriting another user's note content. The `_wp_suggestion` and `_wp_suggestion_status` meta `auth_callback`s follow the same `edit_post`-on-parent pattern.
- **Payload size**: `_wp_suggestion` meta is capped at 64 KB via a `sanitize_callback`. Requests exceeding that limit are truncated to prevent arbitrarily large JSON blobs from flooding comment meta storage.
- **Rich-text format fidelity**: the word-level diff operates on the serialized HTML string, which may produce noisy diffs when formatting (bold, links) changes. Progressive enhancement planned.
4 changes: 3 additions & 1 deletion docs/reference-guides/data/data-core-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -1525,7 +1525,9 @@ Sets the current editor intent.

The intent represents the user's editing purpose: directly editing content (`edit`), suggesting changes that the author can apply or reject (`suggest`), or viewing the post in a read-only mode (`view`). It is orthogonal to the `editorMode` preference (visual vs. code).

The value persists via the preferences store so the intent survives reloads. Unknown intents are silently rejected (no dispatch, no announcement) to keep typos or stale values from corrupting the preference; valid values are listed in `EDITOR_INTENTS`.
The intent is _session-scoped_ — held in the editor reducer (not the preferences store), so reloading the editor always returns to `edit`. Persisting suggest/view across reloads surprises users who don't realize they left the editor in a non-default state.

Unknown intents are silently rejected (no dispatch, no announcement) so typos from a bookmarklet, browser extension, or third-party plugin can't poison the editor state; valid values are listed in `EDITOR_INTENTS`.

_Parameters_

Expand Down
17 changes: 15 additions & 2 deletions lib/compat/wordpress-6.9/block-comments.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,22 @@ function gutenberg_register_block_comment_metadata() {
return $value;
},
'auth_callback' => function ( $allowed, $meta_key, $object_id ) {
// During comment creation the comment does not yet exist, so
// `object_id` is 0. Defer to the comment controller's own
// create permission — if the request can create the
// comment at all, it can set the suggestion meta on it.
if ( ! $object_id ) {
return current_user_can( 'edit_posts' );
}
$comment = get_comment( $object_id );
if ( $comment && 'note' === $comment->comment_type ) {
return current_user_can( 'edit_post', $comment->comment_post_ID );
}
return current_user_can( 'edit_comment', $object_id );
},
)
);

// Lifecycle status for a suggestion. `pending` on creation; moved to
// `applied` or `rejected` by the apply/reject actions.
register_meta(
'comment',
'_wp_suggestion_status',
Expand All @@ -141,6 +150,10 @@ function gutenberg_register_block_comment_metadata() {
),
),
'auth_callback' => function ( $allowed, $meta_key, $object_id ) {
$comment = get_comment( $object_id );
if ( $comment && 'note' === $comment->comment_type ) {
return current_user_can( 'edit_post', $comment->comment_post_ID );
}
return current_user_can( 'edit_comment', $object_id );
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,100 @@ public function get_item_permissions_check( $request ) {
return true;
}

/**
* Checks if a given request has access to update a comment.
*
* Extends core's check so that users who can `edit_post` on the parent
* post are also allowed to update note-type comments — but only for
* suggestion-lifecycle fields (status and `_wp_suggestion_status` meta).
* This unblocks the suggestion workflow where a post editor applies or
* rejects a suggestion authored by someone else, without granting them
* the ability to rewrite the note's content, reassign authorship, or
* otherwise modify another user's comment.
*
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has access, WP_Error otherwise.
*/
public function update_item_permissions_check( $request ) {
$comment = $this->get_comment( $request['id'] );
if ( is_wp_error( $comment ) ) {
return $comment;
}

// For note comments, allow users who can edit the parent post to
// update suggestion-lifecycle fields only.
if (
'note' === $comment->comment_type &&
self::is_suggestion_lifecycle_update( $request )
) {
$post = get_post( $comment->comment_post_ID );
if ( $post && current_user_can( 'edit_post', $post->ID ) ) {
return true;
}
}

// Fall back to core's default check (moderate_comments or edit_comment).
return parent::update_item_permissions_check( $request );
}

/**
* Determines whether a note-update request touches only the fields used
* by the suggestion apply/reject lifecycle.
*
* Allowed fields:
* - `status` (limited to `approved` or `hold`)
* - `meta._wp_suggestion_status`
*
* Any other field present in the request body disqualifies the request
* from the `edit_post` shortcut, forcing it through core's edit_comment
* check instead.
*
* @param WP_REST_Request $request
* @return bool
*/
private static function is_suggestion_lifecycle_update( $request ) {
// Accept either a JSON body (the block editor client) or a form-
// encoded body (custom integrations / curl scripts). Either way the
// shortcut is gated by the same allowlist below — query/URL params
// are intentionally excluded so the body is the source of truth for
// what's being written.
$params = $request->get_json_params();
if ( ! is_array( $params ) ) {
$params = $request->get_body_params();
}
if ( ! is_array( $params ) || empty( $params ) ) {
return false;
}

$allowed_keys = array( 'id', 'status', 'meta' );
foreach ( array_keys( $params ) as $key ) {
if ( ! in_array( $key, $allowed_keys, true ) ) {
return false;
}
}

if (
isset( $params['status'] ) &&
! in_array( $params['status'], array( 'approved', 'hold' ), true )
) {
return false;
}

if ( isset( $params['meta'] ) ) {
if ( ! is_array( $params['meta'] ) ) {
return false;
}
$allowed_meta = array( '_wp_suggestion_status' );
foreach ( array_keys( $params['meta'] ) as $meta_key ) {
if ( ! in_array( $meta_key, $allowed_meta, true ) ) {
return false;
}
}
}

return true;
}

public function create_item_permissions_check( $request ) {
$is_note = ! empty( $request['type'] ) && 'note' === $request['type'];

Expand Down Expand Up @@ -296,6 +390,55 @@ public function create_item_permissions_check( $request ) {
return true;
}

/**
* Validates that an incoming request's `_wp_suggestion` meta is within the
* allowed byte budget. Truncating arbitrary JSON corrupts the payload, so
* we reject before any storage happens.
*
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if no payload or within bounds, WP_Error otherwise.
*/
protected static function validate_suggestion_payload_size( $request ) {
$meta = $request['meta'] ?? null;
if ( ! is_array( $meta ) || ! isset( $meta['_wp_suggestion'] ) ) {
return true;
}
$value = $meta['_wp_suggestion'];
if ( ! is_string( $value ) ) {
return true;
}
if ( strlen( $value ) > GUTENBERG_SUGGESTION_PAYLOAD_MAX_BYTES ) {
return new WP_Error(
'rest_suggestion_too_large',
sprintf(
/* translators: %d: maximum allowed byte length. */
__( 'Suggestion payload exceeds the %d-byte limit.', 'gutenberg' ),
GUTENBERG_SUGGESTION_PAYLOAD_MAX_BYTES
),
array( 'status' => 413 )
);
}
return true;
}

/**
* Updates a comment.
*
* Wraps core's update path with a pre-flight size check on the suggestion
* payload so oversized values are rejected with a clean 413 instead of
* silently dropped by the meta sanitize_callback.
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error
*/
public function update_item( $request ) {
$size_check = self::validate_suggestion_payload_size( $request );
if ( is_wp_error( $size_check ) ) {
return $size_check;
}
return parent::update_item( $request );
}

/**
* Creates a comment.
*
Expand Down Expand Up @@ -325,6 +468,14 @@ public function create_item( $request ) {
);
}

// Reject oversized suggestion payloads with a 413 so the client knows.
// Without this, the meta sanitize_callback would silently reject the
// value and the suggestion would disappear server-side.
$size_check = self::validate_suggestion_payload_size( $request );
if ( is_wp_error( $size_check ) ) {
return $size_check;
}

$prepared_comment = $this->prepare_item_for_database( $request );
if ( is_wp_error( $prepared_comment ) ) {
return $prepared_comment;
Expand Down Expand Up @@ -655,5 +806,6 @@ protected function check_is_comment_content_allowed( $prepared_comment ) {
function () {
$controller = new Gutenberg_REST_Comment_Controller_6_9();
$controller->register_routes();
}
},
11
);
1 change: 1 addition & 0 deletions packages/editor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Added an `editorIntent` preference (`edit`, `suggest`, `view`) with a matching `setEditorIntent` action and `getEditorIntent` selector. Surfaced as an Edit / Suggest / View menu in the editor options for post types that support notes. The `view` intent puts the block editor into a read-only preview. Keyboard shortcuts follow the Google Docs convention: Ctrl+Alt+Shift+Z (Edit), +X (Suggest), +C (View) on Windows / ⌘⌥⇧Z/X/C on macOS.
- Added a suggestion-overlay subsystem that powers the `suggest` intent. When active, an `editor.BlockEdit` filter diverts `setAttributes` into an in-memory overlay keyed by `clientId`; the block renders with the pending change merged on top of its real attributes, but the block-editor store stays at the baseline. A toolbar button on the selected block submits the overlay as a note comment with a `_wp_suggestion` meta payload (`schemaVersion`, `blockName`, `baseRevision`, `operations`) or discards it. Phase 2: capture only; Apply/Reject and diff rendering follow.
- `setEditorIntent` now surfaces mode transitions with a snackbar ('You're suggesting' / 'You're editing' / 'You're viewing') alongside the existing a11y announcement.

## 14.45.0 (2026-04-29)

Expand Down
Loading
Loading