@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
@@ -13,11 +13,12 @@ import { CountBadge } from "./ui/count-badge.mjs";
13
13
  import { CommentPin } from "./ui/comment-pin.mjs";
14
14
  import { CommentThread } from "./ui/comment-thread.mjs";
15
15
  import {
16
+ CommentListItem,
16
17
  CommentsList,
17
18
  isOpenInNewTabClick
18
19
  } from "./ui/comments-list.mjs";
19
20
  import { EmptyState } from "./ui/empty-state.mjs";
20
- import { formatRelativeTime } from "./ui/format-time.mjs";
21
+ import { formatFullDateTime, formatRelativeTime } from "./ui/format-time.mjs";
21
22
  import {
22
23
  DEFAULT_REACTION_EMOJI,
23
24
  EmojiPicker,
@@ -65,15 +66,21 @@ import { CommentsFilterMenu } from "./canvas/comments-filter-menu.mjs";
65
66
  import { CommentsMenuItem } from "./canvas/comments-menu-item.mjs";
66
67
  import { CanvasComments } from "./canvas/comments-overlay.mjs";
67
68
  import {
69
+ defaultCanModifyComment,
68
70
  defaultCommentingOptions,
69
71
  getCanComment,
72
+ getCanModifyComment,
70
73
  getCommentingOptions,
71
74
  useCanComment,
75
+ useCanModifyComment,
72
76
  useCommentingOptions
73
77
  } from "./canvas/options.mjs";
74
78
  import { CommentsOverflowMenu } from "./canvas/comments-overflow-menu.mjs";
75
79
  import { CommentsVisibilityToggle } from "./canvas/comments-visibility-toggle.mjs";
76
- import { CanvasCommentsSidebar } from "./canvas/comments-sidebar.mjs";
80
+ import {
81
+ CanvasCommentsSidebar,
82
+ sortSidebarRows
83
+ } from "./canvas/comments-sidebar.mjs";
77
84
  import { useComments, useCommentThreads, useThreadComments } from "./canvas/hooks.mjs";
78
85
  import { useCommentingEnabled } from "./canvas/license.mjs";
79
86
  import { richTextToPlaintext } from "./canvas/rich-text.mjs";
@@ -96,7 +103,7 @@ import {
96
103
  import { anchorPagePoint, focusThread, shapeAnchorAt } from "./canvas/thread-state.mjs";
97
104
  registerTldrawLibraryVersion(
98
105
  "@tldraw/commenting",
99
- "5.3.0-next.2fa9c61a8de6",
106
+ "5.3.0-next.7654e7ac2a02",
100
107
  "esm"
101
108
  );
102
109
  export {
@@ -107,6 +114,7 @@ export {
107
114
  CommentBody,
108
115
  CommentCard,
109
116
  CommentComposer,
117
+ CommentListItem,
110
118
  CommentPin,
111
119
  CommentReactionPicker,
112
120
  CommentReactions,
@@ -136,6 +144,7 @@ export {
136
144
  commentsHidden,
137
145
  commentsSidebarOpen,
138
146
  createMentionSuggestion,
147
+ defaultCanModifyComment,
139
148
  defaultCommentingOptions,
140
149
  defaultRenderReaction,
141
150
  deleteComment,
@@ -143,8 +152,10 @@ export {
143
152
  editComment,
144
153
  filterMentionMembers,
145
154
  focusThread,
155
+ formatFullDateTime,
146
156
  formatRelativeTime,
147
157
  getCanComment,
158
+ getCanModifyComment,
148
159
  getCommentReactions,
149
160
  getCommentRecord,
150
161
  getCommentThreads,
@@ -165,11 +176,13 @@ export {
165
176
  richTextToPlaintext,
166
177
  shapeAnchorAt,
167
178
  sidebarFilters,
179
+ sortSidebarRows,
168
180
  summarizeReactions,
169
181
  toggleCommentReaction,
170
182
  toggleCommentsHidden,
171
183
  toggleCommentsSidebar,
172
184
  useCanComment,
185
+ useCanModifyComment,
173
186
  useCommentReactions,
174
187
  useCommentThreads,
175
188
  useCommentingEnabled,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["import { registerTldrawLibraryVersion } from '@tldraw/utils'\n\n// Presentational commenting components. These are tldraw-independent and can be used to build\n// custom commenting UI. The mention picker, pill, and avatar are re-exported from @tldraw/mentions\n// so the commenting public API is unchanged.\nexport {\n\tAvatar,\n\ttype AvatarProps,\n\ttype CommentAuthor,\n\tcreateMentionSuggestion,\n\tfilterMentionMembers,\n\tMention,\n\tMentionList,\n\ttype MentionListProps,\n\ttype MentionMember,\n\ttype MentionProps,\n\ttype MentionSuggestionOptions,\n} from '@tldraw/mentions'\nexport { Byline, type BylineProps } from './ui/byline'\nexport { CommentCard, type CommentCardProps } from './ui/comment-card'\nexport { CommentComposer, type CommentComposerProps } from './ui/comment-composer'\nexport { CountBadge, type CountBadgeProps } from './ui/count-badge'\nexport { CommentPin, type CommentPinProps } from './ui/comment-pin'\nexport { CommentThread, type CommentThreadProps } from './ui/comment-thread'\nexport {\n\tCommentsList,\n\tisOpenInNewTabClick,\n\ttype CommentListItemProps,\n\ttype CommentsListProps,\n} from './ui/comments-list'\nexport { EmptyState, type EmptyStateProps } from './ui/empty-state'\nexport { formatRelativeTime } from './ui/format-time'\nexport {\n\tDEFAULT_REACTION_EMOJI,\n\tEmojiPicker,\n\ttype EmojiPickerProps,\n\tisAllowedReactionEmoji,\n} from './ui/emoji-picker'\nexport {\n\tReaction,\n\ttype ReactionProps,\n\ttype RenderReaction,\n\tdefaultRenderReaction,\n\tDefaultReactionTooltip,\n\tDefaultReactionTooltipContent,\n\ttype ReactionTooltipProps,\n} from './ui/reaction'\nexport { ReactionPicker, type ReactionPickerProps } from './ui/reaction-picker'\nexport {\n\tReactions,\n\ttype ReactionReactor,\n\ttype ReactionsProps,\n\ttype ReactionSummary,\n} from './ui/reactions'\nexport { SendButton, type SendButtonProps } from './ui/send-button'\n\n// The tldraw-coupled commenting layer: the comment tool, reactive hooks over the comment\n// records, a rich-text body renderer, and a batteries-included <CanvasComments> overlay. Pairs\n// with the presentational components above.\nexport { registerCommentAnchorLifecycle } from './canvas/anchor-lifecycle'\nexport { CommentBody, type CommentBodyProps } from './canvas/comment-body'\nexport {\n\tdeleteComment,\n\tdeleteThread,\n\teditComment,\n\tputCommentRecords,\n\tremoveCommentRecords,\n\treopenThread,\n\tresolveThread,\n} from './canvas/comment-mutations'\nexport {\n\tCommentReactionPicker,\n\ttype CommentReactionPickerProps,\n\tCommentReactions,\n\ttype CommentReactionsProps,\n\tsummarizeReactions,\n\ttoggleCommentReaction,\n\tuseCommentReactions,\n} from './canvas/comment-reactions'\nexport { CommentTool, commentToolOverrides, commentTools } from './canvas/comment-tool'\nexport {\n\tgetCommentReactions,\n\tgetCommentRecord,\n\tgetComments,\n\tgetCommentThreads,\n\tgetLiveComments,\n\tgetLiveCommentThreads,\n\ttype TLCommentRecord,\n} from './canvas/comment-store'\nexport { type CommentingContext } from './canvas/context'\nexport { CommentsFilterMenu, type CommentsFilterMenuProps } from './canvas/comments-filter-menu'\nexport { CommentsMenuItem } from './canvas/comments-menu-item'\nexport { CanvasComments, type CanvasCommentsProps } from './canvas/comments-overlay'\nexport {\n\ttype CommentingComponents,\n\ttype CommentingOptions,\n\tdefaultCommentingOptions,\n\tgetCanComment,\n\tgetCommentingOptions,\n\ttype ShapeCommentPrecisionContext,\n\tuseCanComment,\n\tuseCommentingOptions,\n} from './canvas/options'\nexport { CommentsOverflowMenu } from './canvas/comments-overflow-menu'\nexport { CommentsVisibilityToggle } from './canvas/comments-visibility-toggle'\nexport { CanvasCommentsSidebar, type CanvasCommentsSidebarProps } from './canvas/comments-sidebar'\nexport { useComments, useCommentThreads, useThreadComments } from './canvas/hooks'\nexport { useCommentingEnabled } from './canvas/license'\nexport { richTextToPlaintext } from './canvas/rich-text'\nexport { DEFAULT_SIDEBAR_FILTERS, type SidebarFilters } from './canvas/sidebar-filters'\nexport {\n\tcommentsHidden,\n\tcommentsSidebarOpen,\n\tgetRevealThreadPending,\n\topenThreadId,\n\trevealThread,\n\tsidebarFilters,\n\ttoggleCommentsHidden,\n\ttoggleCommentsSidebar,\n\tuseCommentsHidden,\n\tuseCommentsSidebarOpen,\n\tuseOpenThreadId,\n\tuseRevealThreadPending,\n\tuseSidebarFilters,\n} from './canvas/state'\nexport { anchorPagePoint, focusThread, shapeAnchorAt } from './canvas/thread-state'\n\nregisterTldrawLibraryVersion(\n\t(globalThis as any).TLDRAW_LIBRARY_NAME,\n\t(globalThis as any).TLDRAW_LIBRARY_VERSION,\n\t(globalThis as any).TLDRAW_LIBRARY_MODULES\n)\n"],
5
- "mappings": "AAAA,SAAS,oCAAoC;AAK7C;AAAA,EACC;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAKM;AACP,SAAS,cAAgC;AACzC,SAAS,mBAA0C;AACnD,SAAS,uBAAkD;AAC3D,SAAS,kBAAwC;AACjD,SAAS,kBAAwC;AACjD,SAAS,qBAA8C;AACvD;AAAA,EACC;AAAA,EACA;AAAA,OAGM;AACP,SAAS,kBAAwC;AACjD,SAAS,0BAA0B;AACnC;AAAA,EACC;AAAA,EACA;AAAA,EAEA;AAAA,OACM;AACP;AAAA,EACC;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,OAEM;AACP,SAAS,sBAAgD;AACzD;AAAA,EACC;AAAA,OAIM;AACP,SAAS,kBAAwC;AAKjD,SAAS,sCAAsC;AAC/C,SAAS,mBAA0C;AACnD;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP;AAAA,EACC;AAAA,EAEA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,aAAa,sBAAsB,oBAAoB;AAChE;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEM;AAEP,SAAS,0BAAwD;AACjE,SAAS,wBAAwB;AACjC,SAAS,sBAAgD;AACzD;AAAA,EAGC;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OACM;AACP,SAAS,4BAA4B;AACrC,SAAS,gCAAgC;AACzC,SAAS,6BAA8D;AACvE,SAAS,aAAa,mBAAmB,yBAAyB;AAClE,SAAS,4BAA4B;AACrC,SAAS,2BAA2B;AACpC,SAAS,+BAAoD;AAC7D;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,iBAAiB,aAAa,qBAAqB;AAE5D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AACF;",
4
+ "sourcesContent": ["import { registerTldrawLibraryVersion } from '@tldraw/utils'\n\n// Presentational commenting components. These are tldraw-independent and can be used to build\n// custom commenting UI. The mention picker, pill, and avatar are re-exported from @tldraw/mentions\n// so the commenting public API is unchanged.\nexport {\n\tAvatar,\n\ttype AvatarProps,\n\ttype CommentAuthor,\n\tcreateMentionSuggestion,\n\tfilterMentionMembers,\n\tMention,\n\tMentionList,\n\ttype MentionListProps,\n\ttype MentionMember,\n\ttype MentionProps,\n\ttype MentionSuggestionOptions,\n} from '@tldraw/mentions'\nexport { Byline, type BylineProps } from './ui/byline'\nexport { CommentCard, type CommentCardProps } from './ui/comment-card'\nexport { CommentComposer, type CommentComposerProps } from './ui/comment-composer'\nexport { CountBadge, type CountBadgeProps } from './ui/count-badge'\nexport { CommentPin, type CommentPinProps } from './ui/comment-pin'\nexport { CommentThread, type CommentThreadProps } from './ui/comment-thread'\nexport {\n\tCommentListItem,\n\tCommentsList,\n\tisOpenInNewTabClick,\n\ttype CommentListItemProps,\n\ttype CommentListItemRenderProps,\n\ttype CommentsListProps,\n} from './ui/comments-list'\nexport { EmptyState, type EmptyStateProps } from './ui/empty-state'\nexport { formatFullDateTime, formatRelativeTime } from './ui/format-time'\nexport {\n\tDEFAULT_REACTION_EMOJI,\n\tEmojiPicker,\n\ttype EmojiPickerProps,\n\tisAllowedReactionEmoji,\n} from './ui/emoji-picker'\nexport {\n\tReaction,\n\ttype ReactionProps,\n\ttype RenderReaction,\n\tdefaultRenderReaction,\n\tDefaultReactionTooltip,\n\tDefaultReactionTooltipContent,\n\ttype ReactionTooltipProps,\n} from './ui/reaction'\nexport { ReactionPicker, type ReactionPickerProps } from './ui/reaction-picker'\nexport {\n\tReactions,\n\ttype ReactionReactor,\n\ttype ReactionsProps,\n\ttype ReactionSummary,\n} from './ui/reactions'\nexport { SendButton, type SendButtonProps } from './ui/send-button'\n\n// The tldraw-coupled commenting layer: the comment tool, reactive hooks over the comment\n// records, a rich-text body renderer, and a batteries-included <CanvasComments> overlay. Pairs\n// with the presentational components above.\nexport { registerCommentAnchorLifecycle } from './canvas/anchor-lifecycle'\nexport { CommentBody, type CommentBodyProps } from './canvas/comment-body'\nexport {\n\tdeleteComment,\n\tdeleteThread,\n\teditComment,\n\tputCommentRecords,\n\tremoveCommentRecords,\n\treopenThread,\n\tresolveThread,\n} from './canvas/comment-mutations'\nexport {\n\tCommentReactionPicker,\n\ttype CommentReactionPickerProps,\n\tCommentReactions,\n\ttype CommentReactionsProps,\n\ttype ReactionSummaryInput,\n\tsummarizeReactions,\n\ttoggleCommentReaction,\n\tuseCommentReactions,\n} from './canvas/comment-reactions'\nexport { CommentTool, commentToolOverrides, commentTools } from './canvas/comment-tool'\nexport {\n\tgetCommentReactions,\n\tgetCommentRecord,\n\tgetComments,\n\tgetCommentThreads,\n\tgetLiveComments,\n\tgetLiveCommentThreads,\n\ttype TLCommentRecord,\n} from './canvas/comment-store'\nexport { type CommentingContext } from './canvas/context'\nexport { CommentsFilterMenu, type CommentsFilterMenuProps } from './canvas/comments-filter-menu'\nexport { CommentsMenuItem } from './canvas/comments-menu-item'\nexport { CanvasComments, type CanvasCommentsProps } from './canvas/comments-overlay'\nexport {\n\ttype CommentingComponents,\n\ttype CommentingOptions,\n\ttype CommentModification,\n\ttype CommentModificationContext,\n\tdefaultCanModifyComment,\n\tdefaultCommentingOptions,\n\tgetCanComment,\n\tgetCanModifyComment,\n\tgetCommentingOptions,\n\ttype ShapeCommentPrecisionContext,\n\tuseCanComment,\n\tuseCanModifyComment,\n\tuseCommentingOptions,\n} from './canvas/options'\nexport { CommentsOverflowMenu } from './canvas/comments-overflow-menu'\nexport { CommentsVisibilityToggle } from './canvas/comments-visibility-toggle'\nexport {\n\tCanvasCommentsSidebar,\n\tsortSidebarRows,\n\ttype CanvasCommentsSidebarProps,\n\ttype SidebarRow,\n} from './canvas/comments-sidebar'\nexport { useComments, useCommentThreads, useThreadComments } from './canvas/hooks'\nexport { useCommentingEnabled } from './canvas/license'\nexport { richTextToPlaintext } from './canvas/rich-text'\nexport { DEFAULT_SIDEBAR_FILTERS, type SidebarFilters } from './canvas/sidebar-filters'\nexport {\n\tcommentsHidden,\n\tcommentsSidebarOpen,\n\tgetRevealThreadPending,\n\topenThreadId,\n\trevealThread,\n\tsidebarFilters,\n\ttoggleCommentsHidden,\n\ttoggleCommentsSidebar,\n\tuseCommentsHidden,\n\tuseCommentsSidebarOpen,\n\tuseOpenThreadId,\n\tuseRevealThreadPending,\n\tuseSidebarFilters,\n} from './canvas/state'\nexport { anchorPagePoint, focusThread, shapeAnchorAt } from './canvas/thread-state'\n\nregisterTldrawLibraryVersion(\n\t(globalThis as any).TLDRAW_LIBRARY_NAME,\n\t(globalThis as any).TLDRAW_LIBRARY_VERSION,\n\t(globalThis as any).TLDRAW_LIBRARY_MODULES\n)\n"],
5
+ "mappings": "AAAA,SAAS,oCAAoC;AAK7C;AAAA,EACC;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAKM;AACP,SAAS,cAAgC;AACzC,SAAS,mBAA0C;AACnD,SAAS,uBAAkD;AAC3D,SAAS,kBAAwC;AACjD,SAAS,kBAAwC;AACjD,SAAS,qBAA8C;AACvD;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,OAIM;AACP,SAAS,kBAAwC;AACjD,SAAS,oBAAoB,0BAA0B;AACvD;AAAA,EACC;AAAA,EACA;AAAA,EAEA;AAAA,OACM;AACP;AAAA,EACC;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,OAEM;AACP,SAAS,sBAAgD;AACzD;AAAA,EACC;AAAA,OAIM;AACP,SAAS,kBAAwC;AAKjD,SAAS,sCAAsC;AAC/C,SAAS,mBAA0C;AACnD;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP;AAAA,EACC;AAAA,EAEA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,aAAa,sBAAsB,oBAAoB;AAChE;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEM;AAEP,SAAS,0BAAwD;AACjE,SAAS,wBAAwB;AACjC,SAAS,sBAAgD;AACzD;AAAA,EAKC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,4BAA4B;AACrC,SAAS,gCAAgC;AACzC;AAAA,EACC;AAAA,EACA;AAAA,OAGM;AACP,SAAS,aAAa,mBAAmB,yBAAyB;AAClE,SAAS,4BAA4B;AACrC,SAAS,2BAA2B;AACpC,SAAS,+BAAoD;AAC7D;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,iBAAiB,aAAa,qBAAqB;AAE5D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,14 +1,16 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Avatar } from "@tldraw/mentions";
3
- import { formatRelativeTime } from "./format-time.mjs";
3
+ import { TldrawUiTooltip, useCurrentTranslation } from "tldraw";
4
+ import { formatFullDateTime, formatRelativeTime } from "./format-time.mjs";
4
5
  function Byline({ author, date, edited }) {
6
+ const { locale } = useCurrentTranslation();
5
7
  return /* @__PURE__ */ jsxs("div", { className: "tlui-cmt-head", children: [
6
8
  /* @__PURE__ */ jsx(Avatar, { author }),
7
9
  /* @__PURE__ */ jsx("span", { className: "tlui-cmt-author", children: author.name }),
8
- /* @__PURE__ */ jsxs("span", { className: "tlui-cmt-time", children: [
9
- formatRelativeTime(date),
10
+ /* @__PURE__ */ jsx(TldrawUiTooltip, { content: formatFullDateTime(date, locale), children: /* @__PURE__ */ jsxs("span", { className: "tlui-cmt-time", children: [
11
+ formatRelativeTime(date, locale),
10
12
  edited && /* @__PURE__ */ jsx("span", { className: "tlui-cmt-edited", children: " \xB7 edited" })
11
- ] })
13
+ ] }) })
12
14
  ] });
13
15
  }
14
16
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/byline.tsx"],
4
- "sourcesContent": ["import { Avatar, type CommentAuthor } from '@tldraw/mentions'\nimport { formatRelativeTime } from './format-time'\n\n/** @public */\nexport interface BylineProps {\n\tauthor: CommentAuthor\n\t/** ISO datetime; formatted to relative time by the component. */\n\tdate: string\n\t/** Shows an \"edited\" marker when the comment has been edited. */\n\tedited?: boolean\n}\n\n/** A comment's metadata line: author name, relative time, and an edited marker. @public @react */\nexport function Byline({ author, date, edited }: BylineProps) {\n\treturn (\n\t\t<div className=\"tlui-cmt-head\">\n\t\t\t<Avatar author={author} />\n\t\t\t<span className=\"tlui-cmt-author\">{author.name}</span>\n\t\t\t<span className=\"tlui-cmt-time\">\n\t\t\t\t{formatRelativeTime(date)}\n\t\t\t\t{edited && <span className=\"tlui-cmt-edited\"> \u00B7 edited</span>}\n\t\t\t</span>\n\t\t</div>\n\t)\n}\n"],
5
- "mappings": "AAgBG,cAEA,YAFA;AAhBH,SAAS,cAAkC;AAC3C,SAAS,0BAA0B;AAY5B,SAAS,OAAO,EAAE,QAAQ,MAAM,OAAO,GAAgB;AAC7D,SACC,qBAAC,SAAI,WAAU,iBACd;AAAA,wBAAC,UAAO,QAAgB;AAAA,IACxB,oBAAC,UAAK,WAAU,mBAAmB,iBAAO,MAAK;AAAA,IAC/C,qBAAC,UAAK,WAAU,iBACd;AAAA,yBAAmB,IAAI;AAAA,MACvB,UAAU,oBAAC,UAAK,WAAU,mBAAkB,0BAAS;AAAA,OACvD;AAAA,KACD;AAEF;",
4
+ "sourcesContent": ["import { Avatar, type CommentAuthor } from '@tldraw/mentions'\nimport { TldrawUiTooltip, useCurrentTranslation } from 'tldraw'\nimport { formatFullDateTime, formatRelativeTime } from './format-time'\n\n/** @public */\nexport interface BylineProps {\n\tauthor: CommentAuthor\n\t/** ISO datetime; formatted to relative time by the component. */\n\tdate: string\n\t/** Shows an \"edited\" marker when the comment has been edited. */\n\tedited?: boolean\n}\n\n/**\n * A comment's metadata line: author name, relative time, and an edited marker. Hovering the time\n * shows the full date and time in a tooltip.\n * @public @react\n */\nexport function Byline({ author, date, edited }: BylineProps) {\n\t// The relative time is the one piece of a byline that isn't the host's data, so it follows the\n\t// same translation context as every other string in the UI rather than a prop of its own.\n\tconst { locale } = useCurrentTranslation()\n\treturn (\n\t\t<div className=\"tlui-cmt-head\">\n\t\t\t<Avatar author={author} />\n\t\t\t<span className=\"tlui-cmt-author\">{author.name}</span>\n\t\t\t<TldrawUiTooltip content={formatFullDateTime(date, locale)}>\n\t\t\t\t<span className=\"tlui-cmt-time\">\n\t\t\t\t\t{formatRelativeTime(date, locale)}\n\t\t\t\t\t{edited && <span className=\"tlui-cmt-edited\"> \u00B7 edited</span>}\n\t\t\t\t</span>\n\t\t\t</TldrawUiTooltip>\n\t\t</div>\n\t)\n}\n"],
5
+ "mappings": "AAwBG,cAGC,YAHD;AAxBH,SAAS,cAAkC;AAC3C,SAAS,iBAAiB,6BAA6B;AACvD,SAAS,oBAAoB,0BAA0B;AAgBhD,SAAS,OAAO,EAAE,QAAQ,MAAM,OAAO,GAAgB;AAG7D,QAAM,EAAE,OAAO,IAAI,sBAAsB;AACzC,SACC,qBAAC,SAAI,WAAU,iBACd;AAAA,wBAAC,UAAO,QAAgB;AAAA,IACxB,oBAAC,UAAK,WAAU,mBAAmB,iBAAO,MAAK;AAAA,IAC/C,oBAAC,mBAAgB,SAAS,mBAAmB,MAAM,MAAM,GACxD,+BAAC,UAAK,WAAU,iBACd;AAAA,yBAAmB,MAAM,MAAM;AAAA,MAC/B,UAAU,oBAAC,UAAK,WAAU,mBAAkB,0BAAS;AAAA,OACvD,GACD;AAAA,KACD;AAEF;",
6
6
  "names": []
7
7
  }
@@ -110,9 +110,8 @@ function CommentComposer({
110
110
  extensions,
111
111
  content: value ?? EMPTY_COMMENT,
112
112
  editable: interactive,
113
- // tldraw's default extensions add their own TextDirection extension (so it can be
114
- // overridden), so disable TipTap's core one to avoid a duplicate-extension warning —
115
- // mirrors RichTextArea's setup.
113
+ // tldraw ships its own TextDirection extension, so TipTap's core one would warn about a
114
+ // duplicate. Mirrors RichTextArea's setup.
116
115
  enableCoreExtensions: { textDirection: false },
117
116
  textDirection: "auto",
118
117
  editorProps: {
@@ -172,21 +171,6 @@ function CommentComposer({
172
171
  const raf = requestAnimationFrame(() => editor.commands.focus("end"));
173
172
  return () => cancelAnimationFrame(raf);
174
173
  }, [autoFocus, editor]);
175
- useEffect(() => {
176
- const wrap = inputWrapRef.current;
177
- if (!wrap) return;
178
- const onWheel = (e) => {
179
- const input = wrap.querySelector(".tlui-cmt-input");
180
- if (!input || input.scrollHeight <= input.clientHeight) return;
181
- const atTop = input.scrollTop <= 0;
182
- const atBottom = input.scrollTop >= input.scrollHeight - input.clientHeight - 1;
183
- if (e.deltaY < 0 && !atTop || e.deltaY > 0 && !atBottom) {
184
- e.stopPropagation();
185
- }
186
- };
187
- wrap.addEventListener("wheel", onWheel);
188
- return () => wrap.removeEventListener("wheel", onWheel);
189
- }, []);
190
174
  const focusEditorFromField = (e) => {
191
175
  const target = e.target;
192
176
  if (target.closest(".tlui-cmt-input") || target.closest(".tlui-cmt-send")) return;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/comment-composer.tsx"],
4
- "sourcesContent": ["import { JSONContent } from '@tiptap/core'\nimport { EditorContent, useEditor } from '@tiptap/react'\nimport {\n\tAvatar,\n\ttype CommentAuthor,\n\tcreateMentionExtension,\n\tcreateMentionSuggestion,\n\tisMentionPickerOpen,\n\tMentionMember,\n} from '@tldraw/mentions'\nimport {\n\ttype MouseEvent as ReactMouseEvent,\n\tReactNode,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { isEqual, TLRichText, useMaybeEditor } from 'tldraw'\nimport { commentTipTapExtensions, EMPTY_COMMENT, isCommentEmpty } from './comment-extensions'\nimport { SendButton } from './send-button'\n\n/** @public */\nexport interface CommentComposerProps {\n\tauthor: CommentAuthor\n\tplaceholder: string\n\t/** Controlled rich-text value. Omit for the presentational (display-only) composer. */\n\tvalue?: TLRichText\n\tonChange?(value: TLRichText): void\n\t/** Called on Send click or Enter. When set, the composer is interactive. */\n\tonSubmit?(): void\n\tsendLabel?: string\n\t/** Called when Up is pressed in an empty composer \u2014 e.g. to start editing the comment above,\n\t * the way chat apps edit your last message. With content in the field, Up moves the cursor. */\n\tonArrowUpWhenEmpty?(): void\n\tdisabled?: boolean\n\tautoFocus?: boolean\n\t/** The leading element before the field. Defaults to the author's avatar. */\n\tleading?: ReactNode\n\t/** Resolve the members matching an `@`-query (sync or async). Provide to enable mentions. */\n\tgetMentionSuggestions?(query: string): MentionMember[] | Promise<MentionMember[]>\n\t/** Override a picker row's content. Defaults to avatar + name (+ secondary). */\n\trenderMentionSuggestion?(member: MentionMember): ReactNode\n}\n\n/**\n * The input for writing a comment: a TipTap rich-text editor restricted to the comment extension\n * set (bold, italic, lists, links, code, highlight \u2014 no headings), with a Send button. Formatting\n * is applied through markdown and keyboard shortcuts (e.g. `**bold**`, `- `, Cmd+B); there's no\n * floating toolbar. Presentational by default; pass value/onChange/onSubmit to drive it as a form.\n * @public @react\n */\nexport function CommentComposer({\n\tauthor,\n\tplaceholder,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tsendLabel = 'Send',\n\tonArrowUpWhenEmpty,\n\tdisabled,\n\tautoFocus,\n\tleading,\n\tgetMentionSuggestions,\n\trenderMentionSuggestion,\n}: CommentComposerProps) {\n\tconst interactive = !!onChange || !!onSubmit\n\t// The canvas editor the composer lives in, if any \u2014 lets the mention popup track the camera. Null\n\t// when the composer is used outside a tldraw editor (e.g. an isolated demo).\n\tconst tlEditor = useMaybeEditor()\n\n\t// Callbacks are read through refs so the editor instance doesn't need to be recreated when they\n\t// change identity between renders.\n\tconst onChangeRef = useRef(onChange)\n\tonChangeRef.current = onChange\n\tconst onSubmitRef = useRef(onSubmit)\n\tonSubmitRef.current = onSubmit\n\tconst onArrowUpWhenEmptyRef = useRef(onArrowUpWhenEmpty)\n\tonArrowUpWhenEmptyRef.current = onArrowUpWhenEmpty\n\tconst disabledRef = useRef(disabled)\n\tdisabledRef.current = disabled\n\tconst getMentionSuggestionsRef = useRef(getMentionSuggestions)\n\tgetMentionSuggestionsRef.current = getMentionSuggestions\n\tconst renderMentionSuggestionRef = useRef(renderMentionSuggestion)\n\trenderMentionSuggestionRef.current = renderMentionSuggestion\n\n\tconst [isEmpty, setIsEmpty] = useState(() => !value || isCommentEmpty(value))\n\t// Whether the field has grown to two rows: input across the full width, send button below.\n\t// Flips when the (single-line) content width reaches the space left beside the send button.\n\tconst [expanded, setExpanded] = useState(false)\n\tconst expandedRef = useRef(expanded)\n\texpandedRef.current = expanded\n\tconst inputWrapRef = useRef<HTMLDivElement>(null)\n\tconst mirrorRef = useRef<HTMLDivElement>(null)\n\n\t// Measure whether the content still fits on one line beside the send button. The mirror is a\n\t// hidden, nowrap clone of the editor's rendered content, so marks and mention chips measure at\n\t// their true width. A small dead zone between the expand and collapse thresholds keeps the\n\t// layout from flapping while typing at the boundary.\n\tconst remeasure = () => {\n\t\tconst wrap = inputWrapRef.current\n\t\tconst mirror = mirrorRef.current\n\t\tconst editor = editorRef.current\n\t\tif (!wrap || !mirror || !editor) return\n\t\t// Empty always fits \u2014 and measuring before TipTap's DOM has laid out reads zero widths,\n\t\t// which would flash the expanded layout for a frame on mount.\n\t\tif (editor.isEmpty) {\n\t\t\tif (expandedRef.current) setExpanded(false)\n\t\t\treturn\n\t\t}\n\t\tif (wrap.clientWidth === 0) return\n\t\tconst doc = editor.state.doc\n\t\tconst multiBlock =\n\t\t\tdoc.childCount > 1 || (doc.firstChild !== null && doc.firstChild.type.name !== 'paragraph')\n\t\tif (multiBlock) {\n\t\t\tif (!expandedRef.current) setExpanded(true)\n\t\t\treturn\n\t\t}\n\t\tconst field = wrap.parentElement\n\t\tconst send = field ? field.querySelector<HTMLElement>('.tlui-cmt-send') : null\n\t\tconst input = wrap.querySelector('.tlui-cmt-input')\n\t\tif (!send || !input) return\n\t\tmirror.innerHTML = input.innerHTML\n\t\tconst textWidth = mirror.offsetWidth\n\t\t// The single-line space the input has beside the send button: the wrap's *content* box\n\t\t// (clientWidth minus its own horizontal padding \u2014 the text wraps there, not at clientWidth),\n\t\t// less the button plus the field's 6px gap when already expanded (the wrap then spans the\n\t\t// full field). Without subtracting the padding, a padded wrap wraps to a second line ~padding\n\t\t// px before expansion fires, so the input grows a line and then snaps to the expanded layout.\n\t\tconst wrapStyle = getComputedStyle(wrap)\n\t\tconst wrapPadX = parseFloat(wrapStyle.paddingLeft) + parseFloat(wrapStyle.paddingRight)\n\t\tconst collapsedAvailable =\n\t\t\twrap.clientWidth - wrapPadX - (expandedRef.current ? send.offsetWidth + 6 : 0)\n\t\tif (!expandedRef.current && textWidth > collapsedAvailable - 8) {\n\t\t\tsetExpanded(true)\n\t\t} else if (expandedRef.current && textWidth < collapsedAvailable - 24) {\n\t\t\tsetExpanded(false)\n\t\t}\n\t}\n\tconst remeasureRef = useRef(remeasure)\n\tremeasureRef.current = remeasure\n\n\t// The editor instance, reachable from `handleKeyDown` (which is created before `useEditor`\n\t// returns). Updated on every render so the ref never points at a stale editor.\n\tconst editorRef = useRef<ReturnType<typeof useEditor>>(null)\n\n\t// Set while we replay Enter through the keymaps for a Shift+Enter newline: `commands.enter()`\n\t// re-dispatches Enter through `handleKeyDown`, and without this guard our own handler would catch\n\t// that synthetic Enter and submit instead.\n\tconst replayingEnter = useRef(false)\n\n\t// Enter and Cmd/Ctrl+Enter submit the comment; Shift+Enter inserts a new line for the occasional\n\t// multi-line comment. This is handled through `editorProps.handleKeyDown` (below) rather than a\n\t// keyboard-shortcut extension: ProseMirror runs `handleKeyDown` before every keymap plugin, so it\n\t// can tell Shift+Enter apart from Enter \u2014 an `Enter` keymap binding also fires on Shift/Cmd+Enter\n\t// and would otherwise swallow the newline.\n\n\t// The suggestion plugin is built once (the editor is recreated only on `interactive`) and runs\n\t// outside React, so it must read the mention callbacks through refs \u2014 like onChange/onSubmit \u2014\n\t// or it queries the roster present at mount forever, never seeing a member who loads or joins\n\t// later. Whether mentions (and a custom picker row) are wired at all is fixed at mount; only the\n\t// callbacks themselves are live.\n\tconst mentionsEnabled = !!getMentionSuggestions\n\tconst hasCustomRow = !!renderMentionSuggestion\n\tconst extensions = useMemo(() => {\n\t\tconst list = [...commentTipTapExtensions]\n\t\t// Always register the mention node so an existing body that contains a mention keeps it on\n\t\t// edit (an unregistered node would be stripped by ProseMirror when the content loads). The `@`\n\t\t// picker itself only turns on when the host provides a resolver; otherwise the node is present\n\t\t// but its trigger is disabled.\n\t\tif (mentionsEnabled) {\n\t\t\tconst resolveSuggestions = (query: string) => getMentionSuggestionsRef.current?.(query) ?? []\n\t\t\tconst renderRow = hasCustomRow\n\t\t\t\t? (member: MentionMember) => renderMentionSuggestionRef.current?.(member)\n\t\t\t\t: undefined\n\t\t\tlist.push(\n\t\t\t\tcreateMentionExtension({\n\t\t\t\t\tsuggestion: createMentionSuggestion(resolveSuggestions, {\n\t\t\t\t\t\trenderMember: renderRow,\n\t\t\t\t\t\teditor: tlEditor,\n\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t)\n\t\t} else {\n\t\t\tlist.push(createMentionExtension({ suggestion: { char: '@', allow: () => false } }))\n\t\t}\n\t\treturn list\n\t}, [mentionsEnabled, hasCustomRow, tlEditor])\n\n\tconst editor = useEditor(\n\t\t{\n\t\t\textensions,\n\t\t\tcontent: (value ?? EMPTY_COMMENT) as JSONContent,\n\t\t\teditable: interactive,\n\t\t\t// tldraw's default extensions add their own TextDirection extension (so it can be\n\t\t\t// overridden), so disable TipTap's core one to avoid a duplicate-extension warning \u2014\n\t\t\t// mirrors RichTextArea's setup.\n\t\t\tenableCoreExtensions: { textDirection: false },\n\t\t\ttextDirection: 'auto',\n\t\t\teditorProps: {\n\t\t\t\tattributes: {\n\t\t\t\t\tclass: 'tlui-cmt-input',\n\t\t\t\t\t'aria-label': placeholder,\n\t\t\t\t\trole: 'textbox',\n\t\t\t\t\t'aria-multiline': 'true',\n\t\t\t\t},\n\t\t\t\t// Runs before every keymap plugin, so it can distinguish Shift+Enter from Enter \u2014 an\n\t\t\t\t// `Enter` keymap binding also fires on Shift+Enter and would otherwise swallow it.\n\t\t\t\thandleKeyDown: (_view, event) => {\n\t\t\t\t\t// Let the keymaps handle the synthetic Enter we replay for a Shift+Enter newline.\n\t\t\t\t\tif (replayingEnter.current) return false\n\t\t\t\t\t// Up in an empty composer hands off to the host (edit the comment above). With\n\t\t\t\t\t// content, Up stays cursor movement; with the mention picker open, it navigates it.\n\t\t\t\t\tif (event.key === 'ArrowUp' && !event.isComposing) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tonArrowUpWhenEmptyRef.current &&\n\t\t\t\t\t\t\teditorRef.current?.isEmpty &&\n\t\t\t\t\t\t\t!isMentionPickerOpen()\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tonArrowUpWhenEmptyRef.current()\n\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (event.key !== 'Enter' || event.isComposing) return false\n\t\t\t\t\t// While the @-mention picker is open, Enter selects the highlighted member \u2014 defer.\n\t\t\t\t\tif (isMentionPickerOpen()) return false\n\t\t\t\t\t// Shift+Enter inserts a new line. Replay a plain Enter through the keymaps (guarded so\n\t\t\t\t\t// we don't re-enter and submit) to reuse the editor's list-aware Enter handling \u2014 a new\n\t\t\t\t\t// list item in a list, a new paragraph otherwise. tldraw doesn't do soft breaks.\n\t\t\t\t\tif (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey) {\n\t\t\t\t\t\t// An empty field has nothing to break onto \u2014 swallow the keypress so it doesn't\n\t\t\t\t\t\t// open the comment with a stray leading blank line.\n\t\t\t\t\t\tif (editorRef.current?.isEmpty) return true\n\t\t\t\t\t\treplayingEnter.current = true\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\teditorRef.current?.commands.enter()\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\treplayingEnter.current = false\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\t// Enter, Cmd+Enter, and Ctrl+Enter submit.\n\t\t\t\t\tif (!disabledRef.current) onSubmitRef.current?.()\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t},\n\t\t\tonUpdate: ({ editor }) => {\n\t\t\t\tsetIsEmpty(editor.isEmpty)\n\t\t\t\t// Same-value state sets don't re-render, so measure here \u2014 the editor's DOM is\n\t\t\t\t// already updated when onUpdate fires.\n\t\t\t\tremeasureRef.current()\n\t\t\t\tonChangeRef.current?.(editor.getJSON() as TLRichText)\n\t\t\t},\n\t\t},\n\t\t[interactive, placeholder]\n\t)\n\teditorRef.current = editor\n\n\t// Sync controlled resets (e.g. the parent clearing to EMPTY_COMMENT after posting) into the\n\t// editor without echoing back the value the editor itself just emitted.\n\tuseEffect(() => {\n\t\tif (!editor || value === undefined) return\n\t\tif (isEqual(editor.getJSON(), value)) return\n\t\teditor.commands.setContent(value as JSONContent)\n\t\tsetIsEmpty(editor.isEmpty)\n\t}, [editor, value])\n\n\t// Measure once the editor's content is in the DOM, so a composer that mounts pre-filled (the\n\t// edit-in-place composer) starts in the right layout.\n\tuseLayoutEffect(() => {\n\t\tremeasureRef.current()\n\t}, [editor, value])\n\n\t// Focus on the next frame rather than via TipTap's autofocus: the composer often mounts from a\n\t// canvas pointer event whose default focus handling would otherwise steal it back.\n\tuseEffect(() => {\n\t\tif (!autoFocus || !editor) return\n\t\tconst raf = requestAnimationFrame(() => editor.commands.focus('end'))\n\t\treturn () => cancelAnimationFrame(raf)\n\t}, [autoFocus, editor])\n\n\t// tldraw's pass-through-wheel hook (on the floating composer's wrapper) forwards a wheel to the\n\t// canvas unless ITS element is scrollable \u2014 but the scrollable element is this input, not that\n\t// wrapper, so an overflowing input never scrolled: the wheel panned the canvas instead. Catch the\n\t// wheel on the wrap first (a native listener here fires before the wrapper's, deeper in the tree)\n\t// and stop it while the input can still scroll that way; at the scroll boundary let it through so\n\t// the canvas still zooms.\n\tuseEffect(() => {\n\t\tconst wrap = inputWrapRef.current\n\t\tif (!wrap) return\n\t\tconst onWheel = (e: WheelEvent) => {\n\t\t\tconst input = wrap.querySelector<HTMLElement>('.tlui-cmt-input')\n\t\t\tif (!input || input.scrollHeight <= input.clientHeight) return\n\t\t\tconst atTop = input.scrollTop <= 0\n\t\t\tconst atBottom = input.scrollTop >= input.scrollHeight - input.clientHeight - 1\n\t\t\tif ((e.deltaY < 0 && !atTop) || (e.deltaY > 0 && !atBottom)) {\n\t\t\t\te.stopPropagation()\n\t\t\t}\n\t\t}\n\t\twrap.addEventListener('wheel', onWheel)\n\t\treturn () => wrap.removeEventListener('wheel', onWheel)\n\t}, [])\n\n\t// The whole field behaves like the text input: clicking its empty area (the padding, or the\n\t// space beside/below a short line) focuses the editor rather than only the text glyphs being\n\t// clickable. The input (caret placement) and the send button keep their own click handling.\n\tconst focusEditorFromField = (e: ReactMouseEvent<HTMLDivElement>) => {\n\t\tconst target = e.target as HTMLElement\n\t\tif (target.closest('.tlui-cmt-input') || target.closest('.tlui-cmt-send')) return\n\t\te.preventDefault()\n\t\teditor?.commands.focus('end')\n\t}\n\n\treturn (\n\t\t<div className=\"tlui-cmt-composer\">\n\t\t\t{leading ?? <Avatar author={author} />}\n\t\t\t<div\n\t\t\t\tclassName={[\n\t\t\t\t\t'tlui-cmt-composer__field',\n\t\t\t\t\tinteractive && expanded && 'tlui-cmt-composer__field--expanded',\n\t\t\t\t]\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.join(' ')}\n\t\t\t\tonMouseDown={interactive ? focusEditorFromField : undefined}\n\t\t\t>\n\t\t\t\t<div className=\"tlui-cmt-composer__input-wrap\" ref={inputWrapRef}>\n\t\t\t\t\t<EditorContent editor={editor} />\n\t\t\t\t\t{isEmpty && (\n\t\t\t\t\t\t<div className=\"tlui-cmt-input__placeholder\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t{placeholder}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"tlui-cmt-composer__mirror tlui-cmt-input\"\n\t\t\t\t\t\tref={mirrorRef}\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t{interactive && <SendButton label={sendLabel} onClick={onSubmit} disabled={disabled} />}\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { JSONContent } from '@tiptap/core'\nimport { EditorContent, useEditor } from '@tiptap/react'\nimport {\n\tAvatar,\n\ttype CommentAuthor,\n\tcreateMentionExtension,\n\tcreateMentionSuggestion,\n\tisMentionPickerOpen,\n\tMentionMember,\n} from '@tldraw/mentions'\nimport {\n\ttype MouseEvent as ReactMouseEvent,\n\tReactNode,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { isEqual, TLRichText, useMaybeEditor } from 'tldraw'\nimport { commentTipTapExtensions, EMPTY_COMMENT, isCommentEmpty } from './comment-extensions'\nimport { SendButton } from './send-button'\n\n/** @public */\nexport interface CommentComposerProps {\n\tauthor: CommentAuthor\n\tplaceholder: string\n\t/** Controlled rich-text value. Omit for the presentational (display-only) composer. */\n\tvalue?: TLRichText\n\tonChange?(value: TLRichText): void\n\t/** Called on Send click or Enter. When set, the composer is interactive. */\n\tonSubmit?(): void\n\tsendLabel?: string\n\t/** Called when Up is pressed in an empty composer \u2014 e.g. to start editing the comment above,\n\t * the way chat apps edit your last message. With content in the field, Up moves the cursor. */\n\tonArrowUpWhenEmpty?(): void\n\tdisabled?: boolean\n\tautoFocus?: boolean\n\t/** The leading element before the field. Defaults to the author's avatar. */\n\tleading?: ReactNode\n\t/** Resolve the members matching an `@`-query (sync or async). Provide to enable mentions. */\n\tgetMentionSuggestions?(query: string): MentionMember[] | Promise<MentionMember[]>\n\t/** Override a picker row's content. Defaults to avatar + name (+ secondary). */\n\trenderMentionSuggestion?(member: MentionMember): ReactNode\n}\n\n/**\n * The input for writing a comment: a TipTap rich-text editor restricted to the comment extension\n * set (bold, italic, lists, links, code, highlight \u2014 no headings), with a Send button. Formatting\n * is applied through markdown and keyboard shortcuts (e.g. `**bold**`, `- `, Cmd+B); there's no\n * floating toolbar. Presentational by default; pass value/onChange/onSubmit to drive it as a form.\n * @public @react\n */\nexport function CommentComposer({\n\tauthor,\n\tplaceholder,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tsendLabel = 'Send',\n\tonArrowUpWhenEmpty,\n\tdisabled,\n\tautoFocus,\n\tleading,\n\tgetMentionSuggestions,\n\trenderMentionSuggestion,\n}: CommentComposerProps) {\n\tconst interactive = !!onChange || !!onSubmit\n\t// Lets the mention popup track the camera. Null outside a tldraw editor.\n\tconst tlEditor = useMaybeEditor()\n\n\t// Read through refs so the editor isn't recreated when callback identity changes.\n\tconst onChangeRef = useRef(onChange)\n\tonChangeRef.current = onChange\n\tconst onSubmitRef = useRef(onSubmit)\n\tonSubmitRef.current = onSubmit\n\tconst onArrowUpWhenEmptyRef = useRef(onArrowUpWhenEmpty)\n\tonArrowUpWhenEmptyRef.current = onArrowUpWhenEmpty\n\tconst disabledRef = useRef(disabled)\n\tdisabledRef.current = disabled\n\tconst getMentionSuggestionsRef = useRef(getMentionSuggestions)\n\tgetMentionSuggestionsRef.current = getMentionSuggestions\n\tconst renderMentionSuggestionRef = useRef(renderMentionSuggestion)\n\trenderMentionSuggestionRef.current = renderMentionSuggestion\n\n\tconst [isEmpty, setIsEmpty] = useState(() => !value || isCommentEmpty(value))\n\t// Whether the field has grown to two rows: input across the full width, send button below.\n\t// Flips when the (single-line) content width reaches the space left beside the send button.\n\tconst [expanded, setExpanded] = useState(false)\n\tconst expandedRef = useRef(expanded)\n\texpandedRef.current = expanded\n\tconst inputWrapRef = useRef<HTMLDivElement>(null)\n\tconst mirrorRef = useRef<HTMLDivElement>(null)\n\n\t// Measure whether the content still fits on one line beside the send button, against a hidden\n\t// nowrap clone so marks and mention chips measure at their true width. The gap between the\n\t// expand and collapse thresholds is a dead zone, so the layout can't flap at the boundary.\n\tconst remeasure = () => {\n\t\tconst wrap = inputWrapRef.current\n\t\tconst mirror = mirrorRef.current\n\t\tconst editor = editorRef.current\n\t\tif (!wrap || !mirror || !editor) return\n\t\t// Empty always fits \u2014 and measuring before TipTap has laid out reads zero widths, flashing\n\t\t// the expanded layout for a frame on mount.\n\t\tif (editor.isEmpty) {\n\t\t\tif (expandedRef.current) setExpanded(false)\n\t\t\treturn\n\t\t}\n\t\tif (wrap.clientWidth === 0) return\n\t\tconst doc = editor.state.doc\n\t\tconst multiBlock =\n\t\t\tdoc.childCount > 1 || (doc.firstChild !== null && doc.firstChild.type.name !== 'paragraph')\n\t\tif (multiBlock) {\n\t\t\tif (!expandedRef.current) setExpanded(true)\n\t\t\treturn\n\t\t}\n\t\tconst field = wrap.parentElement\n\t\tconst send = field ? field.querySelector<HTMLElement>('.tlui-cmt-send') : null\n\t\tconst input = wrap.querySelector('.tlui-cmt-input')\n\t\tif (!send || !input) return\n\t\tmirror.innerHTML = input.innerHTML\n\t\tconst textWidth = mirror.offsetWidth\n\t\t// Measured against the wrap's *content* box: text wraps at clientWidth minus padding, so\n\t\t// without subtracting it the input grows a line before expansion fires and then snaps.\n\t\tconst wrapStyle = getComputedStyle(wrap)\n\t\tconst wrapPadX = parseFloat(wrapStyle.paddingLeft) + parseFloat(wrapStyle.paddingRight)\n\t\tconst collapsedAvailable =\n\t\t\twrap.clientWidth - wrapPadX - (expandedRef.current ? send.offsetWidth + 6 : 0)\n\t\tif (!expandedRef.current && textWidth > collapsedAvailable - 8) {\n\t\t\tsetExpanded(true)\n\t\t} else if (expandedRef.current && textWidth < collapsedAvailable - 24) {\n\t\t\tsetExpanded(false)\n\t\t}\n\t}\n\tconst remeasureRef = useRef(remeasure)\n\tremeasureRef.current = remeasure\n\n\t// Reachable from `handleKeyDown`, which is created before `useEditor` returns.\n\tconst editorRef = useRef<ReturnType<typeof useEditor>>(null)\n\n\t// `commands.enter()` re-dispatches through `handleKeyDown`; without this guard our own handler\n\t// would catch the synthetic Enter and submit.\n\tconst replayingEnter = useRef(false)\n\n\t// The suggestion plugin is built once and runs outside React, so it reads the mention callbacks\n\t// through refs \u2014 otherwise it queries the roster present at mount forever, never seeing a member\n\t// who joins later. Whether mentions are wired at all is fixed at mount; the callbacks are live.\n\tconst mentionsEnabled = !!getMentionSuggestions\n\tconst hasCustomRow = !!renderMentionSuggestion\n\tconst extensions = useMemo(() => {\n\t\tconst list = [...commentTipTapExtensions]\n\t\t// The mention node is always registered, or ProseMirror strips existing mentions when an\n\t\t// edited body loads. Only the `@` trigger is gated on the host providing a resolver.\n\t\tif (mentionsEnabled) {\n\t\t\tconst resolveSuggestions = (query: string) => getMentionSuggestionsRef.current?.(query) ?? []\n\t\t\tconst renderRow = hasCustomRow\n\t\t\t\t? (member: MentionMember) => renderMentionSuggestionRef.current?.(member)\n\t\t\t\t: undefined\n\t\t\tlist.push(\n\t\t\t\tcreateMentionExtension({\n\t\t\t\t\tsuggestion: createMentionSuggestion(resolveSuggestions, {\n\t\t\t\t\t\trenderMember: renderRow,\n\t\t\t\t\t\teditor: tlEditor,\n\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t)\n\t\t} else {\n\t\t\tlist.push(createMentionExtension({ suggestion: { char: '@', allow: () => false } }))\n\t\t}\n\t\treturn list\n\t}, [mentionsEnabled, hasCustomRow, tlEditor])\n\n\tconst editor = useEditor(\n\t\t{\n\t\t\textensions,\n\t\t\tcontent: (value ?? EMPTY_COMMENT) as JSONContent,\n\t\t\teditable: interactive,\n\t\t\t// tldraw ships its own TextDirection extension, so TipTap's core one would warn about a\n\t\t\t// duplicate. Mirrors RichTextArea's setup.\n\t\t\tenableCoreExtensions: { textDirection: false },\n\t\t\ttextDirection: 'auto',\n\t\t\teditorProps: {\n\t\t\t\tattributes: {\n\t\t\t\t\tclass: 'tlui-cmt-input',\n\t\t\t\t\t'aria-label': placeholder,\n\t\t\t\t\trole: 'textbox',\n\t\t\t\t\t'aria-multiline': 'true',\n\t\t\t\t},\n\t\t\t\t// Runs before every keymap plugin, so it can distinguish Shift+Enter from Enter \u2014 an\n\t\t\t\t// `Enter` keymap binding also fires on Shift+Enter and would otherwise swallow it.\n\t\t\t\thandleKeyDown: (_view, event) => {\n\t\t\t\t\t// Let the keymaps handle the synthetic Enter we replay for a Shift+Enter newline.\n\t\t\t\t\tif (replayingEnter.current) return false\n\t\t\t\t\t// Up in an empty composer hands off to the host. With content it stays cursor\n\t\t\t\t\t// movement, and with the picker open it navigates the roster.\n\t\t\t\t\tif (event.key === 'ArrowUp' && !event.isComposing) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tonArrowUpWhenEmptyRef.current &&\n\t\t\t\t\t\t\teditorRef.current?.isEmpty &&\n\t\t\t\t\t\t\t!isMentionPickerOpen()\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tonArrowUpWhenEmptyRef.current()\n\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (event.key !== 'Enter' || event.isComposing) return false\n\t\t\t\t\t// While the @-mention picker is open, Enter selects the highlighted member \u2014 defer.\n\t\t\t\t\tif (isMentionPickerOpen()) return false\n\t\t\t\t\t// Shift+Enter inserts a new line by replaying a plain Enter through the keymaps, reusing\n\t\t\t\t\t// the editor's list-aware handling. tldraw doesn't do soft breaks.\n\t\t\t\t\tif (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey) {\n\t\t\t\t\t\t// An empty field would open the comment with a stray leading blank line.\n\t\t\t\t\t\tif (editorRef.current?.isEmpty) return true\n\t\t\t\t\t\treplayingEnter.current = true\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\teditorRef.current?.commands.enter()\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\treplayingEnter.current = false\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\t// Enter, Cmd+Enter, and Ctrl+Enter submit.\n\t\t\t\t\tif (!disabledRef.current) onSubmitRef.current?.()\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t},\n\t\t\tonUpdate: ({ editor }) => {\n\t\t\t\tsetIsEmpty(editor.isEmpty)\n\t\t\t\t// Same-value state sets don't re-render, and the DOM is already updated here.\n\t\t\t\tremeasureRef.current()\n\t\t\t\tonChangeRef.current?.(editor.getJSON() as TLRichText)\n\t\t\t},\n\t\t},\n\t\t[interactive, placeholder]\n\t)\n\teditorRef.current = editor\n\n\t// Sync controlled resets (e.g. the parent clearing to EMPTY_COMMENT after posting) into the\n\t// editor without echoing back the value the editor itself just emitted.\n\tuseEffect(() => {\n\t\tif (!editor || value === undefined) return\n\t\tif (isEqual(editor.getJSON(), value)) return\n\t\teditor.commands.setContent(value as JSONContent)\n\t\tsetIsEmpty(editor.isEmpty)\n\t}, [editor, value])\n\n\t// Measure once the editor's content is in the DOM, so a composer that mounts pre-filled (the\n\t// edit-in-place composer) starts in the right layout.\n\tuseLayoutEffect(() => {\n\t\tremeasureRef.current()\n\t}, [editor, value])\n\n\t// Focus on the next frame rather than via TipTap's autofocus: the composer often mounts from a\n\t// canvas pointer event whose default focus handling would otherwise steal it back.\n\tuseEffect(() => {\n\t\tif (!autoFocus || !editor) return\n\t\tconst raf = requestAnimationFrame(() => editor.commands.focus('end'))\n\t\treturn () => cancelAnimationFrame(raf)\n\t}, [autoFocus, editor])\n\n\t// The whole field behaves like the text input: clicking its empty area (the padding, or the\n\t// space beside/below a short line) focuses the editor rather than only the text glyphs being\n\t// clickable. The input (caret placement) and the send button keep their own click handling.\n\tconst focusEditorFromField = (e: ReactMouseEvent<HTMLDivElement>) => {\n\t\tconst target = e.target as HTMLElement\n\t\tif (target.closest('.tlui-cmt-input') || target.closest('.tlui-cmt-send')) return\n\t\te.preventDefault()\n\t\teditor?.commands.focus('end')\n\t}\n\n\treturn (\n\t\t<div className=\"tlui-cmt-composer\">\n\t\t\t{leading ?? <Avatar author={author} />}\n\t\t\t<div\n\t\t\t\tclassName={[\n\t\t\t\t\t'tlui-cmt-composer__field',\n\t\t\t\t\tinteractive && expanded && 'tlui-cmt-composer__field--expanded',\n\t\t\t\t]\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.join(' ')}\n\t\t\t\tonMouseDown={interactive ? focusEditorFromField : undefined}\n\t\t\t>\n\t\t\t\t<div className=\"tlui-cmt-composer__input-wrap\" ref={inputWrapRef}>\n\t\t\t\t\t<EditorContent editor={editor} />\n\t\t\t\t\t{isEmpty && (\n\t\t\t\t\t\t<div className=\"tlui-cmt-input__placeholder\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t{placeholder}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"tlui-cmt-composer__mirror tlui-cmt-input\"\n\t\t\t\t\t\tref={mirrorRef}\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t{interactive && <SendButton label={sendLabel} onClick={onSubmit} disabled={disabled} />}\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["editor"]
7
7
  }
@@ -1,19 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- const resolvedCheck = /* @__PURE__ */ jsx(
3
- "svg",
4
- {
5
- viewBox: "0 0 24 24",
6
- width: "15",
7
- height: "15",
8
- fill: "none",
9
- stroke: "currentColor",
10
- strokeWidth: "2.5",
11
- strokeLinecap: "round",
12
- strokeLinejoin: "round",
13
- "aria-hidden": "true",
14
- children: /* @__PURE__ */ jsx("path", { d: "M4 12.5l5 5L20 6.5" })
15
- }
16
- );
2
+ import { PinCheckIcon } from "./icons.mjs";
3
+ const resolvedCheck = /* @__PURE__ */ jsx(PinCheckIcon, {});
17
4
  function CommentPin({ children, resolved, open }) {
18
5
  const className = [
19
6
  // `tlui-cmt-marker` carries the resting shadow and the hover lift — the same treatment a
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/comment-pin.tsx"],
4
- "sourcesContent": ["import { ReactNode } from 'react'\n\n/** @public */\nexport interface CommentPinProps {\n\t/** What the pin shows when unresolved \u2014 a number, an author initial, an <Avatar>, etc.\n\t * Left as a lever so consumers aren't locked into a count. */\n\tchildren?: ReactNode\n\tresolved?: boolean\n\t/** The pin's thread is open \u2014 shows the active/selected indicator state. */\n\topen?: boolean\n}\n\n/* An inline check, not a text glyph \u2014 the '\u2713' character sits off-baseline and varies by font. */\nconst resolvedCheck = (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"15\"\n\t\theight=\"15\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"2.5\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M4 12.5l5 5L20 6.5\" />\n\t</svg>\n)\n\n/** A canvas comment marker: shows its `children` (or a check when resolved). Purely\n * presentational \u2014 it reflects open/resolved state via CSS; wrap it to make it clickable.\n * @public @react */\nexport function CommentPin({ children, resolved, open }: CommentPinProps) {\n\tconst className = [\n\t\t// `tlui-cmt-marker` carries the resting shadow and the hover lift \u2014 the same treatment a\n\t\t// count badge wears, so the two markers behave alike.\n\t\t'tlui-cmt-marker',\n\t\t'tlui-cmt-pin',\n\t\tresolved && 'tlui-cmt-pin--resolved',\n\t\topen && 'tlui-cmt-marker--open',\n\t]\n\t\t.filter(Boolean)\n\t\t.join(' ')\n\treturn <div className={className}>{resolved ? resolvedCheck : children}</div>\n}\n"],
5
- "mappings": "AAyBE;AAZF,MAAM,gBACL;AAAA,EAAC;AAAA;AAAA,IACA,SAAQ;AAAA,IACR,OAAM;AAAA,IACN,QAAO;AAAA,IACP,MAAK;AAAA,IACL,QAAO;AAAA,IACP,aAAY;AAAA,IACZ,eAAc;AAAA,IACd,gBAAe;AAAA,IACf,eAAY;AAAA,IAEZ,8BAAC,UAAK,GAAE,sBAAqB;AAAA;AAC9B;AAMM,SAAS,WAAW,EAAE,UAAU,UAAU,KAAK,GAAoB;AACzE,QAAM,YAAY;AAAA;AAAA;AAAA,IAGjB;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,QAAQ;AAAA,EACT,EACE,OAAO,OAAO,EACd,KAAK,GAAG;AACV,SAAO,oBAAC,SAAI,WAAuB,qBAAW,gBAAgB,UAAS;AACxE;",
4
+ "sourcesContent": ["import { ReactNode } from 'react'\nimport { PinCheckIcon } from './icons'\n\n/** @public */\nexport interface CommentPinProps {\n\t/** What the pin shows when unresolved \u2014 a number, an author initial, an <Avatar>, etc.\n\t * Left as a lever so consumers aren't locked into a count. */\n\tchildren?: ReactNode\n\tresolved?: boolean\n\t/** The pin's thread is open \u2014 shows the active/selected indicator state. */\n\topen?: boolean\n}\n\n/* Decorative \u2014 the pin's accessible name lives on whatever wraps it. */\nconst resolvedCheck = <PinCheckIcon />\n\n/** A canvas comment marker: shows its `children` (or a check when resolved). Purely\n * presentational \u2014 it reflects open/resolved state via CSS; wrap it to make it clickable.\n * @public @react */\nexport function CommentPin({ children, resolved, open }: CommentPinProps) {\n\tconst className = [\n\t\t// `tlui-cmt-marker` carries the resting shadow and the hover lift \u2014 the same treatment a\n\t\t// count badge wears, so the two markers behave alike.\n\t\t'tlui-cmt-marker',\n\t\t'tlui-cmt-pin',\n\t\tresolved && 'tlui-cmt-pin--resolved',\n\t\topen && 'tlui-cmt-marker--open',\n\t]\n\t\t.filter(Boolean)\n\t\t.join(' ')\n\treturn <div className={className}>{resolved ? resolvedCheck : children}</div>\n}\n"],
5
+ "mappings": "AAcsB;AAbtB,SAAS,oBAAoB;AAa7B,MAAM,gBAAgB,oBAAC,gBAAa;AAK7B,SAAS,WAAW,EAAE,UAAU,UAAU,KAAK,GAAoB;AACzE,QAAM,YAAY;AAAA;AAAA;AAAA,IAGjB;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,QAAQ;AAAA,EACT,EACE,OAAO,OAAO,EACd,KAAK,GAAG;AACV,SAAO,oBAAC,SAAI,WAAuB,qBAAW,gBAAgB,UAAS;AACxE;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,10 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Avatar } from "@tldraw/mentions";
3
+ import { Fragment } from "react";
3
4
  import { useTranslation } from "tldraw";
4
5
  import { Byline } from "./byline.mjs";
6
+ import { CheckIcon } from "./icons.mjs";
7
+ import { Reactions } from "./reactions.mjs";
5
8
  import { replyCountLabel } from "./reply-count.mjs";
6
9
  function CommentsList({
7
10
  items,
@@ -17,17 +20,10 @@ function CommentsList({
17
20
  header !== void 0 && /* @__PURE__ */ jsx("span", { className: "tlui-cmt-list__header-title", children: header }),
18
21
  headerAction
19
22
  ] }),
20
- items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "tlui-cmt-list__empty", children: empty }) : /* @__PURE__ */ jsx("div", { className: "tlui-cmt-list__items", children: items.map(
21
- (item) => renderItem ? renderItem(item) : /* @__PURE__ */ jsx(
22
- CommentListItem,
23
- {
24
- ...item,
25
- resolvedLabel,
26
- onSelect
27
- },
28
- item.id
29
- )
30
- ) })
23
+ items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "tlui-cmt-list__empty", children: empty }) : /* @__PURE__ */ jsx("div", { className: "tlui-cmt-list__items", children: items.map((item) => {
24
+ const props = { ...item, resolvedLabel, onSelect };
25
+ return renderItem ? /* @__PURE__ */ jsx(Fragment, { children: renderItem(props) }, item.id) : /* @__PURE__ */ jsx(CommentListItem, { ...props }, item.id);
26
+ }) })
31
27
  ] });
32
28
  }
33
29
  function CommentListItem({
@@ -39,6 +35,7 @@ function CommentListItem({
39
35
  page,
40
36
  count,
41
37
  selected,
38
+ reactions,
42
39
  href,
43
40
  resolvedLabel = "Resolved",
44
41
  onSelect
@@ -64,6 +61,7 @@ function CommentListItem({
64
61
  /* @__PURE__ */ jsxs("div", { className: "tlui-cmt-list__item-body", children: [
65
62
  /* @__PURE__ */ jsx(Byline, { author, date }),
66
63
  /* @__PURE__ */ jsx("div", { className: "tlui-cmt-list__item-preview", children: preview }),
64
+ reactions && /* @__PURE__ */ jsx(Reactions, { reactions, canReact: false, enableHoverList: false }),
67
65
  (resolved || page !== void 0 || replies) && /* @__PURE__ */ jsxs("div", { className: "tlui-cmt-list__item-meta", children: [
68
66
  resolved && /* @__PURE__ */ jsxs("span", { className: "tlui-cmt-list__item-resolved", children: [
69
67
  /* @__PURE__ */ jsx(CheckIcon, {}),
@@ -81,19 +79,8 @@ function CommentListItem({
81
79
  function isOpenInNewTabClick(e) {
82
80
  return e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0;
83
81
  }
84
- function CheckIcon() {
85
- return /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
86
- "path",
87
- {
88
- d: "M2.5 6.2 4.7 8.4 9.5 3.6",
89
- stroke: "currentColor",
90
- strokeWidth: "1.5",
91
- strokeLinecap: "round",
92
- strokeLinejoin: "round"
93
- }
94
- ) });
95
- }
96
82
  export {
83
+ CommentListItem,
97
84
  CommentsList,
98
85
  isOpenInNewTabClick
99
86
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/comments-list.tsx"],
4
- "sourcesContent": ["import { Avatar, type CommentAuthor } from '@tldraw/mentions'\nimport { MouseEvent, ReactNode } from 'react'\nimport { useTranslation } from 'tldraw'\nimport { Byline } from './byline'\nimport { replyCountLabel } from './reply-count'\n\n/** @public */\nexport interface CommentListItemProps {\n\tid: string\n\tauthor: CommentAuthor\n\t/** A short preview of the thread \u2014 e.g. the first comment's body. */\n\tpreview: ReactNode\n\t/** ISO datetime of the thread's first comment. */\n\tdate: string\n\tresolved?: boolean\n\t/** Name of the page the thread lives on, shown as a small label. Omit to hide. */\n\tpage?: string\n\t/** Total comments in the thread. */\n\tcount?: number\n\t/** Whether this thread is the open one. */\n\tselected?: boolean\n\t/**\n\t * Link target for the item. When set, the row renders as an anchor so browser affordances\n\t * (ctrl/cmd-click, middle-click) open it in a new tab; a plain click still calls `onSelect`.\n\t */\n\thref?: string\n}\n\n/** @public */\nexport interface CommentsListProps {\n\titems: CommentListItemProps[]\n\t/** Called with a thread id when an item is chosen. */\n\tonSelect?(id: string): void\n\t/** Shown above the list (e.g. \"Comments\"). Omit for none. */\n\theader?: ReactNode\n\t/** Rendered at the right of the header row \u2014 e.g. a filter menu. */\n\theaderAction?: ReactNode\n\t/** Shown in place of the list when there are no threads. */\n\tempty?: ReactNode\n\t/** Label for a resolved thread's marker on its row. Defaults to \"Resolved\". */\n\tresolvedLabel?: string\n\t/** Override how each item renders. Defaults to `<CommentListItem>`. */\n\trenderItem?(item: CommentListItemProps): ReactNode\n}\n\n/**\n * A scrollable list of comment threads \u2014 each an avatar, byline, and a one-line preview.\n * Presentational: you supply the items (already summarised) and an `onSelect` handler; the canvas\n * `CanvasCommentsSidebar` wires it to the store, but a consumer can build their own list from this.\n * @public @react\n */\nexport function CommentsList({\n\titems,\n\tonSelect,\n\theader,\n\theaderAction,\n\tempty,\n\tresolvedLabel,\n\trenderItem,\n}: CommentsListProps) {\n\treturn (\n\t\t<div className=\"tlui-cmt-list\">\n\t\t\t{(header !== undefined || headerAction) && (\n\t\t\t\t<div className=\"tlui-cmt-list__header\">\n\t\t\t\t\t{header !== undefined && <span className=\"tlui-cmt-list__header-title\">{header}</span>}\n\t\t\t\t\t{headerAction}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{items.length === 0 ? (\n\t\t\t\t<div className=\"tlui-cmt-list__empty\">{empty}</div>\n\t\t\t) : (\n\t\t\t\t<div className=\"tlui-cmt-list__items\">\n\t\t\t\t\t{items.map((item) =>\n\t\t\t\t\t\trenderItem ? (\n\t\t\t\t\t\t\trenderItem(item)\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<CommentListItem\n\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t{...item}\n\t\t\t\t\t\t\t\tresolvedLabel={resolvedLabel}\n\t\t\t\t\t\t\t\tonSelect={onSelect}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n\nfunction CommentListItem({\n\tid,\n\tauthor,\n\tpreview,\n\tdate,\n\tresolved,\n\tpage,\n\tcount,\n\tselected,\n\thref,\n\tresolvedLabel = 'Resolved',\n\tonSelect,\n}: CommentListItemProps & { resolvedLabel?: string; onSelect?(id: string): void }) {\n\tconst msg = useTranslation()\n\tconst handleClick = (e: MouseEvent) => {\n\t\tif (href && isOpenInNewTabClick(e)) return\n\t\te.preventDefault()\n\t\tif (onSelect) onSelect(id)\n\t}\n\tconst Tag = href ? 'a' : 'button'\n\t// `count` is the thread's total comments; a reply is every comment after the opening one.\n\tconst replies = count !== undefined ? replyCountLabel(msg, count - 1) : null\n\treturn (\n\t\t<Tag\n\t\t\t{...(href ? { href } : { type: 'button' })}\n\t\t\tclassName={\n\t\t\t\tselected ? 'tlui-cmt-list__item tlui-cmt-list__item--selected' : 'tlui-cmt-list__item'\n\t\t\t}\n\t\t\tdata-resolved={resolved || undefined}\n\t\t\tonClick={handleClick}\n\t\t>\n\t\t\t<Avatar author={author} />\n\t\t\t<div className=\"tlui-cmt-list__item-body\">\n\t\t\t\t<Byline author={author} date={date} />\n\t\t\t\t<div className=\"tlui-cmt-list__item-preview\">{preview}</div>\n\t\t\t\t{(resolved || page !== undefined || replies) && (\n\t\t\t\t\t<div className=\"tlui-cmt-list__item-meta\">\n\t\t\t\t\t\t{resolved && (\n\t\t\t\t\t\t\t<span className=\"tlui-cmt-list__item-resolved\">\n\t\t\t\t\t\t\t\t<CheckIcon />\n\t\t\t\t\t\t\t\t{resolvedLabel}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{page !== undefined && <span className=\"tlui-cmt-list__item-page\">{page}</span>}\n\t\t\t\t\t\t{replies && <span className=\"tlui-cmt-list__item-replies\">{replies}</span>}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</Tag>\n\t)\n}\n\n/**\n * Whether a click should be left to the browser's link handling (new tab, new window, or download).\n * @public\n */\nexport function isOpenInNewTabClick(e: MouseEvent) {\n\treturn e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0\n}\n\nfunction CheckIcon() {\n\treturn (\n\t\t<svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t<path\n\t\t\t\td=\"M2.5 6.2 4.7 8.4 9.5 3.6\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.5\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t)\n}\n"],
5
- "mappings": "AA+DI,SAC0B,KAD1B;AA/DJ,SAAS,cAAkC;AAE3C,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AACvB,SAAS,uBAAuB;AA+CzB,SAAS,aAAa;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAsB;AACrB,SACC,qBAAC,SAAI,WAAU,iBACZ;AAAA,gBAAW,UAAa,iBACzB,qBAAC,SAAI,WAAU,yBACb;AAAA,iBAAW,UAAa,oBAAC,UAAK,WAAU,+BAA+B,kBAAO;AAAA,MAC9E;AAAA,OACF;AAAA,IAEA,MAAM,WAAW,IACjB,oBAAC,SAAI,WAAU,wBAAwB,iBAAM,IAE7C,oBAAC,SAAI,WAAU,wBACb,gBAAM;AAAA,MAAI,CAAC,SACX,aACC,WAAW,IAAI,IAEf;AAAA,QAAC;AAAA;AAAA,UAEC,GAAG;AAAA,UACJ;AAAA,UACA;AAAA;AAAA,QAHK,KAAK;AAAA,MAIX;AAAA,IAEF,GACD;AAAA,KAEF;AAEF;AAEA,SAAS,gBAAgB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AACD,GAAmF;AAClF,QAAM,MAAM,eAAe;AAC3B,QAAM,cAAc,CAAC,MAAkB;AACtC,QAAI,QAAQ,oBAAoB,CAAC,EAAG;AACpC,MAAE,eAAe;AACjB,QAAI,SAAU,UAAS,EAAE;AAAA,EAC1B;AACA,QAAM,MAAM,OAAO,MAAM;AAEzB,QAAM,UAAU,UAAU,SAAY,gBAAgB,KAAK,QAAQ,CAAC,IAAI;AACxE,SACC;AAAA,IAAC;AAAA;AAAA,MACC,GAAI,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,SAAS;AAAA,MACxC,WACC,WAAW,sDAAsD;AAAA,MAElE,iBAAe,YAAY;AAAA,MAC3B,SAAS;AAAA,MAET;AAAA,4BAAC,UAAO,QAAgB;AAAA,QACxB,qBAAC,SAAI,WAAU,4BACd;AAAA,8BAAC,UAAO,QAAgB,MAAY;AAAA,UACpC,oBAAC,SAAI,WAAU,+BAA+B,mBAAQ;AAAA,WACpD,YAAY,SAAS,UAAa,YACnC,qBAAC,SAAI,WAAU,4BACb;AAAA,wBACA,qBAAC,UAAK,WAAU,gCACf;AAAA,kCAAC,aAAU;AAAA,cACV;AAAA,eACF;AAAA,YAEA,SAAS,UAAa,oBAAC,UAAK,WAAU,4BAA4B,gBAAK;AAAA,YACvE,WAAW,oBAAC,UAAK,WAAU,+BAA+B,mBAAQ;AAAA,aACpE;AAAA,WAEF;AAAA;AAAA;AAAA,EACD;AAEF;AAMO,SAAS,oBAAoB,GAAe;AAClD,SAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW;AACzE;AAEA,SAAS,YAAY;AACpB,SACC,oBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,eAAY,QACvE;AAAA,IAAC;AAAA;AAAA,MACA,GAAE;AAAA,MACF,QAAO;AAAA,MACP,aAAY;AAAA,MACZ,eAAc;AAAA,MACd,gBAAe;AAAA;AAAA,EAChB,GACD;AAEF;",
4
+ "sourcesContent": ["import { Avatar, type CommentAuthor } from '@tldraw/mentions'\nimport { Fragment, MouseEvent, ReactNode } from 'react'\nimport { useTranslation } from 'tldraw'\nimport { Byline } from './byline'\nimport { CheckIcon } from './icons'\nimport { Reactions, type ReactionSummary } from './reactions'\nimport { replyCountLabel } from './reply-count'\n\n/** @public */\nexport interface CommentListItemProps {\n\tid: string\n\tauthor: CommentAuthor\n\t/** A short preview of the thread \u2014 e.g. the first comment's body. */\n\tpreview: ReactNode\n\t/** ISO datetime of the thread's first comment. */\n\tdate: string\n\tresolved?: boolean\n\t/** Name of the page the thread lives on, shown as a small label. Omit to hide. */\n\tpage?: string\n\t/** Total comments in the thread. */\n\tcount?: number\n\t/** Whether this thread is the open one. */\n\tselected?: boolean\n\t/** Tallied reactions for the row (a thread's, or a single comment's when the row is one\n\t * comment), shown as inert pills under the preview. Omit to hide. */\n\treactions?: ReactionSummary[]\n\t/**\n\t * Link target for the item. When set, the row renders as an anchor so browser affordances\n\t * (ctrl/cmd-click, middle-click) open it in a new tab; a plain click still calls `onSelect`.\n\t */\n\thref?: string\n}\n\n/**\n * What a row is rendered with: the item, plus the list-level wiring it needs to be interactive.\n * A custom row gets the same props the default `<CommentListItem>` does, so it can wrap the\n * default rather than reimplement it.\n *\n * @public\n */\nexport interface CommentListItemRenderProps extends CommentListItemProps {\n\t/** Label for a resolved thread's marker on its row. */\n\tresolvedLabel?: string\n\t/** Called with the thread id when the row is chosen. */\n\tonSelect?(id: string): void\n}\n\n/** @public */\nexport interface CommentsListProps {\n\titems: CommentListItemProps[]\n\t/** Called with a thread id when an item is chosen. */\n\tonSelect?(id: string): void\n\t/** Shown above the list (e.g. \"Comments\"). Omit for none. */\n\theader?: ReactNode\n\t/** Rendered at the right of the header row \u2014 e.g. a filter menu. */\n\theaderAction?: ReactNode\n\t/** Shown in place of the list when there are no threads. */\n\tempty?: ReactNode\n\t/** Label for a resolved thread's marker on its row. Defaults to \"Resolved\". */\n\tresolvedLabel?: string\n\t/**\n\t * Override how each item renders. Defaults to `<CommentListItem>`, which is exported \u2014 so a\n\t * row that only adds something can spread these props into it rather than start over. The list\n\t * supplies the key, so a custom row doesn't need one.\n\t */\n\trenderItem?(props: CommentListItemRenderProps): ReactNode\n}\n\n/**\n * A scrollable list of comment threads \u2014 each an avatar, byline, and a one-line preview.\n * Presentational: you supply the items (already summarised) and an `onSelect` handler; the canvas\n * `CanvasCommentsSidebar` wires it to the store, but a consumer can build their own list from this.\n * @public @react\n */\nexport function CommentsList({\n\titems,\n\tonSelect,\n\theader,\n\theaderAction,\n\tempty,\n\tresolvedLabel,\n\trenderItem,\n}: CommentsListProps) {\n\treturn (\n\t\t<div className=\"tlui-cmt-list\">\n\t\t\t{(header !== undefined || headerAction) && (\n\t\t\t\t<div className=\"tlui-cmt-list__header\">\n\t\t\t\t\t{header !== undefined && <span className=\"tlui-cmt-list__header-title\">{header}</span>}\n\t\t\t\t\t{headerAction}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{items.length === 0 ? (\n\t\t\t\t<div className=\"tlui-cmt-list__empty\">{empty}</div>\n\t\t\t) : (\n\t\t\t\t<div className=\"tlui-cmt-list__items\">\n\t\t\t\t\t{items.map((item) => {\n\t\t\t\t\t\tconst props: CommentListItemRenderProps = { ...item, resolvedLabel, onSelect }\n\t\t\t\t\t\t// A custom row is wrapped rather than keyed directly: it's the consumer's element,\n\t\t\t\t\t\t// and requiring them to remember a key is the kind of thing that only shows up as a\n\t\t\t\t\t\t// console warning in someone else's app.\n\t\t\t\t\t\treturn renderItem ? (\n\t\t\t\t\t\t\t<Fragment key={item.id}>{renderItem(props)}</Fragment>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<CommentListItem key={item.id} {...props} />\n\t\t\t\t\t\t)\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n\n/** One thread's row in a {@link CommentsList}. @public @react */\nexport function CommentListItem({\n\tid,\n\tauthor,\n\tpreview,\n\tdate,\n\tresolved,\n\tpage,\n\tcount,\n\tselected,\n\treactions,\n\thref,\n\tresolvedLabel = 'Resolved',\n\tonSelect,\n}: CommentListItemRenderProps) {\n\tconst msg = useTranslation()\n\tconst handleClick = (e: MouseEvent) => {\n\t\tif (href && isOpenInNewTabClick(e)) return\n\t\te.preventDefault()\n\t\tif (onSelect) onSelect(id)\n\t}\n\tconst Tag = href ? 'a' : 'button'\n\t// `count` is the thread's total comments; a reply is every comment after the opening one.\n\tconst replies = count !== undefined ? replyCountLabel(msg, count - 1) : null\n\treturn (\n\t\t<Tag\n\t\t\t{...(href ? { href } : { type: 'button' })}\n\t\t\tclassName={\n\t\t\t\tselected ? 'tlui-cmt-list__item tlui-cmt-list__item--selected' : 'tlui-cmt-list__item'\n\t\t\t}\n\t\t\tdata-resolved={resolved || undefined}\n\t\t\tonClick={handleClick}\n\t\t>\n\t\t\t<Avatar author={author} />\n\t\t\t<div className=\"tlui-cmt-list__item-body\">\n\t\t\t\t<Byline author={author} date={date} />\n\t\t\t\t<div className=\"tlui-cmt-list__item-preview\">{preview}</div>\n\t\t\t\t{reactions && <Reactions reactions={reactions} canReact={false} enableHoverList={false} />}\n\t\t\t\t{(resolved || page !== undefined || replies) && (\n\t\t\t\t\t<div className=\"tlui-cmt-list__item-meta\">\n\t\t\t\t\t\t{resolved && (\n\t\t\t\t\t\t\t<span className=\"tlui-cmt-list__item-resolved\">\n\t\t\t\t\t\t\t\t<CheckIcon />\n\t\t\t\t\t\t\t\t{resolvedLabel}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{page !== undefined && <span className=\"tlui-cmt-list__item-page\">{page}</span>}\n\t\t\t\t\t\t{replies && <span className=\"tlui-cmt-list__item-replies\">{replies}</span>}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</Tag>\n\t)\n}\n\n/**\n * Whether a click should be left to the browser's link handling (new tab, new window, or download).\n * @public\n */\nexport function isOpenInNewTabClick(e: MouseEvent) {\n\treturn e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0\n}\n"],
5
+ "mappings": "AAsFI,SAC0B,KAD1B;AAtFJ,SAAS,cAAkC;AAC3C,SAAS,gBAAuC;AAChD,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,iBAAuC;AAChD,SAAS,uBAAuB;AAoEzB,SAAS,aAAa;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAsB;AACrB,SACC,qBAAC,SAAI,WAAU,iBACZ;AAAA,gBAAW,UAAa,iBACzB,qBAAC,SAAI,WAAU,yBACb;AAAA,iBAAW,UAAa,oBAAC,UAAK,WAAU,+BAA+B,kBAAO;AAAA,MAC9E;AAAA,OACF;AAAA,IAEA,MAAM,WAAW,IACjB,oBAAC,SAAI,WAAU,wBAAwB,iBAAM,IAE7C,oBAAC,SAAI,WAAU,wBACb,gBAAM,IAAI,CAAC,SAAS;AACpB,YAAM,QAAoC,EAAE,GAAG,MAAM,eAAe,SAAS;AAI7E,aAAO,aACN,oBAAC,YAAwB,qBAAW,KAAK,KAA1B,KAAK,EAAuB,IAE3C,oBAAC,mBAA+B,GAAG,SAAb,KAAK,EAAe;AAAA,IAE5C,CAAC,GACF;AAAA,KAEF;AAEF;AAGO,SAAS,gBAAgB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AACD,GAA+B;AAC9B,QAAM,MAAM,eAAe;AAC3B,QAAM,cAAc,CAAC,MAAkB;AACtC,QAAI,QAAQ,oBAAoB,CAAC,EAAG;AACpC,MAAE,eAAe;AACjB,QAAI,SAAU,UAAS,EAAE;AAAA,EAC1B;AACA,QAAM,MAAM,OAAO,MAAM;AAEzB,QAAM,UAAU,UAAU,SAAY,gBAAgB,KAAK,QAAQ,CAAC,IAAI;AACxE,SACC;AAAA,IAAC;AAAA;AAAA,MACC,GAAI,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,SAAS;AAAA,MACxC,WACC,WAAW,sDAAsD;AAAA,MAElE,iBAAe,YAAY;AAAA,MAC3B,SAAS;AAAA,MAET;AAAA,4BAAC,UAAO,QAAgB;AAAA,QACxB,qBAAC,SAAI,WAAU,4BACd;AAAA,8BAAC,UAAO,QAAgB,MAAY;AAAA,UACpC,oBAAC,SAAI,WAAU,+BAA+B,mBAAQ;AAAA,UACrD,aAAa,oBAAC,aAAU,WAAsB,UAAU,OAAO,iBAAiB,OAAO;AAAA,WACtF,YAAY,SAAS,UAAa,YACnC,qBAAC,SAAI,WAAU,4BACb;AAAA,wBACA,qBAAC,UAAK,WAAU,gCACf;AAAA,kCAAC,aAAU;AAAA,cACV;AAAA,eACF;AAAA,YAEA,SAAS,UAAa,oBAAC,UAAK,WAAU,4BAA4B,gBAAK;AAAA,YACvE,WAAW,oBAAC,UAAK,WAAU,+BAA+B,mBAAQ;AAAA,aACpE;AAAA,WAEF;AAAA;AAAA;AAAA,EACD;AAEF;AAMO,SAAS,oBAAoB,GAAe;AAClD,SAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW;AACzE;",
6
6
  "names": []
7
7
  }
@@ -23,7 +23,19 @@ function formatRelativeTime(iso, locale = "en") {
23
23
  }
24
24
  return "";
25
25
  }
26
+ const fullDateTimeFormatters = /* @__PURE__ */ new Map();
27
+ function formatFullDateTime(iso, locale = "en") {
28
+ const date = new Date(iso);
29
+ if (Number.isNaN(date.getTime())) return "";
30
+ let formatter = fullDateTimeFormatters.get(locale);
31
+ if (!formatter) {
32
+ formatter = new Intl.DateTimeFormat(locale, { dateStyle: "full", timeStyle: "short" });
33
+ fullDateTimeFormatters.set(locale, formatter);
34
+ }
35
+ return formatter.format(date);
36
+ }
26
37
  export {
38
+ formatFullDateTime,
27
39
  formatRelativeTime
28
40
  };
29
41
  //# sourceMappingURL=format-time.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/format-time.ts"],
4
- "sourcesContent": ["const DIVISIONS = [\n\t{ amount: 60, unit: 'second' },\n\t{ amount: 60, unit: 'minute' },\n\t{ amount: 24, unit: 'hour' },\n\t{ amount: 7, unit: 'day' },\n\t{ amount: 4.34524, unit: 'week' },\n\t{ amount: 12, unit: 'month' },\n\t{ amount: Number.POSITIVE_INFINITY, unit: 'year' },\n] as const\n\n/**\n * Format an ISO datetime as short relative time (\"2 hr. ago\", \"yesterday\", \"last wk.\").\n * Locale-aware via Intl.RelativeTimeFormat.\n * @public\n */\nexport function formatRelativeTime(iso: string, locale = 'en'): string {\n\tconst then = new Date(iso).getTime()\n\tif (Number.isNaN(then)) return ''\n\n\tconst rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto', style: 'short' })\n\tlet duration = (then - Date.now()) / 1000\n\t// Under a minute is just \"now\" \u2014 with second granularity the label visibly ticks while\n\t// typing a reply (every keystroke re-renders the card).\n\tif (Math.abs(duration) < 60) {\n\t\treturn rtf.format(0, 'second')\n\t}\n\tfor (const division of DIVISIONS) {\n\t\tif (Math.abs(duration) < division.amount) {\n\t\t\treturn rtf.format(Math.round(duration), division.unit)\n\t\t}\n\t\tduration /= division.amount\n\t}\n\treturn ''\n}\n"],
5
- "mappings": "AAAA,MAAM,YAAY;AAAA,EACjB,EAAE,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC7B,EAAE,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC7B,EAAE,QAAQ,IAAI,MAAM,OAAO;AAAA,EAC3B,EAAE,QAAQ,GAAG,MAAM,MAAM;AAAA,EACzB,EAAE,QAAQ,SAAS,MAAM,OAAO;AAAA,EAChC,EAAE,QAAQ,IAAI,MAAM,QAAQ;AAAA,EAC5B,EAAE,QAAQ,OAAO,mBAAmB,MAAM,OAAO;AAClD;AAOO,SAAS,mBAAmB,KAAa,SAAS,MAAc;AACtE,QAAM,OAAO,IAAI,KAAK,GAAG,EAAE,QAAQ;AACnC,MAAI,OAAO,MAAM,IAAI,EAAG,QAAO;AAE/B,QAAM,MAAM,IAAI,KAAK,mBAAmB,QAAQ,EAAE,SAAS,QAAQ,OAAO,QAAQ,CAAC;AACnF,MAAI,YAAY,OAAO,KAAK,IAAI,KAAK;AAGrC,MAAI,KAAK,IAAI,QAAQ,IAAI,IAAI;AAC5B,WAAO,IAAI,OAAO,GAAG,QAAQ;AAAA,EAC9B;AACA,aAAW,YAAY,WAAW;AACjC,QAAI,KAAK,IAAI,QAAQ,IAAI,SAAS,QAAQ;AACzC,aAAO,IAAI,OAAO,KAAK,MAAM,QAAQ,GAAG,SAAS,IAAI;AAAA,IACtD;AACA,gBAAY,SAAS;AAAA,EACtB;AACA,SAAO;AACR;",
4
+ "sourcesContent": ["const DIVISIONS = [\n\t{ amount: 60, unit: 'second' },\n\t{ amount: 60, unit: 'minute' },\n\t{ amount: 24, unit: 'hour' },\n\t{ amount: 7, unit: 'day' },\n\t{ amount: 4.34524, unit: 'week' },\n\t{ amount: 12, unit: 'month' },\n\t{ amount: Number.POSITIVE_INFINITY, unit: 'year' },\n] as const\n\n/**\n * Format an ISO datetime as short relative time (\"2 hr. ago\", \"yesterday\", \"last wk.\").\n * Locale-aware via Intl.RelativeTimeFormat.\n * @public\n */\nexport function formatRelativeTime(iso: string, locale = 'en'): string {\n\tconst then = new Date(iso).getTime()\n\tif (Number.isNaN(then)) return ''\n\n\tconst rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto', style: 'short' })\n\tlet duration = (then - Date.now()) / 1000\n\t// Under a minute is just \"now\" \u2014 with second granularity the label visibly ticks while\n\t// typing a reply (every keystroke re-renders the card).\n\tif (Math.abs(duration) < 60) {\n\t\treturn rtf.format(0, 'second')\n\t}\n\tfor (const division of DIVISIONS) {\n\t\tif (Math.abs(duration) < division.amount) {\n\t\t\treturn rtf.format(Math.round(duration), division.unit)\n\t\t}\n\t\tduration /= division.amount\n\t}\n\treturn ''\n}\n\n// Cached per locale \u2014 comment cards re-render on every reply keystroke, and constructing a\n// DateTimeFormat is far more expensive than formatting with one.\nconst fullDateTimeFormatters = new Map<string, Intl.DateTimeFormat>()\n\n/**\n * Format an ISO datetime as a full date and time (\"Tuesday, July 22, 2025 at 4:44 PM\").\n * Locale-aware via Intl.DateTimeFormat.\n * @public\n */\nexport function formatFullDateTime(iso: string, locale = 'en'): string {\n\tconst date = new Date(iso)\n\tif (Number.isNaN(date.getTime())) return ''\n\tlet formatter = fullDateTimeFormatters.get(locale)\n\tif (!formatter) {\n\t\tformatter = new Intl.DateTimeFormat(locale, { dateStyle: 'full', timeStyle: 'short' })\n\t\tfullDateTimeFormatters.set(locale, formatter)\n\t}\n\treturn formatter.format(date)\n}\n"],
5
+ "mappings": "AAAA,MAAM,YAAY;AAAA,EACjB,EAAE,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC7B,EAAE,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC7B,EAAE,QAAQ,IAAI,MAAM,OAAO;AAAA,EAC3B,EAAE,QAAQ,GAAG,MAAM,MAAM;AAAA,EACzB,EAAE,QAAQ,SAAS,MAAM,OAAO;AAAA,EAChC,EAAE,QAAQ,IAAI,MAAM,QAAQ;AAAA,EAC5B,EAAE,QAAQ,OAAO,mBAAmB,MAAM,OAAO;AAClD;AAOO,SAAS,mBAAmB,KAAa,SAAS,MAAc;AACtE,QAAM,OAAO,IAAI,KAAK,GAAG,EAAE,QAAQ;AACnC,MAAI,OAAO,MAAM,IAAI,EAAG,QAAO;AAE/B,QAAM,MAAM,IAAI,KAAK,mBAAmB,QAAQ,EAAE,SAAS,QAAQ,OAAO,QAAQ,CAAC;AACnF,MAAI,YAAY,OAAO,KAAK,IAAI,KAAK;AAGrC,MAAI,KAAK,IAAI,QAAQ,IAAI,IAAI;AAC5B,WAAO,IAAI,OAAO,GAAG,QAAQ;AAAA,EAC9B;AACA,aAAW,YAAY,WAAW;AACjC,QAAI,KAAK,IAAI,QAAQ,IAAI,SAAS,QAAQ;AACzC,aAAO,IAAI,OAAO,KAAK,MAAM,QAAQ,GAAG,SAAS,IAAI;AAAA,IACtD;AACA,gBAAY,SAAS;AAAA,EACtB;AACA,SAAO;AACR;AAIA,MAAM,yBAAyB,oBAAI,IAAiC;AAO7D,SAAS,mBAAmB,KAAa,SAAS,MAAc;AACtE,QAAM,OAAO,IAAI,KAAK,GAAG;AACzB,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO;AACzC,MAAI,YAAY,uBAAuB,IAAI,MAAM;AACjD,MAAI,CAAC,WAAW;AACf,gBAAY,IAAI,KAAK,eAAe,QAAQ,EAAE,WAAW,QAAQ,WAAW,QAAQ,CAAC;AACrF,2BAAuB,IAAI,QAAQ,SAAS;AAAA,EAC7C;AACA,SAAO,UAAU,OAAO,IAAI;AAC7B;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,95 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ function PinCheckIcon() {
3
+ return /* @__PURE__ */ jsx(
4
+ "svg",
5
+ {
6
+ viewBox: "0 0 24 24",
7
+ width: "15",
8
+ height: "15",
9
+ fill: "none",
10
+ stroke: "currentColor",
11
+ strokeWidth: "2.5",
12
+ strokeLinecap: "round",
13
+ strokeLinejoin: "round",
14
+ "aria-hidden": "true",
15
+ children: /* @__PURE__ */ jsx("path", { d: "M4 12.5l5 5L20 6.5" })
16
+ }
17
+ );
18
+ }
19
+ function CheckIcon() {
20
+ return /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
21
+ "path",
22
+ {
23
+ d: "M2.5 6.2 4.7 8.4 9.5 3.6",
24
+ stroke: "currentColor",
25
+ strokeWidth: "1.5",
26
+ strokeLinecap: "round",
27
+ strokeLinejoin: "round"
28
+ }
29
+ ) });
30
+ }
31
+ function SmileyIcon() {
32
+ return /* @__PURE__ */ jsx("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
33
+ "path",
34
+ {
35
+ d: "M7.49991 0.876892C3.84222 0.876892 0.877075 3.84204 0.877075 7.49972C0.877075 11.1574 3.84222 14.1226 7.49991 14.1226C11.1576 14.1226 14.1227 11.1574 14.1227 7.49972C14.1227 3.84204 11.1576 0.876892 7.49991 0.876892ZM1.82708 7.49972C1.82708 4.36671 4.36689 1.82689 7.49991 1.82689C10.6329 1.82689 13.1727 4.36671 13.1727 7.49972C13.1727 10.6327 10.6329 13.1726 7.49991 13.1726C4.36689 13.1726 1.82708 10.6327 1.82708 7.49972ZM5.03747 9.21395C4.87949 8.98746 4.56782 8.93193 4.34133 9.08991C4.11484 9.24789 4.05931 9.55956 4.21729 9.78605C4.93926 10.8211 6.14033 11.5 7.50004 11.5C8.85974 11.5 10.0608 10.8211 10.7828 9.78605C10.9408 9.55956 10.8852 9.24789 10.6587 9.08991C10.4323 8.93193 10.1206 8.98746 9.9626 9.21395C9.41963 9.99238 8.51907 10.5 7.50004 10.5C6.481 10.5 5.58044 9.99238 5.03747 9.21395ZM5.37503 6.84998C5.85828 6.84998 6.25003 6.45815 6.25003 5.97498C6.25003 5.4918 5.85828 5.09998 5.37503 5.09998C4.89179 5.09998 4.50003 5.4918 4.50003 5.97498C4.50003 6.45815 4.89179 6.84998 5.37503 6.84998ZM10.5 5.97498C10.5 6.45815 10.1083 6.84998 9.62503 6.84998C9.14179 6.84998 8.75003 6.45815 8.75003 5.97498C8.75003 5.4918 9.14179 5.09998 9.62503 5.09998C10.1083 5.09998 10.5 5.4918 10.5 5.97498Z",
36
+ fill: "currentColor",
37
+ fillRule: "evenodd",
38
+ clipRule: "evenodd"
39
+ }
40
+ ) });
41
+ }
42
+ function SendIcon() {
43
+ return /* @__PURE__ */ jsx("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
44
+ "path",
45
+ {
46
+ d: "M7.14645 2.14645C7.34171 1.95118 7.65829 1.95118 7.85355 2.14645L11.8536 6.14645C12.0488 6.34171 12.0488 6.65829 11.8536 6.85355C11.6583 7.04882 11.3417 7.04882 11.1464 6.85355L8 3.70711L8 12.5C8 12.7761 7.77614 13 7.5 13C7.22386 13 7 12.7761 7 12.5L7 3.70711L3.85355 6.85355C3.65829 7.04882 3.34171 7.04882 3.14645 6.85355C2.95118 6.65829 2.95118 6.34171 3.14645 6.14645L7.14645 2.14645Z",
47
+ fill: "currentColor",
48
+ fillRule: "evenodd",
49
+ clipRule: "evenodd"
50
+ }
51
+ ) });
52
+ }
53
+ function EyeOpenIcon() {
54
+ return /* @__PURE__ */ jsx("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
55
+ "path",
56
+ {
57
+ d: "M7.5 11C4.80285 11 2.52952 9.62184 1.09622 7.50001C2.52952 5.37816 4.80285 4 7.5 4C10.1971 4 12.4705 5.37816 13.9038 7.5C12.4705 9.62183 10.1971 11 7.5 11ZM7.5 3C4.30786 3 1.65639 4.70638 0.0760002 7.23501C-0.0253338 7.39715 -0.0253334 7.60288 0.0760014 7.76501C1.65639 10.2936 4.30786 12 7.5 12C10.6921 12 13.3436 10.2936 14.924 7.76501C15.0253 7.60288 15.0253 7.39715 14.924 7.23501C13.3436 4.70638 10.6921 3 7.5 3ZM7.5 9.5C8.60457 9.5 9.5 8.60457 9.5 7.5C9.5 6.39543 8.60457 5.5 7.5 5.5C6.39543 5.5 5.5 6.39543 5.5 7.5C5.5 8.60457 6.39543 9.5 7.5 9.5Z",
58
+ fill: "currentColor",
59
+ fillRule: "evenodd",
60
+ clipRule: "evenodd"
61
+ }
62
+ ) });
63
+ }
64
+ function EyeClosedIcon() {
65
+ return /* @__PURE__ */ jsx("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
66
+ "path",
67
+ {
68
+ d: "M14.7649 6.07596C14.9991 6.22231 15.0703 6.53079 14.9239 6.76495C14.4849 7.46743 13.9632 8.10645 13.3702 8.66305L14.5712 9.86406C14.7664 10.0593 14.7664 10.3759 14.5712 10.5712C14.3759 10.7664 14.0593 10.7664 13.8641 10.5712L12.6011 9.30817C11.805 9.90283 10.9089 10.3621 9.93375 10.651L10.383 12.3277C10.4544 12.5944 10.2961 12.8685 10.0294 12.94C9.76267 13.0115 9.4885 12.8532 9.41704 12.5865L8.95917 10.8775C8.48743 10.958 8.00036 11 7.50001 11C6.99965 11 6.51257 10.958 6.04082 10.8775L5.58299 12.5864C5.51153 12.8532 5.23737 13.0115 4.97064 12.94C4.7039 12.8686 4.5456 12.5944 4.61706 12.3277L5.06625 10.651C4.09111 10.3621 3.19503 9.90282 2.39889 9.30815L1.1359 10.5712C0.940638 10.7664 0.624058 10.7664 0.428798 10.5712C0.233537 10.3759 0.233537 10.0593 0.428798 9.86405L1.62982 8.66303C1.03682 8.10643 0.515113 7.46742 0.0760677 6.76495C-0.0702867 6.53079 0.000898544 6.22231 0.235065 6.07596C0.469231 5.9296 0.777703 6.00079 0.924058 6.23496C1.40354 7.00213 1.989 7.68057 2.66233 8.2427C2.67315 8.25096 2.6837 8.25972 2.69397 8.26898C4.00897 9.35527 5.65537 10 7.50001 10C10.3078 10 12.6564 8.5063 14.076 6.23495C14.2223 6.00079 14.5308 5.9296 14.7649 6.07596Z",
69
+ fill: "currentColor",
70
+ fillRule: "evenodd",
71
+ clipRule: "evenodd"
72
+ }
73
+ ) });
74
+ }
75
+ function FilterIcon() {
76
+ return /* @__PURE__ */ jsx("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
77
+ "path",
78
+ {
79
+ 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",
80
+ fill: "currentColor",
81
+ fillRule: "evenodd",
82
+ clipRule: "evenodd"
83
+ }
84
+ ) });
85
+ }
86
+ export {
87
+ CheckIcon,
88
+ EyeClosedIcon,
89
+ EyeOpenIcon,
90
+ FilterIcon,
91
+ PinCheckIcon,
92
+ SendIcon,
93
+ SmileyIcon
94
+ };
95
+ //# sourceMappingURL=icons.mjs.map