---
title: Share Comments (inline and general commenting on shared links)
---
# Share Comments (inline and general commenting on shared links)

## What it is

Viewers of an Omniscio Share can leave comments directly on the share page. Five comment types exist:

- **General comments** — free-form, not anchored to any content position.
- **Inline text-selection comments** — the viewer selects text on a markdown / plain-text / code / SVG share, and the comment anchors to that selection.
- **Coordinate-pin comments** — on image and PDF shares, the viewer clicks a point on the content and the comment anchors to those x/y coordinates (rendered as a numbered pin).
- **Iframe-bridged text-selection comments** — for HTML and React shares that run inside a sandboxed iframe, the share page uses `postMessage` to bridge the viewer's text selection out of the iframe so an inline comment can anchor to it.
- **Video-timestamp comments** — on a shared **screen recording**, the viewer pins a comment to a moment in the video ("Comment at 2:35"); clicking that comment later seeks the video to the moment. This is the Loom-style timestamped-comment surface.

Comments appear in real time for all viewers (Firestore live subscription) and are mirrored back to the desktop app so you can see them without opening the share page.

## Where to find it

Commenting happens on the published share page, not inside the app: a floating comment button sits at the bottom-right of every share page that has commenting enabled, and signing in with Google is required to post. The capability itself is switched on in **Settings → Lab**.

## How it behaves

### How to use it

### Enable the feature

Share Comments is **in development**. To reveal it:

- **Settings toggle:** Settings -> Lab -> "Share Comments" (`shareCommentsEnabled`).
- **Environment variable:** launch Omniscio with `AMC_SHOW_SHARE_COMMENTS=1`.

### On the share page (viewer side)

1. **Open the comment sidebar.** A floating comment button sits in the bottom-right corner of every share page that has commenting enabled. Click it to open the sidebar.
2. **Sign in.** Google sign-in is required to leave a comment. Click "Sign in with Google" in the sidebar; a popup authenticates with Firebase Auth.
3. **Leave a general comment.** Type in the sidebar's comment box and submit. Comments support inline formatting: `**bold**`, `*italic*`, `~~strikethrough~~`, `[link text](url)`, and `> blockquote`. Use Ctrl+K (Cmd+K on Mac) to insert a hyperlink around selected text. A formatting hint below the text area shows the available syntax.
4. **Leave an inline comment (static shares).** Select text on a markdown, text, code, or SVG share. A small "Comment" tooltip appears near the selection. Click it to open a reply box pre-anchored to the highlighted passage.
5. **Leave a coordinate-pin comment (image / PDF shares).** Click anywhere on the image or PDF. A numbered pin drops at that point and the sidebar opens a reply box anchored to those coordinates.
6. **Leave an inline comment (HTML / React shares).** Select text inside the sandboxed iframe. The share page bridges the selection out via `postMessage`; the same "Comment" tooltip appears and the flow continues as for static shares.
7. **Leave a video-timestamp comment (screen-recording shares).** As the video plays, the sidebar shows a **"Comment at M:SS"** button reflecting the current position. Click it to attach that moment to your comment (shown as a "▶ 2:35" chip on the comment); clicking the chip later seeks the video back to that moment. Emoji reactions work the same as on any share.
8. **Reply to a comment.** Click "Reply" on any comment. A reply indicator appears above the composer showing a truncated snippet of the parent comment's text (Messenger/Slack style). Click the snippet to scroll back to the parent comment. After posting, the reply displays a quoted context snippet of the parent above the reply text.
8. **Real-time updates.** New comments from other viewers appear instantly (Firestore `onSnapshot` subscription). No refresh needed.
9. **See where the comments are, and jump to them.** While the sidebar is open, every text-anchored comment is marked on the page itself — a gold dotted underline with a small dot — so you can see which passages have feedback. Click a comment's quoted-text chip in the sidebar and the page scrolls to that passage and briefly highlights it. (This works on HTML / React shares too, where the content runs in a sandboxed frame.)

### On the desktop app (author side)

1. **Shares detail pane.** Open the Shares sidebar, select a share. A collapsible **Comments** section appears in the detail pane with a comment count in the section header (e.g. "Comments (3)").
2. **Inline anchor highlights.** For HTML and React shares, anchored comments are visually marked in the preview iframe with a gold dotted underline and a superscript dot indicator. Click a highlight mark to scroll the matching comment into view in the comment list. Highlights update reactively when comments are added or removed.
3. **Allow comments toggle.** Inside the Comments section, an "Allow comments" toggle enables or disables commenting per share. New shares default to the global `shareCommentsDefaultOn` setting.
4. **Inbox notifications.** When a new comment arrives, an inbox item is created (gated on `shareCommentsNotifyInbox`). For a single new comment, the title shows the commenter's name (e.g. "Jane Doe commented on 'My Share'"). For multiple new comments, it shows the count. The body includes a clickable "[View and respond]" link that opens the share with the Comments section expanded and the newest comment scrolled into view with a brief highlight animation. A "View comments" button also appears in the alert's footer alongside Start session. If the share has **Mute notifications** enabled, comment inbox alerts are suppressed for that share regardless of the global setting.

### Team Chat integration

When publishing a share, you can optionally pick a **Team Chat channel** to link. When a viewer leaves a comment on that share:

1. The `onCommentCreate` Cloud Function fires.
2. If `shareCommentsCrosspostTeamChat` is enabled and a channel was linked during publish, the function posts the comment text as a thread reply in that Team Chat channel.
3. Team members in the channel see the comment without opening the share page.

Cross-posting is one-way: Team Chat replies do not appear on the share page.

### How it works

### Data model

Comments are stored in Firestore at `shares/{token}/comments/{commentId}`. Each comment document contains:

- `authorUid`, `authorDisplayName`, `authorPhotoUrl` — from Google sign-in.
- `text` — the comment body (plain text with optional Markdown-style formatting; rendered at display time, not stored as HTML).
- `createdAt` — server timestamp.
- `anchor` — optional object describing the comment's position: `{ type: 'text-selection', quotedText, selector, startOffset, endOffset }` for inline text, `{ type: 'coordinate', x, y }` for image/PDF pins, `{ type: 'video-timestamp', timeMs }` for a moment in a recording, or absent for general comments.

### Share page comment sidebar

The comment sidebar is a standalone React app bundled as an IIFE and injected into the share shell HTML at publish time. It mounts its own React root, manages its own Firebase Auth + Firestore connection, and renders the comment list + input. The sidebar communicates with the host page (for text-selection anchoring and coordinate pins) via DOM events and `postMessage` (for iframe-bridged selections).

Reply comments display a **reply context snippet** -- a clickable quoted preview of the parent comment's first line (truncated to 100 characters with ellipsis). Clicking the snippet scrolls to and briefly flashes the parent comment. The reply indicator above the composer also shows this snippet with the parent's author name and a cancel button. Both surfaces have light and dark mode CSS variants. The desktop mirror (`ShareCommentsList.tsx`) renders the same snippet using Tailwind `surface-*` tokens and the shared `truncateEnd` utility.

### Cloud Function: onCommentCreate

A Firestore-triggered Cloud Function (`onCommentCreate`) runs on every new `shares/{token}/comments/{commentId}` document. It:

1. Reads the parent share document to find the linked `teamChatChannelId`.
2. If a channel is linked and cross-posting is enabled, posts the comment to that Team Chat channel as a thread reply.
3. Writes an alert document for the desktop poller to pick up.

### Desktop poller

A periodic task on the desktop mirrors comment counts from Firestore to the local SQLite `shares` table. On each tick it:

1. Reads comment counts for shares with `comments_enabled = 1`.
2. Updates the local `comment_count` column.
3. For any share whose count increased, creates an inbox item (if `shareCommentsNotifyInbox` is on and the share's `muteNotifications` is off). The alert's "[View and respond]" link carries a `#comments` URL fragment that the renderer's `interceptShareUrl` recognizes, setting `pendingCommentScroll` in the share store so the Comments section scrolls to the newest comment on open.

### Inline anchor highlights (desktop preview)

When the shares detail pane displays an HTML or React share with comments enabled, `SharesDetailPane` posts a `share-comment-highlight-all` message to the preview iframe after it loads. The message carries an array of anchors (comment ID, quoted text, CSS selector, offsets). Inside the iframe, the `COMMENT_BRIDGE_SCRIPT` (injected at build time into the runnable doc) receives the message and:

1. Clears any existing `<mark class="amc-comment-anchor">` elements and dot indicators.
2. For each anchor, tries the CSS selector + offset path first, then falls back to a full-document text search (`findTextInNode`).
3. Wraps the matched range in a `<mark>` with a gold dotted underline and inserts a superscript dot indicator.
4. Attaches click handlers that post `share-comment-anchor-click` back to the parent, which scrolls the matching comment row into view and pulses it with the shared `setting-highlight-pulse` keyframe.

The highlight effect re-fires whenever the Zustand `commentsByToken` slice changes, so highlights stay in sync with comment additions and deletions. An empty anchors array clears all marks (handles the case where every anchored comment is deleted).

### Inline anchor highlights + click-to-scroll (web viewer)

The published web viewer (`share-comment-app`, at shares.omniscio.com) does the same for the person reading the share. While the sidebar is open, `CommentSidebar.tsx` (via `iframe-highlight.ts`) posts `share-comment-highlight-all` with the anchored comments to the content iframe, so the same gold dotted-underline `<mark>` + dot indicators appear on the page. Because the author document renders only once the tab is visible, the bridge posts `share-comment-bridge-ready` when it loads and the shell re-sends the anchors then — so the marks appear even for a share opened in a background tab.

Clicking a comment's anchor chip posts `share-comment-scroll-to`. The bridge flashes the mark (`.amc-comment-anchor-active`), scrolls it into view, and — because an opaque-origin sandboxed frame cannot scroll its parent — reports the mark's unscaled offset as `share-comment-scroll-to-y`. Whichever frame is the author's parent then scrolls the outer viewport to it: the `FIT_HOST_SCRIPT` / `RESPONSIVE_HOST_SCRIPT` (scaling by their fit factor) for a scaled render, or the shell's `scrollContentToY` for a directly-served one. The whole feature is baked into the artifact at publish time, so an already-published share picks it up only when it is re-published.

### Recording shares — the comment shell + video bridge

A screen-recording share is a `<video>` served in an opaque-origin sandbox (`/s/<token>/run`) where the comment app's Firebase Auth cannot run. So — exactly like HTML/React shares — a recording that hosts comments is published as a first-party **shell** at `/s/<token>` (`buildRecordingShellHtml`) that frames the `/run` video doc in an iframe and hosts the comment app beside it. The `/run` viewer carries a small `share-comment-video-*` bridge (`VIDEO_COMMENT_BRIDGE`): it posts the live playback position UP (`share-comment-video-time`) so the sidebar's "Comment at M:SS" button stays current, and seeks the `<video>` when the sidebar posts `share-comment-video-seek {ms}` DOWN (validated finite ≥0, clamped to the video's duration). The comment app treats `artifactKind: 'video'` as its own mode — no text-selection or coordinate-pin capture. Reactions, notify-on-comment, and the Firestore/rules/poller path are the SAME as every other share (no kind-specific backend). Ship-dark behind `shareCommentsEnabled`; when off, a recording share is byte-identical to before (the plain `/run` viewer, no shell).

### IPC channels

- `SHARE_COMMENTS_LIST` (invoke) — returns the list of comments for a given share token, used by the Shares detail pane Comments section.
- `SHARE_COMMENTS_UPDATED` (push) — emitted when the poller detects new comments, so the detail pane can refresh.

### Troubleshooting

**Comments not showing on the share page?**
The feature must be enabled in Settings -> Lab. Also check that the specific share has "Allow comments" toggled on in the Shares detail pane.

**Can't sign in on the share page?**
Google sign-in opens a popup window. If the browser blocks popups (common in incognito / private browsing), sign-in fails silently. Allow popups for `shares.omniscio.com` and retry.

**Cross-posting to Team Chat not working?**
Three things must be true: (1) a Team Chat channel was linked during publish, (2) `shareCommentsCrosspostTeamChat` is enabled in Settings, and (3) the Team Chat integration itself is enabled and connected. Check all three.

**No inbox notification for a new comment?**
Verify `shareCommentsNotifyInbox` is enabled in Settings. The desktop poller runs on a 60-second interval, so there may be a short delay before the inbox item appears.

**Clicking "View comments" in the inbox doesn't scroll to the comment?**
The scroll effect waits for comments to load from the backend. If the share's comment list is empty (all comments were deleted since the notification), the scroll is skipped gracefully. If the share itself was deleted or revoked, an error toast appears instead.

**Comments appear on the share page but not in the desktop detail pane?**
The desktop mirrors comments via a periodic poller, not a live subscription. Wait up to 60 seconds for the next poll cycle. If they still don't appear, check the main process logs for Firestore connectivity errors.

## For agents

### Settings

Three settings control commenting behavior (all in Settings -> Sharing or the Lab section):

| Setting                          | Default | Effect                                                    |
| -------------------------------- | ------- | --------------------------------------------------------- |
| `shareCommentsDefaultOn`         | `true`  | New shares have commenting enabled by default             |
| `shareCommentsNotifyInbox`       | `true`  | Create an inbox notification when a new comment is posted |
| `shareCommentsCrosspostTeamChat` | `true`  | Cross-post new comments to the linked Team Chat channel   |

### CLI

The `POST /share/publish` endpoint accepts two additional fields when Share Comments is enabled:

- `commentsEnabled` (boolean) — whether the published share allows comments. Defaults to the `shareCommentsDefaultOn` setting value.
- `teamChatChannelId` (string) — the Team Chat channel ID to link for cross-posting. Optional; omit to publish without a channel link.

## Related

- [shares-view.md](shares-view.md) -- Shares detail pane (where the Comments section and per-share toggle live)
- [screen-recorder.md](screen-recorder.md) -- Screen Recorder (recording shares host video-timestamp comments via the comment shell)
- [share-artifacts.md](share-artifacts.md) -- Share publishing (the publish flow that sets `commentsEnabled` and `teamChatChannelId`)
- [share-cli.md](share-cli.md) -- CLI endpoints (the `POST /share/publish` fields for comments)
- [team-chat.md](team-chat.md) -- Team Chat (the cross-posting destination for share comments)
- [share-comments-contract.md](../../.claude/memory/contracts/share-comments-contract.md) -- Feature contract (invariants and engineering rules)
