@tldraw/commenting 0.0.0-bootstrap → 5.3.0-canary.04044ed9e96d

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 (338) hide show
  1. package/README.md +0 -2
  2. package/commenting.css +1152 -350
  3. package/dist-cjs/canvas/anchor-lifecycle.js +148 -0
  4. package/dist-cjs/canvas/anchor-lifecycle.js.map +7 -0
  5. package/dist-cjs/{ui/avatar.js → canvas/canvas-events.js} +14 -22
  6. package/dist-cjs/canvas/canvas-events.js.map +7 -0
  7. package/dist-cjs/canvas/cluster-badge.js +121 -0
  8. package/dist-cjs/canvas/cluster-badge.js.map +7 -0
  9. package/dist-cjs/canvas/cluster-fade.js +85 -0
  10. package/dist-cjs/canvas/cluster-fade.js.map +7 -0
  11. package/dist-cjs/canvas/cluster-input.js +53 -3
  12. package/dist-cjs/canvas/cluster-input.js.map +2 -2
  13. package/dist-cjs/canvas/cluster-model.js +252 -0
  14. package/dist-cjs/canvas/cluster-model.js.map +7 -0
  15. package/dist-cjs/canvas/comment-body.js +1 -1
  16. package/dist-cjs/canvas/comment-body.js.map +2 -2
  17. package/dist-cjs/canvas/comment-drafts.js +54 -0
  18. package/dist-cjs/canvas/comment-drafts.js.map +7 -0
  19. package/dist-cjs/canvas/comment-mutations.js +152 -0
  20. package/dist-cjs/canvas/comment-mutations.js.map +7 -0
  21. package/dist-cjs/canvas/comment-reactions.js +160 -0
  22. package/dist-cjs/canvas/comment-reactions.js.map +7 -0
  23. package/dist-cjs/canvas/comment-render.js +9 -6
  24. package/dist-cjs/canvas/comment-render.js.map +2 -2
  25. package/dist-cjs/canvas/comment-store.js +19 -9
  26. package/dist-cjs/canvas/comment-store.js.map +2 -2
  27. package/dist-cjs/canvas/comment-tool.js +63 -14
  28. package/dist-cjs/canvas/comment-tool.js.map +2 -2
  29. package/dist-cjs/canvas/comments-filter-menu.js +17 -37
  30. package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
  31. package/dist-cjs/canvas/comments-overflow-menu.js +25 -22
  32. package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
  33. package/dist-cjs/canvas/comments-overlay.js +151 -989
  34. package/dist-cjs/canvas/comments-overlay.js.map +3 -3
  35. package/dist-cjs/canvas/comments-sidebar.js +49 -41
  36. package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
  37. package/dist-cjs/canvas/comments-visibility-toggle.js +45 -0
  38. package/dist-cjs/canvas/comments-visibility-toggle.js.map +7 -0
  39. package/dist-cjs/canvas/context.js +17 -0
  40. package/dist-cjs/canvas/context.js.map +7 -0
  41. package/dist-cjs/canvas/hooks.js +28 -3
  42. package/dist-cjs/canvas/hooks.js.map +2 -2
  43. package/dist-cjs/canvas/license.js.map +1 -1
  44. package/dist-cjs/canvas/mobile-placement.js +91 -0
  45. package/dist-cjs/canvas/mobile-placement.js.map +7 -0
  46. package/dist-cjs/canvas/options.js +57 -2
  47. package/dist-cjs/canvas/options.js.map +2 -2
  48. package/dist-cjs/canvas/pending-composer.js +134 -0
  49. package/dist-cjs/canvas/pending-composer.js.map +7 -0
  50. package/dist-cjs/canvas/pin-stacking.js +81 -0
  51. package/dist-cjs/canvas/pin-stacking.js.map +7 -0
  52. package/dist-cjs/canvas/region-box.js +113 -0
  53. package/dist-cjs/canvas/region-box.js.map +7 -0
  54. package/dist-cjs/canvas/sidebar-filters.js +2 -2
  55. package/dist-cjs/canvas/sidebar-filters.js.map +2 -2
  56. package/dist-cjs/canvas/state.js +28 -14
  57. package/dist-cjs/canvas/state.js.map +2 -2
  58. package/dist-cjs/canvas/thread-pin.js +304 -0
  59. package/dist-cjs/canvas/thread-pin.js.map +7 -0
  60. package/dist-cjs/canvas/thread-preview.js +177 -0
  61. package/dist-cjs/canvas/thread-preview.js.map +7 -0
  62. package/dist-cjs/canvas/thread-stack.js +199 -0
  63. package/dist-cjs/canvas/thread-stack.js.map +7 -0
  64. package/dist-cjs/canvas/thread-state.js +82 -21
  65. package/dist-cjs/canvas/thread-state.js.map +2 -2
  66. package/dist-cjs/canvas/thread-view.js +450 -0
  67. package/dist-cjs/canvas/thread-view.js.map +7 -0
  68. package/dist-cjs/clustering/computeClusterTable.js +6 -6
  69. package/dist-cjs/clustering/computeClusterTable.js.map +2 -2
  70. package/dist-cjs/clustering/replay.js +89 -22
  71. package/dist-cjs/clustering/replay.js.map +3 -3
  72. package/dist-cjs/clustering/runtime.js +126 -7
  73. package/dist-cjs/clustering/runtime.js.map +2 -2
  74. package/dist-cjs/clustering/types.js.map +1 -1
  75. package/dist-cjs/index.d.ts +904 -353
  76. package/dist-cjs/index.js +52 -28
  77. package/dist-cjs/index.js.map +2 -2
  78. package/dist-cjs/ui/byline.js +10 -6
  79. package/dist-cjs/ui/byline.js.map +2 -2
  80. package/dist-cjs/ui/comment-card.js +15 -8
  81. package/dist-cjs/ui/comment-card.js.map +2 -2
  82. package/dist-cjs/ui/comment-composer.js +124 -39
  83. package/dist-cjs/ui/comment-composer.js.map +2 -2
  84. package/dist-cjs/ui/comment-pin.js +11 -2
  85. package/dist-cjs/ui/comment-pin.js.map +2 -2
  86. package/dist-cjs/ui/comment-thread.js +9 -7
  87. package/dist-cjs/ui/comment-thread.js.map +2 -2
  88. package/dist-cjs/ui/comments-list.js +39 -40
  89. package/dist-cjs/ui/comments-list.js.map +2 -2
  90. package/dist-cjs/ui/count-badge.js +3 -2
  91. package/dist-cjs/ui/count-badge.js.map +2 -2
  92. package/dist-cjs/ui/emoji-picker.js +54 -0
  93. package/dist-cjs/ui/emoji-picker.js.map +7 -0
  94. package/dist-cjs/ui/empty-state.js +3 -3
  95. package/dist-cjs/ui/empty-state.js.map +2 -2
  96. package/dist-cjs/ui/format-time.js +16 -1
  97. package/dist-cjs/ui/format-time.js.map +2 -2
  98. package/dist-cjs/ui/icons.js +115 -0
  99. package/dist-cjs/ui/icons.js.map +7 -0
  100. package/dist-cjs/ui/reaction-picker.js +71 -0
  101. package/dist-cjs/ui/reaction-picker.js.map +7 -0
  102. package/dist-cjs/ui/reaction.js +58 -6
  103. package/dist-cjs/ui/reaction.js.map +2 -2
  104. package/dist-cjs/ui/reactions.js +20 -7
  105. package/dist-cjs/ui/reactions.js.map +2 -2
  106. package/dist-cjs/ui/{mention.js → reply-count.js} +9 -11
  107. package/dist-cjs/ui/reply-count.js.map +7 -0
  108. package/dist-cjs/ui/send-button.js +13 -1
  109. package/dist-cjs/ui/send-button.js.map +2 -2
  110. package/dist-cjs/ui/{comment-text.js → visual-viewport.js} +12 -9
  111. package/dist-cjs/ui/visual-viewport.js.map +7 -0
  112. package/dist-esm/canvas/anchor-lifecycle.mjs +128 -0
  113. package/dist-esm/canvas/anchor-lifecycle.mjs.map +7 -0
  114. package/dist-esm/canvas/canvas-events.mjs +15 -0
  115. package/dist-esm/canvas/canvas-events.mjs.map +7 -0
  116. package/dist-esm/canvas/cluster-badge.mjs +106 -0
  117. package/dist-esm/canvas/cluster-badge.mjs.map +7 -0
  118. package/dist-esm/canvas/cluster-fade.mjs +65 -0
  119. package/dist-esm/canvas/cluster-fade.mjs.map +7 -0
  120. package/dist-esm/canvas/cluster-input.mjs +54 -4
  121. package/dist-esm/canvas/cluster-input.mjs.map +2 -2
  122. package/dist-esm/canvas/cluster-model.mjs +236 -0
  123. package/dist-esm/canvas/cluster-model.mjs.map +7 -0
  124. package/dist-esm/canvas/comment-body.mjs +1 -1
  125. package/dist-esm/canvas/comment-body.mjs.map +2 -2
  126. package/dist-esm/canvas/comment-drafts.mjs +34 -0
  127. package/dist-esm/canvas/comment-drafts.mjs.map +7 -0
  128. package/dist-esm/canvas/comment-mutations.mjs +132 -0
  129. package/dist-esm/canvas/comment-mutations.mjs.map +7 -0
  130. package/dist-esm/canvas/comment-reactions.mjs +145 -0
  131. package/dist-esm/canvas/comment-reactions.mjs.map +7 -0
  132. package/dist-esm/canvas/comment-render.mjs +10 -7
  133. package/dist-esm/canvas/comment-render.mjs.map +2 -2
  134. package/dist-esm/canvas/comment-store.mjs +19 -9
  135. package/dist-esm/canvas/comment-store.mjs.map +2 -2
  136. package/dist-esm/canvas/comment-tool.mjs +66 -17
  137. package/dist-esm/canvas/comment-tool.mjs.map +2 -2
  138. package/dist-esm/canvas/comments-filter-menu.mjs +17 -36
  139. package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
  140. package/dist-esm/canvas/comments-overflow-menu.mjs +27 -22
  141. package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
  142. package/dist-esm/canvas/comments-overlay.mjs +157 -1010
  143. package/dist-esm/canvas/comments-overlay.mjs.map +3 -3
  144. package/dist-esm/canvas/comments-sidebar.mjs +53 -46
  145. package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
  146. package/dist-esm/canvas/comments-visibility-toggle.mjs +25 -0
  147. package/dist-esm/canvas/comments-visibility-toggle.mjs.map +7 -0
  148. package/dist-esm/canvas/context.mjs +1 -0
  149. package/dist-esm/canvas/context.mjs.map +7 -0
  150. package/dist-esm/canvas/hooks.mjs +35 -5
  151. package/dist-esm/canvas/hooks.mjs.map +2 -2
  152. package/dist-esm/canvas/license.mjs.map +1 -1
  153. package/dist-esm/canvas/mobile-placement.mjs +71 -0
  154. package/dist-esm/canvas/mobile-placement.mjs.map +7 -0
  155. package/dist-esm/canvas/options.mjs +59 -3
  156. package/dist-esm/canvas/options.mjs.map +2 -2
  157. package/dist-esm/canvas/pending-composer.mjs +126 -0
  158. package/dist-esm/canvas/pending-composer.mjs.map +7 -0
  159. package/dist-esm/canvas/pin-stacking.mjs +61 -0
  160. package/dist-esm/canvas/pin-stacking.mjs.map +7 -0
  161. package/dist-esm/canvas/region-box.mjs +93 -0
  162. package/dist-esm/canvas/region-box.mjs.map +7 -0
  163. package/dist-esm/canvas/sidebar-filters.mjs +2 -2
  164. package/dist-esm/canvas/sidebar-filters.mjs.map +2 -2
  165. package/dist-esm/canvas/state.mjs +29 -19
  166. package/dist-esm/canvas/state.mjs.map +2 -2
  167. package/dist-esm/canvas/thread-pin.mjs +310 -0
  168. package/dist-esm/canvas/thread-pin.mjs.map +7 -0
  169. package/dist-esm/canvas/thread-preview.mjs +163 -0
  170. package/dist-esm/canvas/thread-preview.mjs.map +7 -0
  171. package/dist-esm/canvas/thread-stack.mjs +189 -0
  172. package/dist-esm/canvas/thread-stack.mjs.map +7 -0
  173. package/dist-esm/canvas/thread-state.mjs +86 -22
  174. package/dist-esm/canvas/thread-state.mjs.map +2 -2
  175. package/dist-esm/canvas/thread-view.mjs +461 -0
  176. package/dist-esm/canvas/thread-view.mjs.map +7 -0
  177. package/dist-esm/clustering/computeClusterTable.mjs +6 -6
  178. package/dist-esm/clustering/computeClusterTable.mjs.map +2 -2
  179. package/dist-esm/clustering/replay.mjs +89 -22
  180. package/dist-esm/clustering/replay.mjs.map +3 -3
  181. package/dist-esm/clustering/runtime.mjs +126 -7
  182. package/dist-esm/clustering/runtime.mjs.map +2 -2
  183. package/dist-esm/index.d.mts +904 -353
  184. package/dist-esm/index.mjs +102 -43
  185. package/dist-esm/index.mjs.map +2 -2
  186. package/dist-esm/ui/byline.mjs +11 -7
  187. package/dist-esm/ui/byline.mjs.map +2 -2
  188. package/dist-esm/ui/comment-card.mjs +15 -8
  189. package/dist-esm/ui/comment-card.mjs.map +2 -2
  190. package/dist-esm/ui/comment-composer.mjs +135 -39
  191. package/dist-esm/ui/comment-composer.mjs.map +2 -2
  192. package/dist-esm/ui/comment-pin.mjs +11 -2
  193. package/dist-esm/ui/comment-pin.mjs.map +2 -2
  194. package/dist-esm/ui/comment-thread.mjs +9 -7
  195. package/dist-esm/ui/comment-thread.mjs.map +2 -2
  196. package/dist-esm/ui/comments-list.mjs +51 -50
  197. package/dist-esm/ui/comments-list.mjs.map +2 -2
  198. package/dist-esm/ui/count-badge.mjs +3 -2
  199. package/dist-esm/ui/count-badge.mjs.map +2 -2
  200. package/dist-esm/ui/emoji-picker.mjs +34 -0
  201. package/dist-esm/ui/emoji-picker.mjs.map +7 -0
  202. package/dist-esm/ui/empty-state.mjs +3 -3
  203. package/dist-esm/ui/empty-state.mjs.map +2 -2
  204. package/dist-esm/ui/format-time.mjs +16 -1
  205. package/dist-esm/ui/format-time.mjs.map +2 -2
  206. package/dist-esm/ui/icons.mjs +95 -0
  207. package/dist-esm/ui/icons.mjs.map +7 -0
  208. package/dist-esm/ui/reaction-picker.mjs +59 -0
  209. package/dist-esm/ui/reaction-picker.mjs.map +7 -0
  210. package/dist-esm/ui/reaction.mjs +59 -7
  211. package/dist-esm/ui/reaction.mjs.map +2 -2
  212. package/dist-esm/ui/reactions.mjs +21 -8
  213. package/dist-esm/ui/reactions.mjs.map +2 -2
  214. package/dist-esm/ui/reply-count.mjs +9 -0
  215. package/dist-esm/ui/reply-count.mjs.map +7 -0
  216. package/dist-esm/ui/send-button.mjs +13 -1
  217. package/dist-esm/ui/send-button.mjs.map +2 -2
  218. package/dist-esm/ui/visual-viewport.mjs +12 -0
  219. package/dist-esm/ui/visual-viewport.mjs.map +7 -0
  220. package/package.json +6 -6
  221. package/src/canvas/anchor-lifecycle.test.ts +290 -0
  222. package/src/canvas/anchor-lifecycle.ts +191 -0
  223. package/src/canvas/canvas-events.ts +18 -0
  224. package/src/canvas/canvas.css +464 -55
  225. package/src/canvas/cluster-badge.tsx +133 -0
  226. package/src/canvas/cluster-fade.ts +102 -0
  227. package/src/canvas/cluster-input.test.ts +178 -36
  228. package/src/canvas/cluster-input.ts +95 -9
  229. package/src/canvas/cluster-model.ts +378 -0
  230. package/src/canvas/comment-body.tsx +1 -1
  231. package/src/canvas/comment-drafts.test.ts +55 -0
  232. package/src/canvas/comment-drafts.ts +46 -0
  233. package/src/canvas/comment-mutations.test.ts +452 -0
  234. package/src/canvas/comment-mutations.ts +299 -0
  235. package/src/canvas/comment-reactions.test.ts +154 -0
  236. package/src/canvas/comment-reactions.tsx +246 -0
  237. package/src/canvas/comment-render.test.ts +1 -1
  238. package/src/canvas/comment-render.ts +17 -13
  239. package/src/canvas/comment-store.ts +68 -33
  240. package/src/canvas/comment-tool.test.ts +162 -0
  241. package/src/canvas/comment-tool.tsx +103 -25
  242. package/src/canvas/comments-filter-menu.tsx +20 -36
  243. package/src/canvas/comments-overflow-menu.tsx +17 -19
  244. package/src/canvas/comments-overlay.tsx +252 -1369
  245. package/src/canvas/comments-sidebar.test.ts +45 -0
  246. package/src/canvas/comments-sidebar.tsx +112 -73
  247. package/src/canvas/comments-visibility-toggle.tsx +25 -0
  248. package/src/canvas/context.ts +48 -0
  249. package/src/canvas/hooks.test.ts +94 -0
  250. package/src/canvas/hooks.ts +66 -11
  251. package/src/canvas/license.ts +1 -1
  252. package/src/canvas/mobile-placement.ts +115 -0
  253. package/src/canvas/options.test.ts +176 -4
  254. package/src/canvas/options.ts +276 -18
  255. package/src/canvas/pending-composer.tsx +157 -0
  256. package/src/canvas/pin-stacking.test.ts +224 -0
  257. package/src/canvas/pin-stacking.ts +103 -0
  258. package/src/canvas/region-box.tsx +124 -0
  259. package/src/canvas/sidebar-filters.ts +4 -3
  260. package/src/canvas/state.ts +88 -39
  261. package/src/canvas/thread-pin.tsx +418 -0
  262. package/src/canvas/thread-preview.test.ts +91 -0
  263. package/src/canvas/thread-preview.tsx +294 -0
  264. package/src/canvas/thread-stack.tsx +242 -0
  265. package/src/canvas/thread-state.test.ts +260 -0
  266. package/src/canvas/thread-state.ts +171 -37
  267. package/src/canvas/thread-view.test.ts +72 -0
  268. package/src/canvas/thread-view.tsx +614 -0
  269. package/src/clustering/computeClusterTable.test.ts +24 -23
  270. package/src/clustering/computeClusterTable.ts +17 -8
  271. package/src/clustering/replay.test.ts +0 -7
  272. package/src/clustering/replay.ts +131 -32
  273. package/src/clustering/runtime.test.ts +267 -13
  274. package/src/clustering/runtime.ts +176 -19
  275. package/src/clustering/schedule.test.ts +0 -6
  276. package/src/clustering/screen-offsets.test.ts +171 -0
  277. package/src/clustering/types.ts +19 -9
  278. package/src/index.ts +91 -42
  279. package/src/ui/byline.tsx +21 -9
  280. package/src/ui/comment-card.tsx +20 -10
  281. package/src/ui/comment-composer.tsx +178 -56
  282. package/src/ui/comment-pin.tsx +13 -2
  283. package/src/ui/comment-thread.tsx +13 -6
  284. package/src/ui/comments-list.test.ts +31 -0
  285. package/src/ui/comments-list.tsx +82 -47
  286. package/src/ui/comments.css +577 -294
  287. package/src/ui/count-badge.tsx +9 -2
  288. package/src/ui/emoji-picker.test.ts +25 -0
  289. package/src/ui/emoji-picker.tsx +76 -0
  290. package/src/ui/empty-state.tsx +3 -3
  291. package/src/ui/format-time.test.ts +69 -0
  292. package/src/ui/format-time.ts +27 -2
  293. package/src/ui/icons.tsx +116 -0
  294. package/src/ui/reaction-picker.tsx +102 -0
  295. package/src/ui/reaction.tsx +125 -6
  296. package/src/ui/reactions.tsx +69 -10
  297. package/src/ui/reply-count.ts +16 -0
  298. package/src/ui/send-button.tsx +13 -3
  299. package/src/ui/visual-viewport.test.ts +36 -0
  300. package/src/ui/visual-viewport.ts +27 -0
  301. package/dist-cjs/canvas/region-options.js +0 -49
  302. package/dist-cjs/canvas/region-options.js.map +0 -7
  303. package/dist-cjs/ui/avatar.js.map +0 -7
  304. package/dist-cjs/ui/comment-mention.js +0 -42
  305. package/dist-cjs/ui/comment-mention.js.map +0 -7
  306. package/dist-cjs/ui/comment-text.js.map +0 -7
  307. package/dist-cjs/ui/mention-list.js +0 -70
  308. package/dist-cjs/ui/mention-list.js.map +0 -7
  309. package/dist-cjs/ui/mention-suggestion.js +0 -206
  310. package/dist-cjs/ui/mention-suggestion.js.map +0 -7
  311. package/dist-cjs/ui/mention.js.map +0 -7
  312. package/dist-cjs/ui/render-markdown.js +0 -63
  313. package/dist-cjs/ui/render-markdown.js.map +0 -7
  314. package/dist-esm/canvas/region-options.mjs +0 -29
  315. package/dist-esm/canvas/region-options.mjs.map +0 -7
  316. package/dist-esm/ui/avatar.mjs +0 -23
  317. package/dist-esm/ui/avatar.mjs.map +0 -7
  318. package/dist-esm/ui/comment-mention.mjs +0 -22
  319. package/dist-esm/ui/comment-mention.mjs.map +0 -7
  320. package/dist-esm/ui/comment-text.mjs +0 -9
  321. package/dist-esm/ui/comment-text.mjs.map +0 -7
  322. package/dist-esm/ui/mention-list.mjs +0 -50
  323. package/dist-esm/ui/mention-list.mjs.map +0 -7
  324. package/dist-esm/ui/mention-suggestion.mjs +0 -186
  325. package/dist-esm/ui/mention-suggestion.mjs.map +0 -7
  326. package/dist-esm/ui/mention.mjs +0 -11
  327. package/dist-esm/ui/mention.mjs.map +0 -7
  328. package/dist-esm/ui/render-markdown.mjs +0 -45
  329. package/dist-esm/ui/render-markdown.mjs.map +0 -7
  330. package/src/canvas/region-options.ts +0 -57
  331. package/src/ui/avatar.tsx +0 -31
  332. package/src/ui/comment-mention.ts +0 -47
  333. package/src/ui/comment-text.tsx +0 -12
  334. package/src/ui/mention-list.tsx +0 -106
  335. package/src/ui/mention-suggestion.test.ts +0 -18
  336. package/src/ui/mention-suggestion.tsx +0 -285
  337. package/src/ui/mention.tsx +0 -9
  338. package/src/ui/render-markdown.tsx +0 -72
@@ -22,61 +22,23 @@ __export(comments_overlay_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(comments_overlay_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
+ var import_mentions = require("@tldraw/mentions");
25
26
  var import_react = require("react");
26
- var import_react_dom = require("react-dom");
27
27
  var import_tldraw = require("tldraw");
28
- var import_computeClusterTable = require("../clustering/computeClusterTable");
29
- var import_runtime = require("../clustering/runtime");
30
- var import_comment_card = require("../ui/comment-card");
31
- var import_comment_composer = require("../ui/comment-composer");
32
- var import_comment_extensions = require("../ui/comment-extensions");
33
- var import_comment_pin = require("../ui/comment-pin");
34
- var import_comment_thread = require("../ui/comment-thread");
35
- var import_count_badge = require("../ui/count-badge");
36
- var import_mention_suggestion = require("../ui/mention-suggestion");
37
- var import_cluster_input = require("./cluster-input");
38
- var import_comment_body = require("./comment-body");
39
- var import_comment_render = require("./comment-render");
28
+ var import_anchor_lifecycle = require("./anchor-lifecycle");
29
+ var import_cluster_badge = require("./cluster-badge");
30
+ var import_cluster_fade = require("./cluster-fade");
31
+ var import_cluster_model = require("./cluster-model");
40
32
  var import_comment_store = require("./comment-store");
41
33
  var import_hooks = require("./hooks");
42
34
  var import_license = require("./license");
43
35
  var import_options = require("./options");
44
- var import_region_options = require("./region-options");
36
+ var import_pending_composer = require("./pending-composer");
37
+ var import_pin_stacking = require("./pin-stacking");
38
+ var import_region_box = require("./region-box");
45
39
  var import_state = require("./state");
46
- var import_thread_state = require("./thread-state");
47
- const stop = (e) => e.stopPropagation();
48
- const initialOf = (name) => ((0, import_tldraw.getFirstCharacter)(name.trim()) || "?").toUpperCase();
49
- const CLUSTER_FADE_MS = 150;
50
- const CLUSTER_EXPAND_ZOOM_MS = 450;
51
- const draftAvatar = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_comment_pin.CommentPin, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
52
- "svg",
53
- {
54
- viewBox: "0 0 24 24",
55
- width: "15",
56
- height: "15",
57
- fill: "none",
58
- stroke: "currentColor",
59
- strokeWidth: "2",
60
- strokeLinecap: "round",
61
- strokeLinejoin: "round",
62
- "aria-hidden": "true",
63
- children: [
64
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 20h9" }),
65
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z" })
66
- ]
67
- }
68
- ) });
69
- function toCardProps(comment, props, components) {
70
- const Body = components.CommentBody;
71
- const body = Body ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Body, { comment }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_comment_body.CommentBody, { richText: comment.body, resolveName: props.resolveName });
72
- return {
73
- author: props.resolveName(comment.authorId) ?? import_comment_render.UNKNOWN_AUTHOR,
74
- body,
75
- date: new Date(comment.createdAt).toISOString(),
76
- you: comment.authorId === props.currentUserId,
77
- edited: comment.editedAt != null
78
- };
79
- }
40
+ var import_thread_pin = require("./thread-pin");
41
+ var import_thread_stack = require("./thread-stack");
80
42
  function CanvasComments(props) {
81
43
  const commentingEnabled = (0, import_license.useCommentingEnabled)();
82
44
  if (!commentingEnabled) return null;
@@ -85,166 +47,112 @@ function CanvasComments(props) {
85
47
  function CanvasCommentsLayer(props) {
86
48
  const editor = (0, import_tldraw.useEditor)();
87
49
  const options = (0, import_options.useCommentingOptions)();
88
- const container = (0, import_tldraw.useContainer)();
89
- const regionOptions = (0, import_react.useMemo)(
90
- () => ({ ...import_region_options.DEFAULT_REGION_COMMENT_OPTIONS, ...props.regionOptions }),
91
- [props.regionOptions]
92
- );
93
- (0, import_react.useEffect)(() => (0, import_region_options.setRegionCommentOptions)(editor, regionOptions), [editor, regionOptions]);
94
- const layerRef = (0, import_react.useRef)(null);
95
- (0, import_tldraw.usePassThroughWheelEvents)(layerRef);
96
- (0, import_tldraw.usePassThroughMouseOverEvents)(layerRef);
97
- const deepLinkHandled = (0, import_react.useRef)(false);
98
- const threads = (0, import_hooks.useCommentThreads)(editor);
50
+ const allThreads = (0, import_hooks.useCommentThreads)(editor);
99
51
  const pending = (0, import_state.usePendingComment)();
100
- const openId = (0, import_tldraw.useValue)("open thread id", () => import_state.openThreadId.get(editor), [editor]);
101
- const impreciseShapeAnchor = props.impreciseShapeAnchor ?? options.impreciseShapeAnchor;
102
- const [movedThreadIds, setMovedThreadIds] = (0, import_react.useState)(EMPTY_SET);
103
- const adoptOnRebuild = (0, import_react.useRef)(false);
104
- const clusterLeaves = (0, import_tldraw.useValue)(
105
- "comment cluster leaves",
106
- () => (0, import_cluster_input.collectClusterLeaves)(
107
- editor,
108
- threads.filter((thread) => !movedThreadIds.has(thread.id)),
109
- import_state.openThreadId.get(editor),
110
- impreciseShapeAnchor
111
- ),
112
- [editor, threads, impreciseShapeAnchor, movedThreadIds]
113
- );
114
- const clusterZoomBounds = (0, import_tldraw.useValue)(
115
- "comment cluster zoom bounds",
116
- () => getClusterZoomBounds(editor),
117
- [editor]
118
- );
119
- const latestModel = (0, import_react.useMemo)(() => {
120
- const table = (0, import_computeClusterTable.computeClusterTable)(clusterLeaves, clusterZoomBounds);
121
- const runtime = (0, import_runtime.createClusterRuntime)(table);
122
- runtime.seed(editor.getZoomLevel());
123
- return { runtime, table };
124
- }, [clusterLeaves, clusterZoomBounds, editor]);
125
- const [renderedModel, setRenderedModel] = (0, import_react.useState)(latestModel);
126
- let clusterModel = renderedModel;
127
- if (renderedModel !== latestModel && (adoptOnRebuild.current || hasRemovedLeaves(renderedModel.table, latestModel.table))) {
128
- adoptOnRebuild.current = false;
129
- latestModel.runtime.seedFrom(editor.getZoomLevel(), renderedModel.runtime.getVisible());
130
- setRenderedModel(latestModel);
131
- clusterModel = latestModel;
132
- }
133
- const newlyMovedIds = findMovedClusteredLeafIds(clusterModel, latestModel);
134
- if (newlyMovedIds.length > 0) {
135
- console.debug(`[comments] pins popped out of clustering: ${newlyMovedIds.join(", ")}`);
136
- const next = new Set(movedThreadIds);
137
- for (const id of newlyMovedIds) next.add(id);
138
- setMovedThreadIds(next);
139
- }
140
- (0, import_react.useEffect)(() => {
141
- if (movedThreadIds.size === 0) return;
142
- let lastZoom = editor.getZoomLevel();
143
- return (0, import_tldraw.react)("rejoin moved comment pins on zoom out", () => {
144
- const zoom = editor.getZoomLevel();
145
- const prevZoom = lastZoom;
146
- lastZoom = zoom;
147
- if (zoom >= prevZoom) return;
148
- adoptOnRebuild.current = true;
149
- setMovedThreadIds(EMPTY_SET);
150
- });
151
- }, [movedThreadIds, editor]);
52
+ const canComment = (0, import_options.useCanComment)(props.currentUserId);
53
+ const canRenderComposer = canComment || options.components.ComposerFallback != null;
54
+ const showPendingComposer = pending != null && canRenderComposer;
152
55
  (0, import_react.useEffect)(() => {
153
- if (clusterModel === latestModel) return;
154
- let lastZoom = editor.getZoomLevel();
155
- return (0, import_tldraw.react)("adopt pending cluster model on zoom out", () => {
156
- const zoom = editor.getZoomLevel();
157
- const prevZoom = lastZoom;
158
- lastZoom = zoom;
159
- if (zoom >= prevZoom) return;
160
- latestModel.runtime.seedFrom(zoom, clusterModel.runtime.getVisible());
161
- setRenderedModel(latestModel);
162
- });
163
- }, [clusterModel, latestModel, editor]);
164
- const orphanThreads = (0, import_react.useMemo)(() => {
165
- if (clusterModel === latestModel) return [];
166
- const renderedIds = new Set(clusterModel.table.leaves.map((leaf) => leaf.id));
167
- const latestIds = new Set(latestModel.table.leaves.map((leaf) => leaf.id));
168
- return threads.filter((thread) => latestIds.has(thread.id) && !renderedIds.has(thread.id));
169
- }, [clusterModel, latestModel, threads]);
170
- const movedThreads = (0, import_react.useMemo)(
171
- () => threads.filter((thread) => movedThreadIds.has(thread.id) && thread.id !== openId),
172
- [threads, movedThreadIds, openId]
173
- );
174
- const clusterCursor = (0, import_tldraw.useValue)(
175
- "comment cluster cursor",
176
- () => {
177
- clusterModel.runtime.onCamera(editor.getZoomLevel());
178
- return clusterModel.runtime.k;
179
- },
180
- [clusterModel, editor]
56
+ if (pending && !showPendingComposer) import_state.pendingComment.set(editor, null);
57
+ }, [editor, pending, showPendingComposer]);
58
+ const openId = (0, import_tldraw.useValue)("open thread id", () => import_state.openThreadId.get(editor), [editor]);
59
+ const showResolved = (0, import_tldraw.useValue)("show resolved", () => import_state.sidebarFilters.get(editor).showResolved, [
60
+ editor
61
+ ]);
62
+ const threads = (0, import_react.useMemo)(
63
+ () => allThreads.filter((t) => showResolved || t.resolved == null || t.id === openId),
64
+ [allThreads, showResolved, openId]
181
65
  );
182
- const visibleNodes = (0, import_react.useMemo)(() => {
183
- const nodes = Array.from(clusterModel.runtime.getVisible().values());
184
- console.debug(
185
- `[comments] cluster cursor k=${clusterCursor} \u2192 re-rendering ${nodes.length} visible nodes`
186
- );
187
- return nodes;
188
- }, [clusterModel, clusterCursor]);
189
- const fadeNodes = useFadeVisibleNodes(visibleNodes, clusterModel);
66
+ (0, import_react.useEffect)(() => (0, import_anchor_lifecycle.registerCommentAnchorLifecycle)(editor), [editor]);
67
+ const {
68
+ model: clusterModel,
69
+ zoomBounds: clusterZoomBounds,
70
+ fadeNodes,
71
+ orphanThreads,
72
+ heldThreads
73
+ } = (0, import_cluster_model.useClusterModel)(editor, threads, openId);
190
74
  const threadsById = (0, import_react.useMemo)(
191
75
  () => new Map(threads.map((thread) => [thread.id, thread])),
192
76
  [threads]
193
77
  );
78
+ const pinStacksRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
79
+ const pinStacks = (0, import_tldraw.useValue)(
80
+ "comment pin stacks",
81
+ () => {
82
+ const stacks = (0, import_pin_stacking.computePinStacks)(editor, threads);
83
+ if ((0, import_pin_stacking.pinStacksEqual)(pinStacksRef.current, stacks)) return pinStacksRef.current;
84
+ pinStacksRef.current = stacks;
85
+ return stacks;
86
+ },
87
+ [editor, threads]
88
+ );
194
89
  const openThread = openId ? threadsById.get(openId) : null;
195
90
  const hidden = (0, import_tldraw.useValue)("comments hidden", () => import_state.commentsHidden.get(editor), [editor]);
196
91
  (0, import_react.useEffect)(() => {
197
92
  return () => {
198
93
  import_state.openThreadId.set(editor, null);
94
+ import_state.openStackId.set(editor, null);
199
95
  import_state.pendingComment.set(editor, null);
96
+ import_state.revealThreadRequest.set(editor, null);
200
97
  };
201
98
  }, [editor]);
99
+ const openStackKeyIsStale = (0, import_tldraw.useValue)(
100
+ "open stack key stale",
101
+ () => {
102
+ const key = import_state.openStackId.get(editor);
103
+ if (!key) return false;
104
+ return !(0, import_pin_stacking.isOpenStackKeyLive)(editor, key, pinStacks, threadsById);
105
+ },
106
+ [editor, pinStacks, threadsById]
107
+ );
202
108
  (0, import_react.useEffect)(() => {
203
- if (deepLinkHandled.current) return;
204
- const id = new URLSearchParams(window.location.search).get("comment");
205
- if (!id) {
206
- deepLinkHandled.current = true;
207
- return;
208
- }
209
- const record = (0, import_comment_store.getCommentRecord)(editor, id);
210
- if (!record) return;
211
- let thread;
212
- if (record.typeName === "comment") {
213
- thread = threadsById.get(record.threadId);
214
- } else {
215
- thread = record;
216
- }
217
- if (!thread) return;
218
- deepLinkHandled.current = true;
219
- revealDeepLinkedThread(
220
- editor,
221
- thread,
222
- clusterModel.table,
223
- clusterZoomBounds,
224
- options,
225
- impreciseShapeAnchor
226
- );
227
- import_state.openThreadId.set(editor, thread.id);
228
- }, [clusterModel.table, clusterZoomBounds, editor, threadsById, impreciseShapeAnchor, options]);
229
- const zoomToClusterSplit = (0, import_react.useCallback)(
230
- (node) => {
231
- const event = clusterModel.table.events.find((e) => e.result.id === node.id);
232
- if (!event || !Number.isFinite(event.zSplit)) return;
233
- const zoom = clamp(
234
- event.zSplit * options.clusterSplitZoomFactor,
235
- clusterZoomBounds.minZoom,
236
- clusterZoomBounds.maxZoom
109
+ if (openStackKeyIsStale) import_state.openStackId.set(editor, null);
110
+ }, [editor, openStackKeyIsStale]);
111
+ const requestedRevealThread = (0, import_tldraw.useValue)(
112
+ "requested reveal thread",
113
+ () => {
114
+ const id = import_state.revealThreadRequest.get(editor);
115
+ if (!id) return null;
116
+ const record = (0, import_comment_store.getCommentRecord)(editor, id);
117
+ if (!record) return null;
118
+ const thread = record.typeName === "comment" ? (0, import_comment_store.getCommentRecord)(editor, record.threadId) : record;
119
+ return thread?.typeName === "comment-thread" ? thread : null;
120
+ },
121
+ [editor]
122
+ );
123
+ (0, import_react.useEffect)(() => {
124
+ if (!requestedRevealThread) return;
125
+ import_state.revealThreadRequest.set(editor, null);
126
+ import_state.commentsHidden.set(editor, false);
127
+ (0, import_cluster_model.revealThreadPin)(editor, requestedRevealThread, clusterModel.table, clusterZoomBounds, options);
128
+ import_state.openThreadId.set(editor, requestedRevealThread.id);
129
+ }, [requestedRevealThread, clusterModel.table, clusterZoomBounds, editor, options]);
130
+ const revealClusteredThread = (0, import_react.useCallback)(
131
+ (thread) => {
132
+ (0, import_cluster_model.revealThreadPin)(
133
+ editor,
134
+ thread,
135
+ clusterModel.table,
136
+ clusterZoomBounds,
137
+ options,
138
+ import_cluster_model.CLUSTER_EXPAND_ZOOM_MS
237
139
  );
238
- centerOnPointAtZoom(editor, node.centroid, zoom, CLUSTER_EXPAND_ZOOM_MS);
140
+ import_state.openThreadId.set(editor, thread.id);
239
141
  },
240
- [clusterModel, clusterZoomBounds, editor, options]
142
+ [clusterModel.table, clusterZoomBounds, editor, options]
143
+ );
144
+ const expandCluster = (0, import_react.useCallback)(
145
+ (node) => {
146
+ (0, import_cluster_model.zoomToClusterSplit)(editor, clusterModel.table, clusterZoomBounds, node);
147
+ },
148
+ [clusterModel.table, clusterZoomBounds, editor]
241
149
  );
242
150
  (0, import_react.useEffect)(() => {
243
151
  const onKeyDown = (e) => {
244
152
  if (e.key !== "Escape" || import_state.openThreadId.get(editor) === null) return;
245
- if ((0, import_mention_suggestion.isMentionPickerOpen)()) return;
153
+ if ((0, import_mentions.isMentionPickerOpen)()) return;
246
154
  const target = e.target;
247
- if (target && target.closest(".cmt-editing")) return;
155
+ if (target && target.closest(".tlui-cmt-editing")) return;
248
156
  import_state.openThreadId.set(editor, null);
249
157
  e.preventDefault();
250
158
  e.stopPropagation();
@@ -264,818 +172,72 @@ function CanvasCommentsLayer(props) {
264
172
  return () => document.removeEventListener("keydown", onKeyDown, true);
265
173
  }, [editor]);
266
174
  if (hidden) return null;
267
- return (0, import_react_dom.createPortal)(
268
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: layerRef, className: "cmt-canvas-layer", children: [
269
- options.enableClustering ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
270
- fadeNodes.map(({ node, phase }) => {
271
- let content;
272
- if (node.count === 1) {
273
- const thread = threadsById.get(node.id);
274
- if (!thread) return null;
275
- content = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
276
- ThreadPin,
277
- {
278
- editor,
279
- thread,
280
- ...props,
281
- regionOptions
282
- }
283
- );
284
- } else {
285
- content = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ClusterBadge, { editor, node, onExpand: zoomToClusterSplit });
286
- }
287
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: clusterFadeClassName(phase), children: content }, `cluster-fade:${node.id}`);
288
- }),
289
- orphanThreads.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
290
- ThreadPin,
291
- {
292
- editor,
293
- thread,
294
- ...props,
295
- regionOptions
296
- },
297
- thread.id
298
- )),
299
- movedThreads.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
300
- ThreadPin,
301
- {
302
- editor,
303
- thread,
304
- ...props,
305
- regionOptions
306
- },
307
- thread.id
308
- ))
309
- ] }) : (
310
- // Clustering off: every thread renders as its own live pin (each returns null when it's
311
- // not on the current page or its anchor is missing). The open thread is excluded here and
312
- // rendered once below, mirroring how the clustering path keeps it out of the cluster leaves —
313
- // otherwise it would mount a second, stacked pin.
314
- threads.filter((thread) => thread.id !== openId).map((thread) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
315
- ThreadPin,
316
- {
317
- editor,
318
- thread,
319
- ...props,
320
- regionOptions
321
- },
322
- thread.id
323
- ))
324
- ),
325
- openThread && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
326
- ThreadPin,
327
- {
328
- editor,
329
- thread: openThread,
330
- ...props,
331
- regionOptions
332
- },
333
- `open:${openThread.id}`
334
- ),
335
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RegionDraftBox, { editor }),
336
- pending?.anchor.type === "region" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RegionBox, { editor, box: pending.anchor }),
337
- pending && props.currentUserId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PendingComposer, { editor, pending, ...props })
338
- ] }),
339
- container
340
- );
341
- }
342
- const EMPTY_SET = /* @__PURE__ */ new Set();
343
- const MOVED_LEAF_EPSILON = 1e-6;
344
- function useFadeVisibleNodes(nodes, resetKey) {
345
- const resetKeyRef = (0, import_react.useRef)(resetKey);
346
- const didReset = resetKeyRef.current !== resetKey;
347
- if (didReset) {
348
- resetKeyRef.current = resetKey;
349
- }
350
- const [fadeNodes, setFadeNodes] = (0, import_react.useState)(() => toPresentFadeNodes(nodes));
351
- const renderedNodes = didReset ? toPresentFadeNodes(nodes) : fadeNodes;
352
- (0, import_react.useEffect)(() => {
353
- setFadeNodes(toPresentFadeNodes(nodes));
354
- }, [resetKey]);
355
- (0, import_react.useEffect)(() => {
356
- if (didReset) return;
357
- setFadeNodes((previous) => reconcileFadeNodes(previous, nodes));
358
- }, [didReset, nodes]);
359
- const hasEntering = renderedNodes.some((item) => item.phase === "entering");
360
- (0, import_react.useEffect)(() => {
361
- if (!hasEntering) return;
362
- const frame = requestClusterFadeFrame(() => {
363
- setFadeNodes(
364
- (previous) => previous.map((item) => item.phase === "entering" ? { ...item, phase: "present" } : item)
365
- );
366
- });
367
- return () => cancelClusterFadeFrame(frame);
368
- }, [hasEntering, renderedNodes]);
369
- const hasExiting = renderedNodes.some((item) => item.phase === "exiting");
370
- (0, import_react.useEffect)(() => {
371
- if (!hasExiting) return;
372
- const timeout = window.setTimeout(() => {
373
- setFadeNodes((previous) => previous.filter((item) => item.phase !== "exiting"));
374
- }, CLUSTER_FADE_MS);
375
- return () => window.clearTimeout(timeout);
376
- }, [hasExiting, renderedNodes]);
377
- return renderedNodes;
378
- }
379
- function toPresentFadeNodes(nodes) {
380
- return nodes.map((node) => ({ node, phase: "present" }));
381
- }
382
- function reconcileFadeNodes(previous, nextNodes) {
383
- const previousById = new Map(previous.map((item) => [item.node.id, item]));
384
- const nextIds = new Set(nextNodes.map((node) => node.id));
385
- const next = [];
386
- for (const node of nextNodes) {
387
- const previousItem = previousById.get(node.id);
388
- next.push({
389
- node,
390
- phase: previousItem && previousItem.phase !== "exiting" ? previousItem.phase : previousItem ? "present" : "entering"
391
- });
392
- }
393
- for (const item of previous) {
394
- if (nextIds.has(item.node.id)) continue;
395
- next.push(item.phase === "exiting" ? item : { ...item, phase: "exiting" });
396
- }
397
- return next;
398
- }
399
- function requestClusterFadeFrame(callback) {
400
- if (typeof requestAnimationFrame === "function") return requestAnimationFrame(callback);
401
- return window.setTimeout(() => callback(0), 16);
402
- }
403
- function cancelClusterFadeFrame(frame) {
404
- if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(frame);
405
- else window.clearTimeout(frame);
406
- }
407
- function clusterFadeClassName(phase) {
408
- return `cmt-cluster-fade cmt-cluster-fade--${phase}`;
409
- }
410
- function findMovedClusteredLeafIds(rendered, latest) {
411
- if (rendered.table === latest.table) return [];
412
- const visible = rendered.runtime.getVisible();
413
- const latestById = new Map(latest.table.leaves.map((leaf) => [leaf.id, leaf]));
414
- const moved = [];
415
- for (const leaf of rendered.table.leaves) {
416
- if (visible.has(leaf.id)) continue;
417
- const current = latestById.get(leaf.id);
418
- if (!current) continue;
419
- if (Math.abs(current.centroid.x - leaf.centroid.x) > MOVED_LEAF_EPSILON || Math.abs(current.centroid.y - leaf.centroid.y) > MOVED_LEAF_EPSILON) {
420
- moved.push(leaf.id);
421
- }
422
- }
423
- return moved;
424
- }
425
- function hasRemovedLeaves(rendered, latest) {
426
- if (rendered.leaves.length === 0) return false;
427
- const latestIds = new Set(latest.leaves.map((leaf) => leaf.id));
428
- return rendered.leaves.some((leaf) => !latestIds.has(leaf.id));
429
- }
430
- function getClusterZoomBounds(editor) {
431
- const cameraOptions = editor.getCameraOptions();
432
- const baseZoom = cameraOptions.constraints ? editor.getBaseZoom() : 1;
433
- const zoomSteps = cameraOptions.zoomSteps;
434
- return {
435
- minZoom: zoomSteps[0] * baseZoom,
436
- maxZoom: zoomSteps[zoomSteps.length - 1] * baseZoom
175
+ const stackGroupOf = (node) => {
176
+ const group = pinStacks.get(node.members[0]);
177
+ if (!group) return null;
178
+ return node.members.every((id) => group.includes(id)) ? group : null;
437
179
  };
438
- }
439
- function revealDeepLinkedThread(editor, thread, table, zoomBounds, options, impreciseShapeAnchor) {
440
- if (thread.pageId !== editor.getCurrentPageId()) {
441
- editor.setCurrentPage(thread.pageId);
442
- }
443
- const point = (0, import_thread_state.anchorPagePoint)(editor, thread.anchor, impreciseShapeAnchor);
444
- if (!point) return;
180
+ const renderedThreadIds = /* @__PURE__ */ new Set();
445
181
  if (options.enableClustering) {
446
- const parentEvent = findDirectParentEvent(table, thread.id);
447
- if (parentEvent && Number.isFinite(parentEvent.zSplit) && parentEvent.zSplit <= zoomBounds.maxZoom) {
448
- const zoom = clamp(
449
- parentEvent.zSplit * options.clusterSplitZoomFactor,
450
- zoomBounds.minZoom,
451
- zoomBounds.maxZoom
452
- );
453
- centerOnPointAtZoom(editor, point, zoom);
454
- return;
182
+ for (const { node } of fadeNodes) {
183
+ if (node.count === 1) renderedThreadIds.add(node.id);
184
+ else if (stackGroupOf(node)) for (const id of node.members) renderedThreadIds.add(id);
455
185
  }
186
+ for (const thread of orphanThreads) renderedThreadIds.add(thread.id);
187
+ for (const thread of heldThreads) renderedThreadIds.add(thread.id);
188
+ } else {
189
+ for (const thread of threads) renderedThreadIds.add(thread.id);
456
190
  }
457
- editor.centerOnPoint(point, { animation: { duration: 200 } });
458
- }
459
- function findDirectParentEvent(table, threadId) {
460
- return table.events.find((event) => event.children.some((child) => child.id === threadId));
461
- }
462
- function centerOnPointAtZoom(editor, point, zoom, duration = 200) {
463
- const viewport = editor.getViewportScreenBounds();
464
- editor.setCamera(
465
- {
466
- x: viewport.w / (2 * zoom) - point.x,
467
- y: viewport.h / (2 * zoom) - point.y,
468
- z: zoom
469
- },
470
- { animation: { duration } }
471
- );
472
- }
473
- function clamp(value, min, max) {
474
- return Math.max(min, Math.min(max, value));
475
- }
476
- const ClusterBadge = (0, import_react.memo)(function ClusterBadge2({
477
- editor,
478
- node,
479
- onExpand
480
- }) {
481
- const point = (0, import_tldraw.useValue)(
482
- "cluster badge point",
483
- () => {
484
- const pagePoint = editor.pageToViewport(node.centroid);
485
- if (!isInInflatedViewport(editor, pagePoint)) return null;
486
- return pagePoint;
487
- },
488
- [editor, node]
489
- );
490
- if (!point) return null;
491
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
492
- "div",
493
- {
494
- className: "cmt-canvas-cluster",
495
- style: { left: point.x, top: point.y },
496
- onPointerDown: stop,
497
- onClick: (e) => {
498
- e.stopPropagation();
499
- onExpand(node);
500
- },
501
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_count_badge.CountBadge, { count: node.count })
191
+ if (openThread) renderedThreadIds.add(openThread.id);
192
+ const renderThreadPin = (thread) => {
193
+ const group = pinStacks.get(thread.id);
194
+ if (group) {
195
+ const owner = group.find((id) => renderedThreadIds.has(id));
196
+ if (owner !== thread.id) return null;
197
+ const stackThreads = group.map((id) => threadsById.get(id)).filter((t) => t !== void 0);
198
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_thread_stack.ThreadStackPin, { editor, threads: stackThreads, ...props });
502
199
  }
503
- );
504
- });
505
- function isInInflatedViewport(editor, point) {
506
- const viewport = editor.getViewportScreenBounds();
507
- const margin = (0, import_options.getCommentingOptions)(editor).clusterCullMargin;
508
- return point.x >= -margin && point.y >= -margin && point.x <= viewport.w + margin && point.y <= viewport.h + margin;
509
- }
510
- function ThreadPopover({
511
- container,
512
- style,
513
- children
514
- }) {
515
- const ref = (0, import_react.useRef)(null);
516
- (0, import_tldraw.usePassThroughWheelEvents)(ref);
517
- (0, import_tldraw.usePassThroughMouseOverEvents)(ref);
518
- return (0, import_react_dom.createPortal)(
519
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref, className: "cmt-canvas-popover", style, onPointerDown: stop, children }),
520
- container
521
- );
522
- }
523
- function RegionBox({
524
- editor,
525
- box,
526
- movable,
527
- onPreview,
528
- onCommit
529
- }) {
530
- const rect = (0, import_tldraw.useValue)(
531
- "region rect",
532
- () => {
533
- const topLeft = editor.pageToViewport({ x: box.x, y: box.y });
534
- const zoom = editor.getZoomLevel();
535
- return { left: topLeft.x, top: topLeft.y, width: box.w * zoom, height: box.h * zoom };
536
- },
537
- [editor, box.x, box.y, box.w, box.h]
538
- );
539
- const grabRef = (0, import_react.useRef)(null);
540
- const translated = (e) => {
541
- const g = grabRef.current;
542
- const p = editor.screenToPage({ x: e.clientX, y: e.clientY });
543
- return { ...g.box, x: g.box.x + (p.x - g.page.x), y: g.box.y + (p.y - g.page.y) };
544
- };
545
- const startMove = (e) => {
546
- e.stopPropagation();
547
- grabRef.current = { page: editor.screenToPage({ x: e.clientX, y: e.clientY }), box };
548
- e.currentTarget.setPointerCapture(e.pointerId);
549
- };
550
- const onMove = (e) => {
551
- if (grabRef.current) onPreview?.(translated(e));
552
- };
553
- const endMove = (e) => {
554
- if (!grabRef.current) return;
555
- const bounds = translated(e);
556
- grabRef.current = null;
557
- if (e.currentTarget.hasPointerCapture(e.pointerId))
558
- e.currentTarget.releasePointerCapture(e.pointerId);
559
- onCommit?.(bounds);
560
- };
561
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
562
- "div",
563
- {
564
- className: movable ? "cmt-canvas-region cmt-canvas-region--movable" : "cmt-canvas-region",
565
- style: rect,
566
- onPointerDown: movable ? startMove : void 0,
567
- onPointerMove: movable ? onMove : void 0,
568
- onPointerUp: movable ? endMove : void 0
569
- }
570
- );
571
- }
572
- function RegionDraftBox({ editor }) {
573
- const box = (0, import_tldraw.useValue)("region draft", () => import_state.regionDraft.get(editor), [editor]);
574
- if (!box) return null;
575
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RegionBox, { editor, box });
576
- }
577
- const REGION_CORNERS = [
578
- { x: 0, y: 0, cursor: "nwse-resize" },
579
- { x: 1, y: 0, cursor: "nesw-resize" },
580
- { x: 0, y: 1, cursor: "nesw-resize" },
581
- { x: 1, y: 1, cursor: "nwse-resize" }
582
- ];
583
- const REGION_EDGES = [
584
- { x: 0.5, y: 0, cursor: "ns-resize" },
585
- { x: 1, y: 0.5, cursor: "ew-resize" },
586
- { x: 0.5, y: 1, cursor: "ns-resize" },
587
- { x: 0, y: 0.5, cursor: "ew-resize" }
588
- ];
589
- const REGION_HANDLE_MARGIN_PX = 12;
590
- function resizeRegion(box, handle, cursor) {
591
- const controlsX = handle.x !== 0.5;
592
- const controlsY = handle.y !== 0.5;
593
- const fixedX = box.x + (1 - handle.x) * box.w;
594
- const fixedY = box.y + (1 - handle.y) * box.h;
595
- return {
596
- x: controlsX ? Math.min(fixedX, cursor.x) : box.x,
597
- y: controlsY ? Math.min(fixedY, cursor.y) : box.y,
598
- w: controlsX ? Math.abs(cursor.x - fixedX) : box.w,
599
- h: controlsY ? Math.abs(cursor.y - fixedY) : box.h
600
- };
601
- }
602
- function RegionResizeHandles({
603
- editor,
604
- box,
605
- handles,
606
- onPreview,
607
- onCommit
608
- }) {
609
- const boxRef = (0, import_react.useRef)(null);
610
- const points = (0, import_tldraw.useValue)(
611
- "region handle points",
612
- () => handles.map((h) => {
613
- const p = editor.pageToViewport({ x: box.x + h.x * box.w, y: box.y + h.y * box.h });
614
- return { ...h, key: `${h.x}-${h.y}`, left: p.x, top: p.y };
615
- }),
616
- [editor, box.x, box.y, box.w, box.h, handles]
617
- );
618
- const startResize = (e) => {
619
- e.stopPropagation();
620
- boxRef.current = box;
621
- e.currentTarget.setPointerCapture(e.pointerId);
622
- };
623
- const resizedTo = (h, e) => resizeRegion(boxRef.current, h, editor.screenToPage({ x: e.clientX, y: e.clientY }));
624
- const onResize = (h) => (e) => {
625
- if (boxRef.current) onPreview(resizedTo(h, e));
626
- };
627
- const endResize = (h) => (e) => {
628
- if (!boxRef.current) return;
629
- const bounds = resizedTo(h, e);
630
- boxRef.current = null;
631
- if (e.currentTarget.hasPointerCapture(e.pointerId))
632
- e.currentTarget.releasePointerCapture(e.pointerId);
633
- onCommit(bounds);
634
- };
635
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: points.map((h) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
636
- "div",
637
- {
638
- className: "cmt-canvas-region-handle",
639
- style: { left: h.left, top: h.top, cursor: h.cursor },
640
- onPointerDown: startResize,
641
- onPointerMove: onResize(h),
642
- onPointerUp: endResize(h)
643
- },
644
- h.key
645
- )) });
646
- }
647
- const ThreadPin = (0, import_react.memo)(function ThreadPin2({
648
- editor,
649
- thread,
650
- regionOptions,
651
- ...props
652
- }) {
653
- const {
654
- currentUserId,
655
- resolveName,
656
- onPostComment,
657
- isCommentUnread,
658
- onCommentRead,
659
- getMentionSuggestions,
660
- renderMentionSuggestion
661
- } = props;
662
- const options = (0, import_options.useCommentingOptions)();
663
- const impreciseShapeAnchor = props.impreciseShapeAnchor ?? options.impreciseShapeAnchor;
664
- const container = (0, import_tldraw.useContainer)();
665
- const comments = (0, import_hooks.useThreadComments)(editor, thread.id);
666
- const msg = (0, import_tldraw.useTranslation)();
667
- const open = (0, import_tldraw.useValue)("thread open", () => import_state.openThreadId.get(editor) === thread.id, [
668
- editor,
669
- thread.id
670
- ]);
671
- const [reply, setReply] = (0, import_react.useState)(import_comment_extensions.EMPTY_COMMENT);
672
- const [editingId, setEditingId] = (0, import_react.useState)(null);
673
- const [editText, setEditText] = (0, import_react.useState)(import_comment_extensions.EMPTY_COMMENT);
674
- const [dragPagePoint, setDragPagePoint] = (0, import_react.useState)(null);
675
- const [resizeBounds, setResizeBounds] = (0, import_react.useState)(null);
676
- const [pinHovered, setPinHovered] = (0, import_react.useState)(false);
677
- const pointerInRegion = (0, import_tldraw.useValue)(
678
- "pointer in region",
679
- () => {
680
- if (thread.anchor.type !== "region" || thread.pageId !== editor.getCurrentPageId())
681
- return false;
682
- const m = REGION_HANDLE_MARGIN_PX / editor.getZoomLevel();
683
- const p = editor.inputs.getCurrentPagePoint();
684
- const a = thread.anchor;
685
- return p.x >= a.x - m && p.x <= a.x + a.w + m && p.y >= a.y - m && p.y <= a.y + a.h + m;
686
- },
687
- [editor, thread.anchor, thread.pageId]
688
- );
689
- const revealed = open || resizeBounds != null || regionOptions.reveal === "pointer" && pointerInRegion || regionOptions.reveal === "pin-hover" && pinHovered;
690
- const resizeHandles = (0, import_react.useMemo)(
691
- () => regionOptions.resize === "edges" ? REGION_EDGES : REGION_CORNERS.filter(
692
- (c) => c.x !== regionOptions.pinCorner.x || c.y !== regionOptions.pinCorner.y
693
- ),
694
- [regionOptions.resize, regionOptions.pinCorner]
695
- );
696
- const dragRef = (0, import_react.useRef)(null);
697
- const markerRef = (0, import_react.useRef)(null);
698
- (0, import_react.useEffect)(() => {
699
- if (!open) return;
700
- const onPointerDown = (e) => {
701
- const target = e.target;
702
- if (!target) return;
703
- if (target.closest(".cmt-canvas-popover")) return;
704
- const marker = markerRef.current;
705
- if (marker && marker.contains(target)) return;
706
- if (target.closest(".cmt-canvas-region-handle, .cmt-canvas-region--movable")) return;
707
- if (target.closest(".tlui-menu, [data-radix-popper-content-wrapper], .cmt-mention-popup"))
708
- return;
709
- import_state.openThreadId.set(editor, null);
710
- };
711
- document.addEventListener("pointerdown", onPointerDown, true);
712
- return () => document.removeEventListener("pointerdown", onPointerDown, true);
713
- }, [open, editor]);
714
- const point = (0, import_tldraw.useValue)(
715
- "pin point",
716
- () => {
717
- if (thread.pageId !== editor.getCurrentPageId()) return null;
718
- const pagePoint = (0, import_thread_state.anchorPagePoint)(editor, thread.anchor, impreciseShapeAnchor);
719
- return pagePoint ? editor.pageToViewport(pagePoint) : null;
720
- },
721
- [editor, thread.anchor, thread.pageId, impreciseShapeAnchor]
722
- );
723
- const visible = point !== null;
724
- (0, import_react.useEffect)(() => {
725
- if (!open || !visible || !isCommentUnread || !onCommentRead) return;
726
- for (const comment of comments) {
727
- if (isCommentUnread(comment.id)) {
728
- onCommentRead(comment.id);
729
- }
730
- }
731
- }, [open, visible, comments, isCommentUnread, onCommentRead]);
732
- if (!point) return null;
733
- const postReply = () => {
734
- if ((0, import_comment_extensions.isCommentEmpty)(reply) || !currentUserId) return;
735
- (0, import_state.commitCommentMutation)(editor, () => {
736
- const comment = (0, import_tldraw.createComment)({
737
- threadId: thread.id,
738
- pageId: thread.pageId,
739
- authorId: currentUserId,
740
- body: reply
741
- });
742
- (0, import_comment_store.putCommentRecords)(editor, [comment]);
743
- if (onPostComment) onPostComment(comment);
744
- });
745
- setReply(import_comment_extensions.EMPTY_COMMENT);
746
- };
747
- const toggleResolve = () => {
748
- if (!currentUserId) return;
749
- (0, import_state.commitCommentMutation)(editor, () => {
750
- (0, import_comment_store.putCommentRecords)(editor, [
751
- {
752
- ...thread,
753
- resolved: thread.resolved ? null : { at: Date.now(), by: currentUserId }
754
- }
755
- ]);
756
- });
757
- };
758
- const deleteThread = () => {
759
- import_state.openThreadId.set(editor, null);
760
- (0, import_state.commitCommentMutation)(
761
- editor,
762
- () => (0, import_comment_store.removeCommentRecords)(editor, [thread.id, ...comments.map((c) => c.id)])
763
- );
764
- };
765
- const startEdit = (comment) => {
766
- setEditingId(comment.id);
767
- setEditText(comment.body);
768
- };
769
- const saveEdit = () => {
770
- const comment = comments.find((c) => c.id === editingId);
771
- if (!comment || (0, import_comment_extensions.isCommentEmpty)(editText)) return;
772
- (0, import_state.commitCommentMutation)(editor, () => {
773
- (0, import_comment_store.putCommentRecords)(editor, [{ ...comment, body: editText, editedAt: Date.now() }]);
774
- });
775
- setEditingId(null);
776
- };
777
- const renderComment = (card, index) => {
778
- const comment = comments[index];
779
- if (editingId === comment.id) {
780
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
781
- "div",
782
- {
783
- className: "cmt-editing",
784
- onKeyDown: (e) => {
785
- if (e.key === "Escape") {
786
- setEditingId(null);
787
- e.stopPropagation();
788
- }
789
- },
790
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
791
- import_comment_composer.CommentComposer,
200
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_thread_pin.ThreadPin, { editor, thread, ...props });
201
+ };
202
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.EditorPortal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-cmt-canvas-layer", children: [
203
+ options.enableClustering ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
204
+ fadeNodes.map(({ node, phase }) => {
205
+ let content;
206
+ const stackGroup = node.count > 1 ? stackGroupOf(node) : null;
207
+ if (node.count === 1) {
208
+ const thread = threadsById.get(node.id);
209
+ if (!thread) return null;
210
+ content = renderThreadPin(thread);
211
+ } else if (stackGroup) {
212
+ const owner = stackGroup.find((id) => renderedThreadIds.has(id));
213
+ content = owner && node.members.includes(owner) ? renderThreadPin(threadsById.get(owner)) : null;
214
+ } else {
215
+ content = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
216
+ import_cluster_badge.ClusterBadge,
792
217
  {
793
- author: card.author,
794
- placeholder: msg("comments.edit-placeholder"),
795
- value: editText,
796
- onChange: setEditText,
797
- onSubmit: saveEdit,
798
- sendLabel: msg("comments.save"),
799
- disabled: (0, import_comment_extensions.isCommentEmpty)(editText),
800
- getMentionSuggestions,
801
- renderMentionSuggestion,
802
- autoFocus: true
218
+ editor,
219
+ node,
220
+ onExpand: expandCluster,
221
+ onSelectThread: revealClusteredThread,
222
+ threadsById,
223
+ currentUserId: props.currentUserId,
224
+ resolveAuthor: props.resolveAuthor
803
225
  }
804
- )
226
+ );
805
227
  }
806
- );
807
- }
808
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
809
- import_comment_card.CommentCard,
810
- {
811
- ...card,
812
- actions: comment.authorId === currentUserId ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
813
- "button",
814
- {
815
- className: "cmt-thread__action",
816
- title: msg("comments.edit"),
817
- onClick: () => startEdit(comment),
818
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiIcon, { icon: "dots-horizontal", label: msg("comments.edit"), small: true })
819
- }
820
- ) : void 0
821
- }
822
- );
823
- };
824
- const headerActions = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
825
- currentUserId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
826
- "button",
827
- {
828
- className: "cmt-thread__action",
829
- title: msg(thread.resolved ? "comments.reopen" : "comments.resolve"),
830
- onClick: toggleResolve,
831
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
832
- import_tldraw.TldrawUiIcon,
833
- {
834
- icon: "check",
835
- label: msg(thread.resolved ? "comments.reopen" : "comments.resolve"),
836
- small: true
837
- }
838
- )
839
- }
840
- ),
841
- currentUserId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
842
- "button",
843
- {
844
- className: "cmt-thread__action",
845
- title: msg("comments.delete"),
846
- onClick: deleteThread,
847
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiIcon, { icon: "trash", label: msg("comments.delete"), small: true })
848
- }
849
- ),
850
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
851
- "button",
852
- {
853
- className: "cmt-thread__action",
854
- title: msg("comments.dismiss"),
855
- onClick: () => import_state.openThreadId.set(editor, null),
856
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiIcon, { icon: "cross-2", label: msg("comments.dismiss"), small: true })
857
- }
858
- )
859
- ] });
860
- const PinContent = options.components.PinContent;
861
- const pinContent = PinContent ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PinContent, { thread, comments }) : initialOf(resolveName(thread.createdBy) ?? import_comment_render.UNKNOWN_AUTHOR);
862
- const isRegion = thread.anchor.type === "region";
863
- const pinMovable = regionOptions.move !== "body";
864
- const bodyMovable = regionOptions.move !== "pin";
865
- const startDrag = (e) => {
866
- e.stopPropagation();
867
- dragRef.current = { startX: e.clientX, startY: e.clientY, moved: false };
868
- e.currentTarget.setPointerCapture(e.pointerId);
869
- };
870
- const onDrag = (e) => {
871
- const drag = dragRef.current;
872
- if (!drag) return;
873
- if (isRegion && !pinMovable) return;
874
- if (!drag.moved && Math.hypot(e.clientX - drag.startX, e.clientY - drag.startY) < 4) return;
875
- drag.moved = true;
876
- setDragPagePoint(editor.screenToPage({ x: e.clientX, y: e.clientY }));
877
- };
878
- const endDrag = (e) => {
879
- const drag = dragRef.current;
880
- dragRef.current = null;
881
- if (e.currentTarget.hasPointerCapture(e.pointerId)) {
882
- e.currentTarget.releasePointerCapture(e.pointerId);
883
- }
884
- if (!drag) return;
885
- if (!drag.moved) {
886
- import_state.openThreadId.set(editor, import_state.openThreadId.get(editor) === thread.id ? null : thread.id);
887
- return;
888
- }
889
- const pagePoint = editor.screenToPage({ x: e.clientX, y: e.clientY });
890
- setDragPagePoint(null);
891
- let anchor;
892
- if (thread.anchor.type === "region") {
893
- anchor = {
894
- ...thread.anchor,
895
- x: pagePoint.x - regionOptions.pinCorner.x * thread.anchor.w,
896
- y: pagePoint.y - regionOptions.pinCorner.y * thread.anchor.h
897
- };
898
- } else {
899
- const hit = editor.getShapeAtPoint(pagePoint, { hitInside: true });
900
- anchor = hit ? (0, import_thread_state.shapeAnchorAt)(editor, hit.id, pagePoint, e.altKey) : { type: "point", x: pagePoint.x, y: pagePoint.y };
901
- }
902
- (0, import_state.commitCommentMutation)(editor, () => (0, import_comment_store.putCommentRecords)(editor, [{ ...thread, anchor }]), "drag");
903
- };
904
- const livePinPage = resizeBounds ? (0, import_thread_state.regionPinPoint)(resizeBounds, regionOptions.pinCorner) : dragPagePoint;
905
- const renderPoint = livePinPage ? editor.pageToViewport(livePinPage) : point;
906
- const regionAnchor = thread.anchor.type === "region" ? thread.anchor : void 0;
907
- const movedRegion = regionAnchor && dragPagePoint ? {
908
- ...regionAnchor,
909
- x: dragPagePoint.x - regionOptions.pinCorner.x * regionAnchor.w,
910
- y: dragPagePoint.y - regionOptions.pinCorner.y * regionAnchor.h
911
- } : regionAnchor;
912
- const regionBoxBounds = resizeBounds ?? movedRegion;
913
- const commitResize = (bounds) => {
914
- setResizeBounds(null);
915
- editor.run(
916
- () => (0, import_comment_store.putCommentRecords)(editor, [{ ...thread, anchor: { type: "region", ...bounds } }]),
917
- {
918
- history: "ignore"
919
- }
920
- );
921
- };
922
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
923
- regionBoxBounds && (dragPagePoint || revealed) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
924
- RegionBox,
925
- {
926
- editor,
927
- box: regionBoxBounds,
928
- movable: bodyMovable && !dragPagePoint,
929
- onPreview: setResizeBounds,
930
- onCommit: commitResize
931
- }
932
- ),
933
- regionBoxBounds && revealed && !dragPagePoint && regionOptions.resize !== "none" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
934
- RegionResizeHandles,
935
- {
936
- editor,
937
- box: regionBoxBounds,
938
- handles: resizeHandles,
939
- onPreview: setResizeBounds,
940
- onCommit: commitResize
941
- }
942
- ),
943
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
944
- "div",
945
- {
946
- className: open ? "cmt-canvas-pin cmt-canvas-pin--open" : "cmt-canvas-pin",
947
- style: { left: renderPoint.x, top: renderPoint.y },
948
- children: [
949
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
950
- "div",
951
- {
952
- ref: markerRef,
953
- className: "cmt-canvas-pin__marker",
954
- onPointerDown: startDrag,
955
- onPointerMove: onDrag,
956
- onPointerUp: endDrag,
957
- onPointerEnter: () => setPinHovered(true),
958
- onPointerLeave: () => setPinHovered(false),
959
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_comment_pin.CommentPin, { resolved: thread.resolved != null, open, children: pinContent })
960
- }
961
- ),
962
- open && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
963
- ThreadPopover,
964
- {
965
- container,
966
- style: { left: renderPoint.x + 36, top: renderPoint.y - 28 },
967
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
968
- import_comment_thread.CommentThread,
969
- {
970
- header: msg("comments.thread-title"),
971
- headerActions,
972
- renderComment,
973
- comments: comments.map((c) => toCardProps(c, props, options.components)),
974
- resolvedBanner: thread.resolved ? msg("comments.resolved-by").replace(
975
- "{name}",
976
- resolveName(thread.resolved.by) ?? import_comment_render.UNKNOWN_AUTHOR
977
- ) : void 0,
978
- composer: currentUserId && !thread.resolved ? {
979
- author: resolveName(currentUserId) ?? import_comment_render.UNKNOWN_AUTHOR,
980
- placeholder: msg("comments.reply-placeholder"),
981
- sendLabel: msg("comments.send"),
982
- value: reply,
983
- onChange: setReply,
984
- onSubmit: postReply,
985
- disabled: (0, import_comment_extensions.isCommentEmpty)(reply),
986
- getMentionSuggestions,
987
- renderMentionSuggestion
988
- } : void 0
989
- }
990
- )
991
- }
992
- )
993
- ]
994
- }
995
- )
996
- ] });
997
- });
998
- function PendingComposer({
999
- editor,
1000
- pending,
1001
- currentUserId,
1002
- resolveName,
1003
- onPostComment,
1004
- getMentionSuggestions,
1005
- renderMentionSuggestion
1006
- }) {
1007
- const [text, setText] = (0, import_react.useState)(import_comment_extensions.EMPTY_COMMENT);
1008
- const ref = (0, import_react.useRef)(null);
1009
- const msg = (0, import_tldraw.useTranslation)();
1010
- const container = (0, import_tldraw.useContainer)();
1011
- (0, import_tldraw.usePassThroughWheelEvents)(ref);
1012
- (0, import_tldraw.usePassThroughMouseOverEvents)(ref);
1013
- const point = (0, import_tldraw.useValue)("composer point", () => editor.pageToViewport(pending.point), [
1014
- editor,
1015
- pending.point
1016
- ]);
1017
- (0, import_react.useEffect)(() => {
1018
- const onPointerDown = (e) => {
1019
- const el = ref.current;
1020
- const target = e.target;
1021
- if (!el || !target) return;
1022
- if (el.contains(target) || target.closest(".cmt-mention-popup")) return;
1023
- import_state.pendingComment.set(editor, null);
1024
- };
1025
- document.addEventListener("pointerdown", onPointerDown, true);
1026
- return () => document.removeEventListener("pointerdown", onPointerDown, true);
1027
- }, [editor]);
1028
- const submit = () => {
1029
- if ((0, import_comment_extensions.isCommentEmpty)(text) || !currentUserId) return;
1030
- (0, import_state.commitCommentMutation)(editor, () => {
1031
- const pageId = editor.getCurrentPageId();
1032
- const thread = (0, import_tldraw.createCommentThread)({
1033
- pageId,
1034
- anchor: pending.anchor,
1035
- createdBy: currentUserId
1036
- });
1037
- const comment = (0, import_tldraw.createComment)({
1038
- threadId: thread.id,
1039
- pageId,
1040
- authorId: currentUserId,
1041
- body: text
1042
- });
1043
- (0, import_comment_store.putCommentRecords)(editor, [thread, comment]);
1044
- if (onPostComment) onPostComment(comment);
1045
- });
1046
- setText(import_comment_extensions.EMPTY_COMMENT);
1047
- import_state.pendingComment.set(editor, null);
1048
- };
1049
- return (0, import_react_dom.createPortal)(
1050
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1051
- "div",
1052
- {
1053
- ref,
1054
- className: "cmt-canvas-composer",
1055
- style: { left: point.x, top: point.y },
1056
- onPointerDown: stop,
1057
- onKeyDown: (e) => {
1058
- if (e.key === "Escape" && !(0, import_mention_suggestion.isMentionPickerOpen)()) import_state.pendingComment.set(editor, null);
1059
- },
1060
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1061
- import_comment_composer.CommentComposer,
1062
- {
1063
- author: currentUserId ? resolveName(currentUserId) ?? import_comment_render.UNKNOWN_AUTHOR : "",
1064
- placeholder: msg("comments.add-placeholder"),
1065
- sendLabel: msg("comments.send"),
1066
- value: text,
1067
- onChange: setText,
1068
- onSubmit: submit,
1069
- disabled: (0, import_comment_extensions.isCommentEmpty)(text),
1070
- getMentionSuggestions,
1071
- renderMentionSuggestion,
1072
- autoFocus: true,
1073
- leading: draftAvatar
1074
- }
1075
- )
1076
- }
228
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_cluster_fade.clusterFadeClassName)(phase), children: content }, `cluster-fade:${node.id}`);
229
+ }),
230
+ orphanThreads.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Fragment, { children: renderThreadPin(thread) }, thread.id)),
231
+ heldThreads.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Fragment, { children: renderThreadPin(thread) }, thread.id))
232
+ ] }) : (
233
+ // Clustering off: every thread renders its own pin. The open thread is excluded and
234
+ // rendered once below, or it would mount a second, stacked pin.
235
+ threads.filter((thread) => thread.id !== openId).map((thread) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Fragment, { children: renderThreadPin(thread) }, thread.id))
1077
236
  ),
1078
- container
1079
- );
237
+ openThread && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Fragment, { children: renderThreadPin(openThread) }, `open:${openThread.id}`),
238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_region_box.RegionDraftBox, { editor }),
239
+ pending?.anchor.type === "region" && showPendingComposer && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_region_box.RegionBox, { editor, box: pending.anchor }),
240
+ pending && showPendingComposer && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_pending_composer.PendingComposer, { editor, pending, ...props })
241
+ ] }) });
1080
242
  }
1081
243
  //# sourceMappingURL=comments-overlay.js.map