The open-source marketing CMS
Menu

Comments in context

Status: proposed and approved 2026-11-13 (Kevin), with the decisions at the end. Phase 1 is built: threads with replies, mentions (email and the bell), pins on blocks in the preview's comment mode and from the page builder's block toolbar, placement across revisions (placed or removed), the thread rule for resolving, and block pins for the AI's comments. Not yet: text and field pins in context (phase 2) and comments outside the admin (phase 3).

Reviewers should be able to point at the thing they mean: click a block, a heading or a sentence, in the page builder or in a preview, and leave a comment there that others see in the same place, reply to and resolve, as in Figma or Google Docs. Today a comment belongs to the whole item, or to one field at most (Checks and comments), and a reviewer has to describe where the problem is in words.

What exists

  • Comments in the editor's right rail: a body, an optional field, resolve and reopen, delete your own; revisionId records the revision a comment was written against, and that revision is never pruned.
  • Preview: /_preview/<id> renders the latest revision for anyone with content.view_drafts, with a bar across the top.
  • Suggested changes: a proposed new version an editor applies or declines.
  • Notifications (the bell and email) for new comments on content you wrote or review.
  • Blocks have stable ids (Text-1), which the AI's layout tools and the checks already use to point at a block.

What people see

Comment mode. A Comment button on the preview bar and in the page builder's toolbar (and the C key) turns on comment mode. The cursor becomes a pin; hovering outlines the block under it.

  • Click a block to pin a comment to it. The pin sits at the point clicked, so a comment on an image or a card can point at a part of it.
  • Select text inside a block, then Comment, to pin it to the words: they are highlighted for everyone while the thread is open.
  • Right-click a block in the page builder (or use its toolbar's menu): Comment is one of the actions.
  • In the entity form, each field has a comment button that opens a thread on that field, as Comment on a field does now.

Threads. A pin opens a thread: the comment, replies under it, Resolve (and Reopen), and @name to mention someone on the site, who is notified. Threads list in the right rail too, in page order, with filters (open, resolved, mine, mentioning me); clicking one scrolls to its pin. Resolved threads are hidden on the page unless Show resolved is on.

Across revisions. A thread belongs to the item and remembers the revision and the place it was written on. On a later revision:

  • the block still exists: the pin stays on it;
  • the text it quoted changed: the pin stays on the block with Text changed since the comment, and the original quote in the thread;
  • the block is gone: the thread is listed under On removed content, still readable, with View in the revision (the preview of that revision).

Comparing revisions shows the threads opened and resolved between them.

Where comments can be made.

PlaceWhoWhich version
Page builderAnyone who may view drafts of the itemThe version being edited
Preview (/_preview/<id>)The sameThe latest revision
Preview of a past revisionThe sameThat revision (read-only threads stay on it)
The live page, for signed-in staffThe same, with comment mode from the admin barThe published revision
A review link for people without an accountGuests the item's editor invites (phase 3)The revision the link was made for

Review. The review queue shows each item's open threads, and the item's page shows them beside the checks. A site setting, Open comments block publishing (off by default), makes open threads an error in the checks, so an item cannot be published while a reviewer is waiting; someone who may publish can still choose Publish anyway.

How it works

  • Anchors. A comment gains an anchor: { blockId, point?: { x, y } as fractions of the block's box, quote?: { exact, prefix, suffix } }, plus the revisionId it already has and the fieldKey for field comments. The quote is matched again by text (the exact words, then the words around them), so it survives edits nearby, as web annotations do.
  • Threads. A reply is a comment with parentId; resolving resolves the thread. Mentions are stored as user ids and notify through the existing notifications (comment.added gains mentions).
  • Drawing pins. In comment mode the preview renders each block's outer element with its id (data-sw-block="Text-1"; public pages keep their markup unchanged), and the page builder knows its blocks by id already. The comment layer is a small script that finds the block, places the pin and wraps the quoted text with a highlight. It is loaded only for a signed-in person in comment mode, never on public pages.
  • Permissions. Commenting stays with content.view_drafts (section roles included). Resolving a thread: its author, the item's author, and anyone with content.publish or content.edit_any on it. Deleting: the comment's author, or content.edit_any.
  • API. POST /api/entities/:id/comments takes anchor and parentId; GET returns threads with their replies and each thread's state on the current revision (placed, text_changed, removed). PUT /api/comments/:id resolves the thread. A migration adds parent_id, anchor and mentions to the comments table.
  • AI. add_comment takes a blockId and an optional quote, and list_comments returns anchors and threads, so an agent can leave its review where the problem is ("This heading skips a level") and reply in a thread. The checks' block ids feed the same anchors.

Phases

  1. Threads and block pins. Replies, resolve by thread, mentions; comment mode in the page builder and the preview with pins on blocks (a point inside the block); the rail's thread list; pins kept across revisions by block id. The AI tools take block anchors.
  2. Text and fields. Pins on selected text with re-anchoring, the field comment buttons in the entity form, comments on past revisions, threads in the revision comparison, Open comments block publishing.
  3. Outside the admin. Comment mode on the live page for signed-in staff, and review links for guests without an account (an expiring link the item's editor sends; guests comment and reply, nothing else, and their name is shown as given).

Decisions

Decided 2026-11-13 (Kevin):

  1. Guests. Review links for people without an account, in phase 3: an expiring link the item's editor sends; guests comment and reply, nothing else.
  2. Publishing. A site setting, Open comments block publishing, off by default; someone who may publish can still choose Publish anyway.
  3. Live pages. Comment mode on the live page for signed-in staff, on the published revision.
  4. Resolving. The thread's author, the item's author, and anyone with content.publish or content.edit_any on the item.

Spillway 0.1.1 · Edit this page on GitHub

Was this page helpful?