Skip to content

Suggest mode 4/9: inline marker primitive - #80430

Open
adamsilverstein wants to merge 54 commits into
suggest/block-capturefrom
suggest/inline-markers
Open

adamsilverstein wants to merge 54 commits into
suggest/block-capturefrom
suggest/inline-markers

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Jul 17, 2026 •

Copy link
Copy Markdown
Member

Part of #73411

What's in this PR

Adds the generic inline-marker layer: wrap a rich-text range in a
core/suggestion , find a marker's range by id, read the caret and
selection in rich-text offset space, reconcile a marker after the user
edits across it, and decorate markers via the annotations API. This is a
self-contained rich-text utility with no suggestion-specific logic; the
suggestion operations that build on it land in the next step.

Diagram

This layer adds the marker primitive itself, so there is nothing to see in the UI yet. What it defines is the shape of a marker and the editor-only accessibility pass layered on top:

Anatomy of an inline suggestion marker, showing the saved mark element with its class and data attributes, and the editor-only role decoration

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 generic inline-marker layer: wrap a rich-text range in a
core/suggestion <mark>, find a marker's range by id, read the caret and
selection in rich-text offset space, reconcile a marker after the user
edits across it, and decorate markers via the annotations API. This is a
self-contained rich-text utility with no suggestion-specific logic; the
suggestion operations that build on it land in the next step.
@github-actions

github-actions Bot commented Jul 17, 2026 •

Copy link
Copy Markdown

Size Change: 0 B

Total Size: 7.93 MB

compressed-size-action

@adamsilverstein
adamsilverstein marked this pull request as ready for review August 5, 2026 22:24
@github-actions

github-actions Bot commented Aug 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>

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 Aug 23, 2026 •

Copy link
Copy Markdown

Flaky tests detected in 5ca7f73.
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/33117683339
📝 Reported tests:

Should save the changes in /test/e2e/specs/editor/plugins/wp-editor-meta-box.spec.js, passed after 2 failed attempts.
TimeoutError: page.waitForFunction: Timeout 10000ms exceeded.
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/plugins/wp-editor-meta-box.spec.js:34:14
TimeoutError: locator.click: Timeout 10000ms exceeded.
Call log:
  - waiting for locator('role=button[name="Visual"i]')

    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/plugins/wp-editor-meta-box.spec.js:33:55
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:35:19", "date_gmt": "2026-08-27T21:35:19", "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:35:19", "modified_gmt": "2026-08-27T21:35:19", "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:35:19", "date_gmt": "2026-08-27T21:35:19", "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:35:19", "modified_gmt": "2026-08-27T21:35:19", "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

@coderabbitai

coderabbitai Bot commented Sep 4, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. To trigger a review, include @coderabbitai review in the PR description. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 7c685ff0-7efe-48dc-b29f-f012c1c9c6b0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

📦 Bundle size

Size Change: +61 B (0%)

Total Size: 8.27 MB

📦 View Changed
Filename Size Change
build/scripts/block-library/index.min.js 366 kB +63 B (+0.02%)
build/scripts/core-data/index.min.js 38.5 kB +409 B (+1.07%)
build/scripts/sync/index.min.js 41.9 kB -411 B (-0.97%)

501f3c6 Run

⚡ Performance

Show the results

Client side metrics exclude the server response time.

front-end-block-theme

Metric a601359 trunk % Change
timeToFirstByte 42.85 ms +14.47% -1.17% 43.4 ms +8.64% -2.53% -1.27%
largestContentfulPaint 70 ms +8.57% -2.86% 72 ms +5.56% -2.78% -2.78%
lcpMinusTtfb 26.2 ms +19.85% -2.48% 28.3 ms +10.95% -9.19% -7.42%
wpBeforeTemplate 21.5 ms +11.35% -1.3% 21.28 ms +10.9% -2.02% 1.03%
wpTemplate 18.37 ms +4.41% -1.85% 18.63 ms +3.27% -2.84% -1.4%
wpTotal 39.67 ms +14.9% -0.91% 40.24 ms +8.95% -2.58% -1.42%
wpMemoryUsage 7.64 MB +0% -0% 7.60 MB +0% -0% 0.49%
wpDbQueries 17 +0% -0% 17 +0% -0% 0%

front-end-classic-theme

Metric a601359 trunk % Change
timeToFirstByte 35.15 ms +6.54% -1.56% 36 ms +5% -2.64% -2.36%
largestContentfulPaint 80 ms +0% -5% 78 ms +2.56% -2.56% 2.56%
lcpMinusTtfb 43.45 ms +3.8% -4.26% 41.7 ms +2.88% -2.28% 4.2%
wpBeforeTemplate 19.04 ms +2.52% -1.94% 18.9 ms +13.97% -1.9% 0.74%
wpTemplate 13.71 ms +5.84% -2.41% 13.89 ms +3.6% -1.73% -1.3%
wpTotal 32.54 ms +7.04% -1.32% 33.31 ms +5.82% -2.58% -2.31%
wpMemoryUsage 6.26 MB +0% -0% 6.22 MB +0% -0% 0.75%
wpDbQueries 14 +0% -0% 14 +0% -0% 0%

media-processing

Metric a601359 trunk % Change
mediaProcessingJpeg 319.99 ms +1.81% -0.23% 318.13 ms +2.11% -0.49% 0.58%
mediaProcessingAvif 4773.04 ms +0.4% -0.2% 4822.64 ms +0.4% -0.05% -1.03%
mediaProcessingJpegToAvif 3342.89 ms +0.25% -0.2% 3362.1 ms +0.01% -0.41% -0.57%

media-upload

Metric a601359 trunk % Change
jpegUploadProcessing 1389.25 ms +0.45% -0.81% 1414.43 ms +0.37% -0.79% -1.78%
pngUploadProcessing 198.14 ms +12.91% -9.17% 174.71 ms +9.22% -4.73% 13.41%
largeJpegUploadProcessing 1393.75 ms +0.32% -0.21% 1388.51 ms +0.4% -0.33% 0.38%
multipleImageUploadProcessing 1459.25 ms +1.18% -0.21% 1480.11 ms +0.8% -0.36% -1.41%

post-editor

Metric a601359 trunk % Change
serverResponse 485.1 ms +7.3% -4.28% 516.24 ms +4.15% -13.91% -6.03%
firstPaint 226.18 ms +18.06% -7.14% 246.8 ms +11.47% -11.69% -8.35%
domContentLoaded 1088.64 ms +0.52% -2.23% 1108.61 ms +1.49% -0.92% -1.8%
loaded 1089.86 ms +0.52% -2.24% 1109.75 ms +1.51% -0.9% -1.79%
firstContentfulPaint 447.36 ms +1.63% -2.15% 458.17 ms +5.37% -1.95% -2.36%
firstBlock 3236.21 ms +1.26% -0.14% 3295.61 ms +0.78% -0.37% -1.8%
type 17.72 ms +7.9% -7.17% 18.6 ms +1.02% -3.71% -4.73%
typeWithoutInspector 18.83 ms +6.27% -2.39% 18.06 ms +4.71% -7.31% 4.26%
typeWithTopToolbar 22.69 ms +3.44% -6.08% 23.91 ms +3.39% -3.3% -5.1%
typeContainer 8.51 ms +13.16% -1.76% 8.45 ms +2.37% -5.56% 0.71%
focus 73.06 ms +2.78% -4.48% 74.05 ms +8.39% -8.8% -1.34%
firstFocus 199.97 ms +0% -0% 203.64 ms +0% -0% -1.8%
selectAll 548.09 ms +4.65% -5.63% 533.27 ms +4.87% -1.03% 2.78%
listViewOpen 63.44 ms +10.37% -9.58% 63.51 ms +7.07% -9.56% -0.11%
inserterOpen 22.98 ms +20.28% -6.09% 24.8 ms +11.57% -16.29% -7.34%
inserterHover 2.03 ms +19.21% -4.43% 2.04 ms +9.8% -7.84% -0.49%
inserterSearch 7.92 ms +5.68% -5.81% 7.82 ms +8.31% -5.12% 1.28%
loadPatterns 641.51 ms +2.1% -4.82% 647.31 ms +7.43% -3.09% -0.9%
wpTotal 474.96 ms +7.1% -4.22% 506.4 ms +4.14% -14.16% -6.21%
wpMemoryUsage 13.23 MB +0% -0% 13.19 MB +0% -0% 0.34%
wpDbQueries 54 +0% -0% 54 +0% -1.85% 0%

site-editor

Metric a601359 trunk % Change
serverResponse 516.18 ms +1.83% -7.78% 485.53 ms +8.9% -6.3% 6.31%
firstPaint 247.32 ms +8.18% -8.3% 273.91 ms +18.96% -20.59% -9.71%
domContentLoaded 1176.67 ms +1.95% -1.57% 1150.62 ms +1.66% -0.66% 2.26%
loaded 1177.91 ms +1.95% -1.55% 1151.71 ms +1.67% -0.65% 2.27%
firstContentfulPaint 461.01 ms +3.84% -1.94% 450.8 ms +2.02% -1% 2.26%
firstBlock 4244.04 ms +0.53% -0.74% 4193.78 ms +1.05% -0.2% 1.2%
type 18.13 ms +3.53% -4.08% 17.52 ms +4.34% -4.34% 3.48%
navigate 111.95 ms +13.77% -2.34% 114.66 ms +15.57% -4.17% -2.36%
loadPatterns 1295.13 ms +1.78% -2.01% 1356.94 ms +2.44% -6.76% -4.56%
loadPages 1039.64 ms +1.9% -0.44% 1052.9 ms +1.67% -1.09% -1.26%
wpTotal 506.64 ms +1.75% -7.88% 475.67 ms +9.11% -6.41% 6.51%
wpMemoryUsage 12.19 MB +0% -0% 12.15 MB +0% -0% 0.36%
wpDbQueries 43 +2.33% -0% 43 +2.33% -0% 0%

501f3c6 Run

@manzoorwanijk
manzoorwanijk removed their request for review September 11, 2026 06:47
adamsilverstein and others added 22 commits September 11, 2026 16:38
findMarkerRange and findMarkerText serialized a RichTextData value to
HTML and parsed it back on every lookup, which runs per suggestion per
store update. RichTextData already exposes formats and text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHz7zkCC2S8crriSWsYPDk
Each range dispatched its own addAnnotation, so every annotated block
re-rendered once per range whenever the ranges changed.

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

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

[Feature] Notes Phase 3 of the Gutenberg roadmap around block commenting [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.

2 participants