Skip to content

Commit fe4cd31

Browse files
Merge remote-tracking branch 'origin/suggest/inline-wiring' into fix/73411-f18-accept-undo
# Conflicts: # packages/editor/CHANGELOG.md
2 parents 6dbb1e5 + 3fb26d2 commit fe4cd31

17 files changed

Lines changed: 1427 additions & 128 deletions

docs/explanations/architecture/suggestions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ The Suggest-mode subsystem lives in `packages/editor/src/components/suggestion-m
165165
| `with-suggestion-overlay.js`| `editor.BlockEdit` HOC that detects format-only / reconcilable content edits and hands them to the marker singletons, diverting everything else into the overlay (marker-stripped); plus the `editor.BlockListBlock` filter for pending-state classes and move ghosts. |
166166
| `store-interceptor.js` | Snapshot/diff/revert subscriber for store-level mutations (attribute and structural); multi-peer accept logic; revert-echo identity tokens. |
167167
| `provider.js` | `useSuggestionsProvider` — the `createSuggestion` / `applySuggestion` / `rejectSuggestion` API. Owns `operationsFromOverlay`, `applyOperations`, `hasAttributeConflict`, `findStructuralOp`, `clearSuggestionMarkerAttributes`, `parseSuggestionPayload`, and the wrapper-aware equality check. |
168-
| `suggestion-summary.js` | Compact sidebar summary ("Add: …", "Delete: …", "Formatting: …") used in thread lists — the sole suggestion renderer in the sidebar. |
168+
| `suggestion-summary.js` | Compact sidebar summary ("Add: …", "Delete: …", "Add formatting: …", "Remove formatting: …") used in thread lists — the sole suggestion renderer in the sidebar. Inline format changes carry their direction, since adding and removing a format are opposite proposals. |
169169
| `word-diff.js` | `wordDiff` — the word-level LCS behind the summary, bounded by `MAX_DIFF_LENGTH` (characters, applied by callers) and `MAX_DIFF_TOKENS` (tokens, applied internally). |
170170
| `auto-save.js` | Debounced background persistence of pending overlays as note comments (replaces the explicit "Submit" affordance from earlier phases). |
171171
| `suggestion-deletion-keyboard.js` | `beforeinput`/`cut`-capture handler turning selection, collapsed-cursor, word/line deletes and cut into `del` markers. |

packages/editor/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- Suggest mode: keep the code editor closed while the Suggest intent is active and while the post still carries unresolved inline suggestion markers, and identify a marker by its element rather than by a substring of the document ([#81662](https://github.com/WordPress/gutenberg/pull/81662)).
1414
- Suggest mode: Announce inline suggestion markers to screen readers. A marker's state was carried entirely by color and text decoration, so a run proposed for deletion was read aloud as ordinary prose. Each marker is now bracketed by an announcement naming the kind of change and the person who proposed it, and add and delete markers carry `role="insertion"` and `role="deletion"`; a formatting suggestion no longer claims to be a deletion. Suggestions over overlapping runs nest, and each is announced and attributed to the person who made it ([#81663](https://github.com/WordPress/gutenberg/pull/81663), [#81957](https://github.com/WordPress/gutenberg/pull/81957)).
1515
- Suggest mode: keep a review decision and its note together through undo. Accepting or rejecting a suggestion changes block content and resolves the note, but only the content half is in the undo stack, so undo put the marker back on a note that stayed resolved - a marked-up run with no Accept/Reject on it and no way to clear it. Undoing a decision made in this session now reopens its note along with the marker ([#81669](https://github.com/WordPress/gutenberg/pull/81669)).
16+
- Suggest mode: extend a format suggestion on a second toggle instead of opening a second one. Toggling a further format over a run that already carries the suggester's own pending `format` marker recorded a suggestion whose before and after were both empty - unreviewable and unapplyable - and made every marker in the block disappear. The existing suggestion is now revised in place, a toggle that restores the original run retracts it rather than storing a note that proposes nothing, and a note that has replies is revised rather than withdrawn ([#81665](https://github.com/WordPress/gutenberg/pull/81665)).
1617
- Suggest mode: refuse post status changes while suggesting. `editPost` drops the `status` field in the `suggest` intent, the status control and the summary panel show the status without offering to change it, and the publish button is disabled there rather than dropping the status edit and saving the post anyway. A status edit that travels with a companion field - the `password` that visibility changes carry, the `date` that scheduling carries - is refused whole rather than half-applied, a status repeated at the value it already holds is not announced as a refusal, and a status staged before the intent changed is discarded on the way in. The refusal is announced and shown in a snackbar ([#81664](https://github.com/WordPress/gutenberg/pull/81664)).
1718
- Register the editor and block editor keyboard shortcuts from the editor provider, so shortcuts work for consumers that mount the editor without rendering `EditorKeyboardShortcutsRegister` themselves ([#81580](https://github.com/WordPress/gutenberg/pull/81580)).
1819
- Header: Allow the Back button column to grow when "Show button text labels" is enabled so the label is not obscured by the following controls ([#81701](https://github.com/WordPress/gutenberg/pull/81701)).

packages/editor/src/components/collab-sidebar/hooks.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,26 @@ import {
3535

3636
const { cleanEmptyObject } = unlock( blockEditorPrivateApis );
3737

38-
export function useNoteThreads( postId ) {
39-
const queryArgs = {
38+
/**
39+
* Query that loads every note thread on a post, replies included. Exported so
40+
* code outside the sidebar can read the same cached list synchronously with
41+
* `select( coreStore ).getEntityRecords()` — a queried list is only found under
42+
* the exact query it was fetched with.
43+
*
44+
* @param {number} postId Post id.
45+
* @return {Object} Query arguments for the `root`/`comment` entity.
46+
*/
47+
export function getNoteThreadsQuery( postId ) {
48+
return {
4049
post: postId,
4150
type: 'note',
4251
status: 'all',
4352
per_page: -1,
4453
};
54+
}
55+
56+
export function useNoteThreads( postId ) {
57+
const queryArgs = getNoteThreadsQuery( postId );
4558

4659
const { records: threads } = useEntityRecords(
4760
'root',

packages/editor/src/components/inline-suggestions/operations.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,19 @@ export function acceptInlineFormat( value, suggestionId ) {
366366
return unwrapMarker( value, suggestionId );
367367
}
368368

369+
/**
370+
* Extract the HTML of a block attribute value, tolerating plain strings.
371+
*
372+
* @param {*} value Block attribute value.
373+
* @return {?string} HTML, or null when the value carries none.
374+
*/
375+
function toHTML( value ) {
376+
if ( value instanceof RichTextData ) {
377+
return value.toHTMLString();
378+
}
379+
return typeof value === 'string' ? value : null;
380+
}
381+
369382
/**
370383
* Reject a suggested formatting change: replace the marked run with the original
371384
* run captured when the suggestion was made, so the proposed formatting (and the
@@ -374,20 +387,25 @@ export function acceptInlineFormat( value, suggestionId ) {
374387
* `plan.beforeHTML`) because the marked run in content holds the *proposed*
375388
* formatting, not the original.
376389
*
377-
* @param {*} value Block attribute value (RichTextData or other).
390+
* Accepts a plain-string value as well as `RichTextData`: the format keyboard's
391+
* retract path passes the raw `content` attribute, which a block may hold as a
392+
* string.
393+
*
394+
* @param {*} value Block attribute value (RichTextData, string, or other).
378395
* @param {number|string} suggestionId Suggestion (marker) id to reject.
379396
* @param {string} beforeHTML HTML of the original run to restore.
380397
* @return {*} New RichTextData with the original run restored, or the original value.
381398
*/
382399
export function rejectInlineFormat( value, suggestionId, beforeHTML ) {
383-
if ( ! ( value instanceof RichTextData ) ) {
400+
const html = toHTML( value );
401+
if ( html === null ) {
384402
return value;
385403
}
386404
const range = findSuggestionRange( value, suggestionId );
387405
if ( ! range ) {
388406
return value;
389407
}
390-
const record = create( { html: value.toHTMLString() } );
408+
const record = create( { html } );
391409
const original = create( { html: beforeHTML ?? '' } );
392410
// `insert` replaces the [start, end) range with the original run, which
393411
// carries neither the proposed formatting nor the marker.

0 commit comments

Comments
 (0)