StepList still reads backwards to me — @Theo Park, can you take a pass before Thursday?Gloss
Threaded comments block for a doc, issue or PR page: an auto-growing composer with bold / code / link toolbar, caret-anchored @mention autocomplete painted as tokens right in the textarea and ⌘/Ctrl+Enter to send; optimistic posting that dims with a spinner, flashes the accent on success and offers Retry / Discard on failure; six-emoji reactions whose chips spring in and roll their counts on Tally drums (yours accent-outlined, hover lists who reacted); one-level replies that collapse to "Show N more replies"; resolvable threads that fold into a receipt line; inline edit, confirm-to-delete and copy-link actions on a roving toolbar; an "N new replies" pill for comments that arrive via ref.receive() while you're scrolled away; Oldest/Newest sort and a polite live region.
The preview is live. The source is Pro.
One purchase, lifetime access: every pro component's source, CLI install with your own registry token, and all future drops.
Installation
CLI
npx shadcn@latest add @crucible/glossManual — install dependencies, then copy the source
npm install motionProps
| Prop | Type | Default | Description |
|---|---|---|---|
| comments | GlossComment[] | a seeded sample discussion | Initial comment tree (the thread owns edits/reactions/posts afterwards and reports them through the callbacks). Passing a new array resets it. |
| people | GlossPerson[] | a seeded sample team | Everyone who can appear in the thread and in @ autocomplete. |
| currentUser | GlossPerson | the first sample person | Who "you" are — your comments get Edit / Delete and your reactions are accent-outlined. |
| onPost | (draft: GlossDraft) => void | Promise<Partial<GlossComment> | void> | New comment or reply. The comment appears immediately (optimistic, dimmed with a spinner). Resolve to confirm — optionally with server fields such as { id, createdAt } — or reject to show "Failed to send · Retry · Discard". | |
| onEdit | (comment: GlossComment, body: string) => void | Promise<unknown> | Comment edited. Return a Promise to hold the editor in a saving state; reject keeps it open with an error. | |
| onDelete | (comment: GlossComment) => void | Promise<unknown> | Comment deleted (after the confirm popover). Return a Promise to hold the confirm in a pending state. | |
| onReact | (comment: GlossComment, emoji: string, active: boolean) => void | Promise<unknown> | Reaction toggled (active = you now reacted). Applied optimistically; reject reverts it. | |
| onResolve | (comment: GlossComment, resolved: boolean) => void | Promise<unknown> | Thread resolved (true) or reopened (false). Applied optimistically; reject reverts it. | |
| onCommentsChange | (comments: GlossComment[]) => void | Fired after every change with the full tree. | |
| linkFor | (comment: GlossComment) => string | current page + #comment-<id> | URL copied by "Copy link". |
| composerPosition | "top" | "bottom" | "top" | Where the main composer sits. |
| defaultSort | "oldest" | "newest" | "oldest" | Initial sort of top-level threads. |
| collapseAfter | number | 2 | Replies shown under a long thread before it collapses to "Show N more replies". |
| title | string | "Discussion" | Header title. |
| placeholder | string | "Add a comment…" | Composer placeholder. |
| accentColor | string | "#fdba74" | The single accent: mentions, your reactions, focus rings, send button, new-comment flash. Any CSS color; hex gets auto-contrast ink. |
| ref | React.Ref<GlossHandle> | Imperative handle — receive(), focusComposer(), replay(), getComments(). | |
| className | string | Classes for the root panel — give it a height (e.g. h-[560px]) to make the thread scroll inside. |
Honors prefers-reduced-motion with a designed static fallback, and passes className through.