---
title: "Authors and comments"
description: "Mark who wrote which text and let readers discuss selections in threads, with a margin or inline layout."
canonical_url: "https://skaile-ai.github.io/forge-editor/guides/authors-and-comments"
---
# Authors and comments

> Mark who wrote which text and let readers discuss selections in threads, with a margin or inline layout.

Both features belong to the advanced editor (not the simple one) and need a collaborative surface for the user's identity: `:collaboration="{ ydoc, provider, user: { id, name, color } }"` (`AuthorIdentity`). Entries: `@skaile/forge-editor/authors/vue` or `/authors/react` (the framework-free `/authors` and `/authors/schema` hold the model), `@skaile/forge-editor/comments/vue` or `/comments/react` (framework-free `/comments`, `/comments/schema`); CSS `styles/authors.css` and `styles/comments.css` (after `core.css`; the Nuxt module adds them).

```ts
import { authorsFeature } from '@skaile/forge-editor/authors/vue'
import { collabFeature } from '@skaile/forge-editor/collab/vue'
import { commentsFeature } from '@skaile/forge-editor/comments/vue'
const preset = defineEditor({ features: [coreContentFeature(), coreUiFeature(), collabFeature(), authorsFeature(), commentsFeature()] })
```

On React the same list comes from `/authors/react`, `/collab/react` and `/comments/react`, and `collaboration` is a prop of the same name.

**authorsFeature()** (no dependency, inert without a user)

- Every inserted text run carries an `author` mark `{ id, name, color }` (persisted, invisible, not in Markdown). Text typed inside someone else's run takes the typist's mark; remote edits and Yjs undo/redo keep the original author. `documentAuthors(doc)` lists the authors with their character counts; `currentAuthor(editor)` is the user the editor writes as.
- The colouring mode is a per-user preference: `editing.authorColors` (off by default) and `editing.authorColorStyle` (`'background'` tint or `'text'` colour). It is set in the „Autorenfarben“ settings tab and in the toolbar's palette button, whose popover also holds a legend of the document's authors (`AuthorColorsPanel`).

**commentsFeature({ model? })** (requires `collab` unless `model` is given)

- `model: (getEditor) => CommentsModel` backs the comments with the host's own data (REST threads, a database) instead of the Y.Doc: the feature then requires no `collab`, the `Comments` plugin reads the user, the thread ids and the ranges from `model.state` (a snapshot with `available`, `canComment`, `user`, `threads`, `ranges`, …), and `useComments(editor)` resolves the model of the editor's surface (`commentsModelOf(ctx)`; the factory runs once for the plugin and once per hook consumer, so share state outside it). `CommentsSnapshot.canComment` gates the selection bubble: the default model says `available` and editable, a host model may allow readers. `CommentsModel`, `CommentsSnapshot`, `CommentsActions`, `DocRange` are exported from `/comments`.
- Select text and choose „Kommentar“ in the selection bubble; others reply, anyone resolves or reopens a thread („Erledigt“ threads lose their highlight), authors edit or delete their own comments. A thread whose text was deleted stays as orphan („Textstelle gelöscht“). Threads live in the Y.Doc map `comments` (`createCommentsModel(getEditor)` is the framework-free model; `useComments(editor)` is the thin Vue hook, returning the `CommentsStore`, and the React binding has the same hook), the range is the `comment` mark (the schema mark is always registered, so stored marks round-trip even without the feature's UI).
- `editing.commentsLayout` (`'margin'` default, `'inline'`) is a per-user setting in the „Kommentare“ settings tab:
  - `margin` („Randspalte“): cards beside the commented text. The host places `CommentsMargin` (props `editor`, `container`, `open`) inside the editor's scroll container (`relative flex`, editor wrapper `min-w-0 flex-1`) and binds `v-model:comments-open` on the surface (React: `commentsOpen`/`onCommentsOpenChange`). The toolbar's „Kommentare“ button and `Mod-Shift-m` toggle it; opening it closes the AI sidebar and vice versa.
  - Layout „Aktiver Faden“: the active thread (or the draft composer) is pinned exactly to the top of its text's line and shows in full; every other open thread is a one-line card (author, excerpt, reply count) that moves up or down out of its way, as close to its own line as its neighbours allow, never overlapping. Without an active thread the cards stack top-down at their lines. A click on a card activates it and reveals its text. Resolved threads are hidden behind the „Erledigt (n)“ toggle at the bottom of the column. A thread whose text was deleted stays as a greyed-out card at the deletion point, labelled „Text gelöscht“ (its position comes from the thread's anchor, a Yjs relative position kept in the Y.Map; a thread without one goes to the end); undoing the deletion attaches it to its text again. The rule is framework-free and testable on its own: `layoutMargin(entries, activeId, { gap, minTop })` maps measured cards (`MarginEntry`: id, line top or `null`, height) to tops, `marginCards(...)` builds the cards in document order, and `createMarginController(source)` does the measuring (`coordsAtPos`, card heights, one layout per animation frame); both `CommentsMargin` components only render it (all from `@skaile/forge-editor/comments`).
  - `inline`: `CommentsPopover` (mounted by the feature as an overlay) opens at the commented text; the toolbar's `CommentsListMenu` lists all threads with the open count. Esc closes the popover.
- Without collaboration the toolbar button is disabled with an explanation. Switching the layout closes any open thread or draft.

```vue
<div ref="scrollEl" class="relative flex overflow-auto">
  <div class="min-w-0 flex-1">
    <AdvancedEditor v-model:comments-open="commentsOpen" :collaboration="{ ydoc, provider, user: { id, name, color } }" />
  </div>
  <CommentsMargin :editor="editor" :container="scrollEl" :open="commentsOpen" />
</div>
```

React, same layout:

```tsx
<main ref={setScrollEl} className="relative flex overflow-auto">
  <div className="min-w-0 flex-1">
    <AdvancedEditor commentsOpen={commentsOpen} onCommentsOpenChange={setCommentsOpen} collaboration={{ ydoc, provider, user: { id, name, color } }} />
  </div>
  <CommentsMargin editor={editor} container={scrollEl} open={commentsOpen} />
</main>
```

`bun run check:editor-entries` proves the isolation with the `authors` and `comments` fixtures; the marks are part of the server schema snapshot.


## Sitemap

See the full [sitemap](https://skaile-ai.github.io/forge-editor/sitemap.md) for all pages.
