@tldraw/commenting 5.3.0-next.2fa9c61a8de6 → 5.3.0-next.7654e7ac2a02

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 (238) hide show
  1. package/commenting.css +63 -71
  2. package/dist-cjs/canvas/anchor-lifecycle.js +2 -2
  3. package/dist-cjs/canvas/anchor-lifecycle.js.map +2 -2
  4. package/dist-cjs/{ui/tooltip-button.js → canvas/canvas-events.js} +16 -13
  5. package/dist-cjs/canvas/canvas-events.js.map +7 -0
  6. package/dist-cjs/canvas/cluster-badge.js +121 -0
  7. package/dist-cjs/canvas/cluster-badge.js.map +7 -0
  8. package/dist-cjs/canvas/cluster-fade.js +85 -0
  9. package/dist-cjs/canvas/cluster-fade.js.map +7 -0
  10. package/dist-cjs/canvas/cluster-input.js +51 -1
  11. package/dist-cjs/canvas/cluster-input.js.map +2 -2
  12. package/dist-cjs/canvas/cluster-model.js +252 -0
  13. package/dist-cjs/canvas/cluster-model.js.map +7 -0
  14. package/dist-cjs/canvas/comment-mutations.js +57 -28
  15. package/dist-cjs/canvas/comment-mutations.js.map +2 -2
  16. package/dist-cjs/canvas/comment-reactions.js +4 -7
  17. package/dist-cjs/canvas/comment-reactions.js.map +2 -2
  18. package/dist-cjs/canvas/comment-render.js +4 -3
  19. package/dist-cjs/canvas/comment-render.js.map +2 -2
  20. package/dist-cjs/canvas/comment-store.js.map +2 -2
  21. package/dist-cjs/canvas/comment-tool.js +0 -1
  22. package/dist-cjs/canvas/comment-tool.js.map +2 -2
  23. package/dist-cjs/canvas/comments-filter-menu.js +11 -13
  24. package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
  25. package/dist-cjs/canvas/comments-overflow-menu.js +10 -2
  26. package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
  27. package/dist-cjs/canvas/comments-overlay.js +77 -870
  28. package/dist-cjs/canvas/comments-overlay.js.map +3 -3
  29. package/dist-cjs/canvas/comments-sidebar.js +16 -11
  30. package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
  31. package/dist-cjs/canvas/comments-visibility-toggle.js +5 -25
  32. package/dist-cjs/canvas/comments-visibility-toggle.js.map +2 -2
  33. package/dist-cjs/canvas/context.js.map +1 -1
  34. package/dist-cjs/canvas/hooks.js +8 -2
  35. package/dist-cjs/canvas/hooks.js.map +2 -2
  36. package/dist-cjs/canvas/license.js.map +1 -1
  37. package/dist-cjs/canvas/mobile-placement.js +91 -0
  38. package/dist-cjs/canvas/mobile-placement.js.map +7 -0
  39. package/dist-cjs/canvas/options.js +39 -1
  40. package/dist-cjs/canvas/options.js.map +2 -2
  41. package/dist-cjs/canvas/pending-composer.js +134 -0
  42. package/dist-cjs/canvas/pending-composer.js.map +7 -0
  43. package/dist-cjs/canvas/pin-stacking.js +26 -1
  44. package/dist-cjs/canvas/pin-stacking.js.map +2 -2
  45. package/dist-cjs/canvas/region-box.js +113 -0
  46. package/dist-cjs/canvas/region-box.js.map +7 -0
  47. package/dist-cjs/canvas/state.js.map +2 -2
  48. package/dist-cjs/canvas/thread-pin.js +304 -0
  49. package/dist-cjs/canvas/thread-pin.js.map +7 -0
  50. package/dist-cjs/canvas/thread-preview.js +41 -49
  51. package/dist-cjs/canvas/thread-preview.js.map +2 -2
  52. package/dist-cjs/canvas/thread-stack.js +7 -7
  53. package/dist-cjs/canvas/thread-stack.js.map +2 -2
  54. package/dist-cjs/canvas/thread-state.js +15 -0
  55. package/dist-cjs/canvas/thread-state.js.map +2 -2
  56. package/dist-cjs/canvas/thread-view.js +120 -67
  57. package/dist-cjs/canvas/thread-view.js.map +3 -3
  58. package/dist-cjs/clustering/computeClusterTable.js +2 -2
  59. package/dist-cjs/clustering/computeClusterTable.js.map +2 -2
  60. package/dist-cjs/clustering/replay.js +89 -22
  61. package/dist-cjs/clustering/replay.js.map +3 -3
  62. package/dist-cjs/clustering/runtime.js +19 -12
  63. package/dist-cjs/clustering/runtime.js.map +2 -2
  64. package/dist-cjs/clustering/types.js.map +1 -1
  65. package/dist-cjs/index.d.ts +323 -202
  66. package/dist-cjs/index.js +7 -1
  67. package/dist-cjs/index.js.map +2 -2
  68. package/dist-cjs/ui/byline.js +5 -3
  69. package/dist-cjs/ui/byline.js.map +2 -2
  70. package/dist-cjs/ui/comment-composer.js +2 -18
  71. package/dist-cjs/ui/comment-composer.js.map +2 -2
  72. package/dist-cjs/ui/comment-pin.js +2 -15
  73. package/dist-cjs/ui/comment-pin.js.map +2 -2
  74. package/dist-cjs/ui/comments-list.js +11 -24
  75. package/dist-cjs/ui/comments-list.js.map +2 -2
  76. package/dist-cjs/ui/format-time.js +12 -0
  77. package/dist-cjs/ui/format-time.js.map +2 -2
  78. package/dist-cjs/ui/icons.js +115 -0
  79. package/dist-cjs/ui/icons.js.map +7 -0
  80. package/dist-cjs/ui/reaction-picker.js +11 -13
  81. package/dist-cjs/ui/reaction-picker.js.map +2 -2
  82. package/dist-cjs/ui/reaction.js +3 -4
  83. package/dist-cjs/ui/reaction.js.map +2 -2
  84. package/dist-cjs/ui/send-button.js +2 -9
  85. package/dist-cjs/ui/send-button.js.map +2 -2
  86. package/dist-cjs/ui/visual-viewport.js +32 -0
  87. package/dist-cjs/ui/visual-viewport.js.map +7 -0
  88. package/dist-esm/canvas/anchor-lifecycle.mjs +3 -3
  89. package/dist-esm/canvas/anchor-lifecycle.mjs.map +2 -2
  90. package/dist-esm/canvas/canvas-events.mjs +15 -0
  91. package/dist-esm/canvas/canvas-events.mjs.map +7 -0
  92. package/dist-esm/canvas/cluster-badge.mjs +106 -0
  93. package/dist-esm/canvas/cluster-badge.mjs.map +7 -0
  94. package/dist-esm/canvas/cluster-fade.mjs +65 -0
  95. package/dist-esm/canvas/cluster-fade.mjs.map +7 -0
  96. package/dist-esm/canvas/cluster-input.mjs +52 -2
  97. package/dist-esm/canvas/cluster-input.mjs.map +2 -2
  98. package/dist-esm/canvas/cluster-model.mjs +236 -0
  99. package/dist-esm/canvas/cluster-model.mjs.map +7 -0
  100. package/dist-esm/canvas/comment-mutations.mjs +57 -28
  101. package/dist-esm/canvas/comment-mutations.mjs.map +2 -2
  102. package/dist-esm/canvas/comment-reactions.mjs +5 -12
  103. package/dist-esm/canvas/comment-reactions.mjs.map +2 -2
  104. package/dist-esm/canvas/comment-render.mjs +5 -4
  105. package/dist-esm/canvas/comment-render.mjs.map +2 -2
  106. package/dist-esm/canvas/comment-store.mjs.map +2 -2
  107. package/dist-esm/canvas/comment-tool.mjs +0 -1
  108. package/dist-esm/canvas/comment-tool.mjs.map +2 -2
  109. package/dist-esm/canvas/comments-filter-menu.mjs +12 -13
  110. package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
  111. package/dist-esm/canvas/comments-overflow-menu.mjs +11 -2
  112. package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
  113. package/dist-esm/canvas/comments-overlay.mjs +54 -878
  114. package/dist-esm/canvas/comments-overlay.mjs.map +3 -3
  115. package/dist-esm/canvas/comments-sidebar.mjs +18 -14
  116. package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
  117. package/dist-esm/canvas/comments-visibility-toggle.mjs +5 -25
  118. package/dist-esm/canvas/comments-visibility-toggle.mjs.map +2 -2
  119. package/dist-esm/canvas/hooks.mjs +9 -2
  120. package/dist-esm/canvas/hooks.mjs.map +2 -2
  121. package/dist-esm/canvas/license.mjs.map +1 -1
  122. package/dist-esm/canvas/mobile-placement.mjs +71 -0
  123. package/dist-esm/canvas/mobile-placement.mjs.map +7 -0
  124. package/dist-esm/canvas/options.mjs +39 -1
  125. package/dist-esm/canvas/options.mjs.map +2 -2
  126. package/dist-esm/canvas/pending-composer.mjs +126 -0
  127. package/dist-esm/canvas/pending-composer.mjs.map +7 -0
  128. package/dist-esm/canvas/pin-stacking.mjs +26 -1
  129. package/dist-esm/canvas/pin-stacking.mjs.map +2 -2
  130. package/dist-esm/canvas/region-box.mjs +93 -0
  131. package/dist-esm/canvas/region-box.mjs.map +7 -0
  132. package/dist-esm/canvas/state.mjs.map +2 -2
  133. package/dist-esm/canvas/thread-pin.mjs +310 -0
  134. package/dist-esm/canvas/thread-pin.mjs.map +7 -0
  135. package/dist-esm/canvas/thread-preview.mjs +42 -49
  136. package/dist-esm/canvas/thread-preview.mjs.map +2 -2
  137. package/dist-esm/canvas/thread-stack.mjs +8 -9
  138. package/dist-esm/canvas/thread-stack.mjs.map +2 -2
  139. package/dist-esm/canvas/thread-state.mjs +15 -0
  140. package/dist-esm/canvas/thread-state.mjs.map +2 -2
  141. package/dist-esm/canvas/thread-view.mjs +131 -72
  142. package/dist-esm/canvas/thread-view.mjs.map +3 -3
  143. package/dist-esm/clustering/computeClusterTable.mjs +2 -2
  144. package/dist-esm/clustering/computeClusterTable.mjs.map +2 -2
  145. package/dist-esm/clustering/replay.mjs +89 -22
  146. package/dist-esm/clustering/replay.mjs.map +3 -3
  147. package/dist-esm/clustering/runtime.mjs +19 -12
  148. package/dist-esm/clustering/runtime.mjs.map +2 -2
  149. package/dist-esm/index.d.mts +323 -202
  150. package/dist-esm/index.mjs +16 -3
  151. package/dist-esm/index.mjs.map +2 -2
  152. package/dist-esm/ui/byline.mjs +6 -4
  153. package/dist-esm/ui/byline.mjs.map +2 -2
  154. package/dist-esm/ui/comment-composer.mjs +2 -18
  155. package/dist-esm/ui/comment-composer.mjs.map +2 -2
  156. package/dist-esm/ui/comment-pin.mjs +2 -15
  157. package/dist-esm/ui/comment-pin.mjs.map +2 -2
  158. package/dist-esm/ui/comments-list.mjs +10 -23
  159. package/dist-esm/ui/comments-list.mjs.map +2 -2
  160. package/dist-esm/ui/format-time.mjs +12 -0
  161. package/dist-esm/ui/format-time.mjs.map +2 -2
  162. package/dist-esm/ui/icons.mjs +95 -0
  163. package/dist-esm/ui/icons.mjs.map +7 -0
  164. package/dist-esm/ui/reaction-picker.mjs +12 -13
  165. package/dist-esm/ui/reaction-picker.mjs.map +2 -2
  166. package/dist-esm/ui/reaction.mjs +3 -4
  167. package/dist-esm/ui/reaction.mjs.map +2 -2
  168. package/dist-esm/ui/send-button.mjs +2 -9
  169. package/dist-esm/ui/send-button.mjs.map +2 -2
  170. package/dist-esm/ui/visual-viewport.mjs +12 -0
  171. package/dist-esm/ui/visual-viewport.mjs.map +7 -0
  172. package/package.json +5 -5
  173. package/src/canvas/anchor-lifecycle.test.ts +2 -2
  174. package/src/canvas/anchor-lifecycle.ts +25 -39
  175. package/src/canvas/canvas-events.ts +18 -0
  176. package/src/canvas/canvas.css +5 -11
  177. package/src/canvas/cluster-badge.tsx +133 -0
  178. package/src/canvas/cluster-fade.ts +102 -0
  179. package/src/canvas/cluster-input.test.ts +148 -14
  180. package/src/canvas/cluster-input.ts +92 -5
  181. package/src/canvas/cluster-model.ts +378 -0
  182. package/src/canvas/comment-mutations.test.ts +46 -4
  183. package/src/canvas/comment-mutations.ts +115 -104
  184. package/src/canvas/comment-reactions.test.ts +22 -0
  185. package/src/canvas/comment-reactions.tsx +33 -32
  186. package/src/canvas/comment-render.ts +8 -8
  187. package/src/canvas/comment-store.ts +12 -17
  188. package/src/canvas/comment-tool.tsx +8 -12
  189. package/src/canvas/comments-filter-menu.tsx +9 -16
  190. package/src/canvas/comments-overflow-menu.tsx +8 -3
  191. package/src/canvas/comments-overlay.tsx +154 -1344
  192. package/src/canvas/comments-sidebar.tsx +40 -19
  193. package/src/canvas/comments-visibility-toggle.tsx +6 -30
  194. package/src/canvas/context.ts +4 -3
  195. package/src/canvas/hooks.test.ts +94 -0
  196. package/src/canvas/hooks.ts +17 -3
  197. package/src/canvas/license.ts +1 -1
  198. package/src/canvas/mobile-placement.ts +115 -0
  199. package/src/canvas/options.test.ts +124 -2
  200. package/src/canvas/options.ts +192 -58
  201. package/src/canvas/pending-composer.tsx +157 -0
  202. package/src/canvas/pin-stacking.test.ts +111 -1
  203. package/src/canvas/pin-stacking.ts +46 -0
  204. package/src/canvas/region-box.tsx +124 -0
  205. package/src/canvas/state.ts +20 -28
  206. package/src/canvas/thread-pin.tsx +418 -0
  207. package/src/canvas/thread-preview.tsx +77 -87
  208. package/src/canvas/thread-stack.tsx +19 -29
  209. package/src/canvas/thread-state.test.ts +51 -0
  210. package/src/canvas/thread-state.ts +56 -23
  211. package/src/canvas/thread-view.test.ts +72 -0
  212. package/src/canvas/thread-view.tsx +231 -133
  213. package/src/clustering/computeClusterTable.ts +12 -3
  214. package/src/clustering/replay.test.ts +0 -7
  215. package/src/clustering/replay.ts +131 -32
  216. package/src/clustering/runtime.test.ts +50 -6
  217. package/src/clustering/runtime.ts +42 -39
  218. package/src/clustering/schedule.test.ts +0 -6
  219. package/src/clustering/screen-offsets.test.ts +171 -0
  220. package/src/clustering/types.ts +10 -0
  221. package/src/index.ts +15 -2
  222. package/src/ui/byline.tsx +16 -6
  223. package/src/ui/comment-composer.tsx +25 -69
  224. package/src/ui/comment-pin.tsx +3 -16
  225. package/src/ui/comments-list.tsx +40 -29
  226. package/src/ui/comments.css +58 -60
  227. package/src/ui/format-time.test.ts +69 -0
  228. package/src/ui/format-time.ts +20 -0
  229. package/src/ui/icons.tsx +116 -0
  230. package/src/ui/reaction-picker.tsx +9 -16
  231. package/src/ui/reaction.tsx +10 -6
  232. package/src/ui/send-button.tsx +3 -8
  233. package/src/ui/visual-viewport.test.ts +36 -0
  234. package/src/ui/visual-viewport.ts +27 -0
  235. package/dist-cjs/ui/tooltip-button.js.map +0 -7
  236. package/dist-esm/ui/tooltip-button.mjs +0 -12
  237. package/dist-esm/ui/tooltip-button.mjs.map +0 -7
  238. package/src/ui/tooltip-button.tsx +0 -20
@@ -76,18 +76,15 @@ function toggleCommentReaction(editor, comment, userId, emoji, now = Date.now())
76
76
  );
77
77
  const removing = mine.some((reaction) => reaction.id === targetId);
78
78
  if (!removing && !isAllowedReaction(emoji)) return;
79
- (0, import_comment_mutations.commitCommentMutation)(editor, () => {
79
+ (0, import_comment_mutations.commitCommentMutation)(editor, ({ put, remove }) => {
80
80
  if (removing) {
81
- (0, import_comment_mutations.removeRecordsInCommit)(editor, [targetId]);
81
+ remove([targetId]);
82
82
  return;
83
83
  }
84
84
  if (!allowMultipleReactions && mine.length > 0) {
85
- (0, import_comment_mutations.removeRecordsInCommit)(
86
- editor,
87
- mine.map((reaction) => reaction.id)
88
- );
85
+ remove(mine.map((reaction) => reaction.id));
89
86
  }
90
- (0, import_comment_mutations.putRecordsInCommit)(editor, [
87
+ put([
91
88
  (0, import_tldraw.createCommentReaction)({
92
89
  commentId: comment.id,
93
90
  threadId: comment.threadId,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/canvas/comment-reactions.tsx"],
4
- "sourcesContent": ["import { useMemo } from 'react'\nimport {\n\tEditor,\n\tTLComment,\n\tTLCommentReaction,\n\tcreateCommentReaction,\n\tcreateCommentReactionId,\n\tuseEditor,\n\tuseValue,\n} from 'tldraw'\nimport { RenderReaction } from '../ui/reaction'\nimport { ReactionPicker } from '../ui/reaction-picker'\nimport { Reactions, ReactionSummary } from '../ui/reactions'\nimport {\n\tcommitCommentMutation,\n\tputRecordsInCommit,\n\tremoveRecordsInCommit,\n} from './comment-mutations'\nimport { UNKNOWN_AUTHOR } from './comment-render'\nimport { getCommentReactions } from './comment-store'\nimport { getCommentingOptions, useCommentingOptions } from './options'\n\n/**\n * One comment's reactions, oldest first, reactively.\n *\n * @public\n */\nexport function useCommentReactions(\n\teditor: Editor,\n\tcommentId: TLComment['id']\n): TLCommentReaction[] {\n\treturn useValue(\n\t\t'comment reactions',\n\t\t() =>\n\t\t\tgetCommentReactions(editor)\n\t\t\t\t.filter((reaction) => reaction.commentId === commentId)\n\t\t\t\t.sort((a, b) => a.createdAt - b.createdAt),\n\t\t[editor, commentId]\n\t)\n}\n\n/**\n * Tally a comment's reactions into an entry per emoji, ordered by when that emoji was first used\n * so the row stays stable as later reactions arrive. `active` marks the emoji the current user\n * reacted with (the pills render those highlighted), and `reactors` lists who reacted with it, in\n * reaction order, for the hover list. `resolveName` names each reactor; an id it can't name falls\n * back to the id itself.\n *\n * @public\n */\nexport function summarizeReactions(\n\treactions: TLCommentReaction[],\n\tcurrentUserId?: string | null,\n\tresolveName?: (userId: string) => string | undefined\n): ReactionSummary[] {\n\tconst groups = new Map<\n\t\tstring,\n\t\t{ count: number; active: boolean; firstAt: number; reactors: ReactionSummary['reactors'] }\n\t>()\n\tfor (const reaction of reactions) {\n\t\tconst mine = currentUserId != null && reaction.userId === currentUserId\n\t\t// Fall back to a generic name, never the raw user id, when the id can't be resolved.\n\t\tconst reactor = { name: resolveName?.(reaction.userId) ?? UNKNOWN_AUTHOR, you: mine }\n\t\tconst group = groups.get(reaction.emoji)\n\t\tif (group) {\n\t\t\tgroup.count++\n\t\t\tgroup.active ||= mine\n\t\t\tgroup.firstAt = Math.min(group.firstAt, reaction.createdAt)\n\t\t\tgroup.reactors.push(reactor)\n\t\t} else {\n\t\t\tgroups.set(reaction.emoji, {\n\t\t\t\tcount: 1,\n\t\t\t\tactive: mine,\n\t\t\t\tfirstAt: reaction.createdAt,\n\t\t\t\treactors: [reactor],\n\t\t\t})\n\t\t}\n\t}\n\treturn [...groups]\n\t\t.sort(([, a], [, b]) => a.firstAt - b.firstAt)\n\t\t.map(([emoji, group]) => ({\n\t\t\temoji,\n\t\t\tcount: group.count,\n\t\t\tactive: group.active,\n\t\t\treactors: group.reactors,\n\t\t}))\n}\n\n/**\n * Toggle one user's reaction with a given emoji on a comment.\n *\n * Each reaction is its own record keyed by (comment, user, emoji), so this only ever touches that\n * one user's own records \u2014 two people reacting at once never conflict. Behaviour depends on the\n * `allowMultipleReactions` option:\n *\n * - **multiple** (default): the emoji toggles independently \u2014 add it if absent, remove it if\n * present, leaving the user's other reactions alone.\n * - **single**: picking a new emoji first removes the user's existing reaction(s) on the comment,\n * then adds this one (a replace); picking the one they already have removes it.\n *\n * @public\n */\nexport function toggleCommentReaction(\n\teditor: Editor,\n\tcomment: TLComment,\n\tuserId: string,\n\temoji: string,\n\tnow = Date.now()\n): void {\n\tconst { allowMultipleReactions, isAllowedReaction } = getCommentingOptions(editor)\n\tconst targetId = createCommentReactionId(comment.id, userId, emoji)\n\tconst mine = getCommentReactions(editor).filter(\n\t\t(reaction) => reaction.commentId === comment.id && reaction.userId === userId\n\t)\n\tconst removing = mine.some((reaction) => reaction.id === targetId)\n\t// Only a token the configured palette allows may be added. Removals always go through \u2014 if a\n\t// reaction somehow carries an off-palette token, the user must still be able to clear it.\n\tif (!removing && !isAllowedReaction(emoji)) return\n\tcommitCommentMutation(editor, () => {\n\t\tif (removing) {\n\t\t\tremoveRecordsInCommit(editor, [targetId])\n\t\t\treturn\n\t\t}\n\t\t// Single-select: a new emoji replaces the user's existing reaction(s) on this comment.\n\t\tif (!allowMultipleReactions && mine.length > 0) {\n\t\t\tremoveRecordsInCommit(\n\t\t\t\teditor,\n\t\t\t\tmine.map((reaction) => reaction.id)\n\t\t\t)\n\t\t}\n\t\tputRecordsInCommit(editor, [\n\t\t\tcreateCommentReaction({\n\t\t\t\tcommentId: comment.id,\n\t\t\t\tthreadId: comment.threadId,\n\t\t\t\tpageId: comment.pageId,\n\t\t\t\tuserId,\n\t\t\t\temoji,\n\t\t\t\tnow,\n\t\t\t}),\n\t\t])\n\t})\n}\n\n/** @public */\nexport interface CommentReactionsProps {\n\tcomment: TLComment\n\t/** The reacting user. Null/omitted gives a read-only row (signed out): counts show, but the\n\t * pills don't toggle. */\n\tcurrentUserId?: string | null\n\t/** Names a reactor id for the hover list. Ids it can't name fall back to the id. */\n\tresolveName?(userId: string): string | undefined\n}\n\n/**\n * Adapts the `ReactionContent` component override (if any) into a `renderReaction` function for the\n * presentational reaction components. Returns undefined when no override is set, so they fall back\n * to the default (the token string, drawn by the OS emoji font).\n */\nfunction useReactionRenderer(): RenderReaction | undefined {\n\tconst { components } = useCommentingOptions()\n\tconst ReactionContent = components.ReactionContent\n\treturn useMemo(\n\t\t() => (ReactionContent ? (token: string) => <ReactionContent token={token} /> : undefined),\n\t\t[ReactionContent]\n\t)\n}\n\n/**\n * The tallied reaction row under one comment. Pair with `CommentReactionPicker`, which is what\n * adds a reaction.\n * @public @react\n */\nexport function CommentReactions({ comment, currentUserId, resolveName }: CommentReactionsProps) {\n\tconst editor = useEditor()\n\tconst renderReaction = useReactionRenderer()\n\tconst { components } = useCommentingOptions()\n\tconst reactions = useCommentReactions(editor, comment.id)\n\tconst summaries = useMemo(\n\t\t() => summarizeReactions(reactions, currentUserId, resolveName),\n\t\t[reactions, currentUserId, resolveName]\n\t)\n\t// Suppress the hover list while any menu is open (the reaction picker, an overflow menu\u2026) so it\n\t// doesn't compete with the menu the user is actually working in. Edit mode already hides the\n\t// pills entirely (the card becomes a composer), so it needs no special case here.\n\tconst anyMenuOpen = useValue('any menu open', () => editor.menus.getOpenMenus().length > 0, [\n\t\teditor,\n\t])\n\treturn (\n\t\t<Reactions\n\t\t\treactions={summaries}\n\t\t\tcanReact={currentUserId != null}\n\t\t\tenableHoverList={!anyMenuOpen}\n\t\t\trenderReaction={renderReaction}\n\t\t\tReactionTooltip={components.ReactionTooltip}\n\t\t\tonToggle={(value) => {\n\t\t\t\tif (currentUserId == null) return\n\t\t\t\ttoggleCommentReaction(editor, comment, currentUserId, value)\n\t\t\t}}\n\t\t/>\n\t)\n}\n\n/** @public */\nexport interface CommentReactionPickerProps {\n\tcomment: TLComment\n\t/** The reacting user. Null/omitted renders nothing \u2014 there's nobody to react as. */\n\tcurrentUserId?: string | null\n\t/** The emoji the picker offers. Defaults to `DEFAULT_REACTION_EMOJI`. */\n\temoji?: string[]\n}\n\n/**\n * The add-reaction button for one comment. Belongs with the comment card's hover actions (under\n * the \u22EF button) rather than in the reaction row, so opening it doesn't chase the row as reactions\n * are added.\n * @public @react\n */\nexport function CommentReactionPicker({\n\tcomment,\n\tcurrentUserId,\n\temoji,\n}: CommentReactionPickerProps) {\n\tconst editor = useEditor()\n\tconst { components } = useCommentingOptions()\n\tconst renderReaction = useReactionRenderer()\n\tconst reactions = useCommentReactions(editor, comment.id)\n\tconst selected = useMemo(\n\t\t() =>\n\t\t\treactions\n\t\t\t\t.filter((reaction) => currentUserId != null && reaction.userId === currentUserId)\n\t\t\t\t.map((reaction) => reaction.emoji),\n\t\t[reactions, currentUserId]\n\t)\n\tif (currentUserId == null) return null\n\treturn (\n\t\t<ReactionPicker\n\t\t\temoji={emoji}\n\t\t\tselected={selected}\n\t\t\trenderReaction={renderReaction}\n\t\t\tpalette={components.ReactionPalette}\n\t\t\tmenuId={`comment-reactions-${comment.id}`}\n\t\t\tonSelect={(value) => toggleCommentReaction(editor, comment, currentUserId, value)}\n\t\t/>\n\t)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { useMemo } from 'react'\nimport {\n\tEditor,\n\tTLComment,\n\tTLCommentReaction,\n\tcreateCommentReaction,\n\tcreateCommentReactionId,\n\tuseEditor,\n\tuseValue,\n} from 'tldraw'\nimport { RenderReaction } from '../ui/reaction'\nimport { ReactionPicker } from '../ui/reaction-picker'\nimport { Reactions, ReactionSummary } from '../ui/reactions'\nimport { commitCommentMutation } from './comment-mutations'\nimport { UNKNOWN_AUTHOR } from './comment-render'\nimport { getCommentReactions } from './comment-store'\nimport { getCommentingOptions, useCommentingOptions } from './options'\n\n/**\n * One comment's reactions, oldest first, reactively.\n *\n * @public\n */\nexport function useCommentReactions(\n\teditor: Editor,\n\tcommentId: TLComment['id']\n): TLCommentReaction[] {\n\treturn useValue(\n\t\t'comment reactions',\n\t\t() =>\n\t\t\tgetCommentReactions(editor)\n\t\t\t\t.filter((reaction) => reaction.commentId === commentId)\n\t\t\t\t.sort((a, b) => a.createdAt - b.createdAt),\n\t\t[editor, commentId]\n\t)\n}\n\n/**\n * The reaction fields {@link summarizeReactions} needs \u2014 a structural subset of\n * {@link tldraw#TLCommentReaction}, so tallies can also be built from rows synced outside the\n * editor store.\n *\n * @public\n */\nexport interface ReactionSummaryInput {\n\tuserId: string\n\temoji: string\n\tcreatedAt: number\n}\n\n/**\n * Tally a comment's reactions into an entry per emoji, ordered by when that emoji was first used so\n * the row stays stable as later reactions arrive. `active` marks the current user's emoji and\n * `reactors` lists who reacted, in reaction order. `resolveName` names each reactor; an id it can't\n * name falls back to a generic \"Someone\", never the raw user id.\n *\n * @public\n */\nexport function summarizeReactions(\n\treactions: readonly ReactionSummaryInput[],\n\tcurrentUserId?: string | null,\n\tresolveName?: (userId: string) => string | undefined\n): ReactionSummary[] {\n\tconst groups = new Map<\n\t\tstring,\n\t\t{ count: number; active: boolean; firstAt: number; reactors: ReactionSummary['reactors'] }\n\t>()\n\tfor (const reaction of reactions) {\n\t\tconst mine = currentUserId != null && reaction.userId === currentUserId\n\t\t// Fall back to a generic name, never the raw user id, when the id can't be resolved.\n\t\tconst reactor = { name: resolveName?.(reaction.userId) ?? UNKNOWN_AUTHOR, you: mine }\n\t\tconst group = groups.get(reaction.emoji)\n\t\tif (group) {\n\t\t\tgroup.count++\n\t\t\tgroup.active ||= mine\n\t\t\tgroup.firstAt = Math.min(group.firstAt, reaction.createdAt)\n\t\t\tgroup.reactors.push(reactor)\n\t\t} else {\n\t\t\tgroups.set(reaction.emoji, {\n\t\t\t\tcount: 1,\n\t\t\t\tactive: mine,\n\t\t\t\tfirstAt: reaction.createdAt,\n\t\t\t\treactors: [reactor],\n\t\t\t})\n\t\t}\n\t}\n\treturn [...groups]\n\t\t.sort(([, a], [, b]) => a.firstAt - b.firstAt)\n\t\t.map(([emoji, group]) => ({\n\t\t\temoji,\n\t\t\tcount: group.count,\n\t\t\tactive: group.active,\n\t\t\treactors: group.reactors,\n\t\t}))\n}\n\n/**\n * Toggle one user's reaction with a given emoji on a comment.\n *\n * Each reaction is its own record keyed by (comment, user, emoji), so this only touches that user's\n * own records and two people reacting at once never conflict. Behaviour depends on\n * `allowMultipleReactions`:\n *\n * - **multiple** (default): the emoji toggles independently, leaving other reactions alone.\n * - **single**: a new emoji replaces the user's existing reaction; the same one removes it.\n *\n * @public\n */\nexport function toggleCommentReaction(\n\teditor: Editor,\n\tcomment: TLComment,\n\tuserId: string,\n\temoji: string,\n\tnow = Date.now()\n): void {\n\tconst { allowMultipleReactions, isAllowedReaction } = getCommentingOptions(editor)\n\tconst targetId = createCommentReactionId(comment.id, userId, emoji)\n\tconst mine = getCommentReactions(editor).filter(\n\t\t(reaction) => reaction.commentId === comment.id && reaction.userId === userId\n\t)\n\tconst removing = mine.some((reaction) => reaction.id === targetId)\n\t// Only a token the configured palette allows may be added. Removals always go through \u2014 if a\n\t// reaction somehow carries an off-palette token, the user must still be able to clear it.\n\tif (!removing && !isAllowedReaction(emoji)) return\n\tcommitCommentMutation(editor, ({ put, remove }) => {\n\t\tif (removing) {\n\t\t\tremove([targetId])\n\t\t\treturn\n\t\t}\n\t\t// Single-select: a new emoji replaces the user's existing reaction(s) on this comment.\n\t\tif (!allowMultipleReactions && mine.length > 0) {\n\t\t\tremove(mine.map((reaction) => reaction.id))\n\t\t}\n\t\tput([\n\t\t\tcreateCommentReaction({\n\t\t\t\tcommentId: comment.id,\n\t\t\t\tthreadId: comment.threadId,\n\t\t\t\tpageId: comment.pageId,\n\t\t\t\tuserId,\n\t\t\t\temoji,\n\t\t\t\tnow,\n\t\t\t}),\n\t\t])\n\t})\n}\n\n/** @public */\nexport interface CommentReactionsProps {\n\tcomment: TLComment\n\t/** The reacting user. Null/omitted gives a read-only row (signed out): counts show, but the\n\t * pills don't toggle. */\n\tcurrentUserId?: string | null\n\t/** Names a reactor id for the hover list. Ids it can't name fall back to a generic \"Someone\". */\n\tresolveName?(userId: string): string | undefined\n}\n\n/**\n * Adapts the `ReactionContent` override into a `renderReaction` function for the presentational\n * components. Undefined when no override is set, so they fall back to drawing the token string.\n */\nfunction useReactionRenderer(): RenderReaction | undefined {\n\tconst { components } = useCommentingOptions()\n\tconst ReactionContent = components.ReactionContent\n\treturn useMemo(\n\t\t() => (ReactionContent ? (token: string) => <ReactionContent token={token} /> : undefined),\n\t\t[ReactionContent]\n\t)\n}\n\n/**\n * The tallied reaction row under one comment. Pair with `CommentReactionPicker`, which is what\n * adds a reaction.\n * @public @react\n */\nexport function CommentReactions({ comment, currentUserId, resolveName }: CommentReactionsProps) {\n\tconst editor = useEditor()\n\tconst renderReaction = useReactionRenderer()\n\tconst { components } = useCommentingOptions()\n\tconst reactions = useCommentReactions(editor, comment.id)\n\tconst summaries = useMemo(\n\t\t() => summarizeReactions(reactions, currentUserId, resolveName),\n\t\t[reactions, currentUserId, resolveName]\n\t)\n\t// Suppress the hover list while any menu is open (the reaction picker, an overflow menu\u2026) so it\n\t// doesn't compete with the menu the user is actually working in. Edit mode already hides the\n\t// pills entirely (the card becomes a composer), so it needs no special case here.\n\tconst anyMenuOpen = useValue('any menu open', () => editor.menus.getOpenMenus().length > 0, [\n\t\teditor,\n\t])\n\treturn (\n\t\t<Reactions\n\t\t\treactions={summaries}\n\t\t\tcanReact={currentUserId != null}\n\t\t\tenableHoverList={!anyMenuOpen}\n\t\t\trenderReaction={renderReaction}\n\t\t\tReactionTooltip={components.ReactionTooltip}\n\t\t\tonToggle={(value) => {\n\t\t\t\tif (currentUserId == null) return\n\t\t\t\ttoggleCommentReaction(editor, comment, currentUserId, value)\n\t\t\t}}\n\t\t/>\n\t)\n}\n\n/** @public */\nexport interface CommentReactionPickerProps {\n\tcomment: TLComment\n\t/** The reacting user. Null/omitted renders nothing \u2014 there's nobody to react as. */\n\tcurrentUserId?: string | null\n\t/** The emoji the picker offers. Defaults to `DEFAULT_REACTION_EMOJI`. */\n\temoji?: string[]\n}\n\n/**\n * The add-reaction button for one comment. Belongs with the comment card's hover actions rather\n * than in the reaction row, so opening it doesn't chase the row as reactions are added.\n * @public @react\n */\nexport function CommentReactionPicker({\n\tcomment,\n\tcurrentUserId,\n\temoji,\n}: CommentReactionPickerProps) {\n\tconst editor = useEditor()\n\tconst { components } = useCommentingOptions()\n\tconst renderReaction = useReactionRenderer()\n\tconst reactions = useCommentReactions(editor, comment.id)\n\tconst selected = useMemo(\n\t\t() =>\n\t\t\treactions\n\t\t\t\t.filter((reaction) => currentUserId != null && reaction.userId === currentUserId)\n\t\t\t\t.map((reaction) => reaction.emoji),\n\t\t[reactions, currentUserId]\n\t)\n\tif (currentUserId == null) return null\n\treturn (\n\t\t<ReactionPicker\n\t\t\temoji={emoji}\n\t\t\tselected={selected}\n\t\t\trenderReaction={renderReaction}\n\t\t\tpalette={components.ReactionPalette}\n\t\t\tmenuId={`comment-reactions-${comment.id}`}\n\t\t\tonSelect={(value) => toggleCommentReaction(editor, comment, currentUserId, value)}\n\t\t/>\n\t)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -26,6 +26,7 @@ __export(comment_render_exports, {
26
26
  module.exports = __toCommonJS(comment_render_exports);
27
27
  var import_core = require("@tiptap/core");
28
28
  var import_mentions = require("@tldraw/mentions");
29
+ var import_tldraw = require("tldraw");
29
30
  var import_comment_extensions = require("../ui/comment-extensions");
30
31
  const UNKNOWN_AUTHOR = "Someone";
31
32
  const UNKNOWN_COMMENT_AUTHOR = Object.freeze({ name: UNKNOWN_AUTHOR });
@@ -49,9 +50,9 @@ function renderCommentHtml(richText, resolveName) {
49
50
  if (cached !== void 0) return cached;
50
51
  }
51
52
  const extensions = mentions ? [...import_comment_extensions.commentTipTapExtensions, (0, import_mentions.createMentionExtension)({ resolveName })] : import_comment_extensions.commentTipTapExtensions;
52
- const html = (0, import_core.generateHTML)(demoteHeadings(richText), extensions).replaceAll(
53
- '<p dir="auto"></p>',
54
- "<p><br /></p>"
53
+ const html = (0, import_tldraw.renderHtmlFromRichTextWithExtensions)(
54
+ demoteHeadings(richText),
55
+ extensions
55
56
  );
56
57
  if (!mentions) htmlCache.set(richText, html);
57
58
  return html;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/canvas/comment-render.ts"],
4
- "sourcesContent": ["import { generateHTML, generateText, JSONContent } from '@tiptap/core'\nimport { type CommentAuthor, createMentionExtension } from '@tldraw/mentions'\nimport { TLRichText } from 'tldraw'\nimport { commentTipTapExtensions, isCommentEmpty } from '../ui/comment-extensions'\n\n/**\n * The author name shown in a byline when no source can name an id (e.g. a deleted account, or a\n * member with no comment and no live presence). The toolkit's one generic default, applied where a\n * byline needs a name; hosts pre-empt it by resolving the id from `resolveAuthor`. Not a\n * translation key \u2014 a single English literal.\n */\nexport const UNKNOWN_AUTHOR = 'Someone'\n\n/** The `CommentAuthor` fallback for an unresolvable id \u2014 just the name, no color or image.\n * Frozen: the one shared instance is handed to host render slots. */\nexport const UNKNOWN_COMMENT_AUTHOR: CommentAuthor = Object.freeze({ name: UNKNOWN_AUTHOR })\n\n/**\n * The comment extension set has no heading node, so a body that nonetheless contains one (e.g. a\n * record created programmatically or synced from a client with a fuller set) would make TipTap\n * throw on an unknown node type. Demote any heading to a paragraph first, so headings can never\n * render as headings and rendering never crashes.\n */\nfunction demoteHeadings(node: JSONContent): JSONContent {\n\tconst content = Array.isArray(node.content) ? node.content.map(demoteHeadings) : node.content\n\tif (node.type === 'heading') {\n\t\tconst { attrs: _attrs, ...rest } = node\n\t\treturn { ...rest, type: 'paragraph', content }\n\t}\n\treturn content === node.content ? node : { ...node, content }\n}\n\n/**\n * Whether a body contains a mention node. Mention text is resolved from a member id at render time,\n * so a body with one can't be cached by identity alone (the same body renders differently as names\n * change) \u2014 those bodies skip the cache and re-render each call.\n */\nfunction hasMention(node: JSONContent): boolean {\n\tif (node.type === 'mention') return true\n\treturn Array.isArray(node.content) ? node.content.some(hasMention) : false\n}\n\nconst htmlCache = new WeakMap<TLRichText, string>()\n\n/**\n * Render a comment body to HTML through the limited comment extension set (no headings), so a body\n * always renders with comment formatting regardless of the host editor's rich-text config. Mirrors\n * tldraw's `renderHtmlFromRichText`, including the empty-paragraph fix that keeps blank lines from\n * collapsing. `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentHtml(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = htmlCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, createMentionExtension({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst html = generateHTML(demoteHeadings(richText as JSONContent), extensions).replaceAll(\n\t\t'<p dir=\"auto\"></p>',\n\t\t'<p><br /></p>'\n\t)\n\tif (!mentions) htmlCache.set(richText, html)\n\treturn html\n}\n\nconst textCache = new WeakMap<TLRichText, string>()\n\n/**\n * Flatten a comment body to plaintext through the limited comment extension set \u2014 paragraphs and\n * list items separated by newlines. Used for previews (e.g. the sidebar) where formatting is dropped.\n * `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentPlaintext(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tif (isCommentEmpty(richText)) return ''\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = textCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, createMentionExtension({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst text = generateText(demoteHeadings(richText as JSONContent), extensions, {\n\t\tblockSeparator: '\\n',\n\t})\n\tif (!mentions) textCache.set(richText, text)\n\treturn text\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAwD;AACxD,sBAA2D;AAE3D,gCAAwD;AAQjD,MAAM,iBAAiB;AAIvB,MAAM,yBAAwC,OAAO,OAAO,EAAE,MAAM,eAAe,CAAC;AAQ3F,SAAS,eAAe,MAAgC;AACvD,QAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,IAAI,cAAc,IAAI,KAAK;AACtF,MAAI,KAAK,SAAS,WAAW;AAC5B,UAAM,EAAE,OAAO,QAAQ,GAAG,KAAK,IAAI;AACnC,WAAO,EAAE,GAAG,MAAM,MAAM,aAAa,QAAQ;AAAA,EAC9C;AACA,SAAO,YAAY,KAAK,UAAU,OAAO,EAAE,GAAG,MAAM,QAAQ;AAC7D;AAOA,SAAS,WAAW,MAA4B;AAC/C,MAAI,KAAK,SAAS,UAAW,QAAO;AACpC,SAAO,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,KAAK,UAAU,IAAI;AACtE;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAQ3C,SAAS,kBACf,UACA,aACS;AACT,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,uDAAyB,wCAAuB,EAAE,YAAY,CAAC,CAAC,IACpE;AACH,QAAM,WAAO,0BAAa,eAAe,QAAuB,GAAG,UAAU,EAAE;AAAA,IAC9E;AAAA,IACA;AAAA,EACD;AACA,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAO3C,SAAS,uBACf,UACA,aACS;AACT,UAAI,0CAAe,QAAQ,EAAG,QAAO;AACrC,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,uDAAyB,wCAAuB,EAAE,YAAY,CAAC,CAAC,IACpE;AACH,QAAM,WAAO,0BAAa,eAAe,QAAuB,GAAG,YAAY;AAAA,IAC9E,gBAAgB;AAAA,EACjB,CAAC;AACD,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;",
4
+ "sourcesContent": ["import { generateText, JSONContent } from '@tiptap/core'\nimport { type CommentAuthor, createMentionExtension } from '@tldraw/mentions'\nimport { renderHtmlFromRichTextWithExtensions, TLRichText } from 'tldraw'\nimport { commentTipTapExtensions, isCommentEmpty } from '../ui/comment-extensions'\n\n/**\n * The author name shown in a byline when no source can name an id (e.g. a deleted account, or a\n * member with no comment and no live presence). The toolkit's one generic default, applied where a\n * byline needs a name; hosts pre-empt it by resolving the id from `resolveAuthor`. Not a\n * translation key \u2014 a single English literal.\n */\nexport const UNKNOWN_AUTHOR = 'Someone'\n\n/** The `CommentAuthor` fallback for an unresolvable id \u2014 just the name, no color or image.\n * Frozen: the one shared instance is handed to host render slots. */\nexport const UNKNOWN_COMMENT_AUTHOR: CommentAuthor = Object.freeze({ name: UNKNOWN_AUTHOR })\n\n/**\n * The comment extension set has no heading node, so a body that nonetheless contains one (e.g. a\n * record created programmatically or synced from a client with a fuller set) would make TipTap\n * throw on an unknown node type. Demote any heading to a paragraph first, so headings can never\n * render as headings and rendering never crashes.\n */\nfunction demoteHeadings(node: JSONContent): JSONContent {\n\tconst content = Array.isArray(node.content) ? node.content.map(demoteHeadings) : node.content\n\tif (node.type === 'heading') {\n\t\tconst { attrs: _attrs, ...rest } = node\n\t\treturn { ...rest, type: 'paragraph', content }\n\t}\n\treturn content === node.content ? node : { ...node, content }\n}\n\n/**\n * Whether a body contains a mention node. Mention text is resolved from a member id at render time,\n * so a body with one can't be cached by identity alone (the same body renders differently as names\n * change) \u2014 those bodies skip the cache and re-render each call.\n */\nfunction hasMention(node: JSONContent): boolean {\n\tif (node.type === 'mention') return true\n\treturn Array.isArray(node.content) ? node.content.some(hasMention) : false\n}\n\nconst htmlCache = new WeakMap<TLRichText, string>()\n\n/**\n * Render a comment body to HTML through the limited comment extension set (no headings), so a body\n * always renders with comment formatting regardless of the host editor's rich-text config. Renders\n * through tldraw's shared helper, so it gets the same empty-paragraph fix that keeps blank lines\n * from collapsing. `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentHtml(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = htmlCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, createMentionExtension({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst html = renderHtmlFromRichTextWithExtensions(\n\t\tdemoteHeadings(richText as JSONContent) as TLRichText,\n\t\textensions\n\t)\n\tif (!mentions) htmlCache.set(richText, html)\n\treturn html\n}\n\nconst textCache = new WeakMap<TLRichText, string>()\n\n/**\n * Flatten a comment body to plaintext through the limited comment extension set \u2014 paragraphs and\n * list items separated by newlines. Used for previews (e.g. the sidebar) where formatting is dropped.\n * `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentPlaintext(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tif (isCommentEmpty(richText)) return ''\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = textCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, createMentionExtension({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst text = generateText(demoteHeadings(richText as JSONContent), extensions, {\n\t\tblockSeparator: '\\n',\n\t})\n\tif (!mentions) textCache.set(richText, text)\n\treturn text\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA0C;AAC1C,sBAA2D;AAC3D,oBAAiE;AACjE,gCAAwD;AAQjD,MAAM,iBAAiB;AAIvB,MAAM,yBAAwC,OAAO,OAAO,EAAE,MAAM,eAAe,CAAC;AAQ3F,SAAS,eAAe,MAAgC;AACvD,QAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,IAAI,cAAc,IAAI,KAAK;AACtF,MAAI,KAAK,SAAS,WAAW;AAC5B,UAAM,EAAE,OAAO,QAAQ,GAAG,KAAK,IAAI;AACnC,WAAO,EAAE,GAAG,MAAM,MAAM,aAAa,QAAQ;AAAA,EAC9C;AACA,SAAO,YAAY,KAAK,UAAU,OAAO,EAAE,GAAG,MAAM,QAAQ;AAC7D;AAOA,SAAS,WAAW,MAA4B;AAC/C,MAAI,KAAK,SAAS,UAAW,QAAO;AACpC,SAAO,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,KAAK,UAAU,IAAI;AACtE;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAQ3C,SAAS,kBACf,UACA,aACS;AACT,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,uDAAyB,wCAAuB,EAAE,YAAY,CAAC,CAAC,IACpE;AACH,QAAM,WAAO;AAAA,IACZ,eAAe,QAAuB;AAAA,IACtC;AAAA,EACD;AACA,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAO3C,SAAS,uBACf,UACA,aACS;AACT,UAAI,0CAAe,QAAQ,EAAG,QAAO;AACrC,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,uDAAyB,wCAAuB,EAAE,YAAY,CAAC,CAAC,IACpE;AACH,QAAM,WAAO,0BAAa,eAAe,QAAuB,GAAG,YAAY;AAAA,IAC9E,gBAAgB;AAAA,EACjB,CAAC;AACD,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/canvas/comment-store.ts"],
4
- "sourcesContent": ["import { Editor, TLComment, TLCommentReaction, TLCommentThread, TLRecord } from 'tldraw'\n\n/**\n * Typed reads of comment records on the editor store.\n *\n * Comment threads and comments live on the editor's local store so the canvas can render them\n * reactively, but they are opt-in records that aren't part of the `TLRecord` union (they ride the\n * sync server's object-store lane on the wire \u2014 see `TLCommentThread`). `editor.store` is therefore\n * statically typed `Store<TLRecord>` and doesn't know about them, so every access has to reinterpret\n * the type. These helpers own that reinterpretation \u2014 an `unknown` hop to exactly the type the store\n * expects, so the rest of each call stays checked \u2014 behind one boundary, and keep call sites typed.\n *\n * Writes do the same reinterpretation, but they also answer to the undo/redo policy, so they live\n * with the rest of the write path in `comment-mutations.ts`.\n */\n\n/**\n * A record that lives in a comment thread: the thread itself, one of its messages, or a reaction\n * to one of those messages.\n * @public\n */\nexport type TLCommentRecord = TLComment | TLCommentThread | TLCommentReaction\n\n/** Read one comment record by id, or `undefined` if the id isn't a present comment record. @public */\nexport function getCommentRecord(editor: Editor, id: string): TLCommentRecord | undefined {\n\tconst record = editor.store.get(id as TLRecord['id']) as unknown as TLCommentRecord | undefined\n\tif (!record) return undefined\n\tif (\n\t\trecord.typeName === 'comment' ||\n\t\trecord.typeName === 'comment-thread' ||\n\t\trecord.typeName === 'comment-reaction'\n\t) {\n\t\treturn record\n\t}\n\treturn undefined\n}\n\n/**\n * Every comment thread in the store, **including soft-deleted ones** and ones left empty by their\n * last comment's delete \u2014 records that are still present but awaiting the server's prune, and that\n * nothing renders. For the set the UI shows, use {@link getLiveCommentThreads}.\n *\n * Non-reactive; wrap in `useValue`, or use `useCommentThreads`, to react.\n * @public\n */\nexport function getCommentThreads(editor: Editor): TLCommentThread[] {\n\tconst typeName = 'comment-thread' as TLRecord['typeName']\n\treturn editor.store.query.records(typeName).get() as unknown as TLCommentThread[]\n}\n\n/**\n * Every comment in the store, **including soft-deleted ones** awaiting the server's prune. For the\n * set the UI shows, use {@link getLiveComments}.\n *\n * Non-reactive; wrap in `useValue`, or use `useComments`, to react.\n * @public\n */\nexport function getComments(editor: Editor): TLComment[] {\n\tconst typeName = 'comment' as TLRecord['typeName']\n\treturn editor.store.query.records(typeName).get() as unknown as TLComment[]\n}\n\n/**\n * The comments that should render: not soft-deleted. A deleted record lingers in the store until\n * the server prunes it, so this \u2014 not {@link getComments} \u2014 is what a count or a list of your own\n * should be built from.\n *\n * Non-reactive; the reactive equivalent is `useComments` (which also sorts oldest first).\n * @public\n */\nexport function getLiveComments(editor: Editor): TLComment[] {\n\treturn getComments(editor).filter((comment) => !comment.isDeleted)\n}\n\n/**\n * The comment threads that should render (pins, sidebar): live \u2014 not soft-deleted \u2014 and still\n * holding at least one live comment. A soft-deleted thread or comment is awaiting the server's\n * prune, as is a thread emptied by its last comment's delete; until the prune lands, the emptied\n * thread record lingers with no surface.\n *\n * Non-reactive; the reactive equivalent is `useCommentThreads`.\n * @public\n */\nexport function getLiveCommentThreads(editor: Editor): TLCommentThread[] {\n\tconst threadIdsWithComments = new Set(getLiveComments(editor).map((comment) => comment.threadId))\n\treturn getCommentThreads(editor).filter(\n\t\t(thread) => !thread.isDeleted && threadIdsWithComments.has(thread.id)\n\t)\n}\n\n/** All comment reactions currently in the store (non-reactive; wrap in `useValue` to react). @public */\nexport function getCommentReactions(editor: Editor): TLCommentReaction[] {\n\tconst typeName = 'comment-reaction' as TLRecord['typeName']\n\treturn editor.store.query.records(typeName).get() as unknown as TLCommentReaction[]\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBO,SAAS,iBAAiB,QAAgB,IAAyC;AACzF,QAAM,SAAS,OAAO,MAAM,IAAI,EAAoB;AACpD,MAAI,CAAC,OAAQ,QAAO;AACpB,MACC,OAAO,aAAa,aACpB,OAAO,aAAa,oBACpB,OAAO,aAAa,oBACnB;AACD,WAAO;AAAA,EACR;AACA,SAAO;AACR;AAUO,SAAS,kBAAkB,QAAmC;AACpE,QAAM,WAAW;AACjB,SAAO,OAAO,MAAM,MAAM,QAAQ,QAAQ,EAAE,IAAI;AACjD;AASO,SAAS,YAAY,QAA6B;AACxD,QAAM,WAAW;AACjB,SAAO,OAAO,MAAM,MAAM,QAAQ,QAAQ,EAAE,IAAI;AACjD;AAUO,SAAS,gBAAgB,QAA6B;AAC5D,SAAO,YAAY,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,QAAQ,SAAS;AAClE;AAWO,SAAS,sBAAsB,QAAmC;AACxE,QAAM,wBAAwB,IAAI,IAAI,gBAAgB,MAAM,EAAE,IAAI,CAAC,YAAY,QAAQ,QAAQ,CAAC;AAChG,SAAO,kBAAkB,MAAM,EAAE;AAAA,IAChC,CAAC,WAAW,CAAC,OAAO,aAAa,sBAAsB,IAAI,OAAO,EAAE;AAAA,EACrE;AACD;AAGO,SAAS,oBAAoB,QAAqC;AACxE,QAAM,WAAW;AACjB,SAAO,OAAO,MAAM,MAAM,QAAQ,QAAQ,EAAE,IAAI;AACjD;",
4
+ "sourcesContent": ["import { Editor, TLComment, TLCommentReaction, TLCommentThread, TLRecord } from 'tldraw'\n\n/**\n * Typed reads of comment records on the editor store.\n *\n * Comment records live on the editor's local store so the canvas can render them reactively, but\n * they're opt-in and aren't part of the `TLRecord` union \u2014 so `editor.store` is statically typed\n * `Store<TLRecord>` and every access has to reinterpret the type. These helpers own that\n * reinterpretation behind one boundary and keep call sites typed.\n *\n * Writes do the same, but also answer to the undo/redo policy, so they live in\n * `comment-mutations.ts`.\n */\n\n/**\n * A record that lives in a comment thread: the thread itself, one of its messages, or a reaction\n * to one of those messages.\n * @public\n */\nexport type TLCommentRecord = TLComment | TLCommentThread | TLCommentReaction\n\n/** Read one comment record by id, or `undefined` if the id isn't a present comment record. @public */\nexport function getCommentRecord(editor: Editor, id: string): TLCommentRecord | undefined {\n\tconst record = editor.store.get(id as TLRecord['id']) as unknown as TLCommentRecord | undefined\n\tif (!record) return undefined\n\tif (\n\t\trecord.typeName === 'comment' ||\n\t\trecord.typeName === 'comment-thread' ||\n\t\trecord.typeName === 'comment-reaction'\n\t) {\n\t\treturn record\n\t}\n\treturn undefined\n}\n\n/**\n * Every comment thread in the store, **including soft-deleted and emptied ones** awaiting the\n * server's prune, which nothing renders. For the set the UI shows, use {@link getLiveCommentThreads}.\n *\n * Non-reactive; wrap in `useValue`, or use `useCommentThreads`, to react.\n * @public\n */\nexport function getCommentThreads(editor: Editor): TLCommentThread[] {\n\tconst typeName = 'comment-thread' as TLRecord['typeName']\n\treturn editor.store.query.records(typeName).get() as unknown as TLCommentThread[]\n}\n\n/**\n * Every comment in the store, **including soft-deleted ones** awaiting the server's prune. For the\n * set the UI shows, use {@link getLiveComments}.\n *\n * Non-reactive; wrap in `useValue`, or use `useComments`, to react.\n * @public\n */\nexport function getComments(editor: Editor): TLComment[] {\n\tconst typeName = 'comment' as TLRecord['typeName']\n\treturn editor.store.query.records(typeName).get() as unknown as TLComment[]\n}\n\n/**\n * The comments that should render: not soft-deleted. A deleted record lingers in the store until\n * the server prunes it, so build counts and lists from this rather than {@link getComments}.\n *\n * Non-reactive; the reactive equivalent is `useComments` (which also sorts oldest first).\n * @public\n */\nexport function getLiveComments(editor: Editor): TLComment[] {\n\treturn getComments(editor).filter((comment) => !comment.isDeleted)\n}\n\n/**\n * The comment threads that should render (pins, sidebar): not soft-deleted, and still holding at\n * least one live comment. A thread emptied by its last comment's delete lingers with no surface\n * until the server's prune lands.\n *\n * Non-reactive; the reactive equivalent is `useCommentThreads`.\n * @public\n */\nexport function getLiveCommentThreads(editor: Editor): TLCommentThread[] {\n\tconst threadIdsWithComments = new Set(getLiveComments(editor).map((comment) => comment.threadId))\n\treturn getCommentThreads(editor).filter(\n\t\t(thread) => !thread.isDeleted && threadIdsWithComments.has(thread.id)\n\t)\n}\n\n/** All comment reactions currently in the store (non-reactive; wrap in `useValue` to react). @public */\nexport function getCommentReactions(editor: Editor): TLCommentReaction[] {\n\tconst typeName = 'comment-reaction' as TLRecord['typeName']\n\treturn editor.store.query.records(typeName).get() as unknown as TLCommentReaction[]\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBO,SAAS,iBAAiB,QAAgB,IAAyC;AACzF,QAAM,SAAS,OAAO,MAAM,IAAI,EAAoB;AACpD,MAAI,CAAC,OAAQ,QAAO;AACpB,MACC,OAAO,aAAa,aACpB,OAAO,aAAa,oBACpB,OAAO,aAAa,oBACnB;AACD,WAAO;AAAA,EACR;AACA,SAAO;AACR;AASO,SAAS,kBAAkB,QAAmC;AACpE,QAAM,WAAW;AACjB,SAAO,OAAO,MAAM,MAAM,QAAQ,QAAQ,EAAE,IAAI;AACjD;AASO,SAAS,YAAY,QAA6B;AACxD,QAAM,WAAW;AACjB,SAAO,OAAO,MAAM,MAAM,QAAQ,QAAQ,EAAE,IAAI;AACjD;AASO,SAAS,gBAAgB,QAA6B;AAC5D,SAAO,YAAY,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,QAAQ,SAAS;AAClE;AAUO,SAAS,sBAAsB,QAAmC;AACxE,QAAM,wBAAwB,IAAI,IAAI,gBAAgB,MAAM,EAAE,IAAI,CAAC,YAAY,QAAQ,QAAQ,CAAC;AAChG,SAAO,kBAAkB,MAAM,EAAE;AAAA,IAChC,CAAC,WAAW,CAAC,OAAO,aAAa,sBAAsB,IAAI,OAAO,EAAE;AAAA,EACrE;AACD;AAGO,SAAS,oBAAoB,QAAqC;AACxE,QAAM,WAAW;AACjB,SAAO,OAAO,MAAM,MAAM,QAAQ,QAAQ,EAAE,IAAI;AACjD;",
6
6
  "names": []
7
7
  }
@@ -92,7 +92,6 @@ class CommentIdle extends import_tldraw.StateNode {
92
92
  this.editor.setCursor({ type: "comment", rotation: 0 });
93
93
  updateAnchorHint(this.editor);
94
94
  }
95
- // Hint the shape a click would attach to.
96
95
  onPointerMove() {
97
96
  updateAnchorHint(this.editor);
98
97
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/canvas/comment-tool.tsx"],
4
- "sourcesContent": ["import {\n\tBoxModel,\n\tEditor,\n\tStateNode,\n\tTLCommentAnchor,\n\tTLStateNodeConstructor,\n\tTLUiOverrides,\n\tVecLike,\n} from 'tldraw'\nimport { type CommentingOptions, defaultCommentingOptions, getCommentingOptions } from './options'\nimport { commentsSidebarOpen, pendingComment, regionDraft } from './state'\nimport { commentTargetShapeAt, regionPinPoint, shapeAnchorAt } from './thread-state'\n\n/** A comment being placed but not yet posted: where its composer sits and what it will anchor\n * to. Shared between the tool (which sets it on click) and the overlay (which renders the\n * composer) through the internal `pendingComment` atom.\n * @internal */\nexport interface PendingComment {\n\tanchor: TLCommentAnchor\n\t/** Page point where the composer opens (the click location, or a region's pin corner). */\n\tpoint: VecLike\n}\n\n/**\n * Merge configure options over a base. Shallow for scalars; `components` is merged rather than\n * replaced so chained `configure` calls layer their slots \u2014 a later `{ components: { PinContent } }`\n * doesn't drop an earlier `{ components: { CommentBody } }`.\n */\nfunction mergeCommentingOptions(\n\tbase: CommentingOptions,\n\toverrides: Partial<CommentingOptions>\n): CommentingOptions {\n\treturn {\n\t\t...base,\n\t\t...overrides,\n\t\tcomponents: { ...base.components, ...overrides.components },\n\t}\n}\n\n/** The page-space rectangle spanned by two points, normalized so w/h are non-negative. Shared by\n * region creation (pointer-down \u2192 cursor) and corner resize (fixed corner \u2192 cursor). */\nexport function regionBetween(a: VecLike, b: VecLike): BoxModel {\n\treturn {\n\t\tx: Math.min(a.x, b.x),\n\t\ty: Math.min(a.y, b.y),\n\t\tw: Math.abs(a.x - b.x),\n\t\th: Math.abs(a.y - b.y),\n\t}\n}\n\n/**\n * The comment tool. Pressing down opens the comment composer immediately at the pointer, and it\n * follows the pointer until release \u2014 like placing a sticky note \u2014 settling on a point, or on a\n * shape when released over one. (With region comments enabled, dragging past the threshold instead\n * draws a region rectangle.) Placement only opens a composer (via `pendingComment`); the records\n * are created when the comment is posted.\n * @public\n */\nexport class CommentTool extends StateNode {\n\tstatic override id = 'comment'\n\tstatic override initial = 'idle'\n\tstatic override children(): TLStateNodeConstructor[] {\n\t\treturn [CommentIdle, CommentPointing, CommentDragging]\n\t}\n\n\t/**\n\t * Configure this tool's {@link CommentTool.options | `options`}, returning a configured subclass\n\t * to register via `tools`. Mirrors `ShapeUtil.configure`. Layers over any prior `configure`, so\n\t * calls can be chained.\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />\n\t * ```\n\t */\n\tstatic configure<T extends TLStateNodeConstructor>(\n\t\tthis: T,\n\t\toptions: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never\n\t): T {\n\t\t// @ts-expect-error -- mirrors ShapeUtil.configure; extending `this` is sound at runtime.\n\t\treturn class extends this {\n\t\t\t// @ts-expect-error\n\t\t\toptions = mergeCommentingOptions(this.options, options)\n\t\t}\n\t}\n\n\t/**\n\t * The merged commenting options for this editor. Read from anywhere via\n\t * {@link getCommentingOptions}. Override with {@link CommentTool.configure}.\n\t */\n\toptions: CommentingOptions = defaultCommentingOptions\n\n\toverride onEnter() {\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\t// Placing comments is canvas-focused \u2014 the thread list gets out of the way while the tool is\n\t\t// active. Reopened via its own control (a button), never by leaving the tool.\n\t\tcommentsSidebarOpen.set(this.editor, false)\n\t}\n\n\toverride onExit() {\n\t\t// Drop the hover hint painted while pointing at shapes (see CommentIdle). The cursor resets\n\t\t// when the next tool takes over.\n\t\tthis.editor.setHintingShapes([])\n\t}\n\n\t// Escape leaves the tool, like the built-in tools (the editor dispatches `cancel` on Escape).\n\toverride onCancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\n/** Hint the shape a comment placed at the pointer would anchor to, using the same hit-test as the\n * anchor resolution on release. Hinting shapes render an indicator ungated by the active tool, so\n * this shows the select-style outline while the comment tool \u2014 not select \u2014 is active. */\nfunction updateAnchorHint(editor: Editor) {\n\tconst hit = commentTargetShapeAt(editor, editor.inputs.getCurrentPagePoint())\n\teditor.setHintingShapes(hit ? [hit.id] : [])\n}\n\nclass CommentIdle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onEnter() {\n\t\t// Back to hovering: restore the pin cursor (a prior placing state may have hidden it).\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\tupdateAnchorHint(this.editor)\n\t}\n\n\t// Hint the shape a click would attach to.\n\toverride onPointerMove() {\n\t\tupdateAnchorHint(this.editor)\n\t}\n\n\toverride onPointerDown() {\n\t\tthis.parent.transition('pointing')\n\t}\n}\n\nclass CommentPointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\toverride onEnter() {\n\t\t// Open the composer immediately at the press point so it's visible from pointer-down (not just\n\t\t// on release), and let it trail the pointer while dragging \u2014 like placing a sticky note. The\n\t\t// anchor is a bare point for now; it's resolved (shape or point) on pointer up.\n\t\tconst { editor } = this\n\t\t// Hide the cursor while placing: the draft composer is the pointer's stand-in now, so the pin\n\t\t// cursor sitting over it just reads as clutter.\n\t\teditor.setCursor({ type: 'none', rotation: 0 })\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'point', x: point.x, y: point.y },\n\t\t\tpoint: { x: point.x, y: point.y },\n\t\t})\n\t}\n\n\toverride onPointerMove() {\n\t\tconst { editor } = this\n\t\t// Once the pointer passes the drag threshold with region comments enabled, this is a region,\n\t\t// not a follow \u2014 hand off to the region drag (which clears this composer and draws the box).\n\t\tif (getCommentingOptions(editor).enableRegions && editor.inputs.getIsDragging()) {\n\t\t\tthis.parent.transition('dragging')\n\t\t\treturn\n\t\t}\n\t\t// Otherwise the composer trails the pointer \u2014 keep hinting the shape a release here would\n\t\t// anchor to, like the idle hover does.\n\t\tupdateAnchorHint(editor)\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.update(editor, (p) => (p ? { ...p, point: { x: point.x, y: point.y } } : p))\n\t}\n\n\t// Settle where the pointer is released: anchor to the shape under it, or drop a point.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tconst hit = commentTargetShapeAt(editor, point)\n\t\tconst anchor: TLCommentAnchor = hit\n\t\t\t? shapeAnchorAt(\n\t\t\t\t\teditor,\n\t\t\t\t\thit.id,\n\t\t\t\t\tpoint,\n\t\t\t\t\tgetCommentingOptions(editor).shouldBePrecise(editor, {\n\t\t\t\t\t\tshapeId: hit.id,\n\t\t\t\t\t\tpoint,\n\t\t\t\t\t\taltKey: editor.inputs.getAltKey(),\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t: { type: 'point', x: point.x, y: point.y }\n\t\tpendingComment.set(editor, { anchor, point: { x: point.x, y: point.y } })\n\t\t// Hand back to select; the open composer is now the focus.\n\t\teditor.setCurrentTool('select')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\t// Abandon the follow composer if placement is interrupted (Escape, focus loss, etc.).\n\tprivate cancel() {\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentDragging extends StateNode {\n\tstatic override id = 'dragging'\n\n\toverride onEnter() {\n\t\t// A region drag supersedes the point-follow composer opened in `pointing`. Show a crosshair\n\t\t// again \u2014 the composer no longer stands in for the pointer, so a hidden cursor would leave\n\t\t// the drag with no pointer at all. Drop the placement hint too: a region anchors to its\n\t\t// rectangle, so a single-shape outline under the dashed box would be stale.\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t\tthis.updateDraft()\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateDraft()\n\t}\n\n\t// Commit the dragged rectangle as a region anchor; the composer opens at its pin corner.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst origin = editor.inputs.getOriginPagePoint()\n\t\tconst current = editor.inputs.getCurrentPagePoint()\n\t\tconst region = regionBetween(origin, current)\n\t\t// The pin lives on the corner the drag released on \u2014 drag up-left, pin top-left.\n\t\tconst pin = { x: current.x >= origin.x ? 1 : 0, y: current.y >= origin.y ? 1 : 0 }\n\t\tregionDraft.set(editor, null)\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'region', ...region, pinX: pin.x, pinY: pin.y },\n\t\t\tpoint: regionPinPoint(region, pin),\n\t\t})\n\t\teditor.setCurrentTool('select')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate updateDraft() {\n\t\tconst { editor } = this\n\t\tregionDraft.set(\n\t\t\teditor,\n\t\t\tregionBetween(editor.inputs.getOriginPagePoint(), editor.inputs.getCurrentPagePoint())\n\t\t)\n\t}\n\n\tprivate cancel() {\n\t\tregionDraft.set(this.editor, null)\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\n/** @public */\nexport const commentTools = [CommentTool]\n\n/** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.\n * Once registered, tldraw's `DefaultToolbarContent` shows the comment button next to the eraser.\n * @public */\nexport const commentToolOverrides: TLUiOverrides = {\n\ttools(editor, tools) {\n\t\ttools.comment = {\n\t\t\tid: 'comment',\n\t\t\ticon: 'comment',\n\t\t\tlabel: 'Comment',\n\t\t\tkbd: 'c',\n\t\t\tonSelect: () => editor.setCurrentTool('comment'),\n\t\t}\n\t\treturn tools\n\t},\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tBoxModel,\n\tEditor,\n\tStateNode,\n\tTLCommentAnchor,\n\tTLStateNodeConstructor,\n\tTLUiOverrides,\n\tVecLike,\n} from 'tldraw'\nimport { type CommentingOptions, defaultCommentingOptions, getCommentingOptions } from './options'\nimport { commentsSidebarOpen, pendingComment, regionDraft } from './state'\nimport { commentTargetShapeAt, regionPinPoint, shapeAnchorAt } from './thread-state'\n\n/** A comment being placed but not yet posted: where its composer sits and what it will anchor\n * to. Shared between the tool (which sets it on click) and the overlay (which renders the\n * composer) through the internal `pendingComment` atom.\n * @internal */\nexport interface PendingComment {\n\tanchor: TLCommentAnchor\n\t/** Page point where the composer opens (the click location, or a region's pin corner). */\n\tpoint: VecLike\n}\n\n/**\n * Merge configure options over a base. Shallow for scalars; `components` is merged rather than\n * replaced so chained `configure` calls layer their slots \u2014 a later `{ components: { PinContent } }`\n * doesn't drop an earlier `{ components: { CommentBody } }`.\n */\nfunction mergeCommentingOptions(\n\tbase: CommentingOptions,\n\toverrides: Partial<CommentingOptions>\n): CommentingOptions {\n\treturn {\n\t\t...base,\n\t\t...overrides,\n\t\tcomponents: { ...base.components, ...overrides.components },\n\t}\n}\n\n/** The page-space rectangle spanned by two points, normalized so w/h are non-negative. Shared by\n * region creation (pointer-down \u2192 cursor) and corner resize (fixed corner \u2192 cursor). */\nexport function regionBetween(a: VecLike, b: VecLike): BoxModel {\n\treturn {\n\t\tx: Math.min(a.x, b.x),\n\t\ty: Math.min(a.y, b.y),\n\t\tw: Math.abs(a.x - b.x),\n\t\th: Math.abs(a.y - b.y),\n\t}\n}\n\n/**\n * The comment tool. Pressing down opens the comment composer at the pointer and it follows until\n * release \u2014 like placing a sticky note \u2014 settling on a point, or on a shape when released over one.\n * With region comments enabled, dragging past the threshold draws a region rectangle instead.\n * Placement only opens a composer; the records are created when the comment is posted.\n * @public\n */\nexport class CommentTool extends StateNode {\n\tstatic override id = 'comment'\n\tstatic override initial = 'idle'\n\tstatic override children(): TLStateNodeConstructor[] {\n\t\treturn [CommentIdle, CommentPointing, CommentDragging]\n\t}\n\n\t/**\n\t * Configure this tool's {@link CommentTool.options | `options`}, returning a configured subclass\n\t * to register via `tools`. Mirrors `ShapeUtil.configure`. Layers over any prior `configure`, so\n\t * calls can be chained.\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />\n\t * ```\n\t */\n\tstatic configure<T extends TLStateNodeConstructor>(\n\t\tthis: T,\n\t\toptions: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never\n\t): T {\n\t\t// @ts-expect-error -- mirrors ShapeUtil.configure; extending `this` is sound at runtime.\n\t\treturn class extends this {\n\t\t\t// @ts-expect-error\n\t\t\toptions = mergeCommentingOptions(this.options, options)\n\t\t}\n\t}\n\n\t/**\n\t * The merged commenting options for this editor. Read from anywhere via\n\t * {@link getCommentingOptions}. Override with {@link CommentTool.configure}.\n\t */\n\toptions: CommentingOptions = defaultCommentingOptions\n\n\toverride onEnter() {\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\t// Placing comments is canvas-focused \u2014 the thread list gets out of the way while the tool is\n\t\t// active. Reopened via its own control (a button), never by leaving the tool.\n\t\tcommentsSidebarOpen.set(this.editor, false)\n\t}\n\n\toverride onExit() {\n\t\t// Drop the hover hint painted while pointing at shapes (see CommentIdle). The cursor resets\n\t\t// when the next tool takes over.\n\t\tthis.editor.setHintingShapes([])\n\t}\n\n\t// Escape leaves the tool, like the built-in tools (the editor dispatches `cancel` on Escape).\n\toverride onCancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\n/** Hint the shape a comment placed at the pointer would anchor to, using the same hit-test as the\n * anchor resolution on release. Hinting shapes render an indicator ungated by the active tool, so\n * this shows the select-style outline while the comment tool \u2014 not select \u2014 is active. */\nfunction updateAnchorHint(editor: Editor) {\n\tconst hit = commentTargetShapeAt(editor, editor.inputs.getCurrentPagePoint())\n\teditor.setHintingShapes(hit ? [hit.id] : [])\n}\n\nclass CommentIdle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onEnter() {\n\t\t// Back to hovering: restore the pin cursor (a prior placing state may have hidden it).\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\tupdateAnchorHint(this.editor)\n\t}\n\n\toverride onPointerMove() {\n\t\tupdateAnchorHint(this.editor)\n\t}\n\n\toverride onPointerDown() {\n\t\tthis.parent.transition('pointing')\n\t}\n}\n\nclass CommentPointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\toverride onEnter() {\n\t\t// Open the composer immediately at the press point so it's visible from pointer-down (not just\n\t\t// on release), and let it trail the pointer while dragging \u2014 like placing a sticky note. The\n\t\t// anchor is a bare point for now; it's resolved (shape or point) on pointer up.\n\t\tconst { editor } = this\n\t\t// Hide the cursor while placing: the draft composer is the pointer's stand-in now, so the pin\n\t\t// cursor sitting over it just reads as clutter.\n\t\teditor.setCursor({ type: 'none', rotation: 0 })\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'point', x: point.x, y: point.y },\n\t\t\tpoint: { x: point.x, y: point.y },\n\t\t})\n\t}\n\n\toverride onPointerMove() {\n\t\tconst { editor } = this\n\t\t// Once the pointer passes the drag threshold with region comments enabled, this is a region,\n\t\t// not a follow \u2014 hand off to the region drag (which clears this composer and draws the box).\n\t\tif (getCommentingOptions(editor).enableRegions && editor.inputs.getIsDragging()) {\n\t\t\tthis.parent.transition('dragging')\n\t\t\treturn\n\t\t}\n\t\t// Otherwise the composer trails the pointer \u2014 keep hinting the shape a release here would\n\t\t// anchor to, like the idle hover does.\n\t\tupdateAnchorHint(editor)\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.update(editor, (p) => (p ? { ...p, point: { x: point.x, y: point.y } } : p))\n\t}\n\n\t// Settle where the pointer is released: anchor to the shape under it, or drop a point.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tconst hit = commentTargetShapeAt(editor, point)\n\t\tconst anchor: TLCommentAnchor = hit\n\t\t\t? shapeAnchorAt(\n\t\t\t\t\teditor,\n\t\t\t\t\thit.id,\n\t\t\t\t\tpoint,\n\t\t\t\t\tgetCommentingOptions(editor).shouldBePrecise(editor, {\n\t\t\t\t\t\tshapeId: hit.id,\n\t\t\t\t\t\tpoint,\n\t\t\t\t\t\taltKey: editor.inputs.getAltKey(),\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t: { type: 'point', x: point.x, y: point.y }\n\t\tpendingComment.set(editor, { anchor, point: { x: point.x, y: point.y } })\n\t\teditor.setCurrentTool('select')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\t// Abandon the follow composer if placement is interrupted (Escape, focus loss, etc.).\n\tprivate cancel() {\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentDragging extends StateNode {\n\tstatic override id = 'dragging'\n\n\toverride onEnter() {\n\t\t// A region drag supersedes the point-follow composer opened in `pointing`. Show a crosshair again,\n\t\t// since the composer no longer stands in for the pointer, and drop the placement hint \u2014 a region\n\t\t// anchors to its rectangle, so a single-shape outline would be stale.\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t\tthis.updateDraft()\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateDraft()\n\t}\n\n\t// Commit the dragged rectangle as a region anchor; the composer opens at its pin corner.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst origin = editor.inputs.getOriginPagePoint()\n\t\tconst current = editor.inputs.getCurrentPagePoint()\n\t\tconst region = regionBetween(origin, current)\n\t\t// The pin lives on the corner the drag released on \u2014 drag up-left, pin top-left.\n\t\tconst pin = { x: current.x >= origin.x ? 1 : 0, y: current.y >= origin.y ? 1 : 0 }\n\t\tregionDraft.set(editor, null)\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'region', ...region, pinX: pin.x, pinY: pin.y },\n\t\t\tpoint: regionPinPoint(region, pin),\n\t\t})\n\t\teditor.setCurrentTool('select')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate updateDraft() {\n\t\tconst { editor } = this\n\t\tregionDraft.set(\n\t\t\teditor,\n\t\t\tregionBetween(editor.inputs.getOriginPagePoint(), editor.inputs.getCurrentPagePoint())\n\t\t)\n\t}\n\n\tprivate cancel() {\n\t\tregionDraft.set(this.editor, null)\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\n/** @public */\nexport const commentTools = [CommentTool]\n\n/** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.\n * Once registered, tldraw's `DefaultQuickActionsContent` shows the comment button.\n * @public */\nexport const commentToolOverrides: TLUiOverrides = {\n\ttools(editor, tools) {\n\t\ttools.comment = {\n\t\t\tid: 'comment',\n\t\t\ticon: 'comment',\n\t\t\tlabel: 'Comment',\n\t\t\tkbd: 'c',\n\t\t\tonSelect: () => editor.setCurrentTool('comment'),\n\t\t}\n\t\treturn tools\n\t},\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -23,7 +23,7 @@ __export(comments_filter_menu_exports, {
23
23
  module.exports = __toCommonJS(comments_filter_menu_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
25
  var import_tldraw = require("tldraw");
26
- var import_tooltip_button = require("../ui/tooltip-button");
26
+ var import_icons = require("../ui/icons");
27
27
  var import_state = require("./state");
28
28
  function CommentsFilterMenu({
29
29
  canFilterByAuthor,
@@ -36,7 +36,16 @@ function CommentsFilterMenu({
36
36
  import_state.sidebarFilters.update(editor, (f) => ({ ...f, [key]: !f[key] }));
37
37
  };
38
38
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_tldraw.TldrawUiDropdownMenuRoot, { id: "comments-filter", children: [
39
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tooltip_button.TooltipButton, { tooltip: msg("comments.filter"), className: "tlui-cmt-header-btn", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilterIcon, {}) }) }),
39
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
40
+ import_tldraw.TldrawUiButton,
41
+ {
42
+ type: "icon",
43
+ tooltip: msg("comments.filter"),
44
+ title: msg("comments.filter"),
45
+ className: "tlui-cmt-header-btn",
46
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.FilterIcon, {})
47
+ }
48
+ ) }),
40
49
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuContent, { side: "bottom", align: "end", alignOffset: 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiMenuContextProvider, { type: "menu", sourceId: "menu", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_tldraw.TldrawUiMenuGroup, { id: "comments-filter", children: [
41
50
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
42
51
  import_tldraw.TldrawUiMenuCheckboxItem,
@@ -77,15 +86,4 @@ function CommentsFilterMenu({
77
86
  ] }) }) })
78
87
  ] });
79
88
  }
80
- function FilterIcon() {
81
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
82
- "path",
83
- {
84
- d: "M5.5 3C4.67157 3 4 3.67157 4 4.5C4 5.32843 4.67157 6 5.5 6C6.32843 6 7 5.32843 7 4.5C7 3.67157 6.32843 3 5.5 3ZM3 5C3.01671 5 3.03323 4.99918 3.04952 4.99758C3.28022 6.1399 4.28967 7 5.5 7C6.71033 7 7.71978 6.1399 7.95048 4.99758C7.96677 4.99918 7.98329 5 8 5H13.5C13.7761 5 14 4.77614 14 4.5C14 4.22386 13.7761 4 13.5 4H8C7.98329 4 7.96677 4.00082 7.95048 4.00242C7.71978 2.86009 6.71033 2 5.5 2C4.28967 2 3.28022 2.86009 3.04952 4.00242C3.03323 4.00082 3.01671 4 3 4H1.5C1.22386 4 1 4.22386 1 4.5C1 4.77614 1.22386 5 1.5 5H3ZM11.9505 10.9976C11.7198 12.1399 10.7103 13 9.5 13C8.28967 13 7.28022 12.1399 7.04952 10.9976C7.03323 10.9992 7.01671 11 7 11H1.5C1.22386 11 1 10.7761 1 10.5C1 10.2239 1.22386 10 1.5 10H7C7.01671 10 7.03323 10.0008 7.04952 10.0024C7.28022 8.8601 8.28967 8 9.5 8C10.7103 8 11.7198 8.8601 11.9505 10.0024C11.9668 10.0008 11.9833 10 12 10H13.5C13.7761 10 14 10.2239 14 10.5C14 10.7761 13.7761 11 13.5 11H12C11.9833 11 11.9668 10.9992 11.9505 10.9976ZM8 10.5C8 9.67157 8.67157 9 9.5 9C10.3284 9 11 9.67157 11 10.5C11 11.3284 10.3284 12 9.5 12C8.67157 12 8 11.3284 8 10.5Z",
85
- fill: "currentColor",
86
- fillRule: "evenodd",
87
- clipRule: "evenodd"
88
- }
89
- ) });
90
- }
91
89
  //# sourceMappingURL=comments-filter-menu.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/canvas/comments-filter-menu.tsx"],
4
- "sourcesContent": ["import {\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tTldrawUiMenuCheckboxItem,\n\tTldrawUiMenuContextProvider,\n\tTldrawUiMenuGroup,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { TooltipButton } from '../ui/tooltip-button'\nimport { SidebarFilters } from './sidebar-filters'\nimport { sidebarFilters } from './state'\n\n/** @public */\nexport interface CommentsFilterMenuProps {\n\t/** Whether to offer the \"only your threads\" toggle (needs a known current user). */\n\tcanFilterByAuthor?: boolean\n\t/** Whether to offer the \"only unread\" toggle (needs a read-status source). */\n\tcanFilterByUnread?: boolean\n}\n\n/** The funnel dropdown in the sidebar header: toggles for which threads the list shows.\n * @public @react */\nexport function CommentsFilterMenu({\n\tcanFilterByAuthor,\n\tcanFilterByUnread,\n}: CommentsFilterMenuProps) {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst filters = useValue('sidebar filters', () => sidebarFilters.get(editor), [editor])\n\tconst toggle = (key: keyof SidebarFilters) => {\n\t\tsidebarFilters.update(editor, (f) => ({ ...f, [key]: !f[key] }))\n\t}\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-filter\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<TooltipButton tooltip={msg('comments.filter')} className=\"tlui-cmt-header-btn\">\n\t\t\t\t\t<FilterIcon />\n\t\t\t\t</TooltipButton>\n\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t{/* No tlui-cmt-menu here: the rows are tldraw menu items, which carry their own insets \u2014\n\t\t\t the extra padding would set this menu apart from the canvas menus it should match. */}\n\t\t\t<TldrawUiDropdownMenuContent side=\"bottom\" align=\"end\" alignOffset={0}>\n\t\t\t\t<TldrawUiMenuContextProvider type=\"menu\" sourceId=\"menu\">\n\t\t\t\t\t<TldrawUiMenuGroup id=\"comments-filter\">\n\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\tid=\"show-all-pages\"\n\t\t\t\t\t\t\tlabel=\"comments.show-all-pages\"\n\t\t\t\t\t\t\tchecked={!filters.onlyCurrentPage}\n\t\t\t\t\t\t\tonSelect={() => toggle('onlyCurrentPage')}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{canFilterByAuthor && (\n\t\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\t\tid=\"only-my-comments\"\n\t\t\t\t\t\t\t\tlabel=\"comments.only-my-comments\"\n\t\t\t\t\t\t\t\tchecked={filters.onlyMine}\n\t\t\t\t\t\t\t\tonSelect={() => toggle('onlyMine')}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{canFilterByUnread && (\n\t\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\t\tid=\"only-unread\"\n\t\t\t\t\t\t\t\tlabel=\"comments.only-unread\"\n\t\t\t\t\t\t\t\tchecked={filters.onlyUnread}\n\t\t\t\t\t\t\t\tonSelect={() => toggle('onlyUnread')}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\tid=\"show-resolved\"\n\t\t\t\t\t\t\tlabel=\"comments.show-resolved\"\n\t\t\t\t\t\t\tchecked={filters.showResolved}\n\t\t\t\t\t\t\tonSelect={() => toggle('showResolved')}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t\t</TldrawUiMenuContextProvider>\n\t\t\t</TldrawUiDropdownMenuContent>\n\t\t</TldrawUiDropdownMenuRoot>\n\t)\n}\n\nfunction FilterIcon() {\n\treturn (\n\t\t<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t<path\n\t\t\t\td=\"M5.5 3C4.67157 3 4 3.67157 4 4.5C4 5.32843 4.67157 6 5.5 6C6.32843 6 7 5.32843 7 4.5C7 3.67157 6.32843 3 5.5 3ZM3 5C3.01671 5 3.03323 4.99918 3.04952 4.99758C3.28022 6.1399 4.28967 7 5.5 7C6.71033 7 7.71978 6.1399 7.95048 4.99758C7.96677 4.99918 7.98329 5 8 5H13.5C13.7761 5 14 4.77614 14 4.5C14 4.22386 13.7761 4 13.5 4H8C7.98329 4 7.96677 4.00082 7.95048 4.00242C7.71978 2.86009 6.71033 2 5.5 2C4.28967 2 3.28022 2.86009 3.04952 4.00242C3.03323 4.00082 3.01671 4 3 4H1.5C1.22386 4 1 4.22386 1 4.5C1 4.77614 1.22386 5 1.5 5H3ZM11.9505 10.9976C11.7198 12.1399 10.7103 13 9.5 13C8.28967 13 7.28022 12.1399 7.04952 10.9976C7.03323 10.9992 7.01671 11 7 11H1.5C1.22386 11 1 10.7761 1 10.5C1 10.2239 1.22386 10 1.5 10H7C7.01671 10 7.03323 10.0008 7.04952 10.0024C7.28022 8.8601 8.28967 8 9.5 8C10.7103 8 11.7198 8.8601 11.9505 10.0024C11.9668 10.0008 11.9833 10 12 10H13.5C13.7761 10 14 10.2239 14 10.5C14 10.7761 13.7761 11 13.5 11H12C11.9833 11 11.9668 10.9992 11.9505 10.9976ZM8 10.5C8 9.67157 8.67157 9 9.5 9C10.3284 9 11 9.67157 11 10.5C11 11.3284 10.3284 12 9.5 12C8.67157 12 8 11.3284 8 10.5Z\"\n\t\t\t\tfill=\"currentColor\"\n\t\t\t\tfillRule=\"evenodd\"\n\t\t\t\tclipRule=\"evenodd\"\n\t\t\t/>\n\t\t</svg>\n\t)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwCK;AAxCL,oBAUO;AACP,4BAA8B;AAE9B,mBAA+B;AAYxB,SAAS,mBAAmB;AAAA,EAClC;AAAA,EACA;AACD,GAA4B;AAC3B,QAAM,aAAS,yBAAU;AACzB,QAAM,UAAM,8BAAe;AAC3B,QAAM,cAAU,wBAAS,mBAAmB,MAAM,4BAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AACtF,QAAM,SAAS,CAAC,QAA8B;AAC7C,gCAAe,OAAO,QAAQ,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE;AAAA,EAChE;AAEA,SACC,6CAAC,0CAAyB,IAAG,mBAC5B;AAAA,gDAAC,6CACA,sDAAC,uCAAc,SAAS,IAAI,iBAAiB,GAAG,WAAU,uBACzD,sDAAC,cAAW,GACb,GACD;AAAA,IAGA,4CAAC,6CAA4B,MAAK,UAAS,OAAM,OAAM,aAAa,GACnE,sDAAC,6CAA4B,MAAK,QAAO,UAAS,QACjD,uDAAC,mCAAkB,IAAG,mBACrB;AAAA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,CAAC,QAAQ;AAAA,UAClB,UAAU,MAAM,OAAO,iBAAiB;AAAA;AAAA,MACzC;AAAA,MACC,qBACA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,UAAU;AAAA;AAAA,MAClC;AAAA,MAEA,qBACA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,YAAY;AAAA;AAAA,MACpC;AAAA,MAED;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,cAAc;AAAA;AAAA,MACtC;AAAA,OACD,GACD,GACD;AAAA,KACD;AAEF;AAEA,SAAS,aAAa;AACrB,SACC,4CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,eAAY,QACvE;AAAA,IAAC;AAAA;AAAA,MACA,GAAE;AAAA,MACF,MAAK;AAAA,MACL,UAAS;AAAA,MACT,UAAS;AAAA;AAAA,EACV,GACD;AAEF;",
4
+ "sourcesContent": ["import {\n\tTldrawUiButton,\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tTldrawUiMenuCheckboxItem,\n\tTldrawUiMenuContextProvider,\n\tTldrawUiMenuGroup,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { FilterIcon } from '../ui/icons'\nimport { SidebarFilters } from './sidebar-filters'\nimport { sidebarFilters } from './state'\n\n/** @public */\nexport interface CommentsFilterMenuProps {\n\t/** Whether to offer the \"only your threads\" toggle (needs a known current user). */\n\tcanFilterByAuthor?: boolean\n\t/** Whether to offer the \"only unread\" toggle (needs a read-status source). */\n\tcanFilterByUnread?: boolean\n}\n\n/** The funnel dropdown in the sidebar header: toggles for which threads the list shows.\n * @public @react */\nexport function CommentsFilterMenu({\n\tcanFilterByAuthor,\n\tcanFilterByUnread,\n}: CommentsFilterMenuProps) {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst filters = useValue('sidebar filters', () => sidebarFilters.get(editor), [editor])\n\tconst toggle = (key: keyof SidebarFilters) => {\n\t\tsidebarFilters.update(editor, (f) => ({ ...f, [key]: !f[key] }))\n\t}\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-filter\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\ttooltip={msg('comments.filter')}\n\t\t\t\t\ttitle={msg('comments.filter')}\n\t\t\t\t\tclassName=\"tlui-cmt-header-btn\"\n\t\t\t\t>\n\t\t\t\t\t<FilterIcon />\n\t\t\t\t</TldrawUiButton>\n\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t{/* No tlui-cmt-menu here: the rows are tldraw menu items, which carry their own insets \u2014\n\t\t\t the extra padding would set this menu apart from the canvas menus it should match. */}\n\t\t\t<TldrawUiDropdownMenuContent side=\"bottom\" align=\"end\" alignOffset={0}>\n\t\t\t\t<TldrawUiMenuContextProvider type=\"menu\" sourceId=\"menu\">\n\t\t\t\t\t<TldrawUiMenuGroup id=\"comments-filter\">\n\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\tid=\"show-all-pages\"\n\t\t\t\t\t\t\tlabel=\"comments.show-all-pages\"\n\t\t\t\t\t\t\tchecked={!filters.onlyCurrentPage}\n\t\t\t\t\t\t\tonSelect={() => toggle('onlyCurrentPage')}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{canFilterByAuthor && (\n\t\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\t\tid=\"only-my-comments\"\n\t\t\t\t\t\t\t\tlabel=\"comments.only-my-comments\"\n\t\t\t\t\t\t\t\tchecked={filters.onlyMine}\n\t\t\t\t\t\t\t\tonSelect={() => toggle('onlyMine')}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{canFilterByUnread && (\n\t\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\t\tid=\"only-unread\"\n\t\t\t\t\t\t\t\tlabel=\"comments.only-unread\"\n\t\t\t\t\t\t\t\tchecked={filters.onlyUnread}\n\t\t\t\t\t\t\t\tonSelect={() => toggle('onlyUnread')}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\tid=\"show-resolved\"\n\t\t\t\t\t\t\tlabel=\"comments.show-resolved\"\n\t\t\t\t\t\t\tchecked={filters.showResolved}\n\t\t\t\t\t\t\tonSelect={() => toggle('showResolved')}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t\t</TldrawUiMenuContextProvider>\n\t\t\t</TldrawUiDropdownMenuContent>\n\t\t</TldrawUiDropdownMenuRoot>\n\t)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA8CK;AA9CL,oBAWO;AACP,mBAA2B;AAE3B,mBAA+B;AAYxB,SAAS,mBAAmB;AAAA,EAClC;AAAA,EACA;AACD,GAA4B;AAC3B,QAAM,aAAS,yBAAU;AACzB,QAAM,UAAM,8BAAe;AAC3B,QAAM,cAAU,wBAAS,mBAAmB,MAAM,4BAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AACtF,QAAM,SAAS,CAAC,QAA8B;AAC7C,gCAAe,OAAO,QAAQ,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE;AAAA,EAChE;AAEA,SACC,6CAAC,0CAAyB,IAAG,mBAC5B;AAAA,gDAAC,6CACA;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,SAAS,IAAI,iBAAiB;AAAA,QAC9B,OAAO,IAAI,iBAAiB;AAAA,QAC5B,WAAU;AAAA,QAEV,sDAAC,2BAAW;AAAA;AAAA,IACb,GACD;AAAA,IAGA,4CAAC,6CAA4B,MAAK,UAAS,OAAM,OAAM,aAAa,GACnE,sDAAC,6CAA4B,MAAK,QAAO,UAAS,QACjD,uDAAC,mCAAkB,IAAG,mBACrB;AAAA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,CAAC,QAAQ;AAAA,UAClB,UAAU,MAAM,OAAO,iBAAiB;AAAA;AAAA,MACzC;AAAA,MACC,qBACA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,UAAU;AAAA;AAAA,MAClC;AAAA,MAEA,qBACA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,YAAY;AAAA;AAAA,MACpC;AAAA,MAED;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,cAAc;AAAA;AAAA,MACtC;AAAA,OACD,GACD,GACD;AAAA,KACD;AAEF;",
6
6
  "names": []
7
7
  }
@@ -23,7 +23,6 @@ __export(comments_overflow_menu_exports, {
23
23
  module.exports = __toCommonJS(comments_overflow_menu_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
25
  var import_tldraw = require("tldraw");
26
- var import_tooltip_button = require("../ui/tooltip-button");
27
26
  var import_state = require("./state");
28
27
  const HIDE_SHORTCUT = "\u21E7C";
29
28
  function CommentsOverflowMenu() {
@@ -31,7 +30,16 @@ function CommentsOverflowMenu() {
31
30
  const msg = (0, import_tldraw.useTranslation)();
32
31
  const hidden = (0, import_tldraw.useValue)("comments hidden", () => import_state.commentsHidden.get(editor), [editor]);
33
32
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_tldraw.TldrawUiDropdownMenuRoot, { id: "comments-overflow", children: [
34
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tooltip_button.TooltipButton, { tooltip: msg("comments.more-options"), className: "tlui-cmt-header-btn", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiIcon, { icon: "dots-vertical", label: msg("comments.more-options"), small: true }) }) }),
33
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
34
+ import_tldraw.TldrawUiButton,
35
+ {
36
+ type: "icon",
37
+ tooltip: msg("comments.more-options"),
38
+ title: msg("comments.more-options"),
39
+ className: "tlui-cmt-header-btn",
40
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiIcon, { icon: "dots-vertical", label: msg("comments.more-options"), small: true })
41
+ }
42
+ ) }),
35
43
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
36
44
  import_tldraw.TldrawUiDropdownMenuContent,
37
45
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/canvas/comments-overflow-menu.tsx"],
4
- "sourcesContent": ["import {\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuGroup,\n\tTldrawUiDropdownMenuItem,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tTldrawUiIcon,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { TooltipButton } from '../ui/tooltip-button'\nimport { commentsHidden, toggleCommentsHidden } from './state'\n\n// A keyboard-shortcut glyph, not translatable copy \u2014 kept out of JSX as a constant.\nconst HIDE_SHORTCUT = '\u21E7C'\n\n/** The overflow (\u22EF) dropdown in the sidebar header. For now it holds the hide/show-comments\n * toggle; it's the home for later comment-wide controls (notifications, mark all as read).\n * @public @react */\nexport function CommentsOverflowMenu() {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-overflow\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<TooltipButton tooltip={msg('comments.more-options')} className=\"tlui-cmt-header-btn\">\n\t\t\t\t\t<TldrawUiIcon icon=\"dots-vertical\" label={msg('comments.more-options')} small />\n\t\t\t\t</TooltipButton>\n\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t<TldrawUiDropdownMenuContent\n\t\t\t\tclassName=\"tlui-cmt-menu\"\n\t\t\t\tside=\"bottom\"\n\t\t\t\talign=\"end\"\n\t\t\t\talignOffset={0}\n\t\t\t\tsideOffset={4}\n\t\t\t>\n\t\t\t\t<TldrawUiDropdownMenuGroup>\n\t\t\t\t\t<TldrawUiDropdownMenuItem>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"tlui-cmt-menu-item\"\n\t\t\t\t\t\t\tonClick={() => toggleCommentsHidden(editor)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span>{hidden ? msg('comments.show') : msg('comments.hide')}</span>\n\t\t\t\t\t\t\t<span className=\"tlui-cmt-menu-item__shortcut\">{HIDE_SHORTCUT}</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</TldrawUiDropdownMenuItem>\n\t\t\t\t</TldrawUiDropdownMenuGroup>\n\t\t\t</TldrawUiDropdownMenuContent>\n\t\t</TldrawUiDropdownMenuRoot>\n\t)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA6BK;AA7BL,oBAUO;AACP,4BAA8B;AAC9B,mBAAqD;AAGrD,MAAM,gBAAgB;AAKf,SAAS,uBAAuB;AACtC,QAAM,aAAS,yBAAU;AACzB,QAAM,UAAM,8BAAe;AAC3B,QAAM,aAAS,wBAAS,mBAAmB,MAAM,4BAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AAErF,SACC,6CAAC,0CAAyB,IAAG,qBAC5B;AAAA,gDAAC,6CACA,sDAAC,uCAAc,SAAS,IAAI,uBAAuB,GAAG,WAAU,uBAC/D,sDAAC,8BAAa,MAAK,iBAAgB,OAAO,IAAI,uBAAuB,GAAG,OAAK,MAAC,GAC/E,GACD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,MAAK;AAAA,QACL,OAAM;AAAA,QACN,aAAa;AAAA,QACb,YAAY;AAAA,QAEZ,sDAAC,2CACA,sDAAC,0CACA;AAAA,UAAC;AAAA;AAAA,YACA,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,UAAM,mCAAqB,MAAM;AAAA,YAE1C;AAAA,0DAAC,UAAM,mBAAS,IAAI,eAAe,IAAI,IAAI,eAAe,GAAE;AAAA,cAC5D,4CAAC,UAAK,WAAU,gCAAgC,yBAAc;AAAA;AAAA;AAAA,QAC/D,GACD,GACD;AAAA;AAAA,IACD;AAAA,KACD;AAEF;",
4
+ "sourcesContent": ["import {\n\tTldrawUiButton,\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuGroup,\n\tTldrawUiDropdownMenuItem,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tTldrawUiIcon,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { commentsHidden, toggleCommentsHidden } from './state'\n\n// A keyboard-shortcut glyph, not translatable copy \u2014 kept out of JSX as a constant.\nconst HIDE_SHORTCUT = '\u21E7C'\n\n/** The overflow (\u22EF) dropdown in the sidebar header. For now it holds the hide/show-comments\n * toggle; it's the home for later comment-wide controls (notifications, mark all as read).\n * @public @react */\nexport function CommentsOverflowMenu() {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-overflow\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\ttooltip={msg('comments.more-options')}\n\t\t\t\t\ttitle={msg('comments.more-options')}\n\t\t\t\t\tclassName=\"tlui-cmt-header-btn\"\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiIcon icon=\"dots-vertical\" label={msg('comments.more-options')} small />\n\t\t\t\t</TldrawUiButton>\n\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t<TldrawUiDropdownMenuContent\n\t\t\t\tclassName=\"tlui-cmt-menu\"\n\t\t\t\tside=\"bottom\"\n\t\t\t\talign=\"end\"\n\t\t\t\talignOffset={0}\n\t\t\t\tsideOffset={4}\n\t\t\t>\n\t\t\t\t<TldrawUiDropdownMenuGroup>\n\t\t\t\t\t<TldrawUiDropdownMenuItem>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"tlui-cmt-menu-item\"\n\t\t\t\t\t\t\tonClick={() => toggleCommentsHidden(editor)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span>{hidden ? msg('comments.show') : msg('comments.hide')}</span>\n\t\t\t\t\t\t\t<span className=\"tlui-cmt-menu-item__shortcut\">{HIDE_SHORTCUT}</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</TldrawUiDropdownMenuItem>\n\t\t\t\t</TldrawUiDropdownMenuGroup>\n\t\t\t</TldrawUiDropdownMenuContent>\n\t\t</TldrawUiDropdownMenuRoot>\n\t)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAkCK;AAlCL,oBAWO;AACP,mBAAqD;AAGrD,MAAM,gBAAgB;AAKf,SAAS,uBAAuB;AACtC,QAAM,aAAS,yBAAU;AACzB,QAAM,UAAM,8BAAe;AAC3B,QAAM,aAAS,wBAAS,mBAAmB,MAAM,4BAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AAErF,SACC,6CAAC,0CAAyB,IAAG,qBAC5B;AAAA,gDAAC,6CACA;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,SAAS,IAAI,uBAAuB;AAAA,QACpC,OAAO,IAAI,uBAAuB;AAAA,QAClC,WAAU;AAAA,QAEV,sDAAC,8BAAa,MAAK,iBAAgB,OAAO,IAAI,uBAAuB,GAAG,OAAK,MAAC;AAAA;AAAA,IAC/E,GACD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,MAAK;AAAA,QACL,OAAM;AAAA,QACN,aAAa;AAAA,QACb,YAAY;AAAA,QAEZ,sDAAC,2CACA,sDAAC,0CACA;AAAA,UAAC;AAAA;AAAA,YACA,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,UAAM,mCAAqB,MAAM;AAAA,YAE1C;AAAA,0DAAC,UAAM,mBAAS,IAAI,eAAe,IAAI,IAAI,eAAe,GAAE;AAAA,cAC5D,4CAAC,UAAK,WAAU,gCAAgC,yBAAc;AAAA;AAAA;AAAA,QAC/D,GACD,GACD;AAAA;AAAA,IACD;AAAA,KACD;AAEF;",
6
6
  "names": []
7
7
  }