import { Avatar } from '@tldraw/mentions'; import { AvatarProps } from '@tldraw/mentions'; import { CommentAuthor } from '@tldraw/mentions'; import { ComponentType } from 'react'; import { createMentionSuggestion } from '@tldraw/mentions'; import { Editor } from 'tldraw'; import { EditorAtom } from 'tldraw'; import { filterMentionMembers } from '@tldraw/mentions'; import { JSX } from 'react/jsx-runtime'; import { KeyboardEvent as KeyboardEvent_2 } from 'react'; import { Mention } from '@tldraw/mentions'; import { MentionList } from '@tldraw/mentions'; import { MentionListProps } from '@tldraw/mentions'; import { MentionMember } from '@tldraw/mentions'; import { MentionProps } from '@tldraw/mentions'; import { MentionSuggestionOptions } from '@tldraw/mentions'; import { MouseEvent as MouseEvent_2 } from 'react'; import { ReactNode } from 'react'; import { StateNode } from 'tldraw'; import { TLComment } from 'tldraw'; import { TLCommentAnchor } from 'tldraw'; import { TLCommentId } from 'tldraw'; import { TLCommentReaction } from 'tldraw'; import { TLCommentReactionId } from 'tldraw'; import { TLCommentThread } from 'tldraw'; import { TLCommentThreadId } from 'tldraw'; import { TLHistoryBatchOptions } from 'tldraw'; import { TLRichText } from 'tldraw'; import { TLShapeId } from 'tldraw'; import { TLStateNodeConstructor } from 'tldraw'; import { TLUiOverrides } from 'tldraw'; import { VecLike } from 'tldraw'; /** * Where a thread's pin sits on the page, for each anchor kind. Null hides the pin. Imprecise shape * anchors use {@link CommentingOptions.impreciseShapeAnchor} rather than the stored `x`/`y`. * * A shape anchor's `x`/`y` are normalized within the shape's bounds and resolved through its page * transform, so the pin rides rotation instead of being left behind in the bounding box. * @public */ export declare function anchorPagePoint(editor: Editor, anchor: TLCommentAnchor): { x: number; y: number; } | null; export { Avatar } export { AvatarProps } /** * A comment's metadata line: author name, relative time, and an edited marker. Hovering the time * shows the full date and time in a tooltip. * @public @react */ export declare function Byline({ author, date, edited }: BylineProps): JSX.Element; /** @public */ export declare interface BylineProps { author: CommentAuthor; /** ISO datetime; formatted to relative time by the component. */ date: string; /** Shows an "edited" marker when the comment has been edited. */ edited?: boolean; } /** * A ready-to-use comments layer for a tldraw canvas: pins each thread at its anchor, opens a * thread popover (with a reply composer) on click, and shows a composer where the comment tool * placed a new thread. Reads/writes comment records straight from `editor.store`. * * It's the batteries-included default: every visible piece is a slot on * `CommentTool.configure({ components })`, and the pieces it composes (`CommentPin`, * `CommentThread`, `CommentComposer`, the hooks, the tool) are all exported, so a consumer can * rebuild it from parts instead. The host wiring is the {@link CommentingContext}, which * `CanvasCommentsSidebar` takes too. * * @public @react */ export declare function CanvasComments(props: CanvasCommentsProps): JSX.Element | null; /** * The host wiring for {@link CanvasComments} — see {@link CommentingContext}, which the sidebar * takes the same fields from. * * @public */ export declare type CanvasCommentsProps = CommentingContext; /** * A comments list panel for the current page, shown while {@link commentsSidebarOpen} is set (e.g. * toggled by a button). Clicking a thread brings its pin into view and opens it. Batteries-included * over the store (a sibling to `CanvasComments`); `CommentsList` is exported for a differently-placed * or always-on list. * @public @react */ export declare function CanvasCommentsSidebar(props: CanvasCommentsSidebarProps): JSX.Element | null; /** * The host wiring for {@link CanvasCommentsSidebar}: the {@link CommentingContext} fields it reads, * plus the panel's own slots. A non-null `currentUserId` enables the "only your threads" filter, and * an `isCommentUnread` the "only unread" one. `CanvasComments` takes the same fields, so a host * mounting both can spread one object into each. * * @public */ export declare interface CanvasCommentsSidebarProps extends Pick { /** Header above the list. */ header?: ReactNode; /** Shown when the page has no threads. */ empty?: ReactNode; } export { CommentAuthor } /** * Renders a comment's rich-text body read-only through the limited comment extension set (no * headings), so formatting (bold, links, lists, highlight) is preserved rather than flattened, and * headings can never render. Use this as the `body` of a `CommentCard` on a canvas. * @public @react */ export declare function CommentBody({ richText, resolveName }: CommentBodyProps): JSX.Element; /** @public */ export declare interface CommentBodyProps { richText: TLRichText; /** Maps a member id to its current display name, so \@mentions show the live name. */ resolveName?(id: string): string | undefined; } /** A single comment: Avatar, Byline, and a body slot the consumer renders. @public @react */ export declare function CommentCard({ author, body, date, you, edited, actions, footer, }: CommentCardProps): JSX.Element; /** @public */ export declare interface CommentCardProps { author: CommentAuthor; /** The rendered comment body. The card doesn't dictate a format — pass a `` * for a comment's rich text, or any node of your own. */ body: ReactNode; /** ISO datetime; formatted to relative time by the component. */ date: string; you: boolean; /** Whether the comment has been edited (shows an "edited" marker). */ edited?: boolean; /** Hover-revealed controls at the card's top-right (e.g. an edit affordance). */ actions?: ReactNode; /** Content under the body, aligned with it rather than the avatar (e.g. a `` row). */ footer?: ReactNode; } /** * The input for writing a comment: a TipTap rich-text editor restricted to the comment extension * set (bold, italic, lists, links, code, highlight — no headings), with a Send button. Formatting * is applied through markdown and keyboard shortcuts (e.g. `**bold**`, `- `, Cmd+B); there's no * floating toolbar. Presentational by default; pass value/onChange/onSubmit to drive it as a form. * @public @react */ export declare function CommentComposer({ author, placeholder, value, onChange, onSubmit, sendLabel, onArrowUpWhenEmpty, disabled, autoFocus, leading, getMentionSuggestions, renderMentionSuggestion, }: CommentComposerProps): JSX.Element; /** @public */ export declare interface CommentComposerProps { author: CommentAuthor; placeholder: string; /** Controlled rich-text value. Omit for the presentational (display-only) composer. */ value?: TLRichText; onChange?(value: TLRichText): void; /** Called on Send click or Enter. When set, the composer is interactive. */ onSubmit?(): void; sendLabel?: string; /** Called when Up is pressed in an empty composer — e.g. to start editing the comment above, * the way chat apps edit your last message. With content in the field, Up moves the cursor. */ onArrowUpWhenEmpty?(): void; disabled?: boolean; autoFocus?: boolean; /** The leading element before the field. Defaults to the author's avatar. */ leading?: ReactNode; /** Resolve the members matching an `@`-query (sync or async). Provide to enable mentions. */ getMentionSuggestions?(query: string): MentionMember[] | Promise; /** Override a picker row's content. Defaults to avatar + name (+ secondary). */ renderMentionSuggestion?(member: MentionMember): ReactNode; } /** * Component overrides for the batteries-included comments layer. Each slot replaces a built-in * piece; leave a slot unset to keep its default. * * @public */ export declare interface CommentingComponents { /** A comment's body. Replaces the default rich-text ``. */ CommentBody?: ComponentType<{ comment: TLComment; }>; /** A pin's inner content. Replaces the author-initial default. */ PinContent?: ComponentType<{ comments: TLComment[]; thread: TLCommentThread; }>; /** A sidebar row's preview. Replaces the plaintext default. */ ThreadPreview?: ComponentType<{ comment: TLComment; }>; /** * A whole sidebar row. Replaces the default ``, which is exported — so a row * that only adds an unread dot or a status chip can spread these props into it. Use * `ThreadPreview` instead when only the preview text is changing. */ ThreadRow?: ComponentType; /** * Extra controls in an open thread's header, added ahead of the built-in resolve and dismiss * buttons rather than replacing them. "Copy link" is already built in whenever the host supplies * `getThreadHref`. */ ThreadActions?: ComponentType<{ comments: TLComment[]; thread: TLCommentThread; }>; /** * A reaction's visual, given its token. The default renders the token string for the OS emoji * font. Override to draw a custom palette — an ``, an SVG, anything. The token is what gets * stored and synced; this only controls how it's drawn. */ ReactionContent?: ComponentType<{ token: string; }>; /** * What the add-reaction button opens. Replaces the default `` grid. Pairs with * `ReactionContent` (which draws the tokens this emits) and `isAllowedReaction` (which must * accept them). */ ReactionPalette?: ComponentType; /** * The hover affordance naming who reacted with an emoji. Receives the reactors and the pill (as * `children`) and owns the whole thing — box, size, shape, position. For a wording change, * translate the `comments.reacted-*` strings instead. */ ReactionTooltip?: ComponentType; /** * Shown where a composer would sit when the viewer can't compose (see * {@link CommentingOptions.canComment}). `context` is the surface rendering it: an open thread * popover (`'thread'`) or the comment tool's placement popover (`'pending'`). Unset, those * surfaces render nothing. */ ComposerFallback?: ComponentType<{ context: 'pending' | 'thread'; }>; } /** * The live, host-supplied half of commenting: who the viewer is, how author ids become names, read * status, and the mention roster. (The static half is {@link CommentingOptions}, configured once on * the tool.) * * Every commenting surface reads the same set, so a host mounting more than one — `CanvasComments` * and `CanvasCommentsSidebar`, say — can build it once and spread it into each rather than * repeating the props: * * ```tsx * const commenting: CommentingContext = { currentUserId, resolveAuthor, isCommentUnread } * * * * ``` * * @public */ export declare interface CommentingContext { /** The signed-in user's id, or null for a read-only viewer. Only a signed-in user composes. */ currentUserId: null | string; /** Map an author id to their display info, or `undefined` when the id can't be resolved. */ resolveAuthor(id: string): CommentAuthor | undefined; /** Called after any comment (a new thread's first comment, or a reply) is posted. */ onPostComment?(comment: TLComment): void; /** Whether a comment is unread for the current user (return true for unread). */ isCommentUnread?(commentId: TLCommentId): boolean; /** * Called with every unread comment shown to the user in an open thread popover, batched per * report, so hosts can record read receipts without a write per comment. Needs * `isCommentUnread` to know what's unread. */ onCommentsRead?(commentIds: TLCommentId[]): void; /** Resolve the members matching an `@`-query in the composers (sync or async). */ getMentionSuggestions?(query: string): MentionMember[] | Promise; /** Override a mention-picker row's content. */ renderMentionSuggestion?(member: MentionMember): ReactNode; /** * The host's URL for a thread, so surfaces can link to it — e.g. the sidebar's rows render as * anchors when this is present, letting ctrl/cmd-click and middle-click open the thread in a * new tab (a plain click still selects the thread in place; the href isn't followed). */ getThreadHref?(threadId: TLCommentThreadId): string | undefined; } /** * Configuration for the commenting layer. Static config only — pass it once via * `CommentTool.configure({ ... })`, mirroring `ShapeUtil.configure`. Live, reactive values * (`currentUserId`, author resolution, read-status callbacks) are the `CommentingContext`, passed as * props to each commenting surface. * * For defaults, see {@link defaultCommentingOptions}. * * @example * ```tsx * * ``` * * @public */ export declare interface CommentingOptions { /** * How comment mutations interact with the editor undo stack. Defaults to `'ignore'` — comments * are deliberately not undoable (see `TLComment`). `'record'` is a multiplayer footgun: undoing * a delete resurrects a thread a collaborator already removed. Safe only single-player. */ readonly history: TLHistoryBatchOptions['history']; /** * History mode for the pin drag-to-move re-anchor specifically. Unlike posts/edits this is a * spatial edit that may reasonably be undoable alongside a shape move. Defaults to `history`. */ readonly dragHistory: TLHistoryBatchOptions['history'] | undefined; /** Fold nearby pins into count badges as the camera zooms out. */ readonly enableClustering: boolean; /** * Whether a user may hold several emoji reactions on one comment. `true` (the default) is the * Slack model: each emoji toggles independently. `false` is single-select: picking a new emoji * replaces the user's existing reaction. Note this is enforced client-side; the server accepts * per-emoji records either way. */ readonly allowMultipleReactions: boolean; /** * Whether a token may be added as a reaction. Defaults to {@link isAllowedReactionEmoji}, which * keeps a scripted client from writing junk values the picker would never offer. Override * alongside a custom `ReactionPalette`. Removals aren't checked — an off-palette reaction must * still be clearable. */ isAllowedReaction(token: string): boolean; /** * Whether dragging the comment tool out creates a region anchor — a comment attached to a * rectangular area, drawn as a dashed box with the pin on the corner the drag released on. Off * by default, where comments attach to points and shapes only and a drag trails the composer. */ readonly enableRegions: boolean; /** * Whether the viewer may participate in commenting: composing, editing, deleting, resolving, and * moving pins. When false, {@link CommentingComponents.ComposerFallback} renders in the * composer's place and action affordances are hidden. Unset, participation is allowed exactly * when `currentUserId` is set. * * Called during render via {@link useCanComment}, so signal reads are tracked. Posting still * needs a `currentUserId`, so returning true for a signed-out viewer yields a composer whose * send button stays disabled. A callback that throws is logged and read as false, rather than * taking the comments layer down with it. */ readonly canComment: ((ctx: { currentUserId: null | string; editor: Editor; }) => boolean) | undefined; /** * Whether the viewer may make a particular write against a particular record: editing or * deleting a comment, or deleting a thread. Unset, each is its record's owner's to make * ({@link defaultCanModifyComment}) — you edit and delete your own comments, and delete threads * you started. Override it to widen that (a workspace admin or moderator who may remove * anyone's comment) or to narrow it (no edits after an hour). Where it returns false the * affordance isn't rendered. * * Checked after {@link CommentingOptions.canComment}, which gates commenting as a whole: a * viewer who may not participate gets no action affordances at all, whatever this returns. * * Called during render via {@link useCanModifyComment}, so reactive reads (signals) are tracked. * A callback that throws is logged and read as false: an affordance is withheld rather than the * comments layer lost, and a denial is what a server enforcing the same rule would have said. * * @example * ```tsx * CommentTool.configure({ * canModifyComment: (ctx) => * // Moderators may delete anything; everything else stays the owner's to do. * (ctx.action !== 'edit-comment' && isModerator(ctx.currentUserId)) || * defaultCanModifyComment(ctx), * }) * ``` */ readonly canModifyComment: ((ctx: CommentModificationContext) => boolean) | undefined; /** Normalized (0–1) spot within a shape where imprecise shape pins sit. Default top-right. */ readonly impreciseShapeAnchor: { readonly x: number; readonly y: number; }; /** * Whether a comment landing on a shape pins to the exact clicked spot, or to the shape as a * whole (rendered at `impreciseShapeAnchor`). Always precise by default; return `false`, or * decide from the context. Governs new placements only — existing anchors render as stored. */ shouldBePrecise(editor: Editor, context: ShapeCommentPrecisionContext): boolean; /** Component overrides. See {@link CommentingComponents}. */ readonly components: CommentingComponents; } /** One thread's row in a {@link CommentsList}. @public @react */ export declare function CommentListItem({ id, author, preview, date, resolved, page, count, selected, reactions, href, resolvedLabel, onSelect, }: CommentListItemRenderProps): JSX.Element; /** @public */ export declare interface CommentListItemProps { id: string; author: CommentAuthor; /** A short preview of the thread — e.g. the first comment's body. */ preview: ReactNode; /** ISO datetime of the thread's first comment. */ date: string; resolved?: boolean; /** Name of the page the thread lives on, shown as a small label. Omit to hide. */ page?: string; /** Total comments in the thread. */ count?: number; /** Whether this thread is the open one. */ selected?: boolean; /** Tallied reactions for the row (a thread's, or a single comment's when the row is one * comment), shown as inert pills under the preview. Omit to hide. */ reactions?: ReactionSummary[]; /** * Link target for the item. When set, the row renders as an anchor so browser affordances * (ctrl/cmd-click, middle-click) open it in a new tab; a plain click still calls `onSelect`. */ href?: string; } /** * What a row is rendered with: the item, plus the list-level wiring it needs to be interactive. * A custom row gets the same props the default `` does, so it can wrap the * default rather than reimplement it. * * @public */ export declare interface CommentListItemRenderProps extends CommentListItemProps { /** Label for a resolved thread's marker on its row. */ resolvedLabel?: string; /** Called with the thread id when the row is chosen. */ onSelect?(id: string): void; } /** * A commenting write that belongs to someone in particular, and the record it targets — the * argument to {@link CommentingOptions.canModifyComment}. * * Resolving, reopening, reacting, and moving a pin aren't here: none of them is anyone's in * particular, so {@link CommentingOptions.canComment} is the only gate on them. * * @public */ export declare type CommentModification = { readonly action: 'delete-comment'; readonly comment: TLComment; } | { readonly action: 'delete-thread'; readonly thread: TLCommentThread; } | { readonly action: 'edit-comment'; readonly comment: TLComment; }; /** * The argument to {@link CommentingOptions.canModifyComment}: which write, against which record, * and by whom. * * @public */ export declare type CommentModificationContext = { readonly currentUserId: null | string; readonly editor: Editor; } & CommentModification; /** A canvas comment marker: shows its `children` (or a check when resolved). Purely * presentational — it reflects open/resolved state via CSS; wrap it to make it clickable. * @public @react */ export declare function CommentPin({ children, resolved, open }: CommentPinProps): JSX.Element; /** @public */ export declare interface CommentPinProps { /** What the pin shows when unresolved — a number, an author initial, an , etc. * Left as a lever so consumers aren't locked into a count. */ children?: ReactNode; resolved?: boolean; /** The pin's thread is open — shows the active/selected indicator state. */ open?: boolean; } /** * The add-reaction button for one comment. Belongs with the comment card's hover actions rather * than in the reaction row, so opening it doesn't chase the row as reactions are added. * @public @react */ export declare function CommentReactionPicker({ comment, currentUserId, emoji, }: CommentReactionPickerProps): JSX.Element | null; /** @public */ export declare interface CommentReactionPickerProps { comment: TLComment; /** The reacting user. Null/omitted renders nothing — there's nobody to react as. */ currentUserId?: null | string; /** The emoji the picker offers. Defaults to `DEFAULT_REACTION_EMOJI`. */ emoji?: string[]; } /** * The tallied reaction row under one comment. Pair with `CommentReactionPicker`, which is what * adds a reaction. * @public @react */ export declare function CommentReactions({ comment, currentUserId, resolveName }: CommentReactionsProps): JSX.Element; /** @public */ export declare interface CommentReactionsProps { comment: TLComment; /** The reacting user. Null/omitted gives a read-only row (signed out): counts show, but the * pills don't toggle. */ currentUserId?: null | string; /** Names a reactor id for the hover list. Ids it can't name fall back to a generic "Someone". */ resolveName?(userId: string): string | undefined; } /** The funnel dropdown in the sidebar header: toggles for which threads the list shows. * @public @react */ export declare function CommentsFilterMenu({ canFilterByAuthor, canFilterByUnread, }: CommentsFilterMenuProps): JSX.Element; /** @public */ export declare interface CommentsFilterMenuProps { /** Whether to offer the "only your threads" toggle (needs a known current user). */ canFilterByAuthor?: boolean; /** Whether to offer the "only unread" toggle (needs a read-status source). */ canFilterByUnread?: boolean; } /** * Whether comment pins are hidden on the canvas. Governs the on-canvas layer (pins + open popover) * only — the sidebar is unaffected. * @public */ export declare const commentsHidden: EditorAtom; /** * A scrollable list of comment threads — each an avatar, byline, and a one-line preview. * Presentational: you supply the items (already summarised) and an `onSelect` handler; the canvas * `CanvasCommentsSidebar` wires it to the store, but a consumer can build their own list from this. * @public @react */ export declare function CommentsList({ items, onSelect, header, headerAction, empty, resolvedLabel, renderItem, }: CommentsListProps): JSX.Element; /** @public */ export declare interface CommentsListProps { items: CommentListItemProps[]; /** Called with a thread id when an item is chosen. */ onSelect?(id: string): void; /** Shown above the list (e.g. "Comments"). Omit for none. */ header?: ReactNode; /** Rendered at the right of the header row — e.g. a filter menu. */ headerAction?: ReactNode; /** Shown in place of the list when there are no threads. */ empty?: ReactNode; /** Label for a resolved thread's marker on its row. Defaults to "Resolved". */ resolvedLabel?: string; /** * Override how each item renders. Defaults to ``, which is exported — so a * row that only adds something can spread these props into it rather than start over. The list * supplies the key, so a custom row doesn't need one. */ renderItem?(props: CommentListItemRenderProps): ReactNode; } /** * A checkbox menu item for a "View" menu that shows/hides comment pins on the canvas — checked * when comments are visible, like tldraw's own grid/snap toggles. Reads and toggles the shared * {@link commentsHidden} signal, so it stays in sync with the Shift+C shortcut and the sidebar * control. Drop it into whichever menu your app owns. * @public @react */ export declare function CommentsMenuItem(): JSX.Element; /** The overflow (⋯) dropdown in the sidebar header. For now it holds the hide/show-comments * toggle; it's the home for later comment-wide controls (notifications, mark all as read). * @public @react */ export declare function CommentsOverflowMenu(): JSX.Element; /** * Whether the comments sidebar (the thread list) is open. Driven by an explicit control rather than * by which tool is active, so browsing threads is separate from placing them. * @public */ export declare const commentsSidebarOpen: EditorAtom; /** The sidebar header's show/hide toggle for comment pins — an eye that closes while comments * are hidden. The same state as the Shift+C shortcut. * @public @react */ export declare function CommentsVisibilityToggle(): JSX.Element; /** * A comment thread: an optional header, an optional resolved banner, the comments, and an * optional reply composer. Presentational — you supply the comments (as card props) and the * composer's handlers; how each comment renders is overridable via `renderComment`. * @public @react */ export declare function CommentThread({ comments, header, headerActions, resolvedBanner, composer, footer, renderComment, }: CommentThreadProps): JSX.Element; /** @public */ export declare interface CommentThreadProps { /** The thread's comments, already adapted to card props (oldest first). */ comments: CommentCardProps[]; /** Optional header, e.g. "Thread". Omit for no header. */ header?: ReactNode; /** Action controls shown at the right of the header (resolve, delete, dismiss…). */ headerActions?: ReactNode; /** When set, shows a banner above the comments (e.g. "Resolved by Jess"). */ resolvedBanner?: ReactNode; /** Reply composer props. Omit for a read-only thread (no composer). */ composer?: CommentComposerProps; /** Shown at the bottom of the thread (e.g. a sign-in prompt when there's no composer). When * both are set, the footer renders after the composer. */ footer?: ReactNode; /** Override how each comment renders. Defaults to ``. */ renderComment?(comment: CommentCardProps, index: number): ReactNode; } /** * The comment tool. Pressing down opens the comment composer at the pointer and it follows until * release — like placing a sticky note — settling on a point, or on a shape when released over one. * With region comments enabled, dragging past the threshold draws a region rectangle instead. * Placement only opens a composer; the records are created when the comment is posted. The tool * stays active while the composer is open — posting returns to select, and clicking elsewhere * re-places the composer. * @public */ export declare class CommentTool extends StateNode { static id: string; static initial: string; static children(): TLStateNodeConstructor[]; /** * Configure this tool's {@link CommentTool.options | `options`}, returning a configured subclass * to register via `tools`. Mirrors `ShapeUtil.configure`. Layers over any prior `configure`, so * calls can be chained. * * @example * ```tsx * * ``` */ static configure(this: T, options: T extends new (...args: any[]) => { options: infer Options; } ? Partial : never): T; /** * The merged commenting options for this editor. Read from anywhere via * {@link getCommentingOptions}. Override with {@link CommentTool.configure}. */ options: CommentingOptions; onEnter(): void; onExit(): void; onCancel(): void; } /** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides. * Once registered, tldraw's `DefaultQuickActionsContent` shows the comment button. * @public */ export declare const commentToolOverrides: TLUiOverrides; /** @public */ export declare const commentTools: (typeof CommentTool)[]; /** @public @react */ export declare function CountBadge({ count, open }: CountBadgeProps): JSX.Element; /** @public */ export declare interface CountBadgeProps { count: number; /** The badge's list is showing — shows the active/selected indicator state, as an open pin does. */ open?: boolean; } export { createMentionSuggestion } /** * The reaction emoji palette: the small, opinionated set the picker offers *and* the set a * reaction is allowed to use. It's deliberately short — a quick affordance under a comment, not a * full emoji keyboard — and it's the single source of truth for "which emoji may a reaction * carry". A reaction's `emoji` field is a free-form `string`, so this palette is what keeps it to * real, offered emoji; check membership with {@link isAllowedReactionEmoji}. Pass a custom `emoji` * array to `ReactionPicker` to offer a different set — validate against that same set if you do. * * @public */ export declare const DEFAULT_REACTION_EMOJI: string[]; /** The out-of-the-box view: every page's threads, resolved ones hidden until asked for. * @public */ export declare const DEFAULT_SIDEBAR_FILTERS: SidebarFilters; /** * The default {@link CommentingOptions.canModifyComment}: a write is its record's owner's to make — * a comment's author edits and deletes it, a thread's creator deletes the thread — and a viewer * with no identity may make none of them. * * Exported so a callback can widen the default rather than restate it: * `(ctx) => isModerator(ctx.currentUserId) || defaultCanModifyComment(ctx)`. * * @public */ export declare function defaultCanModifyComment(ctx: CommentModificationContext): boolean; /** * The default {@link CommentingOptions}. Override via `CommentTool.configure({ ... })`. * * @public */ export declare const defaultCommentingOptions: { readonly allowMultipleReactions: true; readonly canComment: undefined; readonly canModifyComment: undefined; readonly components: {}; readonly dragHistory: undefined; readonly enableClustering: true; readonly enableRegions: false; readonly history: 'ignore'; readonly impreciseShapeAnchor: { readonly x: 1; readonly y: 0; }; readonly isAllowedReaction: typeof isAllowedReactionEmoji; readonly shouldBePrecise: () => true; }; /** * The default reactor tooltip: hangs the built-in inline sentence (`DefaultReactionTooltipContent`) * in a standard tooltip below the pill — the same tooltip the toolbar uses. Replace it via * `CommentingComponents.ReactionTooltip` to present the reactor list any other way (a different box, * avatars, a banner, anywhere on screen); it receives the pill as `children`. * @public @react */ export declare function DefaultReactionTooltip({ reactors, children }: ReactionTooltipProps): JSX.Element; /** * The default reactor sentence naming who reacted — up to three names spelled out, then "and N * others" (e.g. "You reacted", "You and Bo reacted", "You, Bo and Ada reacted", "You, Bo, Ada and 2 * others reacted"). The wording lives in the `comments.reacted-*` translation strings so each locale * controls the grammar. Exported so a custom `ReactionTooltip` can reuse it inside its own box. * @public @react */ export declare function DefaultReactionTooltipContent({ reactors }: { reactors: ReactionReactor[]; }): JSX.Element | null; /** The default reaction renderer: emits the token string for the OS emoji font to draw. @public */ export declare function defaultRenderReaction(token: string): ReactNode; /** * Delete a comment. * * This is a soft delete: it sets `isDeleted` rather than removing the record, and the server prunes * the comment and its reactions once the flag is persisted — so no client removes records it * doesn't own, and a server enforcing per-record permissions has a write it can check. * * Deleting is the author's to do by default; {@link CommentingOptions.canModifyComment} widens * that, as does its counterpart on the server. * * Never undoable, whatever {@link CommentingOptions.history} says: the flag is write-once * server-side, so an undo clearing it would be vetoed rather than bring the comment back. * * Deleting a thread's last comment closes it and leaves the thread record for the server to prune, * since the deleter may not be its creator. An already-deleted comment is a no-op. * * @public */ export declare function deleteComment(editor: Editor, comment: TLComment): void; /** * Delete a thread and, with it, the whole conversation. * * A soft delete on the same model as {@link deleteComment}: the server prunes the thread, its * comments, and their reactions once the flag is persisted. Deleting a thread is its creator's to * do by default ({@link CommentingOptions.canModifyComment}), and the write is never undoable. * Closes the thread if it's the open one; a pruned thread is a no-op. * * @public */ export declare function deleteThread(editor: Editor, thread: TLCommentThread): void; /** * Replace a comment's body and stamp it as edited, which renders the "(edited)" marker on its * byline. Editing is the author's to do by default ({@link CommentingOptions.canModifyComment}), * and a server enforcing per-record permissions rejects anyone else's. Widening one end without the * other leaves an edit that's offered and then rejected, so widen both. * * The body lands on the version the store currently holds, so a stale copy can't revert a later * change or re-create a removed comment — editing one of those does nothing. * * @example * ```ts * editComment(editor, comment, toRichText('Actually, make it dashed')) * ``` * * @public */ export declare function editComment(editor: Editor, comment: TLComment, body: TLRichText): void; /** * A grid of emoji to react with. Presentational and unpositioned — the host owns placement and * dismissal; `ReactionPicker` renders it in a dropdown under the add-reaction button. * @public @react */ export declare function EmojiPicker({ emoji, selected, onSelect, renderReaction, }: EmojiPickerProps): JSX.Element; /** @public */ export declare interface EmojiPickerProps { /** The emoji to offer. Defaults to `DEFAULT_REACTION_EMOJI`. */ emoji?: string[]; /** Emoji the current user has already reacted with; shown as pressed. */ selected?: string[]; /** * Called when an emoji is chosen. Custom palettes should forward their click or keydown event * so `ReactionPicker` can keep the palette open on shift-picks. */ onSelect?(emoji: string, event?: KeyboardEvent_2 | MouseEvent_2): void; /** How to draw each emoji token. Defaults to the token string (OS emoji font). */ renderReaction?: RenderReaction; } /** The empty state shown when a thread has no comments yet. @public @react */ export declare function EmptyState({ message }: EmptyStateProps): JSX.Element; /** @public */ export declare interface EmptyStateProps { message: string; } export { filterMentionMembers } /** Open a thread and bring it into view — switch to its page if needed, then center its pin. @public */ export declare function focusThread(editor: Editor, thread: TLCommentThread): void; /** * Format an ISO datetime as a full date and time ("Tuesday, July 22, 2025 at 4:44 PM"). * Locale-aware via Intl.DateTimeFormat. * @public */ export declare function formatFullDateTime(iso: string, locale?: string): string; /** * Format an ISO datetime as compact relative time ("2h ago", "yesterday", "last wk."). * Locale-aware via Intl.RelativeTimeFormat. * @public */ export declare function formatRelativeTime(iso: string, locale?: string): string; /** * Whether the viewer may participate in commenting, per {@link CommentingOptions.canComment} * (defaulting to `currentUserId != null` when unset). A callback that throws denies. * * This is a plain, untracked read — in React, use {@link useCanComment} instead. * * @public */ export declare function getCanComment(editor: Editor, currentUserId: null | string | undefined): boolean; /** * Whether the viewer may make a given write against a given record, per * {@link CommentingOptions.canModifyComment} (defaulting to {@link defaultCanModifyComment} when * unset). Where this is false the affordance isn't rendered. * * This is the per-record rule alone: the built-in UI additionally requires * {@link CommentingOptions.canComment}, since a viewer who may not participate gets no action * affordances at all. * * A plain, untracked read — a `canModifyComment` callback that reads signals is not observed. In * React, use {@link useCanModifyComment} instead. * * @public */ export declare function getCanModifyComment(editor: Editor, currentUserId: null | string | undefined, modification: CommentModification): boolean; /** * The merged {@link CommentingOptions} for an editor, read off the registered comment tool (which * carries them via `CommentTool.configure`). Falls back to {@link defaultCommentingOptions} when * the comment tool isn't registered. Usable from anywhere with an `Editor` — including the tool's * own state, which has no React context. * * @public */ export declare function getCommentingOptions(editor: Editor): CommentingOptions; /** All comment reactions currently in the store (non-reactive; wrap in `useValue` to react). @public */ export declare function getCommentReactions(editor: Editor): TLCommentReaction[]; /** Read one comment record by id, or `undefined` if the id isn't a present comment record. @public */ export declare function getCommentRecord(editor: Editor, id: string): TLCommentRecord | undefined; /** * Every comment in the store, **including soft-deleted ones** awaiting the server's prune. For the * set the UI shows, use {@link getLiveComments}. * * Non-reactive; wrap in `useValue`, or use `useComments`, to react. * @public */ export declare function getComments(editor: Editor): TLComment[]; /** * Every comment thread in the store, **including soft-deleted and emptied ones** awaiting the * server's prune, which nothing renders. For the set the UI shows, use {@link getLiveCommentThreads}. * * Non-reactive; wrap in `useValue`, or use `useCommentThreads`, to react. * @public */ export declare function getCommentThreads(editor: Editor): TLCommentThread[]; /** * The comments that should render: not soft-deleted. A deleted record lingers in the store until * the server prunes it, so build counts and lists from this rather than {@link getComments}. * * Non-reactive; the reactive equivalent is `useComments` (which also sorts oldest first). * @public */ export declare function getLiveComments(editor: Editor): TLComment[]; /** * The comment threads that should render (pins, sidebar): not soft-deleted, and still holding at * least one live comment. A thread emptied by its last comment's delete lingers with no surface * until the server's prune lands. * * Non-reactive; the reactive equivalent is `useCommentThreads`. * @public */ export declare function getLiveCommentThreads(editor: Editor): TLCommentThread[]; /** * The id passed to the most recent {@link revealThread} call that `CanvasComments` hasn't served * yet, or null. A request also clears when `CanvasComments` unmounts. * * This is a plain, untracked read — in React, use {@link useRevealThreadPending}, unless you need * the value as of *now* rather than as of the render you closed over. * * @public */ export declare function getRevealThreadPending(editor: Editor): null | string; /** * Whether `emoji` belongs to a reaction palette (defaults to {@link DEFAULT_REACTION_EMOJI}). Use * it to reject arbitrary strings before writing a reaction, so a scripted client can't spam a * comment with junk `emoji` values that the picker would never offer. Pass a custom `palette` to * match a customized picker. * * @public */ export declare function isAllowedReactionEmoji(emoji: string, palette?: readonly string[]): boolean; /** * Whether a click should be left to the browser's link handling (new tab, new window, or download). * @public */ export declare function isOpenInNewTabClick(e: MouseEvent_2): boolean; export { Mention } export { MentionList } export { MentionListProps } export { MentionMember } export { MentionProps } export { MentionSuggestionOptions } /** The id of the one open thread (only one popover is open at a time), or null when all closed. * @public */ export declare const openThreadId: EditorAtom; /** * Write comment records to the store, under the configured {@link CommentingOptions.history} * behavior. Defaults to `'ignore'`. * * Use it to seed or import threads, and to save an edit. To delete, prefer {@link deleteComment} * and {@link deleteThread}: comments are soft-deleted, and a synced server rejects the hard delete. * * @public */ export declare function putCommentRecords(editor: Editor, records: TLCommentRecord[]): void; /** * A single emoji reaction pill with a count, highlighted when the user reacted. Hovering it shows a * tooltip naming who reacted (the current user included), when `enableHoverList` is set. * @public @react */ export declare function Reaction({ emoji, count, active, reactors, enableHoverList, renderReaction, ReactionTooltip, onClick, }: ReactionProps): JSX.Element; /** * The add-reaction affordance: a smiley button that opens an emoji grid. * * Anchored to its own button rather than to the reactions row, so the menu keeps its position as * reactions are added and the row reflows. * * Picking an emoji dismisses the grid — adding and removing alike, since either way the picker has * done its job — and hands focus back to the trigger. Shift-picking keeps the grid open, for * choosing several reactions in a row. * @public @react */ export declare function ReactionPicker({ emoji, selected, onSelect, renderReaction, palette: Palette, menuId, className, }: ReactionPickerProps): JSX.Element; /** @public */ export declare interface ReactionPickerProps { /** The emoji to offer. Defaults to `DEFAULT_REACTION_EMOJI`. */ emoji?: string[]; /** Emoji the current user has already reacted with; shown as pressed in the grid. */ selected?: string[]; /** Called when an emoji is chosen. */ onSelect?(emoji: string): void; /** How to draw each emoji token. Defaults to the token string (OS emoji font). */ renderReaction?: RenderReaction; /** * What the button opens — the thing that produces a token. Defaults to `EmojiPicker`, the grid of * emoji. Swap it for any component taking the same props to offer something else entirely (the * drawn reactions example replaces it with a canvas you draw in); the props are passed straight * through either way. */ palette?: ComponentType; /** * Identifies the menu in tldraw's global menu registry, which keys open/closed state by id. * A thread renders one picker per comment, so this must differ per comment — sharing an id * makes every picker in the thread open at once. Defaults to a per-instance generated id; * pass one only for a stabler, more debuggable value. */ menuId?: string; /** Class for the trigger button. Defaults to the card-action style, matching the ⋯ button. */ className?: string; } /** @public */ export declare interface ReactionProps { emoji: string; count: number; active: boolean; /** How to draw the emoji token. Defaults to the token string (OS emoji font). */ renderReaction?: RenderReaction; /** Who reacted with this emoji, in reaction order — shown when the pill is hovered. */ reactors: ReactionReactor[]; /** Whether hovering shows the reactor list. Pass false to suppress it — e.g. while another * popup menu on the comment is open. Defaults to true. */ enableHoverList?: boolean; /** The hover affordance for the pill, naming who reacted. It receives the reactors and the pill * itself (as `children`) and returns the whole thing — so it owns the tooltip, its box, size, * shape, and position, not just the text inside. Defaults to `DefaultReactionTooltip`, which * hangs the built-in inline sentence below the pill. */ ReactionTooltip?: ComponentType; /** Called when the pill is pressed — toggles the current user's reaction. */ onClick?(): void; } /** One person who reacted with a given emoji, for the hover list. @public */ export declare interface ReactionReactor { /** Display name of the person who reacted. */ name: string; /** True for the current user. */ you: boolean; } /** * The row of tallied reactions under a comment. Presentational — the host supplies the summaries * and owns what toggling does. Hovering a pill lists who reacted (see `Reaction`). * * The add-reaction affordance is a separate component (`ReactionPicker`) so it can live outside * this row — on a comment card it sits with the card's hover actions, which keeps its position * fixed as reactions are added here. * @public @react */ export declare function Reactions({ reactions, onToggle, canReact, enableHoverList, renderReaction, ReactionTooltip, }: ReactionsProps): JSX.Element | null; /** @public */ export declare interface ReactionsProps { /** The comment's reactions, grouped by emoji. Empty renders nothing. */ reactions: ReactionSummary[]; /** Toggles the current user's reaction for an emoji. */ onToggle?(emoji: string): void; /** Whether the current user may react. False makes the pills inert (e.g. a signed-out * viewer, or a read-only thread) while still showing counts. */ canReact?: boolean; /** Whether hovering a pill shows its reactor list. Pass false to suppress it — e.g. while * another popup menu on the comment is open. Defaults to true. */ enableHoverList?: boolean; /** How to draw each emoji token. Defaults to the token string (OS emoji font). */ renderReaction?: RenderReaction; /** The tooltip naming who reacted, shown when a pill is hovered. Defaults to an inline sentence * (`DefaultReactionTooltip`). Swap it to render the reactor list however you like. */ ReactionTooltip?: ComponentType; } /** One emoji's reactions on a comment, already tallied. @public */ export declare interface ReactionSummary { emoji: string; /** How many people reacted with this emoji. */ count: number; /** True when the current user is one of them — renders the pill highlighted. */ active: boolean; /** Who reacted with this emoji, in reaction order — shown in the hover list. */ reactors: ReactionReactor[]; } /** * The reaction fields {@link summarizeReactions} needs — a structural subset of * {@link tldraw#TLCommentReaction}, so tallies can also be built from rows synced outside the * editor store. * * @public */ export declare interface ReactionSummaryInput { userId: string; emoji: string; createdAt: number; } /** @public */ export declare interface ReactionTooltipProps { /** Who reacted with this emoji, in reaction order. */ reactors: ReactionReactor[]; /** The reaction pill to wrap. Anchor the hover affordance to this and render it. */ children: ReactNode; } /** * Keep shape-anchored threads alive across their shape's lifecycle: * * - When the shape is deleted, the thread converts to a `point` anchor where its pin last sat, so * the conversation outlives the shape instead of becoming invisible. * - When the shape moves to another page, the thread follows: its `pageId` and each comment's * denormalized `pageId` update, and the anchor keeps riding the shape. * - When a deleted shape comes back, the thread re-attaches — unless its pin was manually moved in * the meantime, in which case the manual placement wins. * * A page move is `deleteShapes` + re-create with preserved ids inside one `editor.run`, but each * store write is its own operation, so "this shape is being moved" is never observable as a single * event. The handlers therefore cooperate across operations: `beforeDelete` snapshots where each * affected pin sits, the operation-complete pass converts threads whose shape is really gone, and * `afterCreate` plus that same pass restore the anchor once the store has settled. Undo/redo of a * move replays as a `parentId` update, so `afterChange` re-homes threads on cross-page reparents — * including threads anchored to descendants, which move without change events of their own. * * Remote changes are ignored: the client that performed the operation runs this same maintenance * and syncs the result. Writes honour the {@link CommentingOptions.history} option. * * Registered by `CanvasComments` on mount; parts-built consumers can call this directly. Returns a * cleanup function that unregisters all handlers. * * @public */ export declare function registerCommentAnchorLifecycle(editor: Editor): () => void; /** * Remove comment records from the store by id, under the configured * {@link CommentingOptions.history} behavior. * * This is a hard delete, which is rarely what you want: the built-in UI soft-deletes * ({@link deleteComment}, {@link deleteThread}) so the server can prune the records, and a server * enforcing per-record permissions vetoes a hard delete outright. Reach for this on a local, * unsynced store, or to drop a reaction (see {@link toggleCommentReaction}). * * @public */ export declare function removeCommentRecords(editor: Editor, ids: (TLCommentId | TLCommentReactionId | TLCommentThreadId)[]): void; /** Render a reaction token to its visual — the emoji glyph by default. @public */ export declare type RenderReaction = (token: string) => ReactNode; /** * Reopen a resolved thread, clearing the resolution. A no-op on a thread that isn't resolved, and * on one that's gone. Like {@link resolveThread}, it touches only the resolution. * * @public */ export declare function reopenThread(editor: Editor, thread: TLCommentThread): void; /** * Mark a thread resolved, stamping who resolved it and when. Resolved threads keep their pin (a * checked one) and are hidden from the sidebar until its "show resolved" filter is on. * * Only the resolution is written — the rest of the thread is read fresh, so a stale copy can't drag * a pin back. A no-op on a thread that's gone. * * @public */ export declare function resolveThread(editor: Editor, thread: TLCommentThread, userId: string): void; /** * Open a thread and bring it into view, given a thread id or the id of any comment in it. Use it to * jump to a thread from outside the canvas — a notification, a deep link, your own list. * * The request is served by `CanvasComments`, so it works before the records have arrived: the layer * waits for them, switches pages, unhides pins, and zooms far enough to split the thread out of any * cluster. That also means nothing happens if `CanvasComments` isn't mounted. * * To open a thread you already hold and skip the wait, see {@link focusThread}. * * @example * ```ts * revealThread(editor, new URLSearchParams(location.search).get('comment')!) * ``` * * @public */ export declare function revealThread(editor: Editor, threadOrCommentId: string): void; /** * Flatten a rich-text comment body to plaintext through the limited comment extension set, * separating paragraphs and list items with newlines. A convenience for consumers rendering bodies * as plain text (e.g. the sidebar preview); richer rendering can read the `TLRichText` directly. * @public */ export declare function richTextToPlaintext(body: TLRichText, resolveName?: (id: string) => string | undefined): string; /** The button that posts a comment — an up arrow. @public @react */ export declare function SendButton({ label, disabled, onClick }: SendButtonProps): JSX.Element; /** @public */ export declare interface SendButtonProps { /** The button's accessible name (e.g. "Send"). Shown as an up-arrow icon, so this is its label. */ label: string; disabled?: boolean; onClick?(): void; } /** * A shape anchor for a page point. `x`/`y` are the point's normalized (0–1) offset within the * shape's own bounds, taken in the shape's own space, so a pin on a rotated shape records the spot * it was dropped on. Remembered either way: when `precise` the pin sits at exactly `x`/`y`, * otherwise at the consumer's imprecise default. * @public */ export declare function shapeAnchorAt(editor: Editor, shapeId: TLShapeId, page: { x: number; y: number; }, precise: boolean): TLCommentAnchor; /** * The gesture that's creating a shape anchor, passed to * {@link CommentingOptions.shouldBePrecise}: the target shape, the page point of the release, and * whether Alt was held. * * @public */ export declare interface ShapeCommentPrecisionContext { readonly shapeId: TLShapeId; readonly point: VecLike; readonly altKey: boolean; } /** * Which threads the comments sidebar shows. Held as an editor-scoped signal (the `sidebarFilters` * `EditorAtom` in `./state`) rather than component state so it survives the sidebar * unmounting when the comment tool deactivates — a user's "hide resolved" choice shouldn't reset * every time they leave the tool. * @public */ export declare interface SidebarFilters { /** Include resolved threads. */ showResolved: boolean; /** Only threads the current user started. Ignored when there's no current user. */ onlyMine: boolean; /** Only threads with unread comments. Ignored when the host provides no read status. */ onlyUnread: boolean; /** Only threads on the current page. Off = every page's threads, each labelled. */ onlyCurrentPage: boolean; } /** Which threads the comments sidebar shows. * @public */ export declare const sidebarFilters: EditorAtom; /** A list row paired with the sort key that isn't part of what the row displays. @public */ export declare interface SidebarRow { item: CommentListItemProps; /** When the thread's most recent comment was posted — what the list orders by. */ lastActivity: number; } /** * Order the list: unresolved threads first, then by most recent activity, id as a stable tiebreak. * Recency is the thread's *latest* comment, not its first, so a thread someone just replied to rises * to the top instead of staying wherever it was started. (The row still shows the thread's opening * comment and its date — that's what identifies the thread; only the ordering follows the replies.) * * Exported so a hand-built list can match the sidebar's ordering instead of re-deriving it. * * @public */ export declare function sortSidebarRows(rows: readonly SidebarRow[]): readonly SidebarRow[]; /** * Tally a comment's reactions into an entry per emoji, ordered by when that emoji was first used so * the row stays stable as later reactions arrive. `active` marks the current user's emoji and * `reactors` lists who reacted, in reaction order. `resolveName` names each reactor; an id it can't * name falls back to a generic "Someone", never the raw user id. * * @public */ export declare function summarizeReactions(reactions: readonly ReactionSummaryInput[], currentUserId?: null | string, resolveName?: (userId: string) => string | undefined): ReactionSummary[]; /** * Typed reads of comment records on the editor store. * * Comment records live on the editor's local store so the canvas can render them reactively, but * they're opt-in and aren't part of the `TLRecord` union — so `editor.store` is statically typed * `Store` and every access has to reinterpret the type. These helpers own that * reinterpretation behind one boundary and keep call sites typed. * * Writes do the same, but also answer to the undo/redo policy, so they live in * `comment-mutations.ts`. */ /** * A record that lives in a comment thread: the thread itself, one of its messages, or a reaction * to one of those messages. * @public */ export declare type TLCommentRecord = TLComment | TLCommentReaction | TLCommentThread; /** * Toggle one user's reaction with a given emoji on a comment. * * Each reaction is its own record keyed by (comment, user, emoji), so this only touches that user's * own records and two people reacting at once never conflict. Behaviour depends on * `allowMultipleReactions`: * * - **multiple** (default): the emoji toggles independently, leaving other reactions alone. * - **single**: a new emoji replaces the user's existing reaction; the same one removes it. * * @public */ export declare function toggleCommentReaction(editor: Editor, comment: TLComment, userId: string, emoji: string, now?: number): void; /** Toggle comment-pin visibility for an editor. * @public */ export declare function toggleCommentsHidden(editor: Editor): void; /** Open or close the comments sidebar for an editor. * @public */ export declare function toggleCommentsSidebar(editor: Editor): void; /** * Reactive React hook for {@link getCanComment}: a `canComment` callback that reads signals * re-evaluates when they change. * * @public */ export declare function useCanComment(currentUserId: null | string | undefined): boolean; /** * Reactive React hook for {@link getCanModifyComment}: a `canModifyComment` callback that reads * signals re-evaluates when they change. * * @public */ export declare function useCanModifyComment(currentUserId: null | string | undefined, modification: CommentModification): boolean; /** * Whether commenting is licensed for this editor. Enabled in development; in production it requires * a tldraw license that includes the commenting feature (or the collaboration umbrella that grants * it). Reactive: re-reads when license validation resolves, and returns `false` while validation is * pending, so gated UI stays hidden until the license is confirmed. Works outside `` too: * UI mounted via `EditorProvider` resolves the license through the editor, and with no editor at * all this is `false`. * * The built-in commenting components (`CanvasComments`, `CanvasCommentsSidebar`, and the comment * tool's Quick Action) gate on this. Use it to gate any custom commenting UI the same way. * @public */ export declare function useCommentingEnabled(): boolean; /** * React hook for {@link getCommentingOptions}. Options are fixed per editor (set at tool * registration), so this doesn't need to be reactive. * * @public */ export declare function useCommentingOptions(): CommentingOptions; /** * One comment's reactions, oldest first, reactively. * * @public */ export declare function useCommentReactions(editor: Editor, commentId: TLComment['id']): TLCommentReaction[]; /** Every live comment in the store ({@link getLiveComments}), oldest first, reactively. Group by * `threadId` for per-thread lists. @public */ export declare function useComments(editor: Editor): TLComment[]; /** React hook for whether comment pins are hidden. * @public */ export declare function useCommentsHidden(): boolean; /** React hook for whether the comments sidebar is open. * @public */ export declare function useCommentsSidebarOpen(): boolean; /** * The comment threads that should render (pins, sidebar), reactively — the live set described by * {@link getLiveCommentThreads}. Use `getCommentThreads` for the unfiltered set, including * soft-deleted threads awaiting the server's prune. * * @public */ export declare function useCommentThreads(editor: Editor): TLCommentThread[]; /** React hook for the open thread id. * @public */ export declare function useOpenThreadId(): null | string; /** * Reactive React hook for {@link getRevealThreadPending}. * * Use it to notice a reveal that never lands — usually a deep link to a deleted comment. Give it a * grace period first, since a request also sits here while its records sync in, and re-check with * {@link getRevealThreadPending} when it elapses. * * @public */ export declare function useRevealThreadPending(): null | string; /** React hook for the current sidebar filters. * @public */ export declare function useSidebarFilters(): SidebarFilters; /** A thread's live comments, oldest first, reactively. @public */ export declare function useThreadComments(editor: Editor, threadId: TLCommentThreadId): TLComment[]; export { }