@tldraw/commenting 0.0.0-bootstrap

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/README.md +11 -0
  2. package/commenting.css +832 -0
  3. package/dist-cjs/canvas/cluster-input.js +40 -0
  4. package/dist-cjs/canvas/cluster-input.js.map +7 -0
  5. package/dist-cjs/canvas/comment-body.js +31 -0
  6. package/dist-cjs/canvas/comment-body.js.map +7 -0
  7. package/dist-cjs/canvas/comment-render.js +72 -0
  8. package/dist-cjs/canvas/comment-render.js.map +7 -0
  9. package/dist-cjs/canvas/comment-store.js +48 -0
  10. package/dist-cjs/canvas/comment-store.js.map +7 -0
  11. package/dist-cjs/canvas/comment-tool.js +160 -0
  12. package/dist-cjs/canvas/comment-tool.js.map +7 -0
  13. package/dist-cjs/canvas/comments-filter-menu.js +109 -0
  14. package/dist-cjs/canvas/comments-filter-menu.js.map +7 -0
  15. package/dist-cjs/canvas/comments-menu-item.js +42 -0
  16. package/dist-cjs/canvas/comments-menu-item.js.map +7 -0
  17. package/dist-cjs/canvas/comments-overflow-menu.js +64 -0
  18. package/dist-cjs/canvas/comments-overflow-menu.js.map +7 -0
  19. package/dist-cjs/canvas/comments-overlay.js +1081 -0
  20. package/dist-cjs/canvas/comments-overlay.js.map +7 -0
  21. package/dist-cjs/canvas/comments-sidebar.js +134 -0
  22. package/dist-cjs/canvas/comments-sidebar.js.map +7 -0
  23. package/dist-cjs/canvas/hooks.js +45 -0
  24. package/dist-cjs/canvas/hooks.js.map +7 -0
  25. package/dist-cjs/canvas/license.js +28 -0
  26. package/dist-cjs/canvas/license.js.map +7 -0
  27. package/dist-cjs/canvas/options.js +45 -0
  28. package/dist-cjs/canvas/options.js.map +7 -0
  29. package/dist-cjs/canvas/region-options.js +49 -0
  30. package/dist-cjs/canvas/region-options.js.map +7 -0
  31. package/dist-cjs/canvas/rich-text.js +28 -0
  32. package/dist-cjs/canvas/rich-text.js.map +7 -0
  33. package/dist-cjs/canvas/sidebar-filters.js +30 -0
  34. package/dist-cjs/canvas/sidebar-filters.js.map +7 -0
  35. package/dist-cjs/canvas/state.js +76 -0
  36. package/dist-cjs/canvas/state.js.map +7 -0
  37. package/dist-cjs/canvas/thread-state.js +81 -0
  38. package/dist-cjs/canvas/thread-state.js.map +7 -0
  39. package/dist-cjs/clustering/computeClusterTable.js +83 -0
  40. package/dist-cjs/clustering/computeClusterTable.js.map +7 -0
  41. package/dist-cjs/clustering/mst.js +117 -0
  42. package/dist-cjs/clustering/mst.js.map +7 -0
  43. package/dist-cjs/clustering/replay.js +275 -0
  44. package/dist-cjs/clustering/replay.js.map +7 -0
  45. package/dist-cjs/clustering/runtime.js +134 -0
  46. package/dist-cjs/clustering/runtime.js.map +7 -0
  47. package/dist-cjs/clustering/schedule.js +170 -0
  48. package/dist-cjs/clustering/schedule.js.map +7 -0
  49. package/dist-cjs/clustering/types.js +17 -0
  50. package/dist-cjs/clustering/types.js.map +7 -0
  51. package/dist-cjs/index.d.ts +868 -0
  52. package/dist-cjs/index.js +126 -0
  53. package/dist-cjs/index.js.map +7 -0
  54. package/dist-cjs/ui/avatar.js +43 -0
  55. package/dist-cjs/ui/avatar.js.map +7 -0
  56. package/dist-cjs/ui/byline.js +35 -0
  57. package/dist-cjs/ui/byline.js.map +7 -0
  58. package/dist-cjs/ui/comment-card.js +37 -0
  59. package/dist-cjs/ui/comment-card.js.map +7 -0
  60. package/dist-cjs/ui/comment-composer.js +136 -0
  61. package/dist-cjs/ui/comment-composer.js.map +7 -0
  62. package/dist-cjs/ui/comment-extensions.js +37 -0
  63. package/dist-cjs/ui/comment-extensions.js.map +7 -0
  64. package/dist-cjs/ui/comment-mention.js +42 -0
  65. package/dist-cjs/ui/comment-mention.js.map +7 -0
  66. package/dist-cjs/ui/comment-pin.js +29 -0
  67. package/dist-cjs/ui/comment-pin.js.map +7 -0
  68. package/dist-cjs/ui/comment-text.js +29 -0
  69. package/dist-cjs/ui/comment-text.js.map +7 -0
  70. package/dist-cjs/ui/comment-thread.js +45 -0
  71. package/dist-cjs/ui/comment-thread.js.map +7 -0
  72. package/dist-cjs/ui/comments-list.js +106 -0
  73. package/dist-cjs/ui/comments-list.js.map +7 -0
  74. package/dist-cjs/ui/count-badge.js +28 -0
  75. package/dist-cjs/ui/count-badge.js.map +7 -0
  76. package/dist-cjs/ui/empty-state.js +31 -0
  77. package/dist-cjs/ui/empty-state.js.map +7 -0
  78. package/dist-cjs/ui/format-time.js +46 -0
  79. package/dist-cjs/ui/format-time.js.map +7 -0
  80. package/dist-cjs/ui/mention-list.js +70 -0
  81. package/dist-cjs/ui/mention-list.js.map +7 -0
  82. package/dist-cjs/ui/mention-suggestion.js +206 -0
  83. package/dist-cjs/ui/mention-suggestion.js.map +7 -0
  84. package/dist-cjs/ui/mention.js +31 -0
  85. package/dist-cjs/ui/mention.js.map +7 -0
  86. package/dist-cjs/ui/reaction.js +31 -0
  87. package/dist-cjs/ui/reaction.js.map +7 -0
  88. package/dist-cjs/ui/reactions.js +34 -0
  89. package/dist-cjs/ui/reactions.js.map +7 -0
  90. package/dist-cjs/ui/render-markdown.js +63 -0
  91. package/dist-cjs/ui/render-markdown.js.map +7 -0
  92. package/dist-cjs/ui/send-button.js +28 -0
  93. package/dist-cjs/ui/send-button.js.map +7 -0
  94. package/dist-esm/canvas/cluster-input.mjs +20 -0
  95. package/dist-esm/canvas/cluster-input.mjs.map +7 -0
  96. package/dist-esm/canvas/comment-body.mjs +11 -0
  97. package/dist-esm/canvas/comment-body.mjs.map +7 -0
  98. package/dist-esm/canvas/comment-render.mjs +52 -0
  99. package/dist-esm/canvas/comment-render.mjs.map +7 -0
  100. package/dist-esm/canvas/comment-store.mjs +28 -0
  101. package/dist-esm/canvas/comment-store.mjs.map +7 -0
  102. package/dist-esm/canvas/comment-tool.mjs +142 -0
  103. package/dist-esm/canvas/comment-tool.mjs.map +7 -0
  104. package/dist-esm/canvas/comments-filter-menu.mjs +99 -0
  105. package/dist-esm/canvas/comments-filter-menu.mjs.map +7 -0
  106. package/dist-esm/canvas/comments-menu-item.mjs +22 -0
  107. package/dist-esm/canvas/comments-menu-item.mjs.map +7 -0
  108. package/dist-esm/canvas/comments-overflow-menu.mjs +53 -0
  109. package/dist-esm/canvas/comments-overflow-menu.mjs.map +7 -0
  110. package/dist-esm/canvas/comments-overlay.mjs +1094 -0
  111. package/dist-esm/canvas/comments-overlay.mjs.map +7 -0
  112. package/dist-esm/canvas/comments-sidebar.mjs +120 -0
  113. package/dist-esm/canvas/comments-sidebar.mjs.map +7 -0
  114. package/dist-esm/canvas/hooks.mjs +25 -0
  115. package/dist-esm/canvas/hooks.mjs.map +7 -0
  116. package/dist-esm/canvas/license.mjs +8 -0
  117. package/dist-esm/canvas/license.mjs.map +7 -0
  118. package/dist-esm/canvas/options.mjs +27 -0
  119. package/dist-esm/canvas/options.mjs.map +7 -0
  120. package/dist-esm/canvas/region-options.mjs +29 -0
  121. package/dist-esm/canvas/region-options.mjs.map +7 -0
  122. package/dist-esm/canvas/rich-text.mjs +8 -0
  123. package/dist-esm/canvas/rich-text.mjs.map +7 -0
  124. package/dist-esm/canvas/sidebar-filters.mjs +10 -0
  125. package/dist-esm/canvas/sidebar-filters.mjs.map +7 -0
  126. package/dist-esm/canvas/state.mjs +60 -0
  127. package/dist-esm/canvas/state.mjs.map +7 -0
  128. package/dist-esm/canvas/thread-state.mjs +61 -0
  129. package/dist-esm/canvas/thread-state.mjs.map +7 -0
  130. package/dist-esm/clustering/computeClusterTable.mjs +63 -0
  131. package/dist-esm/clustering/computeClusterTable.mjs.map +7 -0
  132. package/dist-esm/clustering/mst.mjs +97 -0
  133. package/dist-esm/clustering/mst.mjs.map +7 -0
  134. package/dist-esm/clustering/replay.mjs +255 -0
  135. package/dist-esm/clustering/replay.mjs.map +7 -0
  136. package/dist-esm/clustering/runtime.mjs +114 -0
  137. package/dist-esm/clustering/runtime.mjs.map +7 -0
  138. package/dist-esm/clustering/schedule.mjs +150 -0
  139. package/dist-esm/clustering/schedule.mjs.map +7 -0
  140. package/dist-esm/clustering/types.mjs +1 -0
  141. package/dist-esm/clustering/types.mjs.map +7 -0
  142. package/dist-esm/index.d.mts +868 -0
  143. package/dist-esm/index.mjs +139 -0
  144. package/dist-esm/index.mjs.map +7 -0
  145. package/dist-esm/ui/avatar.mjs +23 -0
  146. package/dist-esm/ui/avatar.mjs.map +7 -0
  147. package/dist-esm/ui/byline.mjs +15 -0
  148. package/dist-esm/ui/byline.mjs.map +7 -0
  149. package/dist-esm/ui/comment-card.mjs +17 -0
  150. package/dist-esm/ui/comment-card.mjs.map +7 -0
  151. package/dist-esm/ui/comment-composer.mjs +116 -0
  152. package/dist-esm/ui/comment-composer.mjs.map +7 -0
  153. package/dist-esm/ui/comment-extensions.mjs +17 -0
  154. package/dist-esm/ui/comment-extensions.mjs.map +7 -0
  155. package/dist-esm/ui/comment-mention.mjs +22 -0
  156. package/dist-esm/ui/comment-mention.mjs.map +7 -0
  157. package/dist-esm/ui/comment-pin.mjs +9 -0
  158. package/dist-esm/ui/comment-pin.mjs.map +7 -0
  159. package/dist-esm/ui/comment-text.mjs +9 -0
  160. package/dist-esm/ui/comment-text.mjs.map +7 -0
  161. package/dist-esm/ui/comment-thread.mjs +25 -0
  162. package/dist-esm/ui/comment-thread.mjs.map +7 -0
  163. package/dist-esm/ui/comments-list.mjs +86 -0
  164. package/dist-esm/ui/comments-list.mjs.map +7 -0
  165. package/dist-esm/ui/count-badge.mjs +8 -0
  166. package/dist-esm/ui/count-badge.mjs.map +7 -0
  167. package/dist-esm/ui/empty-state.mjs +11 -0
  168. package/dist-esm/ui/empty-state.mjs.map +7 -0
  169. package/dist-esm/ui/format-time.mjs +26 -0
  170. package/dist-esm/ui/format-time.mjs.map +7 -0
  171. package/dist-esm/ui/mention-list.mjs +50 -0
  172. package/dist-esm/ui/mention-list.mjs.map +7 -0
  173. package/dist-esm/ui/mention-suggestion.mjs +186 -0
  174. package/dist-esm/ui/mention-suggestion.mjs.map +7 -0
  175. package/dist-esm/ui/mention.mjs +11 -0
  176. package/dist-esm/ui/mention.mjs.map +7 -0
  177. package/dist-esm/ui/reaction.mjs +11 -0
  178. package/dist-esm/ui/reaction.mjs.map +7 -0
  179. package/dist-esm/ui/reactions.mjs +14 -0
  180. package/dist-esm/ui/reactions.mjs.map +7 -0
  181. package/dist-esm/ui/render-markdown.mjs +45 -0
  182. package/dist-esm/ui/render-markdown.mjs.map +7 -0
  183. package/dist-esm/ui/send-button.mjs +8 -0
  184. package/dist-esm/ui/send-button.mjs.map +7 -0
  185. package/package.json +71 -0
  186. package/src/canvas/canvas.css +148 -0
  187. package/src/canvas/cluster-input.test.ts +223 -0
  188. package/src/canvas/cluster-input.ts +29 -0
  189. package/src/canvas/comment-body.tsx +21 -0
  190. package/src/canvas/comment-render.test.ts +90 -0
  191. package/src/canvas/comment-render.ts +92 -0
  192. package/src/canvas/comment-store.ts +55 -0
  193. package/src/canvas/comment-tool.tsx +200 -0
  194. package/src/canvas/comments-filter-menu.tsx +104 -0
  195. package/src/canvas/comments-menu-item.tsx +24 -0
  196. package/src/canvas/comments-overflow-menu.tsx +62 -0
  197. package/src/canvas/comments-overlay.tsx +1460 -0
  198. package/src/canvas/comments-sidebar.tsx +184 -0
  199. package/src/canvas/hooks.ts +28 -0
  200. package/src/canvas/license.ts +15 -0
  201. package/src/canvas/options.test.ts +127 -0
  202. package/src/canvas/options.ts +111 -0
  203. package/src/canvas/region-options.ts +57 -0
  204. package/src/canvas/rich-text.ts +15 -0
  205. package/src/canvas/sidebar-filters.ts +25 -0
  206. package/src/canvas/state.ts +102 -0
  207. package/src/canvas/thread-state.ts +91 -0
  208. package/src/clustering/computeClusterTable.test.ts +299 -0
  209. package/src/clustering/computeClusterTable.ts +82 -0
  210. package/src/clustering/locality.test.ts +189 -0
  211. package/src/clustering/mst.test.ts +383 -0
  212. package/src/clustering/mst.ts +134 -0
  213. package/src/clustering/replay.test.ts +547 -0
  214. package/src/clustering/replay.ts +326 -0
  215. package/src/clustering/runtime.test.ts +596 -0
  216. package/src/clustering/runtime.ts +153 -0
  217. package/src/clustering/schedule.test.ts +514 -0
  218. package/src/clustering/schedule.ts +184 -0
  219. package/src/clustering/types.ts +102 -0
  220. package/src/index.ts +96 -0
  221. package/src/ui/avatar.tsx +31 -0
  222. package/src/ui/byline.tsx +23 -0
  223. package/src/ui/comment-card.tsx +32 -0
  224. package/src/ui/comment-composer.tsx +179 -0
  225. package/src/ui/comment-extensions.ts +25 -0
  226. package/src/ui/comment-mention.ts +47 -0
  227. package/src/ui/comment-pin.tsx +21 -0
  228. package/src/ui/comment-text.tsx +12 -0
  229. package/src/ui/comment-thread.tsx +56 -0
  230. package/src/ui/comments-list.tsx +139 -0
  231. package/src/ui/comments.css +678 -0
  232. package/src/ui/count-badge.tsx +9 -0
  233. package/src/ui/empty-state.tsx +16 -0
  234. package/src/ui/format-time.ts +29 -0
  235. package/src/ui/mention-list.tsx +106 -0
  236. package/src/ui/mention-suggestion.test.ts +18 -0
  237. package/src/ui/mention-suggestion.tsx +285 -0
  238. package/src/ui/mention.tsx +9 -0
  239. package/src/ui/reaction.tsx +16 -0
  240. package/src/ui/reactions.tsx +15 -0
  241. package/src/ui/render-markdown.tsx +72 -0
  242. package/src/ui/send-button.tsx +15 -0
@@ -0,0 +1,868 @@
1
+ import { ComponentType } from 'react';
2
+ import { Editor } from 'tldraw';
3
+ import { EditorAtom } from 'tldraw';
4
+ import { JSX } from 'react/jsx-runtime';
5
+ import type { MentionNodeAttrs } from '@tiptap/extension-mention';
6
+ import { ReactNode } from 'react';
7
+ import { StateNode } from 'tldraw';
8
+ import type { SuggestionOptions } from '@tiptap/suggestion';
9
+ import { TLComment } from 'tldraw';
10
+ import { TLCommentAnchor } from 'tldraw';
11
+ import { TLCommentId } from 'tldraw';
12
+ import { TLCommentThread } from 'tldraw';
13
+ import { TLCommentThreadId } from 'tldraw';
14
+ import { TLHistoryBatchOptions } from 'tldraw';
15
+ import { TLRichText } from 'tldraw';
16
+ import { TLShapeId } from 'tldraw';
17
+ import { TLStateNodeConstructor } from 'tldraw';
18
+ import { TLUiOverrides } from 'tldraw';
19
+ import { VecLike } from 'tldraw';
20
+
21
+ /**
22
+ * Where a thread's pin sits on the page, for each anchor kind. Null hides the pin. For imprecise
23
+ * shape anchors the pin uses `impreciseShapeAnchor` (a normalized 0–1 spot, top-right by default)
24
+ * rather than the stored `x`/`y`.
25
+ * @public
26
+ */
27
+ export declare function anchorPagePoint(editor: Editor, anchor: TLCommentAnchor, impreciseShapeAnchor?: {
28
+ x: number;
29
+ y: number;
30
+ }): {
31
+ x: number;
32
+ y: number;
33
+ } | null;
34
+
35
+ /** A commenter's avatar — their image if provided, otherwise a single-initial coloured circle.
36
+ * @public @react */
37
+ export declare function Avatar({ name, color, image }: AvatarProps): JSX.Element;
38
+
39
+ /** @public */
40
+ export declare interface AvatarProps {
41
+ name: string;
42
+ /** Background colour, used when there's no `image`. Falls back to the default avatar tint. */
43
+ color?: string;
44
+ /** Avatar image URL. When set, shows the image instead of the coloured initial. */
45
+ image?: string;
46
+ }
47
+
48
+ /** A comment's metadata line: author name, relative time, and an edited marker. @public @react */
49
+ export declare function Byline({ author, date, edited }: BylineProps): JSX.Element;
50
+
51
+ /** @public */
52
+ export declare interface BylineProps {
53
+ author: string;
54
+ /** ISO datetime; formatted to relative time by the component. */
55
+ date: string;
56
+ /** Shows an "edited" marker when the comment has been edited. */
57
+ edited?: boolean;
58
+ }
59
+
60
+ /** @public @react */
61
+ export declare function CanvasComments(props: CanvasCommentsProps): JSX.Element | null;
62
+
63
+ /**
64
+ * A ready-to-use comments layer for a tldraw canvas: pins each thread at its anchor, opens a
65
+ * thread popover (with a reply composer) on click, and shows a composer where the comment tool
66
+ * placed a new thread. Reads/writes comment records straight from `editor.store`.
67
+ *
68
+ * It's meant as the batteries-included default — every visible piece is a lever (the `CommentBody`
69
+ * and `PinContent` slots on `CommentTool.configure({ components })`), and the pieces it composes
70
+ * (`CommentPin`, `CommentThread`, `CommentComposer`, the hooks, the tool) are all exported, so a
71
+ * consumer can rebuild this from parts instead.
72
+ * @public
73
+ */
74
+ export declare interface CanvasCommentsProps {
75
+ /** The signed-in user's id, or null for a read-only viewer. Only a signed-in user composes. */
76
+ currentUserId: null | string;
77
+ /** Map an author id to a display name, or `undefined` when the id can't be named. */
78
+ resolveName(id: string): string | undefined;
79
+ /** Called after any comment (a new thread's first comment, or a reply) is posted. */
80
+ onPostComment?(comment: TLComment): void;
81
+ /** Whether a comment is unread for the current user (return true for unread). */
82
+ isCommentUnread?(commentId: TLCommentId): boolean;
83
+ /**
84
+ * Called for each unread comment shown to the user in an open thread popover, so hosts can
85
+ * record a read receipt. Needs `isCommentUnread` to know what's unread.
86
+ */
87
+ onCommentRead?(commentId: TLCommentId): void;
88
+ /** Resolve the members matching an `@`-query in the composers (sync or async). */
89
+ getMentionSuggestions?(query: string): MentionMember[] | Promise<MentionMember[]>;
90
+ /** Override a mention-picker row's content. */
91
+ renderMentionSuggestion?(member: MentionMember): ReactNode;
92
+ /** Where imprecise shape pins sit — a normalized (0–1) spot within the shape. Default top-right. */
93
+ impreciseShapeAnchor?: {
94
+ x: number;
95
+ y: number;
96
+ };
97
+ /** Region comment behaviour. Region is off by default — omit this for click-only point/shape
98
+ * comments. Anything unset falls back to {@link DEFAULT_REGION_COMMENT_OPTIONS}. */
99
+ regionOptions?: Partial<RegionCommentOptions>;
100
+ }
101
+
102
+ /**
103
+ * A comments list panel for the current page, shown while the comment tool is active. Clicking a
104
+ * thread brings its pin into view and opens it. Batteries-included over the store (a sibling to
105
+ * `CanvasComments`); `CommentsList` is exported for a differently-placed or always-on list.
106
+ * @public @react
107
+ */
108
+ export declare function CanvasCommentsSidebar(props: CanvasCommentsSidebarProps): JSX.Element | null;
109
+
110
+ /** @public */
111
+ export declare interface CanvasCommentsSidebarProps {
112
+ /** Map an author id to a display name, or `undefined` when the id can't be named. */
113
+ resolveName(id: string): string | undefined;
114
+ /** The signed-in user's id. Enables the "only your threads" filter when present. */
115
+ currentUserId?: string;
116
+ /**
117
+ * Whether a comment is unread for the current user (return true for unread). Enables the
118
+ * "only unread" filter when present.
119
+ */
120
+ isCommentUnread?(commentId: TLCommentId): boolean;
121
+ /** Tool ids that show the sidebar. Defaults to the comment tool. */
122
+ tools?: string[];
123
+ /** Header above the list. */
124
+ header?: ReactNode;
125
+ /** Shown when the page has no threads. */
126
+ empty?: ReactNode;
127
+ /** Where imprecise shape pins sit, so navigation centres on the same spot. Default top-right. */
128
+ impreciseShapeAnchor?: {
129
+ x: number;
130
+ y: number;
131
+ };
132
+ }
133
+
134
+ /**
135
+ * A cluster in the merge tree: a leaf (one thread) or a merged group.
136
+ * @public
137
+ */
138
+ export declare interface ClusterNode {
139
+ /** Leaves: the thread id verbatim. Merged nodes: `cluster:${count}:${minMemberId}`. */
140
+ id: string;
141
+ /** Page space; count-weighted mean of all member leaf anchors. */
142
+ centroid: VecLike;
143
+ /** Number of member leaves. Leaves = 1. */
144
+ count: number;
145
+ /** All member thread ids, sorted lexicographically ascending. */
146
+ members: string[];
147
+ }
148
+
149
+ /** @public */
150
+ export declare interface ClusterOptions {
151
+ /** Cluster (merge) distance, screen px. Default 40. */
152
+ Tc?: number;
153
+ /** Uncluster (split) distance, screen px. Must be \> Tc. Default 1.5 · Tc. */
154
+ Tu?: number;
155
+ /** Contraction window ratio. Default 0.12. */
156
+ eps?: number;
157
+ /** Max cluster screen extent at birth, screen px. Must be \>= Tc. Default 3 · Tc. */
158
+ Dmax?: number;
159
+ /** Camera zoom bounds — pass the editor's camera constraints. Required. */
160
+ minZoom: number;
161
+ maxZoom: number;
162
+ /**
163
+ * Zoom by which every cluster has split, no matter how close its members are — including
164
+ * coincident anchors, which otherwise never split. Merge thresholds are capped at
165
+ * `maxSplitZoom / (Tu/Tc)` so the hysteresis band keeps its shape below the cap.
166
+ * Default 6 (600%).
167
+ */
168
+ maxSplitZoom?: number;
169
+ }
170
+
171
+ /** @public */
172
+ export declare interface ClusterRuntime {
173
+ /** Events[0..k) are active. Exposed for tests and debugging. */
174
+ readonly k: number;
175
+ /** Reset state from scratch for the given zoom (cold start / after rebuild). */
176
+ seed(zoom: number): void;
177
+ /**
178
+ * Reset state for the given zoom, carrying hysteresis state over from a previous partition
179
+ * (the visible map of the model being replaced). Threshold-forced events ignore history:
180
+ * zoom \<= zMerge is always active, zoom \>= zSplit always inactive. An event inside its band
181
+ * is active iff its members were merged together in `previous`; a band event with no history
182
+ * (e.g. introduced by the rebuild) stays inactive until the next zoom-out crosses its zMerge.
183
+ * Active events must form a prefix of the table, so classification stops at the first
184
+ * inactive event; later band-active events conservatively resolve to split.
185
+ */
186
+ seedFrom(zoom: number, previous: ReadonlyMap<string, ClusterNode>): void;
187
+ /** Advance/retreat the cursor for a camera change. No-op if zoom sits inside all bands. */
188
+ onCamera(zoom: number): void;
189
+ /** The current partition: cluster id → node. Do not mutate. */
190
+ getVisible(): ReadonlyMap<string, ClusterNode>;
191
+ }
192
+
193
+ /**
194
+ * The precomputed clustering schedule for one page's comments.
195
+ * @public
196
+ */
197
+ export declare interface ClusterTable {
198
+ /** Sorted non-increasing by zMerge; satisfies the invariants of CLUSTERING.md §7.6. */
199
+ events: readonly MergeEvent[];
200
+ /** One node per input leaf, in input order. */
201
+ leaves: readonly ClusterNode[];
202
+ }
203
+
204
+ /** @public */
205
+ export declare function collectClusterLeaves(editor: Editor, threads: readonly TLCommentThread[], openThreadId: null | string, impreciseShapeAnchor?: {
206
+ x: number;
207
+ y: number;
208
+ }): LeafInput[];
209
+
210
+ /**
211
+ * Renders a comment's rich-text body read-only through the limited comment extension set (no
212
+ * headings), so formatting (bold, links, lists, highlight) is preserved rather than flattened, and
213
+ * headings can never render. Use this as the `body` of a `CommentCard` on a canvas.
214
+ * @public @react
215
+ */
216
+ export declare function CommentBody({ richText, resolveName }: CommentBodyProps): JSX.Element;
217
+
218
+ /** @public */
219
+ export declare interface CommentBodyProps {
220
+ richText: TLRichText;
221
+ /** Maps a member id to its current display name, so \@mentions show the live name. */
222
+ resolveName?(id: string): string | undefined;
223
+ }
224
+
225
+ /** A single comment: Avatar, Byline, and a body slot the consumer renders. @public @react */
226
+ export declare function CommentCard({ author, body, date, you, edited, actions }: CommentCardProps): JSX.Element;
227
+
228
+ /** @public */
229
+ export declare interface CommentCardProps {
230
+ author: string;
231
+ /** The rendered comment body. The card doesn't dictate a format — pass a `<CommentText>`
232
+ * for markdown, a rich-text render, or any node. */
233
+ body: ReactNode;
234
+ /** ISO datetime; formatted to relative time by the component. */
235
+ date: string;
236
+ you: boolean;
237
+ /** Whether the comment has been edited (shows an "edited" marker). */
238
+ edited?: boolean;
239
+ /** Hover-revealed controls at the card's top-right (e.g. an edit affordance). */
240
+ actions?: ReactNode;
241
+ }
242
+
243
+ /**
244
+ * The input for writing a comment: a TipTap rich-text editor restricted to the comment extension
245
+ * set (bold, italic, lists, links, code, highlight — no headings), with a Send button. Formatting
246
+ * is applied through markdown and keyboard shortcuts (e.g. `**bold**`, `- `, Cmd+B); there's no
247
+ * floating toolbar. Presentational by default; pass value/onChange/onSubmit to drive it as a form.
248
+ * @public @react
249
+ */
250
+ export declare function CommentComposer({ author, placeholder, value, onChange, onSubmit, sendLabel, disabled, autoFocus, leading, getMentionSuggestions, renderMentionSuggestion }: CommentComposerProps): JSX.Element;
251
+
252
+ /** @public */
253
+ export declare interface CommentComposerProps {
254
+ author: string;
255
+ placeholder: string;
256
+ /** Controlled rich-text value. Omit for the presentational (display-only) composer. */
257
+ value?: TLRichText;
258
+ onChange?(value: TLRichText): void;
259
+ /** Called on Send click or Enter. When set, the composer is interactive. */
260
+ onSubmit?(): void;
261
+ sendLabel?: string;
262
+ disabled?: boolean;
263
+ autoFocus?: boolean;
264
+ /** The leading element before the field. Defaults to the author's avatar. */
265
+ leading?: ReactNode;
266
+ /** Resolve the members matching an `@`-query (sync or async). Provide to enable mentions. */
267
+ getMentionSuggestions?(query: string): MentionMember[] | Promise<MentionMember[]>;
268
+ /** Override a picker row's content. Defaults to avatar + name (+ secondary). */
269
+ renderMentionSuggestion?(member: MentionMember): ReactNode;
270
+ }
271
+
272
+ /**
273
+ * Component overrides for the batteries-included comments layer. Each slot replaces a built-in
274
+ * piece; leave a slot unset to keep its default.
275
+ *
276
+ * @public
277
+ */
278
+ export declare interface CommentingComponents {
279
+ /** A comment's body. Replaces the default rich-text `<CommentBody>`. */
280
+ CommentBody?: ComponentType<{
281
+ comment: TLComment;
282
+ }>;
283
+ /** A pin's inner content. Replaces the author-initial default. */
284
+ PinContent?: ComponentType<{
285
+ comments: TLComment[];
286
+ thread: TLCommentThread;
287
+ }>;
288
+ /** A sidebar row's preview. Replaces the plaintext default. */
289
+ ThreadPreview?: ComponentType<{
290
+ comment: TLComment;
291
+ }>;
292
+ }
293
+
294
+ /**
295
+ * Configuration for the commenting layer. Static config only — pass it once via
296
+ * `CommentTool.configure({ ... })`, mirroring `ShapeUtil.configure`. Live, reactive values
297
+ * (`currentUserId`, `resolveName`, read-status callbacks) stay as props on `<CanvasComments>`.
298
+ *
299
+ * For defaults, see {@link defaultCommentingOptions}.
300
+ *
301
+ * @example
302
+ * ```tsx
303
+ * <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />
304
+ * ```
305
+ *
306
+ * @public
307
+ */
308
+ export declare interface CommentingOptions {
309
+ /**
310
+ * How comment mutations (post, reply, edit, resolve, delete) interact with the editor undo
311
+ * stack. Defaults to `'ignore'` — comments are deliberately not undoable (see `TLComment`).
312
+ * `'record'` is a multiplayer footgun: undoing a delete resurrects a thread a collaborator
313
+ * already removed, and undoing a resolve/edit reverts their newer state. Safe only single-player
314
+ * or on a non-synced local comment store.
315
+ */
316
+ readonly history: TLHistoryBatchOptions['history'];
317
+ /**
318
+ * History mode for the pin drag-to-move re-anchor specifically. Unlike posts/edits this is a
319
+ * spatial edit that may reasonably be undoable alongside a shape move. Defaults to `history`.
320
+ */
321
+ readonly dragHistory: TLHistoryBatchOptions['history'] | undefined;
322
+ /** Fold nearby pins into count badges as the camera zooms out. */
323
+ readonly enableClustering: boolean;
324
+ /** Normalized (0–1) spot within a shape where imprecise shape pins sit. Default top-right. */
325
+ readonly impreciseShapeAnchor: {
326
+ readonly x: number;
327
+ readonly y: number;
328
+ };
329
+ /** Screen-pixel margin by which the viewport is inflated when culling cluster badges. */
330
+ readonly clusterCullMargin: number;
331
+ /** How far past a cluster's split zoom to land when expanding it (1.05 = 5% overshoot). */
332
+ readonly clusterSplitZoomFactor: number;
333
+ /** Component overrides. See {@link CommentingComponents}. */
334
+ readonly components: CommentingComponents;
335
+ }
336
+
337
+ /** @public */
338
+ export declare interface CommentListItemProps {
339
+ id: string;
340
+ author: string;
341
+ /** A short preview of the thread — e.g. the first comment's body. */
342
+ preview: ReactNode;
343
+ /** ISO datetime of the thread's first comment. */
344
+ date: string;
345
+ resolved?: boolean;
346
+ /** Name of the page the thread lives on, shown as a small label. Omit to hide. */
347
+ page?: string;
348
+ /** Total comments in the thread. */
349
+ count?: number;
350
+ /** Whether this thread is the open one. */
351
+ selected?: boolean;
352
+ }
353
+
354
+ /** A canvas comment marker: shows its `children` (or a check when resolved). Purely
355
+ * presentational — it reflects open/resolved state via CSS; wrap it to make it clickable.
356
+ * @public @react */
357
+ export declare function CommentPin({ children, resolved, open }: CommentPinProps): JSX.Element;
358
+
359
+ /** @public */
360
+ export declare interface CommentPinProps {
361
+ /** What the pin shows when unresolved — a number, an author initial, an <Avatar>, etc.
362
+ * Left as a lever so consumers aren't locked into a count. */
363
+ children?: ReactNode;
364
+ resolved?: boolean;
365
+ /** The pin's thread is open — shows the active/selected indicator state. */
366
+ open?: boolean;
367
+ }
368
+
369
+ /** The funnel dropdown in the sidebar header: toggles for which threads the list shows.
370
+ * @public @react */
371
+ export declare function CommentsFilterMenu({ canFilterByAuthor, canFilterByUnread }: CommentsFilterMenuProps): JSX.Element;
372
+
373
+ /** @public */
374
+ export declare interface CommentsFilterMenuProps {
375
+ /** Whether to offer the "only your threads" toggle (needs a known current user). */
376
+ canFilterByAuthor?: boolean;
377
+ /** Whether to offer the "only unread" toggle (needs a read-status source). */
378
+ canFilterByUnread?: boolean;
379
+ }
380
+
381
+ /**
382
+ * Whether comment pins are hidden on the canvas. Governs the on-canvas layer (pins + open popover)
383
+ * only — the sidebar is unaffected.
384
+ * @public
385
+ */
386
+ export declare const commentsHidden: EditorAtom<boolean>;
387
+
388
+ /**
389
+ * A scrollable list of comment threads — each an avatar, byline, and a one-line preview.
390
+ * Presentational: you supply the items (already summarised) and an `onSelect` handler; the canvas
391
+ * `CanvasCommentsSidebar` wires it to the store, but a consumer can build their own list from this.
392
+ * @public @react
393
+ */
394
+ export declare function CommentsList({ items, onSelect, header, headerAction, empty, resolvedLabel, renderItem }: CommentsListProps): JSX.Element;
395
+
396
+ /** @public */
397
+ export declare interface CommentsListProps {
398
+ items: CommentListItemProps[];
399
+ /** Called with a thread id when an item is chosen. */
400
+ onSelect?(id: string): void;
401
+ /** Shown above the list (e.g. "Comments"). Omit for none. */
402
+ header?: ReactNode;
403
+ /** Rendered at the right of the header row — e.g. a filter menu. */
404
+ headerAction?: ReactNode;
405
+ /** Shown in place of the list when there are no threads. */
406
+ empty?: ReactNode;
407
+ /** Label for a resolved thread's marker on its row. Defaults to "Resolved". */
408
+ resolvedLabel?: string;
409
+ /** Override how each item renders. Defaults to `<CommentListItem>`. */
410
+ renderItem?(item: CommentListItemProps): ReactNode;
411
+ }
412
+
413
+ /**
414
+ * A checkbox menu item for a "View" menu that shows/hides comment pins on the canvas — checked
415
+ * when comments are visible, like tldraw's own grid/snap toggles. Reads and toggles the shared
416
+ * {@link commentsHidden} signal, so it stays in sync with the Shift+C shortcut and the sidebar
417
+ * control. Drop it into whichever menu your app owns.
418
+ * @public @react
419
+ */
420
+ export declare function CommentsMenuItem(): JSX.Element;
421
+
422
+ /** The overflow (⋯) dropdown in the sidebar header. For now it holds the hide/show-comments
423
+ * toggle; it's the home for later comment-wide controls (notifications, mark all as read).
424
+ * @public @react */
425
+ export declare function CommentsOverflowMenu(): JSX.Element;
426
+
427
+ /** A comment's body, rendered as markdown (bold, italic, code, links, lists). @public @react */
428
+ export declare function CommentText({ text }: CommentTextProps): JSX.Element;
429
+
430
+ /** @public */
431
+ export declare interface CommentTextProps {
432
+ /** Rich text body, authored as markdown. */
433
+ text: string;
434
+ }
435
+
436
+ /**
437
+ * A comment thread: an optional header, an optional resolved banner, the comments, and an
438
+ * optional reply composer. Presentational — you supply the comments (as card props) and the
439
+ * composer's handlers; how each comment renders is overridable via `renderComment`.
440
+ * @public @react
441
+ */
442
+ export declare function CommentThread({ comments, header, headerActions, resolvedBanner, composer, renderComment }: CommentThreadProps): JSX.Element;
443
+
444
+ /** @public */
445
+ export declare interface CommentThreadProps {
446
+ /** The thread's comments, already adapted to card props (oldest first). */
447
+ comments: CommentCardProps[];
448
+ /** Optional header, e.g. "Thread". Omit for no header. */
449
+ header?: ReactNode;
450
+ /** Action controls shown at the right of the header (resolve, delete, dismiss…). */
451
+ headerActions?: ReactNode;
452
+ /** When set, shows a banner above the comments (e.g. "Resolved by Jess"). */
453
+ resolvedBanner?: ReactNode;
454
+ /** Reply composer props. Omit for a read-only thread (no composer). */
455
+ composer?: CommentComposerProps;
456
+ /** Override how each comment renders. Defaults to `<CommentCard>`. */
457
+ renderComment?(comment: CommentCardProps, index: number): ReactNode;
458
+ }
459
+
460
+ /**
461
+ * The comment tool. A click starts a point/shape thread (the pin tracks the shape it lands on);
462
+ * dragging out a rectangle starts a region thread anchored to that area. Placement only opens a
463
+ * composer (via `pendingComment`); the records are created when the comment is posted.
464
+ * @public
465
+ */
466
+ export declare class CommentTool extends StateNode {
467
+ static id: string;
468
+ static initial: string;
469
+ static children(): TLStateNodeConstructor[];
470
+ /**
471
+ * Configure this tool's {@link CommentTool.options | `options`}, returning a configured subclass
472
+ * to register via `tools`. Mirrors `ShapeUtil.configure`. Layers over any prior `configure`, so
473
+ * calls can be chained.
474
+ *
475
+ * @example
476
+ * ```tsx
477
+ * <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />
478
+ * ```
479
+ */
480
+ static configure<T extends TLStateNodeConstructor>(this: T, options: T extends new (...args: any[]) => {
481
+ options: infer Options;
482
+ } ? Partial<Options> : never): T;
483
+ /**
484
+ * The merged commenting options for this editor. Read from anywhere via
485
+ * {@link getCommentingOptions}. Override with {@link CommentTool.configure}.
486
+ */
487
+ options: CommentingOptions;
488
+ onEnter(): void;
489
+ onCancel(): void;
490
+ }
491
+
492
+ /** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.
493
+ * Once registered, tldraw's `DefaultToolbarContent` shows the comment button next to the eraser.
494
+ * @public */
495
+ export declare const commentToolOverrides: TLUiOverrides;
496
+
497
+ /** @public */
498
+ export declare const commentTools: typeof CommentTool[];
499
+
500
+ /**
501
+ * Commit a comment mutation with the configured undo/redo behavior. All comment writes go through
502
+ * here so the {@link CommentingOptions.history} option (and {@link CommentingOptions.dragHistory}
503
+ * for pin re-anchors) governs whether they land on the undo stack. Defaults to `'ignore'`.
504
+ * @public
505
+ */
506
+ export declare function commitCommentMutation<T>(editor: Editor, fn: () => T, kind?: 'drag' | 'mutation'): T;
507
+
508
+ /** @public */
509
+ export declare function computeClusterTable(leaves: readonly LeafInput[], options: ClusterOptions): ClusterTable;
510
+
511
+ /** @public @react */
512
+ export declare function CountBadge({ count }: CountBadgeProps): JSX.Element;
513
+
514
+ /** @public */
515
+ export declare interface CountBadgeProps {
516
+ count: number;
517
+ }
518
+
519
+ /** @public */
520
+ export declare function createClusterRuntime(table: ClusterTable): ClusterRuntime;
521
+
522
+ /**
523
+ * Build the TipTap `suggestion` config for the comment \@-picker. `getSuggestions(query)` is the
524
+ * host's resolver — it returns the members matching the query (sync or async); the SDK owns neither
525
+ * the roster nor the filtering. The plugin runs outside React, so `render` mounts `MentionPopup` via
526
+ * a `ReactRenderer`, forwards navigation keys through the popup's imperative handle, and lets it call
527
+ * `command` to insert.
528
+ * @public
529
+ */
530
+ export declare function createMentionSuggestion(getSuggestions: (query: string) => MentionMember[] | Promise<MentionMember[]>, options?: MentionSuggestionOptions): Omit<SuggestionOptions<MentionMember, MentionNodeAttrs>, 'editor'>;
531
+
532
+ /** Where an imprecise shape comment sits by default: the shape's top-right corner. Overridable. @public */
533
+ export declare const DEFAULT_IMPRECISE_SHAPE_ANCHOR: {
534
+ x: number;
535
+ y: number;
536
+ };
537
+
538
+ /** The out-of-the-box region config: disabled, and — when a consumer enables it — the current
539
+ * bottom-right / pointer-reveal / pin-move / corner-resize behaviour.
540
+ * @public */
541
+ export declare const DEFAULT_REGION_COMMENT_OPTIONS: RegionCommentOptions;
542
+
543
+ /** @public */
544
+ export declare const DEFAULT_SIDEBAR_FILTERS: SidebarFilters;
545
+
546
+ /**
547
+ * The default {@link CommentingOptions}. Override via `CommentTool.configure({ ... })`.
548
+ *
549
+ * @public
550
+ */
551
+ export declare const defaultCommentingOptions: {
552
+ readonly clusterCullMargin: 120;
553
+ readonly clusterSplitZoomFactor: 1.05;
554
+ readonly components: {};
555
+ readonly dragHistory: undefined;
556
+ readonly enableClustering: true;
557
+ readonly history: "ignore";
558
+ readonly impreciseShapeAnchor: {
559
+ readonly x: 1;
560
+ readonly y: 0;
561
+ };
562
+ };
563
+
564
+ /** The empty state shown when a thread has no comments yet. @public @react */
565
+ export declare function EmptyState({ message }: EmptyStateProps): JSX.Element;
566
+
567
+ /** @public */
568
+ export declare interface EmptyStateProps {
569
+ message: string;
570
+ }
571
+
572
+ /** Members whose name contains the query (case-insensitive), capped to the popup's length.
573
+ * @public */
574
+ export declare function filterMentionMembers(members: MentionMember[], query: string): MentionMember[];
575
+
576
+ /** Open a thread and bring it into view — switch to its page if needed, then center its pin. @public */
577
+ export declare function focusThread(editor: Editor, thread: TLCommentThread, impreciseShapeAnchor?: {
578
+ x: number;
579
+ y: number;
580
+ }): void;
581
+
582
+ /**
583
+ * Format an ISO datetime as relative time ("2 hours ago", "yesterday", "last week").
584
+ * Locale-aware via Intl.RelativeTimeFormat.
585
+ * @public
586
+ */
587
+ export declare function formatRelativeTime(iso: string, locale?: string): string;
588
+
589
+ /**
590
+ * The merged {@link CommentingOptions} for an editor, read off the registered comment tool (which
591
+ * carries them via `CommentTool.configure`). Falls back to {@link defaultCommentingOptions} when
592
+ * the comment tool isn't registered. Usable from anywhere with an `Editor` — including the tool's
593
+ * own state, which has no React context.
594
+ *
595
+ * @public
596
+ */
597
+ export declare function getCommentingOptions(editor: Editor): CommentingOptions;
598
+
599
+ /** Read one comment record by id, or `undefined` if the id isn't a present comment record. @public */
600
+ export declare function getCommentRecord(editor: Editor, id: string): TLCommentRecord | undefined;
601
+
602
+ /** All comments currently in the store (non-reactive; wrap in `useValue` to react). @public */
603
+ export declare function getComments(editor: Editor): TLComment[];
604
+
605
+ /** All comment threads currently in the store (non-reactive; wrap in `useValue` to react). @public */
606
+ export declare function getCommentThreads(editor: Editor): TLCommentThread[];
607
+
608
+ /**
609
+ * One comment thread's pin, already resolved to a page-space anchor point.
610
+ * @public
611
+ */
612
+ export declare interface LeafInput {
613
+ /** Unique. Thread id. Uniqueness is a precondition — throw on duplicates. */
614
+ id: string;
615
+ /** Page-space coordinates. Must be finite — throw on NaN/Infinity. */
616
+ point: VecLike;
617
+ }
618
+
619
+ /** An \@-mention pill for referencing a person in a comment. @public @react */
620
+ export declare function Mention({ name }: MentionProps): JSX.Element;
621
+
622
+ /**
623
+ * The \@-mention member picker: a popover list of people to mention, shown as the user types after
624
+ * `@`. Presentational — the composer's suggestion plugin owns resolution, keyboard navigation, and
625
+ * placement, and drives this with the host-resolved `members` and the highlighted `activeIndex`.
626
+ * @public @react
627
+ */
628
+ export declare function MentionList({ members, activeIndex, onSelect, emptyLabel, renderMember }: MentionListProps): JSX.Element;
629
+
630
+ /** @public */
631
+ export declare interface MentionListProps {
632
+ /** Members to choose from — already resolved for the current query by the host. */
633
+ members: MentionMember[];
634
+ /** Index of the highlighted row, driven by the composer's keyboard navigation. */
635
+ activeIndex?: number;
636
+ /** Called when a member is chosen (click, or Enter on the active row). */
637
+ onSelect?(member: MentionMember): void;
638
+ /** Shown when no member matches the query. Defaults to a translated "No matches". */
639
+ emptyLabel?: string;
640
+ /** Override a row's content (inside the selectable button). Defaults to avatar + name (+ secondary). */
641
+ renderMember?(member: MentionMember): ReactNode;
642
+ }
643
+
644
+ /** A person the composer's \@-mention picker can offer. @public */
645
+ export declare interface MentionMember {
646
+ id: string;
647
+ name: string;
648
+ /** Avatar image URL. Falls back to a coloured initial when omitted. */
649
+ avatar?: string;
650
+ /** Avatar background colour, used when there's no `avatar` image. */
651
+ color?: string;
652
+ /** A secondary line under the name — e.g. an email or handle. Omit for a single-line row. */
653
+ secondary?: string;
654
+ /** Marks the current user; shown as a "(You)" suffix after the name. */
655
+ you?: boolean;
656
+ /** Hosts may carry extra fields; a custom `renderMember` can read them. */
657
+ [key: string]: unknown;
658
+ }
659
+
660
+ /** @public */
661
+ export declare interface MentionProps {
662
+ name: string;
663
+ }
664
+
665
+ /** @public */
666
+ export declare interface MentionSuggestionOptions {
667
+ /** Override a member row's content in the picker. Defaults to avatar + name (+ secondary). */
668
+ renderMember?(member: MentionMember): ReactNode;
669
+ /**
670
+ * The tldraw editor the composer lives in. When provided, the popup re-anchors reactively as the
671
+ * canvas camera moves (the composer rides it) instead of polling every frame. Omit off-canvas.
672
+ */
673
+ editor?: null | Editor;
674
+ }
675
+
676
+ /**
677
+ * A finalized merge event, ready for the runtime.
678
+ * @public
679
+ */
680
+ export declare interface MergeEvent {
681
+ zMerge: number;
682
+ /** Reverses (splits) when zoom \>= zSplit. Always \> zMerge. May be +Infinity. */
683
+ zSplit: number;
684
+ children: ClusterNode[];
685
+ result: ClusterNode;
686
+ }
687
+
688
+ /** The id of the one open thread (only one popover is open at a time), or null when all closed.
689
+ * @public */
690
+ export declare const openThreadId: EditorAtom<null | string>;
691
+
692
+ /** A comment being placed but not yet posted: where its composer sits and what it will anchor
693
+ * to. Shared between the tool (which sets it on click) and the overlay (which renders the
694
+ * composer). Null when nothing is being placed. The atom itself lives in `./state`
695
+ * ({@link pendingComment}), scoped per editor.
696
+ * @public */
697
+ export declare interface PendingComment {
698
+ anchor: TLCommentAnchor;
699
+ /** Page point where the composer opens (the click location, or a region's pin corner). */
700
+ point: VecLike;
701
+ }
702
+
703
+ /** The comment currently being placed (composer open, not yet posted), or null.
704
+ * @public */
705
+ export declare const pendingComment: EditorAtom<null | PendingComment>;
706
+
707
+ /** Write comment records to the store. @public */
708
+ export declare function putCommentRecords(editor: Editor, records: TLCommentRecord[]): void;
709
+
710
+ /** A single emoji reaction pill with a count, highlighted when the user reacted. @public @react */
711
+ export declare function Reaction({ emoji, count, active }: ReactionProps): JSX.Element;
712
+
713
+ /** @public */
714
+ export declare interface ReactionProps {
715
+ emoji: string;
716
+ count: number;
717
+ active: boolean;
718
+ }
719
+
720
+ /** The row of reactions under a comment, plus an add-reaction button. @public @react */
721
+ export declare function Reactions(): JSX.Element;
722
+
723
+ /**
724
+ * The configurable dimensions of region comments — a design surface for prototyping the interaction
725
+ * before settling it. Region is **off by default**, so a consumer that leaves it unset keeps plain
726
+ * click-only point/shape commenting. Set it per editor through `<CanvasComments regionOptions>`.
727
+ * @public
728
+ */
729
+ export declare interface RegionCommentOptions {
730
+ /** Whether dragging the comment tool out creates a region anchor. Off → click-only. */
731
+ enabled: boolean;
732
+ /** Which corner the pin and composer sit on, as a normalized 0–1 offset. Default bottom-right. */
733
+ pinCorner: VecLike;
734
+ /** When the dashed box and its handles reveal: while the pointer is within the region, while the
735
+ * pin is hovered, or only while the thread is open. */
736
+ reveal: 'open' | 'pin-hover' | 'pointer';
737
+ /** How a region is moved: dragging its pin, dragging its body, or either. */
738
+ move: 'body' | 'both' | 'pin';
739
+ /** The resize affordance: corner handles, edge handles, or none. */
740
+ resize: 'corners' | 'edges' | 'none';
741
+ }
742
+
743
+ /** Remove comment records from the store by id. @public */
744
+ export declare function removeCommentRecords(editor: Editor, ids: (TLCommentId | TLCommentThreadId)[]): void;
745
+
746
+ /**
747
+ * Minimal markdown for comment bodies: paragraphs, line breaks, bullet lists, and
748
+ * inline **bold**, *italic*, `code`, and [links](url). Not a full CommonMark parser —
749
+ * just the markdown people actually write in comments. Renders React elements only —
750
+ * never raw HTML — so there's no HTML-injection surface.
751
+ * @public
752
+ */
753
+ export declare function renderMarkdown(text: string): ReactNode;
754
+
755
+ /**
756
+ * Flatten a rich-text comment body to plaintext through the limited comment extension set,
757
+ * separating paragraphs and list items with newlines. A convenience for consumers rendering bodies
758
+ * as plain text (e.g. the sidebar preview); richer rendering can read the `TLRichText` directly.
759
+ * @public
760
+ */
761
+ export declare function richTextToPlaintext(body: TLRichText, resolveName?: (id: string) => string | undefined): string;
762
+
763
+ /** The button that posts a comment. @public @react */
764
+ export declare function SendButton({ label, disabled, onClick }: SendButtonProps): JSX.Element;
765
+
766
+ /** @public */
767
+ export declare interface SendButtonProps {
768
+ label: string;
769
+ disabled?: boolean;
770
+ onClick?(): void;
771
+ }
772
+
773
+ /**
774
+ * A shape anchor for a page point. `x`/`y` are the point's normalized (0–1) offset within the
775
+ * shape's page bounds, remembered either way. When `precise` (Alt held) the pin sits at exactly
776
+ * `x`/`y`; otherwise it sits at the consumer's imprecise default (top-right out of the box).
777
+ * @public
778
+ */
779
+ export declare function shapeAnchorAt(editor: Editor, shapeId: TLShapeId, page: {
780
+ x: number;
781
+ y: number;
782
+ }, precise: boolean): TLCommentAnchor;
783
+
784
+ /**
785
+ * Which threads the comments sidebar shows. Held as an editor-scoped signal (the `sidebarFilters`
786
+ * `EditorAtom` in `./state`) rather than component state so it survives the sidebar
787
+ * unmounting when the comment tool deactivates — a user's "hide resolved" choice shouldn't reset
788
+ * every time they leave the tool.
789
+ * @public
790
+ */
791
+ export declare interface SidebarFilters {
792
+ /** Include resolved threads. */
793
+ showResolved: boolean;
794
+ /** Only threads the current user started. Ignored when there's no current user. */
795
+ onlyMine: boolean;
796
+ /** Only threads with unread comments. Ignored when the host provides no read status. */
797
+ onlyUnread: boolean;
798
+ /** Only threads on the current page. Off = every page's threads, each labelled. */
799
+ onlyCurrentPage: boolean;
800
+ }
801
+
802
+ /** Which threads the comments sidebar shows.
803
+ * @public */
804
+ export declare const sidebarFilters: EditorAtom<SidebarFilters>;
805
+
806
+ /**
807
+ * Typed access to comment records on the editor store.
808
+ *
809
+ * Comment threads and comments live on the editor's local store so the canvas can render them
810
+ * reactively, but they are opt-in records that aren't part of the `TLRecord` union (they ride the
811
+ * sync server's object-store lane on the wire — see `TLCommentThread`). `editor.store` is therefore
812
+ * statically typed `Store<TLRecord>` and doesn't know about them, so every access has to reinterpret
813
+ * the type. These helpers own that reinterpretation — an `unknown` hop to exactly the type the store
814
+ * expects, so the rest of each call stays checked — behind one boundary, and keep call sites typed.
815
+ */
816
+ /** A record that lives in a comment thread: the thread itself or one of its messages. @public */
817
+ export declare type TLCommentRecord = TLComment | TLCommentThread;
818
+
819
+ /** Toggle comment-pin visibility for an editor.
820
+ * @public */
821
+ export declare function toggleCommentsHidden(editor: Editor): void;
822
+
823
+ /**
824
+ * Whether commenting is licensed for this editor. Enabled in development; in production it requires
825
+ * a tldraw license that includes the commenting feature (or the collaboration umbrella that grants
826
+ * it). Reactive: re-reads when license validation resolves, and returns `false` while validation is
827
+ * pending, so gated UI stays hidden until the license is confirmed.
828
+ *
829
+ * The built-in commenting components (`CanvasComments`, `CanvasCommentsSidebar`, and the comment
830
+ * tool's toolbar button) gate on this. Use it to gate any custom commenting UI the same way.
831
+ * @public
832
+ */
833
+ export declare function useCommentingEnabled(): boolean;
834
+
835
+ /**
836
+ * React hook for {@link getCommentingOptions}. Options are fixed per editor (set at tool
837
+ * registration), so this doesn't need to be reactive.
838
+ *
839
+ * @public
840
+ */
841
+ export declare function useCommentingOptions(): CommentingOptions;
842
+
843
+ /** Every comment in the store, oldest first, reactively. Group by `threadId` for per-thread lists. @public */
844
+ export declare function useComments(editor: Editor): TLComment[];
845
+
846
+ /** React hook for whether comment pins are hidden.
847
+ * @public */
848
+ export declare function useCommentsHidden(): boolean;
849
+
850
+ /** All comment threads in the store, reactively. @public */
851
+ export declare function useCommentThreads(editor: Editor): TLCommentThread[];
852
+
853
+ /** React hook for the open thread id.
854
+ * @public */
855
+ export declare function useOpenThreadId(): null | string;
856
+
857
+ /** React hook for the pending (being-placed) comment.
858
+ * @public */
859
+ export declare function usePendingComment(): null | PendingComment;
860
+
861
+ /** React hook for the current sidebar filters.
862
+ * @public */
863
+ export declare function useSidebarFilters(): SidebarFilters;
864
+
865
+ /** A thread's comments, oldest first, reactively. @public */
866
+ export declare function useThreadComments(editor: Editor, threadId: TLCommentThreadId): TLComment[];
867
+
868
+ export { }