@tldraw/commenting 0.0.0-bootstrap → 5.3.0-internal.41291fb579ed

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 (179) hide show
  1. package/commenting.css +284 -153
  2. package/dist-cjs/canvas/comment-body.js +1 -1
  3. package/dist-cjs/canvas/comment-body.js.map +2 -2
  4. package/dist-cjs/canvas/comment-drafts.js +54 -0
  5. package/dist-cjs/canvas/comment-drafts.js.map +7 -0
  6. package/dist-cjs/canvas/comment-render.js +2 -0
  7. package/dist-cjs/canvas/comment-render.js.map +2 -2
  8. package/dist-cjs/canvas/comment-tool.js +55 -11
  9. package/dist-cjs/canvas/comment-tool.js.map +2 -2
  10. package/dist-cjs/canvas/comments-filter-menu.js +54 -63
  11. package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
  12. package/dist-cjs/canvas/comments-overflow-menu.js +21 -27
  13. package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
  14. package/dist-cjs/canvas/comments-overlay.js +294 -111
  15. package/dist-cjs/canvas/comments-overlay.js.map +2 -2
  16. package/dist-cjs/canvas/comments-sidebar.js +12 -18
  17. package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
  18. package/dist-cjs/canvas/comments-visibility-toggle.js +66 -0
  19. package/dist-cjs/canvas/comments-visibility-toggle.js.map +7 -0
  20. package/dist-cjs/canvas/sidebar-filters.js +2 -2
  21. package/dist-cjs/canvas/sidebar-filters.js.map +2 -2
  22. package/dist-cjs/canvas/state.js +11 -0
  23. package/dist-cjs/canvas/state.js.map +2 -2
  24. package/dist-cjs/canvas/thread-state.js +8 -1
  25. package/dist-cjs/canvas/thread-state.js.map +2 -2
  26. package/dist-cjs/clustering/runtime.js +119 -7
  27. package/dist-cjs/clustering/runtime.js.map +2 -2
  28. package/dist-cjs/index.d.ts +100 -39
  29. package/dist-cjs/index.js +7 -1
  30. package/dist-cjs/index.js.map +2 -2
  31. package/dist-cjs/ui/avatar.js +14 -6
  32. package/dist-cjs/ui/avatar.js.map +2 -2
  33. package/dist-cjs/ui/byline.js +4 -4
  34. package/dist-cjs/ui/byline.js.map +2 -2
  35. package/dist-cjs/ui/comment-author.js +17 -0
  36. package/dist-cjs/ui/comment-author.js.map +7 -0
  37. package/dist-cjs/ui/comment-card.js +4 -4
  38. package/dist-cjs/ui/comment-card.js.map +2 -2
  39. package/dist-cjs/ui/comment-composer.js +92 -29
  40. package/dist-cjs/ui/comment-composer.js.map +2 -2
  41. package/dist-cjs/ui/comment-mention.js +1 -1
  42. package/dist-cjs/ui/comment-mention.js.map +2 -2
  43. package/dist-cjs/ui/comment-pin.js +23 -3
  44. package/dist-cjs/ui/comment-pin.js.map +2 -2
  45. package/dist-cjs/ui/comment-text.js +1 -1
  46. package/dist-cjs/ui/comment-text.js.map +2 -2
  47. package/dist-cjs/ui/comment-thread.js +6 -6
  48. package/dist-cjs/ui/comment-thread.js.map +2 -2
  49. package/dist-cjs/ui/comments-list.js +12 -12
  50. package/dist-cjs/ui/comments-list.js.map +2 -2
  51. package/dist-cjs/ui/count-badge.js +1 -1
  52. package/dist-cjs/ui/count-badge.js.map +2 -2
  53. package/dist-cjs/ui/empty-state.js +3 -3
  54. package/dist-cjs/ui/empty-state.js.map +2 -2
  55. package/dist-cjs/ui/format-time.js +3 -0
  56. package/dist-cjs/ui/format-time.js.map +2 -2
  57. package/dist-cjs/ui/mention-list.js +8 -8
  58. package/dist-cjs/ui/mention-list.js.map +2 -2
  59. package/dist-cjs/ui/mention-suggestion.js +3 -3
  60. package/dist-cjs/ui/mention-suggestion.js.map +2 -2
  61. package/dist-cjs/ui/mention.js +1 -1
  62. package/dist-cjs/ui/mention.js.map +2 -2
  63. package/dist-cjs/ui/reaction.js +11 -4
  64. package/dist-cjs/ui/reaction.js.map +2 -2
  65. package/dist-cjs/ui/reactions.js +10 -2
  66. package/dist-cjs/ui/reactions.js.map +2 -2
  67. package/dist-cjs/ui/send-button.js +1 -1
  68. package/dist-cjs/ui/send-button.js.map +2 -2
  69. package/dist-cjs/ui/tooltip-button.js +32 -0
  70. package/dist-cjs/ui/tooltip-button.js.map +7 -0
  71. package/dist-esm/canvas/comment-body.mjs +1 -1
  72. package/dist-esm/canvas/comment-body.mjs.map +2 -2
  73. package/dist-esm/canvas/comment-drafts.mjs +34 -0
  74. package/dist-esm/canvas/comment-drafts.mjs.map +7 -0
  75. package/dist-esm/canvas/comment-render.mjs +2 -0
  76. package/dist-esm/canvas/comment-render.mjs.map +2 -2
  77. package/dist-esm/canvas/comment-tool.mjs +56 -12
  78. package/dist-esm/canvas/comment-tool.mjs.map +2 -2
  79. package/dist-esm/canvas/comments-filter-menu.mjs +54 -63
  80. package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
  81. package/dist-esm/canvas/comments-overflow-menu.mjs +22 -27
  82. package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
  83. package/dist-esm/canvas/comments-overlay.mjs +312 -113
  84. package/dist-esm/canvas/comments-overlay.mjs.map +2 -2
  85. package/dist-esm/canvas/comments-sidebar.mjs +15 -21
  86. package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
  87. package/dist-esm/canvas/comments-visibility-toggle.mjs +46 -0
  88. package/dist-esm/canvas/comments-visibility-toggle.mjs.map +7 -0
  89. package/dist-esm/canvas/sidebar-filters.mjs +2 -2
  90. package/dist-esm/canvas/sidebar-filters.mjs.map +2 -2
  91. package/dist-esm/canvas/state.mjs +11 -0
  92. package/dist-esm/canvas/state.mjs.map +2 -2
  93. package/dist-esm/canvas/thread-state.mjs +8 -1
  94. package/dist-esm/canvas/thread-state.mjs.map +2 -2
  95. package/dist-esm/clustering/runtime.mjs +119 -7
  96. package/dist-esm/clustering/runtime.mjs.map +2 -2
  97. package/dist-esm/index.d.mts +100 -39
  98. package/dist-esm/index.mjs +11 -1
  99. package/dist-esm/index.mjs.map +2 -2
  100. package/dist-esm/ui/avatar.mjs +14 -6
  101. package/dist-esm/ui/avatar.mjs.map +2 -2
  102. package/dist-esm/ui/byline.mjs +4 -4
  103. package/dist-esm/ui/byline.mjs.map +2 -2
  104. package/dist-esm/ui/comment-author.mjs +1 -0
  105. package/dist-esm/ui/comment-author.mjs.map +7 -0
  106. package/dist-esm/ui/comment-card.mjs +4 -4
  107. package/dist-esm/ui/comment-card.mjs.map +2 -2
  108. package/dist-esm/ui/comment-composer.mjs +93 -30
  109. package/dist-esm/ui/comment-composer.mjs.map +2 -2
  110. package/dist-esm/ui/comment-mention.mjs +1 -1
  111. package/dist-esm/ui/comment-mention.mjs.map +2 -2
  112. package/dist-esm/ui/comment-pin.mjs +23 -3
  113. package/dist-esm/ui/comment-pin.mjs.map +2 -2
  114. package/dist-esm/ui/comment-text.mjs +1 -1
  115. package/dist-esm/ui/comment-text.mjs.map +2 -2
  116. package/dist-esm/ui/comment-thread.mjs +6 -6
  117. package/dist-esm/ui/comment-thread.mjs.map +2 -2
  118. package/dist-esm/ui/comments-list.mjs +12 -12
  119. package/dist-esm/ui/comments-list.mjs.map +2 -2
  120. package/dist-esm/ui/count-badge.mjs +1 -1
  121. package/dist-esm/ui/count-badge.mjs.map +2 -2
  122. package/dist-esm/ui/empty-state.mjs +3 -3
  123. package/dist-esm/ui/empty-state.mjs.map +2 -2
  124. package/dist-esm/ui/format-time.mjs +3 -0
  125. package/dist-esm/ui/format-time.mjs.map +2 -2
  126. package/dist-esm/ui/mention-list.mjs +8 -8
  127. package/dist-esm/ui/mention-list.mjs.map +2 -2
  128. package/dist-esm/ui/mention-suggestion.mjs +3 -3
  129. package/dist-esm/ui/mention-suggestion.mjs.map +2 -2
  130. package/dist-esm/ui/mention.mjs +1 -1
  131. package/dist-esm/ui/mention.mjs.map +2 -2
  132. package/dist-esm/ui/reaction.mjs +11 -4
  133. package/dist-esm/ui/reaction.mjs.map +2 -2
  134. package/dist-esm/ui/reactions.mjs +10 -2
  135. package/dist-esm/ui/reactions.mjs.map +2 -2
  136. package/dist-esm/ui/send-button.mjs +1 -1
  137. package/dist-esm/ui/send-button.mjs.map +2 -2
  138. package/dist-esm/ui/tooltip-button.mjs +12 -0
  139. package/dist-esm/ui/tooltip-button.mjs.map +7 -0
  140. package/package.json +3 -3
  141. package/src/canvas/canvas.css +79 -36
  142. package/src/canvas/comment-body.tsx +1 -1
  143. package/src/canvas/comment-drafts.test.ts +55 -0
  144. package/src/canvas/comment-drafts.ts +46 -0
  145. package/src/canvas/comment-render.test.ts +1 -1
  146. package/src/canvas/comment-render.ts +7 -2
  147. package/src/canvas/comment-tool.tsx +87 -15
  148. package/src/canvas/comments-filter-menu.tsx +27 -33
  149. package/src/canvas/comments-overflow-menu.tsx +13 -21
  150. package/src/canvas/comments-overlay.tsx +431 -144
  151. package/src/canvas/comments-sidebar.tsx +27 -28
  152. package/src/canvas/comments-visibility-toggle.tsx +50 -0
  153. package/src/canvas/sidebar-filters.ts +4 -3
  154. package/src/canvas/state.ts +22 -0
  155. package/src/canvas/thread-state.ts +13 -1
  156. package/src/clustering/runtime.test.ts +183 -7
  157. package/src/clustering/runtime.ts +164 -15
  158. package/src/index.ts +6 -0
  159. package/src/ui/avatar.tsx +15 -11
  160. package/src/ui/byline.tsx +6 -5
  161. package/src/ui/comment-author.ts +15 -0
  162. package/src/ui/comment-card.tsx +6 -5
  163. package/src/ui/comment-composer.tsx +122 -33
  164. package/src/ui/comment-mention.ts +2 -2
  165. package/src/ui/comment-pin.tsx +35 -4
  166. package/src/ui/comment-text.tsx +1 -1
  167. package/src/ui/comment-thread.tsx +8 -6
  168. package/src/ui/comments-list.tsx +19 -14
  169. package/src/ui/comments.css +205 -117
  170. package/src/ui/count-badge.tsx +1 -1
  171. package/src/ui/empty-state.tsx +3 -3
  172. package/src/ui/format-time.ts +5 -0
  173. package/src/ui/mention-list.tsx +13 -16
  174. package/src/ui/mention-suggestion.tsx +3 -3
  175. package/src/ui/mention.tsx +1 -1
  176. package/src/ui/reaction.tsx +6 -3
  177. package/src/ui/reactions.tsx +6 -2
  178. package/src/ui/send-button.tsx +1 -1
  179. package/src/ui/tooltip-button.tsx +20 -0
@@ -26,6 +26,6 @@ var import_react = require("react");
26
26
  var import_comment_render = require("./comment-render");
27
27
  function CommentBody({ richText, resolveName }) {
28
28
  const html = (0, import_react.useMemo)(() => (0, import_comment_render.renderCommentHtml)(richText, resolveName), [richText, resolveName]);
29
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cmt-text", dangerouslySetInnerHTML: { __html: html } });
29
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-cmt-text", dangerouslySetInnerHTML: { __html: html } });
30
30
  }
31
31
  //# sourceMappingURL=comment-body.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/canvas/comment-body.tsx"],
4
- "sourcesContent": ["import { useMemo } from 'react'\nimport { TLRichText } from 'tldraw'\nimport { renderCommentHtml } from './comment-render'\n\n/** @public */\nexport interface CommentBodyProps {\n\trichText: TLRichText\n\t/** Maps a member id to its current display name, so \\@mentions show the live name. */\n\tresolveName?(id: string): string | undefined\n}\n\n/**\n * Renders a comment's rich-text body read-only through the limited comment extension set (no\n * headings), so formatting (bold, links, lists, highlight) is preserved rather than flattened, and\n * headings can never render. Use this as the `body` of a `CommentCard` on a canvas.\n * @public @react\n */\nexport function CommentBody({ richText, resolveName }: CommentBodyProps) {\n\tconst html = useMemo(() => renderCommentHtml(richText, resolveName), [richText, resolveName])\n\treturn <div className=\"cmt-text\" dangerouslySetInnerHTML={{ __html: html }} />\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBQ;AAnBR,mBAAwB;AAExB,4BAAkC;AAe3B,SAAS,YAAY,EAAE,UAAU,YAAY,GAAqB;AACxE,QAAM,WAAO,sBAAQ,UAAM,yCAAkB,UAAU,WAAW,GAAG,CAAC,UAAU,WAAW,CAAC;AAC5F,SAAO,4CAAC,SAAI,WAAU,YAAW,yBAAyB,EAAE,QAAQ,KAAK,GAAG;AAC7E;",
4
+ "sourcesContent": ["import { useMemo } from 'react'\nimport { TLRichText } from 'tldraw'\nimport { renderCommentHtml } from './comment-render'\n\n/** @public */\nexport interface CommentBodyProps {\n\trichText: TLRichText\n\t/** Maps a member id to its current display name, so \\@mentions show the live name. */\n\tresolveName?(id: string): string | undefined\n}\n\n/**\n * Renders a comment's rich-text body read-only through the limited comment extension set (no\n * headings), so formatting (bold, links, lists, highlight) is preserved rather than flattened, and\n * headings can never render. Use this as the `body` of a `CommentCard` on a canvas.\n * @public @react\n */\nexport function CommentBody({ richText, resolveName }: CommentBodyProps) {\n\tconst html = useMemo(() => renderCommentHtml(richText, resolveName), [richText, resolveName])\n\treturn <div className=\"tlui-cmt-text\" dangerouslySetInnerHTML={{ __html: html }} />\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBQ;AAnBR,mBAAwB;AAExB,4BAAkC;AAe3B,SAAS,YAAY,EAAE,UAAU,YAAY,GAAqB;AACxE,QAAM,WAAO,sBAAQ,UAAM,yCAAkB,UAAU,WAAW,GAAG,CAAC,UAAU,WAAW,CAAC;AAC5F,SAAO,4CAAC,SAAI,WAAU,iBAAgB,yBAAyB,EAAE,QAAQ,KAAK,GAAG;AAClF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var comment_drafts_exports = {};
20
+ __export(comment_drafts_exports, {
21
+ NEW_COMMENT_DRAFT: () => NEW_COMMENT_DRAFT,
22
+ clearCommentDraft: () => clearCommentDraft,
23
+ getCommentDraft: () => getCommentDraft,
24
+ replyDraftSlot: () => replyDraftSlot,
25
+ saveCommentDraft: () => saveCommentDraft
26
+ });
27
+ module.exports = __toCommonJS(comment_drafts_exports);
28
+ var import_utils = require("@tldraw/utils");
29
+ var import_comment_extensions = require("../ui/comment-extensions");
30
+ const DRAFT_KEY_PREFIX = "tldraw-comment-draft";
31
+ const NEW_COMMENT_DRAFT = "new";
32
+ function replyDraftSlot(threadId) {
33
+ return `reply:${threadId}`;
34
+ }
35
+ function getCommentDraft(slot) {
36
+ const raw = (0, import_utils.getFromLocalStorage)(`${DRAFT_KEY_PREFIX}:${slot}`);
37
+ if (!raw) return void 0;
38
+ try {
39
+ return JSON.parse(raw);
40
+ } catch {
41
+ return void 0;
42
+ }
43
+ }
44
+ function saveCommentDraft(slot, value) {
45
+ if ((0, import_comment_extensions.isCommentEmpty)(value)) {
46
+ clearCommentDraft(slot);
47
+ return;
48
+ }
49
+ (0, import_utils.setInLocalStorage)(`${DRAFT_KEY_PREFIX}:${slot}`, JSON.stringify(value));
50
+ }
51
+ function clearCommentDraft(slot) {
52
+ (0, import_utils.deleteFromLocalStorage)(`${DRAFT_KEY_PREFIX}:${slot}`);
53
+ }
54
+ //# sourceMappingURL=comment-drafts.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/canvas/comment-drafts.ts"],
4
+ "sourcesContent": ["import { deleteFromLocalStorage, getFromLocalStorage, setInLocalStorage } from '@tldraw/utils'\nimport type { TLRichText } from 'tldraw'\nimport { isCommentEmpty } from '../ui/comment-extensions'\n\nconst DRAFT_KEY_PREFIX = 'tldraw-comment-draft'\n\n/** The single slot for an unsent new-comment (placement composer) draft. The anchor is re-picked\n * by clicking when the composer reopens, so only the text is worth keeping \u2014 and without a\n * universal document id in the SDK, one slot is the honest scope. */\nexport const NEW_COMMENT_DRAFT = 'new'\n\n/** The draft slot for an unsent reply on a thread. Thread record ids are globally unique and\n * stable across reloads, so replies never need a document key. @public */\nexport function replyDraftSlot(threadId: string): string {\n\treturn `reply:${threadId}`\n}\n\n/** Read a saved unsent draft, or undefined when there is none (or it fails to parse). @public */\nexport function getCommentDraft(slot: string): TLRichText | undefined {\n\tconst raw = getFromLocalStorage(`${DRAFT_KEY_PREFIX}:${slot}`)\n\tif (!raw) return undefined\n\ttry {\n\t\treturn JSON.parse(raw) as TLRichText\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Save an unsent draft so click-away and reload don't lose it \u2014 the flip side of not warning\n * before discard. Saving empty content clears the slot instead, so deleting your text and\n * closing doesn't resurrect an empty draft later.\n * @public\n */\nexport function saveCommentDraft(slot: string, value: TLRichText) {\n\tif (isCommentEmpty(value)) {\n\t\tclearCommentDraft(slot)\n\t\treturn\n\t}\n\tsetInLocalStorage(`${DRAFT_KEY_PREFIX}:${slot}`, JSON.stringify(value))\n}\n\n/** Remove a saved draft (after posting, or when its content was emptied). @public */\nexport function clearCommentDraft(slot: string) {\n\tdeleteFromLocalStorage(`${DRAFT_KEY_PREFIX}:${slot}`)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA+E;AAE/E,gCAA+B;AAE/B,MAAM,mBAAmB;AAKlB,MAAM,oBAAoB;AAI1B,SAAS,eAAe,UAA0B;AACxD,SAAO,SAAS,QAAQ;AACzB;AAGO,SAAS,gBAAgB,MAAsC;AACrE,QAAM,UAAM,kCAAoB,GAAG,gBAAgB,IAAI,IAAI,EAAE;AAC7D,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACH,WAAO,KAAK,MAAM,GAAG;AAAA,EACtB,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAQO,SAAS,iBAAiB,MAAc,OAAmB;AACjE,UAAI,0CAAe,KAAK,GAAG;AAC1B,sBAAkB,IAAI;AACtB;AAAA,EACD;AACA,sCAAkB,GAAG,gBAAgB,IAAI,IAAI,IAAI,KAAK,UAAU,KAAK,CAAC;AACvE;AAGO,SAAS,kBAAkB,MAAc;AAC/C,2CAAuB,GAAG,gBAAgB,IAAI,IAAI,EAAE;AACrD;",
6
+ "names": []
7
+ }
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var comment_render_exports = {};
20
20
  __export(comment_render_exports, {
21
21
  UNKNOWN_AUTHOR: () => UNKNOWN_AUTHOR,
22
+ UNKNOWN_COMMENT_AUTHOR: () => UNKNOWN_COMMENT_AUTHOR,
22
23
  renderCommentHtml: () => renderCommentHtml,
23
24
  renderCommentPlaintext: () => renderCommentPlaintext
24
25
  });
@@ -27,6 +28,7 @@ var import_core = require("@tiptap/core");
27
28
  var import_comment_extensions = require("../ui/comment-extensions");
28
29
  var import_comment_mention = require("../ui/comment-mention");
29
30
  const UNKNOWN_AUTHOR = "Someone";
31
+ const UNKNOWN_COMMENT_AUTHOR = Object.freeze({ name: UNKNOWN_AUTHOR });
30
32
  function demoteHeadings(node) {
31
33
  const content = Array.isArray(node.content) ? node.content.map(demoteHeadings) : node.content;
32
34
  if (node.type === "heading") {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/canvas/comment-render.ts"],
4
- "sourcesContent": ["import { generateHTML, generateText, JSONContent } from '@tiptap/core'\nimport { TLRichText } from 'tldraw'\nimport { commentTipTapExtensions, isCommentEmpty } from '../ui/comment-extensions'\nimport { commentMention } from '../ui/comment-mention'\n\n/**\n * The author name shown in a byline when no source can name an id (e.g. a deleted account, or a\n * member with no comment and no live presence). The toolkit's one generic default, applied where a\n * byline needs a name; hosts pre-empt it by returning a name from `resolveName`. Not a translation\n * key \u2014 a single English literal, matching the prior host-side default.\n */\nexport const UNKNOWN_AUTHOR = 'Someone'\n\n/**\n * The comment extension set has no heading node, so a body that nonetheless contains one (e.g. a\n * record created programmatically or synced from a client with a fuller set) would make TipTap\n * throw on an unknown node type. Demote any heading to a paragraph first, so headings can never\n * render as headings and rendering never crashes.\n */\nfunction demoteHeadings(node: JSONContent): JSONContent {\n\tconst content = Array.isArray(node.content) ? node.content.map(demoteHeadings) : node.content\n\tif (node.type === 'heading') {\n\t\tconst { attrs: _attrs, ...rest } = node\n\t\treturn { ...rest, type: 'paragraph', content }\n\t}\n\treturn content === node.content ? node : { ...node, content }\n}\n\n/**\n * Whether a body contains a mention node. Mention text is resolved from a member id at render time,\n * so a body with one can't be cached by identity alone (the same body renders differently as names\n * change) \u2014 those bodies skip the cache and re-render each call.\n */\nfunction hasMention(node: JSONContent): boolean {\n\tif (node.type === 'mention') return true\n\treturn Array.isArray(node.content) ? node.content.some(hasMention) : false\n}\n\nconst htmlCache = new WeakMap<TLRichText, string>()\n\n/**\n * Render a comment body to HTML through the limited comment extension set (no headings), so a body\n * always renders with comment formatting regardless of the host editor's rich-text config. Mirrors\n * tldraw's `renderHtmlFromRichText`, including the empty-paragraph fix that keeps blank lines from\n * collapsing. `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentHtml(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = htmlCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, commentMention({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst html = generateHTML(demoteHeadings(richText as JSONContent), extensions).replaceAll(\n\t\t'<p dir=\"auto\"></p>',\n\t\t'<p><br /></p>'\n\t)\n\tif (!mentions) htmlCache.set(richText, html)\n\treturn html\n}\n\nconst textCache = new WeakMap<TLRichText, string>()\n\n/**\n * Flatten a comment body to plaintext through the limited comment extension set \u2014 paragraphs and\n * list items separated by newlines. Used for previews (e.g. the sidebar) where formatting is dropped.\n * `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentPlaintext(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tif (isCommentEmpty(richText)) return ''\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = textCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, commentMention({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst text = generateText(demoteHeadings(richText as JSONContent), extensions, {\n\t\tblockSeparator: '\\n',\n\t})\n\tif (!mentions) textCache.set(richText, text)\n\treturn text\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAwD;AAExD,gCAAwD;AACxD,6BAA+B;AAQxB,MAAM,iBAAiB;AAQ9B,SAAS,eAAe,MAAgC;AACvD,QAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,IAAI,cAAc,IAAI,KAAK;AACtF,MAAI,KAAK,SAAS,WAAW;AAC5B,UAAM,EAAE,OAAO,QAAQ,GAAG,KAAK,IAAI;AACnC,WAAO,EAAE,GAAG,MAAM,MAAM,aAAa,QAAQ;AAAA,EAC9C;AACA,SAAO,YAAY,KAAK,UAAU,OAAO,EAAE,GAAG,MAAM,QAAQ;AAC7D;AAOA,SAAS,WAAW,MAA4B;AAC/C,MAAI,KAAK,SAAS,UAAW,QAAO;AACpC,SAAO,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,KAAK,UAAU,IAAI;AACtE;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAQ3C,SAAS,kBACf,UACA,aACS;AACT,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,uDAAyB,uCAAe,EAAE,YAAY,CAAC,CAAC,IAC5D;AACH,QAAM,WAAO,0BAAa,eAAe,QAAuB,GAAG,UAAU,EAAE;AAAA,IAC9E;AAAA,IACA;AAAA,EACD;AACA,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAO3C,SAAS,uBACf,UACA,aACS;AACT,UAAI,0CAAe,QAAQ,EAAG,QAAO;AACrC,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,uDAAyB,uCAAe,EAAE,YAAY,CAAC,CAAC,IAC5D;AACH,QAAM,WAAO,0BAAa,eAAe,QAAuB,GAAG,YAAY;AAAA,IAC9E,gBAAgB;AAAA,EACjB,CAAC;AACD,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;",
4
+ "sourcesContent": ["import { generateHTML, generateText, JSONContent } from '@tiptap/core'\nimport { TLRichText } from 'tldraw'\nimport { CommentAuthor } from '../ui/comment-author'\nimport { commentTipTapExtensions, isCommentEmpty } from '../ui/comment-extensions'\nimport { commentMention } from '../ui/comment-mention'\n\n/**\n * The author name shown in a byline when no source can name an id (e.g. a deleted account, or a\n * member with no comment and no live presence). The toolkit's one generic default, applied where a\n * byline needs a name; hosts pre-empt it by resolving the id from `resolveAuthor`. Not a\n * translation key \u2014 a single English literal.\n */\nexport const UNKNOWN_AUTHOR = 'Someone'\n\n/** The `CommentAuthor` fallback for an unresolvable id \u2014 just the name, no color or image.\n * Frozen: the one shared instance is handed to host render slots. */\nexport const UNKNOWN_COMMENT_AUTHOR: CommentAuthor = Object.freeze({ name: UNKNOWN_AUTHOR })\n\n/**\n * The comment extension set has no heading node, so a body that nonetheless contains one (e.g. a\n * record created programmatically or synced from a client with a fuller set) would make TipTap\n * throw on an unknown node type. Demote any heading to a paragraph first, so headings can never\n * render as headings and rendering never crashes.\n */\nfunction demoteHeadings(node: JSONContent): JSONContent {\n\tconst content = Array.isArray(node.content) ? node.content.map(demoteHeadings) : node.content\n\tif (node.type === 'heading') {\n\t\tconst { attrs: _attrs, ...rest } = node\n\t\treturn { ...rest, type: 'paragraph', content }\n\t}\n\treturn content === node.content ? node : { ...node, content }\n}\n\n/**\n * Whether a body contains a mention node. Mention text is resolved from a member id at render time,\n * so a body with one can't be cached by identity alone (the same body renders differently as names\n * change) \u2014 those bodies skip the cache and re-render each call.\n */\nfunction hasMention(node: JSONContent): boolean {\n\tif (node.type === 'mention') return true\n\treturn Array.isArray(node.content) ? node.content.some(hasMention) : false\n}\n\nconst htmlCache = new WeakMap<TLRichText, string>()\n\n/**\n * Render a comment body to HTML through the limited comment extension set (no headings), so a body\n * always renders with comment formatting regardless of the host editor's rich-text config. Mirrors\n * tldraw's `renderHtmlFromRichText`, including the empty-paragraph fix that keeps blank lines from\n * collapsing. `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentHtml(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = htmlCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, commentMention({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst html = generateHTML(demoteHeadings(richText as JSONContent), extensions).replaceAll(\n\t\t'<p dir=\"auto\"></p>',\n\t\t'<p><br /></p>'\n\t)\n\tif (!mentions) htmlCache.set(richText, html)\n\treturn html\n}\n\nconst textCache = new WeakMap<TLRichText, string>()\n\n/**\n * Flatten a comment body to plaintext through the limited comment extension set \u2014 paragraphs and\n * list items separated by newlines. Used for previews (e.g. the sidebar) where formatting is dropped.\n * `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentPlaintext(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tif (isCommentEmpty(richText)) return ''\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = textCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, commentMention({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst text = generateText(demoteHeadings(richText as JSONContent), extensions, {\n\t\tblockSeparator: '\\n',\n\t})\n\tif (!mentions) textCache.set(richText, text)\n\treturn text\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAwD;AAGxD,gCAAwD;AACxD,6BAA+B;AAQxB,MAAM,iBAAiB;AAIvB,MAAM,yBAAwC,OAAO,OAAO,EAAE,MAAM,eAAe,CAAC;AAQ3F,SAAS,eAAe,MAAgC;AACvD,QAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,IAAI,cAAc,IAAI,KAAK;AACtF,MAAI,KAAK,SAAS,WAAW;AAC5B,UAAM,EAAE,OAAO,QAAQ,GAAG,KAAK,IAAI;AACnC,WAAO,EAAE,GAAG,MAAM,MAAM,aAAa,QAAQ;AAAA,EAC9C;AACA,SAAO,YAAY,KAAK,UAAU,OAAO,EAAE,GAAG,MAAM,QAAQ;AAC7D;AAOA,SAAS,WAAW,MAA4B;AAC/C,MAAI,KAAK,SAAS,UAAW,QAAO;AACpC,SAAO,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,KAAK,UAAU,IAAI;AACtE;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAQ3C,SAAS,kBACf,UACA,aACS;AACT,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,uDAAyB,uCAAe,EAAE,YAAY,CAAC,CAAC,IAC5D;AACH,QAAM,WAAO,0BAAa,eAAe,QAAuB,GAAG,UAAU,EAAE;AAAA,IAC9E;AAAA,IACA;AAAA,EACD;AACA,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAO3C,SAAS,uBACf,UACA,aACS;AACT,UAAI,0CAAe,QAAQ,EAAG,QAAO;AACrC,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,uDAAyB,uCAAe,EAAE,YAAY,CAAC,CAAC,IAC5D;AACH,QAAM,WAAO,0BAAa,eAAe,QAAuB,GAAG,YAAY;AAAA,IAC9E,gBAAgB;AAAA,EACjB,CAAC;AACD,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -72,7 +72,11 @@ class CommentTool extends import_tldraw.StateNode {
72
72
  */
73
73
  options = import_options.defaultCommentingOptions;
74
74
  onEnter() {
75
- this.editor.setCursor({ type: "cross", rotation: 0 });
75
+ this.editor.setCursor({ type: "comment", rotation: 0 });
76
+ import_state.commentsSidebarOpen.set(this.editor, false);
77
+ }
78
+ onExit() {
79
+ this.editor.setHintingShapes([]);
76
80
  }
77
81
  // Escape leaves the tool, like the built-in tools (the editor dispatches `cancel` on Escape).
78
82
  onCancel() {
@@ -81,20 +85,47 @@ class CommentTool extends import_tldraw.StateNode {
81
85
  }
82
86
  class CommentIdle extends import_tldraw.StateNode {
83
87
  static id = "idle";
88
+ onEnter() {
89
+ this.editor.setCursor({ type: "comment", rotation: 0 });
90
+ this.updateHint();
91
+ }
92
+ // Hint the shape a click would attach to, using the same hit-test as onPointerUp below. Hinting
93
+ // shapes render an indicator ungated by the active tool, so this shows the select-style outline
94
+ // while the comment tool — not select — is active.
95
+ onPointerMove() {
96
+ this.updateHint();
97
+ }
84
98
  onPointerDown() {
85
99
  this.parent.transition("pointing");
86
100
  }
101
+ updateHint() {
102
+ const { editor } = this;
103
+ const hit = editor.getShapeAtPoint(editor.inputs.getCurrentPagePoint(), { hitInside: true });
104
+ editor.setHintingShapes(hit ? [hit.id] : []);
105
+ }
87
106
  }
88
107
  class CommentPointing extends import_tldraw.StateNode {
89
108
  static id = "pointing";
90
- // Once the pointer passes the editor's drag threshold this is a region, not a click — but only
91
- // when region comments are enabled; otherwise a drag is treated as a click (point/shape).
109
+ onEnter() {
110
+ const { editor } = this;
111
+ editor.setHintingShapes([]);
112
+ editor.setCursor({ type: "none", rotation: 0 });
113
+ const point = editor.inputs.getCurrentPagePoint();
114
+ import_state.pendingComment.set(editor, {
115
+ anchor: { type: "point", x: point.x, y: point.y },
116
+ point: { x: point.x, y: point.y }
117
+ });
118
+ }
92
119
  onPointerMove() {
93
- if ((0, import_region_options.getRegionCommentOptions)(this.editor).enabled && this.editor.inputs.getIsDragging()) {
120
+ const { editor } = this;
121
+ if ((0, import_region_options.getRegionCommentOptions)(editor).enabled && editor.inputs.getIsDragging()) {
94
122
  this.parent.transition("dragging");
123
+ return;
95
124
  }
125
+ const point = editor.inputs.getCurrentPagePoint();
126
+ import_state.pendingComment.update(editor, (p) => p ? { ...p, point: { x: point.x, y: point.y } } : p);
96
127
  }
97
- // A pointer up with no drag is a click: anchor to the shape under it, or drop a point.
128
+ // Settle where the pointer is released: anchor to the shape under it, or drop a point.
98
129
  onPointerUp() {
99
130
  const { editor } = this;
100
131
  const point = editor.inputs.getCurrentPagePoint();
@@ -103,10 +134,23 @@ class CommentPointing extends import_tldraw.StateNode {
103
134
  import_state.pendingComment.set(editor, { anchor, point: { x: point.x, y: point.y } });
104
135
  editor.setCurrentTool("select");
105
136
  }
137
+ onCancel() {
138
+ this.cancel();
139
+ }
140
+ onInterrupt() {
141
+ this.cancel();
142
+ }
143
+ // Abandon the follow composer if placement is interrupted (Escape, focus loss, etc.).
144
+ cancel() {
145
+ import_state.pendingComment.set(this.editor, null);
146
+ this.editor.setCurrentTool("select");
147
+ }
106
148
  }
107
149
  class CommentDragging extends import_tldraw.StateNode {
108
150
  static id = "dragging";
109
151
  onEnter() {
152
+ import_state.pendingComment.set(this.editor, null);
153
+ this.editor.setCursor({ type: "cross", rotation: 0 });
110
154
  this.updateDraft();
111
155
  }
112
156
  onPointerMove() {
@@ -115,14 +159,14 @@ class CommentDragging extends import_tldraw.StateNode {
115
159
  // Commit the dragged rectangle as a region anchor; the composer opens at its pin corner.
116
160
  onPointerUp() {
117
161
  const { editor } = this;
118
- const region = regionBetween(
119
- editor.inputs.getOriginPagePoint(),
120
- editor.inputs.getCurrentPagePoint()
121
- );
162
+ const origin = editor.inputs.getOriginPagePoint();
163
+ const current = editor.inputs.getCurrentPagePoint();
164
+ const region = regionBetween(origin, current);
165
+ const pin = { x: current.x >= origin.x ? 1 : 0, y: current.y >= origin.y ? 1 : 0 };
122
166
  import_state.regionDraft.set(editor, null);
123
167
  import_state.pendingComment.set(editor, {
124
- anchor: { type: "region", ...region },
125
- point: (0, import_thread_state.regionPinPoint)(region, (0, import_region_options.getRegionCommentOptions)(editor).pinCorner)
168
+ anchor: { type: "region", ...region, pinX: pin.x, pinY: pin.y },
169
+ point: (0, import_thread_state.regionPinPoint)(region, pin)
126
170
  });
127
171
  editor.setCurrentTool("select");
128
172
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/canvas/comment-tool.tsx"],
4
- "sourcesContent": ["import {\n\tBoxModel,\n\tStateNode,\n\tTLCommentAnchor,\n\tTLStateNodeConstructor,\n\tTLUiOverrides,\n\tVecLike,\n} from 'tldraw'\nimport { type CommentingOptions, defaultCommentingOptions } from './options'\nimport { getRegionCommentOptions } from './region-options'\nimport { pendingComment, regionDraft } from './state'\nimport { regionPinPoint, shapeAnchorAt } from './thread-state'\n\n/** A comment being placed but not yet posted: where its composer sits and what it will anchor\n * to. Shared between the tool (which sets it on click) and the overlay (which renders the\n * composer). Null when nothing is being placed. The atom itself lives in `./state`\n * ({@link pendingComment}), scoped per editor.\n * @public */\nexport interface PendingComment {\n\tanchor: TLCommentAnchor\n\t/** Page point where the composer opens (the click location, or a region's pin corner). */\n\tpoint: VecLike\n}\n\n/**\n * Merge configure options over a base. Shallow for scalars; `components` is merged rather than\n * replaced so chained `configure` calls layer their slots \u2014 a later `{ components: { PinContent } }`\n * doesn't drop an earlier `{ components: { CommentBody } }`.\n */\nfunction mergeCommentingOptions(\n\tbase: CommentingOptions,\n\toverrides: Partial<CommentingOptions>\n): CommentingOptions {\n\treturn {\n\t\t...base,\n\t\t...overrides,\n\t\tcomponents: { ...base.components, ...overrides.components },\n\t}\n}\n\n/** The page-space rectangle spanned by two points, normalized so w/h are non-negative. Shared by\n * region creation (pointer-down \u2192 cursor) and corner resize (fixed corner \u2192 cursor). */\nexport function regionBetween(a: VecLike, b: VecLike): BoxModel {\n\treturn {\n\t\tx: Math.min(a.x, b.x),\n\t\ty: Math.min(a.y, b.y),\n\t\tw: Math.abs(a.x - b.x),\n\t\th: Math.abs(a.y - b.y),\n\t}\n}\n\n/**\n * The comment tool. A click starts a point/shape thread (the pin tracks the shape it lands on);\n * dragging out a rectangle starts a region thread anchored to that area. Placement only opens a\n * composer (via `pendingComment`); the records are created when the comment is posted.\n * @public\n */\nexport class CommentTool extends StateNode {\n\tstatic override id = 'comment'\n\tstatic override initial = 'idle'\n\tstatic override children(): TLStateNodeConstructor[] {\n\t\treturn [CommentIdle, CommentPointing, CommentDragging]\n\t}\n\n\t/**\n\t * Configure this tool's {@link CommentTool.options | `options`}, returning a configured subclass\n\t * to register via `tools`. Mirrors `ShapeUtil.configure`. Layers over any prior `configure`, so\n\t * calls can be chained.\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />\n\t * ```\n\t */\n\tstatic configure<T extends TLStateNodeConstructor>(\n\t\tthis: T,\n\t\toptions: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never\n\t): T {\n\t\t// @ts-expect-error -- mirrors ShapeUtil.configure; extending `this` is sound at runtime.\n\t\treturn class extends this {\n\t\t\t// @ts-expect-error\n\t\t\toptions = mergeCommentingOptions(this.options, options)\n\t\t}\n\t}\n\n\t/**\n\t * The merged commenting options for this editor. Read from anywhere via\n\t * {@link getCommentingOptions}. Override with {@link CommentTool.configure}.\n\t */\n\toptions: CommentingOptions = defaultCommentingOptions\n\n\toverride onEnter() {\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t}\n\n\t// Escape leaves the tool, like the built-in tools (the editor dispatches `cancel` on Escape).\n\toverride onCancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentIdle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onPointerDown() {\n\t\tthis.parent.transition('pointing')\n\t}\n}\n\nclass CommentPointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\t// Once the pointer passes the editor's drag threshold this is a region, not a click \u2014 but only\n\t// when region comments are enabled; otherwise a drag is treated as a click (point/shape).\n\toverride onPointerMove() {\n\t\tif (getRegionCommentOptions(this.editor).enabled && this.editor.inputs.getIsDragging()) {\n\t\t\tthis.parent.transition('dragging')\n\t\t}\n\t}\n\n\t// A pointer up with no drag is a click: anchor to the shape under it, or drop a point.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tconst hit = editor.getShapeAtPoint(point, { hitInside: true })\n\t\tconst anchor: TLCommentAnchor = hit\n\t\t\t? shapeAnchorAt(editor, hit.id, point, editor.inputs.getAltKey())\n\t\t\t: { type: 'point', x: point.x, y: point.y }\n\t\tpendingComment.set(editor, { anchor, point: { x: point.x, y: point.y } })\n\t\t// Hand back to select; the open composer is now the focus.\n\t\teditor.setCurrentTool('select')\n\t}\n}\n\nclass CommentDragging extends StateNode {\n\tstatic override id = 'dragging'\n\n\toverride onEnter() {\n\t\tthis.updateDraft()\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateDraft()\n\t}\n\n\t// Commit the dragged rectangle as a region anchor; the composer opens at its pin corner.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst region = regionBetween(\n\t\t\teditor.inputs.getOriginPagePoint(),\n\t\t\teditor.inputs.getCurrentPagePoint()\n\t\t)\n\t\tregionDraft.set(editor, null)\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'region', ...region },\n\t\t\tpoint: regionPinPoint(region, getRegionCommentOptions(editor).pinCorner),\n\t\t})\n\t\teditor.setCurrentTool('select')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate updateDraft() {\n\t\tconst { editor } = this\n\t\tregionDraft.set(\n\t\t\teditor,\n\t\t\tregionBetween(editor.inputs.getOriginPagePoint(), editor.inputs.getCurrentPagePoint())\n\t\t)\n\t}\n\n\tprivate cancel() {\n\t\tregionDraft.set(this.editor, null)\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\n/** @public */\nexport const commentTools = [CommentTool]\n\n/** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.\n * Once registered, tldraw's `DefaultToolbarContent` shows the comment button next to the eraser.\n * @public */\nexport const commentToolOverrides: TLUiOverrides = {\n\ttools(editor, tools) {\n\t\ttools.comment = {\n\t\t\tid: 'comment',\n\t\t\ticon: 'comment',\n\t\t\tlabel: 'Comment',\n\t\t\tkbd: 'c',\n\t\t\tonSelect: () => editor.setCurrentTool('comment'),\n\t\t}\n\t\treturn tools\n\t},\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tBoxModel,\n\tStateNode,\n\tTLCommentAnchor,\n\tTLStateNodeConstructor,\n\tTLUiOverrides,\n\tVecLike,\n} from 'tldraw'\nimport { type CommentingOptions, defaultCommentingOptions } from './options'\nimport { getRegionCommentOptions } from './region-options'\nimport { commentsSidebarOpen, pendingComment, regionDraft } from './state'\nimport { regionPinPoint, shapeAnchorAt } from './thread-state'\n\n/** A comment being placed but not yet posted: where its composer sits and what it will anchor\n * to. Shared between the tool (which sets it on click) and the overlay (which renders the\n * composer). Null when nothing is being placed. The atom itself lives in `./state`\n * ({@link pendingComment}), scoped per editor.\n * @public */\nexport interface PendingComment {\n\tanchor: TLCommentAnchor\n\t/** Page point where the composer opens (the click location, or a region's pin corner). */\n\tpoint: VecLike\n}\n\n/**\n * Merge configure options over a base. Shallow for scalars; `components` is merged rather than\n * replaced so chained `configure` calls layer their slots \u2014 a later `{ components: { PinContent } }`\n * doesn't drop an earlier `{ components: { CommentBody } }`.\n */\nfunction mergeCommentingOptions(\n\tbase: CommentingOptions,\n\toverrides: Partial<CommentingOptions>\n): CommentingOptions {\n\treturn {\n\t\t...base,\n\t\t...overrides,\n\t\tcomponents: { ...base.components, ...overrides.components },\n\t}\n}\n\n/** The page-space rectangle spanned by two points, normalized so w/h are non-negative. Shared by\n * region creation (pointer-down \u2192 cursor) and corner resize (fixed corner \u2192 cursor). */\nexport function regionBetween(a: VecLike, b: VecLike): BoxModel {\n\treturn {\n\t\tx: Math.min(a.x, b.x),\n\t\ty: Math.min(a.y, b.y),\n\t\tw: Math.abs(a.x - b.x),\n\t\th: Math.abs(a.y - b.y),\n\t}\n}\n\n/**\n * The comment tool. Pressing down opens the comment composer immediately at the pointer, and it\n * follows the pointer until release \u2014 like placing a sticky note \u2014 settling on a point, or on a\n * shape when released over one. (With region comments enabled, dragging past the threshold instead\n * draws a region rectangle.) Placement only opens a composer (via `pendingComment`); the records\n * are created when the comment is posted.\n * @public\n */\nexport class CommentTool extends StateNode {\n\tstatic override id = 'comment'\n\tstatic override initial = 'idle'\n\tstatic override children(): TLStateNodeConstructor[] {\n\t\treturn [CommentIdle, CommentPointing, CommentDragging]\n\t}\n\n\t/**\n\t * Configure this tool's {@link CommentTool.options | `options`}, returning a configured subclass\n\t * to register via `tools`. Mirrors `ShapeUtil.configure`. Layers over any prior `configure`, so\n\t * calls can be chained.\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />\n\t * ```\n\t */\n\tstatic configure<T extends TLStateNodeConstructor>(\n\t\tthis: T,\n\t\toptions: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never\n\t): T {\n\t\t// @ts-expect-error -- mirrors ShapeUtil.configure; extending `this` is sound at runtime.\n\t\treturn class extends this {\n\t\t\t// @ts-expect-error\n\t\t\toptions = mergeCommentingOptions(this.options, options)\n\t\t}\n\t}\n\n\t/**\n\t * The merged commenting options for this editor. Read from anywhere via\n\t * {@link getCommentingOptions}. Override with {@link CommentTool.configure}.\n\t */\n\toptions: CommentingOptions = defaultCommentingOptions\n\n\toverride onEnter() {\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\t// Placing comments is canvas-focused \u2014 the thread list gets out of the way while the tool is\n\t\t// active. Reopened via its own control (a button), never by leaving the tool.\n\t\tcommentsSidebarOpen.set(this.editor, false)\n\t}\n\n\toverride onExit() {\n\t\t// Drop the hover hint painted while pointing at shapes (see CommentIdle). The cursor resets\n\t\t// when the next tool takes over.\n\t\tthis.editor.setHintingShapes([])\n\t}\n\n\t// Escape leaves the tool, like the built-in tools (the editor dispatches `cancel` on Escape).\n\toverride onCancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentIdle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onEnter() {\n\t\t// Back to hovering: restore the pin cursor (a prior placing state may have hidden it).\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\tthis.updateHint()\n\t}\n\n\t// Hint the shape a click would attach to, using the same hit-test as onPointerUp below. Hinting\n\t// shapes render an indicator ungated by the active tool, so this shows the select-style outline\n\t// while the comment tool \u2014 not select \u2014 is active.\n\toverride onPointerMove() {\n\t\tthis.updateHint()\n\t}\n\n\toverride onPointerDown() {\n\t\tthis.parent.transition('pointing')\n\t}\n\n\tprivate updateHint() {\n\t\tconst { editor } = this\n\t\tconst hit = editor.getShapeAtPoint(editor.inputs.getCurrentPagePoint(), { hitInside: true })\n\t\teditor.setHintingShapes(hit ? [hit.id] : [])\n\t}\n}\n\nclass CommentPointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\toverride onEnter() {\n\t\t// Open the composer immediately at the press point so it's visible from pointer-down (not just\n\t\t// on release), and let it trail the pointer while dragging \u2014 like placing a sticky note. The\n\t\t// anchor is a bare point for now; it's resolved (shape or point) on pointer up. Drop the idle\n\t\t// hover hint so a region drag doesn't leave a stale single-shape outline under the dashed box.\n\t\tconst { editor } = this\n\t\teditor.setHintingShapes([])\n\t\t// Hide the cursor while placing: the draft composer is the pointer's stand-in now, so the pin\n\t\t// cursor sitting over it just reads as clutter.\n\t\teditor.setCursor({ type: 'none', rotation: 0 })\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'point', x: point.x, y: point.y },\n\t\t\tpoint: { x: point.x, y: point.y },\n\t\t})\n\t}\n\n\toverride onPointerMove() {\n\t\tconst { editor } = this\n\t\t// Once the pointer passes the drag threshold with region comments enabled, this is a region,\n\t\t// not a follow \u2014 hand off to the region drag (which clears this composer and draws the box).\n\t\tif (getRegionCommentOptions(editor).enabled && editor.inputs.getIsDragging()) {\n\t\t\tthis.parent.transition('dragging')\n\t\t\treturn\n\t\t}\n\t\t// Otherwise the composer trails the pointer.\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.update(editor, (p) => (p ? { ...p, point: { x: point.x, y: point.y } } : p))\n\t}\n\n\t// Settle where the pointer is released: anchor to the shape under it, or drop a point.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tconst hit = editor.getShapeAtPoint(point, { hitInside: true })\n\t\tconst anchor: TLCommentAnchor = hit\n\t\t\t? shapeAnchorAt(editor, hit.id, point, editor.inputs.getAltKey())\n\t\t\t: { type: 'point', x: point.x, y: point.y }\n\t\tpendingComment.set(editor, { anchor, point: { x: point.x, y: point.y } })\n\t\t// Hand back to select; the open composer is now the focus.\n\t\teditor.setCurrentTool('select')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\t// Abandon the follow composer if placement is interrupted (Escape, focus loss, etc.).\n\tprivate cancel() {\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentDragging extends StateNode {\n\tstatic override id = 'dragging'\n\n\toverride onEnter() {\n\t\t// A region drag supersedes the point-follow composer opened in `pointing`. Show a crosshair\n\t\t// again \u2014 the composer no longer stands in for the pointer, so a hidden cursor would leave\n\t\t// the drag with no pointer at all.\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t\tthis.updateDraft()\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateDraft()\n\t}\n\n\t// Commit the dragged rectangle as a region anchor; the composer opens at its pin corner.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst origin = editor.inputs.getOriginPagePoint()\n\t\tconst current = editor.inputs.getCurrentPagePoint()\n\t\tconst region = regionBetween(origin, current)\n\t\t// The pin lives on the corner the drag released on \u2014 drag up-left, pin top-left.\n\t\tconst pin = { x: current.x >= origin.x ? 1 : 0, y: current.y >= origin.y ? 1 : 0 }\n\t\tregionDraft.set(editor, null)\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'region', ...region, pinX: pin.x, pinY: pin.y },\n\t\t\tpoint: regionPinPoint(region, pin),\n\t\t})\n\t\teditor.setCurrentTool('select')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate updateDraft() {\n\t\tconst { editor } = this\n\t\tregionDraft.set(\n\t\t\teditor,\n\t\t\tregionBetween(editor.inputs.getOriginPagePoint(), editor.inputs.getCurrentPagePoint())\n\t\t)\n\t}\n\n\tprivate cancel() {\n\t\tregionDraft.set(this.editor, null)\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\n/** @public */\nexport const commentTools = [CommentTool]\n\n/** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.\n * Once registered, tldraw's `DefaultToolbarContent` shows the comment button next to the eraser.\n * @public */\nexport const commentToolOverrides: TLUiOverrides = {\n\ttools(editor, tools) {\n\t\ttools.comment = {\n\t\t\tid: 'comment',\n\t\t\ticon: 'comment',\n\t\t\tlabel: 'Comment',\n\t\t\tkbd: 'c',\n\t\t\tonSelect: () => editor.setCurrentTool('comment'),\n\t\t}\n\t\treturn tools\n\t},\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -23,6 +23,7 @@ __export(comments_filter_menu_exports, {
23
23
  module.exports = __toCommonJS(comments_filter_menu_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
25
  var import_tldraw = require("tldraw");
26
+ var import_tooltip_button = require("../ui/tooltip-button");
26
27
  var import_state = require("./state");
27
28
  function CommentsFilterMenu({
28
29
  canFilterByAuthor,
@@ -35,75 +36,65 @@ function CommentsFilterMenu({
35
36
  import_state.sidebarFilters.update(editor, (f) => ({ ...f, [key]: !f[key] }));
36
37
  };
37
38
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_tldraw.TldrawUiDropdownMenuRoot, { id: "comments-filter", children: [
38
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
39
- "button",
39
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tooltip_button.TooltipButton, { tooltip: msg("comments.filter"), className: "tlui-cmt-header-btn", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilterIcon, {}) }) }),
40
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
41
+ import_tldraw.TldrawUiDropdownMenuContent,
40
42
  {
41
- type: "button",
42
- className: "cmt-header-btn",
43
- title: msg("comments.filter"),
44
- "aria-label": msg("comments.filter"),
45
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilterIcon, {})
43
+ className: "tlui-cmt-menu",
44
+ side: "bottom",
45
+ align: "end",
46
+ alignOffset: 0,
47
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiMenuContextProvider, { type: "menu", sourceId: "menu", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_tldraw.TldrawUiMenuGroup, { id: "comments-filter", children: [
48
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
49
+ import_tldraw.TldrawUiMenuCheckboxItem,
50
+ {
51
+ id: "show-all-pages",
52
+ label: "comments.show-all-pages",
53
+ checked: !filters.onlyCurrentPage,
54
+ onSelect: () => toggle("onlyCurrentPage")
55
+ }
56
+ ),
57
+ canFilterByAuthor && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
58
+ import_tldraw.TldrawUiMenuCheckboxItem,
59
+ {
60
+ id: "show-all-threads",
61
+ label: "comments.show-all-threads",
62
+ checked: !filters.onlyMine,
63
+ onSelect: () => toggle("onlyMine")
64
+ }
65
+ ),
66
+ canFilterByUnread && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
67
+ import_tldraw.TldrawUiMenuCheckboxItem,
68
+ {
69
+ id: "only-unread",
70
+ label: "comments.only-unread",
71
+ checked: filters.onlyUnread,
72
+ onSelect: () => toggle("onlyUnread")
73
+ }
74
+ ),
75
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
76
+ import_tldraw.TldrawUiMenuCheckboxItem,
77
+ {
78
+ id: "show-resolved",
79
+ label: "comments.show-resolved",
80
+ checked: filters.showResolved,
81
+ onSelect: () => toggle("showResolved")
82
+ }
83
+ )
84
+ ] }) })
46
85
  }
47
- ) }),
48
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuContent, { side: "bottom", align: "start", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiMenuContextProvider, { type: "menu", sourceId: "menu", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_tldraw.TldrawUiMenuGroup, { id: "comments-filter", children: [
49
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
50
- import_tldraw.TldrawUiMenuCheckboxItem,
51
- {
52
- id: "show-resolved",
53
- label: "comments.show-resolved",
54
- checked: filters.showResolved,
55
- onSelect: () => toggle("showResolved")
56
- }
57
- ),
58
- canFilterByAuthor && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
59
- import_tldraw.TldrawUiMenuCheckboxItem,
60
- {
61
- id: "only-mine",
62
- label: "comments.only-mine",
63
- checked: filters.onlyMine,
64
- onSelect: () => toggle("onlyMine")
65
- }
66
- ),
67
- canFilterByUnread && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
68
- import_tldraw.TldrawUiMenuCheckboxItem,
69
- {
70
- id: "only-unread",
71
- label: "comments.only-unread",
72
- checked: filters.onlyUnread,
73
- onSelect: () => toggle("onlyUnread")
74
- }
75
- ),
76
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
77
- import_tldraw.TldrawUiMenuCheckboxItem,
78
- {
79
- id: "only-current-page",
80
- label: "comments.only-current-page",
81
- checked: filters.onlyCurrentPage,
82
- onSelect: () => toggle("onlyCurrentPage")
83
- }
84
- )
85
- ] }) }) })
86
+ )
86
87
  ] });
87
88
  }
88
89
  function FilterIcon() {
89
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
90
- "svg",
90
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
91
+ "path",
91
92
  {
92
- width: "16",
93
- height: "16",
94
- viewBox: "0 0 16 16",
95
- fill: "none",
96
- stroke: "currentColor",
97
- strokeWidth: "1.5",
98
- strokeLinecap: "round",
99
- "aria-hidden": "true",
100
- children: [
101
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "2.5", y1: "5", x2: "13.5", y2: "5" }),
102
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "2.5", y1: "11", x2: "13.5", y2: "11" }),
103
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "5", r: "1.9", fill: "currentColor", stroke: "none" }),
104
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10.5", cy: "11", r: "1.9", fill: "currentColor", stroke: "none" })
105
- ]
93
+ 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",
94
+ fill: "currentColor",
95
+ fillRule: "evenodd",
96
+ clipRule: "evenodd"
106
97
  }
107
- );
98
+ ) });
108
99
  }
109
100
  //# sourceMappingURL=comments-filter-menu.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/canvas/comments-filter-menu.tsx"],
4
- "sourcesContent": ["import {\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tTldrawUiMenuCheckboxItem,\n\tTldrawUiMenuContextProvider,\n\tTldrawUiMenuGroup,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { SidebarFilters } from './sidebar-filters'\nimport { sidebarFilters } from './state'\n\n/** @public */\nexport interface CommentsFilterMenuProps {\n\t/** Whether to offer the \"only your threads\" toggle (needs a known current user). */\n\tcanFilterByAuthor?: boolean\n\t/** Whether to offer the \"only unread\" toggle (needs a read-status source). */\n\tcanFilterByUnread?: boolean\n}\n\n/** The funnel dropdown in the sidebar header: toggles for which threads the list shows.\n * @public @react */\nexport function CommentsFilterMenu({\n\tcanFilterByAuthor,\n\tcanFilterByUnread,\n}: CommentsFilterMenuProps) {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst filters = useValue('sidebar filters', () => sidebarFilters.get(editor), [editor])\n\tconst toggle = (key: keyof SidebarFilters) => {\n\t\tsidebarFilters.update(editor, (f) => ({ ...f, [key]: !f[key] }))\n\t}\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-filter\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName=\"cmt-header-btn\"\n\t\t\t\t\ttitle={msg('comments.filter')}\n\t\t\t\t\taria-label={msg('comments.filter')}\n\t\t\t\t>\n\t\t\t\t\t<FilterIcon />\n\t\t\t\t</button>\n\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t<TldrawUiDropdownMenuContent side=\"bottom\" align=\"start\">\n\t\t\t\t<TldrawUiMenuContextProvider type=\"menu\" sourceId=\"menu\">\n\t\t\t\t\t<TldrawUiMenuGroup id=\"comments-filter\">\n\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\tid=\"show-resolved\"\n\t\t\t\t\t\t\tlabel=\"comments.show-resolved\"\n\t\t\t\t\t\t\tchecked={filters.showResolved}\n\t\t\t\t\t\t\tonSelect={() => toggle('showResolved')}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{canFilterByAuthor && (\n\t\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\t\tid=\"only-mine\"\n\t\t\t\t\t\t\t\tlabel=\"comments.only-mine\"\n\t\t\t\t\t\t\t\tchecked={filters.onlyMine}\n\t\t\t\t\t\t\t\tonSelect={() => toggle('onlyMine')}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{canFilterByUnread && (\n\t\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\t\tid=\"only-unread\"\n\t\t\t\t\t\t\t\tlabel=\"comments.only-unread\"\n\t\t\t\t\t\t\t\tchecked={filters.onlyUnread}\n\t\t\t\t\t\t\t\tonSelect={() => toggle('onlyUnread')}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\tid=\"only-current-page\"\n\t\t\t\t\t\t\tlabel=\"comments.only-current-page\"\n\t\t\t\t\t\t\tchecked={filters.onlyCurrentPage}\n\t\t\t\t\t\t\tonSelect={() => toggle('onlyCurrentPage')}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t\t</TldrawUiMenuContextProvider>\n\t\t\t</TldrawUiDropdownMenuContent>\n\t\t</TldrawUiDropdownMenuRoot>\n\t)\n}\n\nfunction FilterIcon() {\n\treturn (\n\t\t<svg\n\t\t\twidth=\"16\"\n\t\t\theight=\"16\"\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"1.5\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t<line x1=\"2.5\" y1=\"5\" x2=\"13.5\" y2=\"5\" />\n\t\t\t<line x1=\"2.5\" y1=\"11\" x2=\"13.5\" y2=\"11\" />\n\t\t\t<circle cx=\"6\" cy=\"5\" r=\"1.9\" fill=\"currentColor\" stroke=\"none\" />\n\t\t\t<circle cx=\"10.5\" cy=\"11\" r=\"1.9\" fill=\"currentColor\" stroke=\"none\" />\n\t\t</svg>\n\t)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA4CK;AA5CL,oBAUO;AAEP,mBAA+B;AAYxB,SAAS,mBAAmB;AAAA,EAClC;AAAA,EACA;AACD,GAA4B;AAC3B,QAAM,aAAS,yBAAU;AACzB,QAAM,UAAM,8BAAe;AAC3B,QAAM,cAAU,wBAAS,mBAAmB,MAAM,4BAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AACtF,QAAM,SAAS,CAAC,QAA8B;AAC7C,gCAAe,OAAO,QAAQ,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE;AAAA,EAChE;AAEA,SACC,6CAAC,0CAAyB,IAAG,mBAC5B;AAAA,gDAAC,6CACA;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,WAAU;AAAA,QACV,OAAO,IAAI,iBAAiB;AAAA,QAC5B,cAAY,IAAI,iBAAiB;AAAA,QAEjC,sDAAC,cAAW;AAAA;AAAA,IACb,GACD;AAAA,IACA,4CAAC,6CAA4B,MAAK,UAAS,OAAM,SAChD,sDAAC,6CAA4B,MAAK,QAAO,UAAS,QACjD,uDAAC,mCAAkB,IAAG,mBACrB;AAAA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,cAAc;AAAA;AAAA,MACtC;AAAA,MACC,qBACA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,UAAU;AAAA;AAAA,MAClC;AAAA,MAEA,qBACA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,YAAY;AAAA;AAAA,MACpC;AAAA,MAED;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAM;AAAA,UACN,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,iBAAiB;AAAA;AAAA,MACzC;AAAA,OACD,GACD,GACD;AAAA,KACD;AAEF;AAEA,SAAS,aAAa;AACrB,SACC;AAAA,IAAC;AAAA;AAAA,MACA,OAAM;AAAA,MACN,QAAO;AAAA,MACP,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAY;AAAA,MACZ,eAAc;AAAA,MACd,eAAY;AAAA,MAEZ;AAAA,oDAAC,UAAK,IAAG,OAAM,IAAG,KAAI,IAAG,QAAO,IAAG,KAAI;AAAA,QACvC,4CAAC,UAAK,IAAG,OAAM,IAAG,MAAK,IAAG,QAAO,IAAG,MAAK;AAAA,QACzC,4CAAC,YAAO,IAAG,KAAI,IAAG,KAAI,GAAE,OAAM,MAAK,gBAAe,QAAO,QAAO;AAAA,QAChE,4CAAC,YAAO,IAAG,QAAO,IAAG,MAAK,GAAE,OAAM,MAAK,gBAAe,QAAO,QAAO;AAAA;AAAA;AAAA,EACrE;AAEF;",
4
+ "sourcesContent": ["import {\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tTldrawUiMenuCheckboxItem,\n\tTldrawUiMenuContextProvider,\n\tTldrawUiMenuGroup,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { TooltipButton } from '../ui/tooltip-button'\nimport { SidebarFilters } from './sidebar-filters'\nimport { sidebarFilters } from './state'\n\n/** @public */\nexport interface CommentsFilterMenuProps {\n\t/** Whether to offer the \"only your threads\" toggle (needs a known current user). */\n\tcanFilterByAuthor?: boolean\n\t/** Whether to offer the \"only unread\" toggle (needs a read-status source). */\n\tcanFilterByUnread?: boolean\n}\n\n/** The funnel dropdown in the sidebar header: toggles for which threads the list shows.\n * @public @react */\nexport function CommentsFilterMenu({\n\tcanFilterByAuthor,\n\tcanFilterByUnread,\n}: CommentsFilterMenuProps) {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst filters = useValue('sidebar filters', () => sidebarFilters.get(editor), [editor])\n\tconst toggle = (key: keyof SidebarFilters) => {\n\t\tsidebarFilters.update(editor, (f) => ({ ...f, [key]: !f[key] }))\n\t}\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-filter\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<TooltipButton tooltip={msg('comments.filter')} className=\"tlui-cmt-header-btn\">\n\t\t\t\t\t<FilterIcon />\n\t\t\t\t</TooltipButton>\n\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t<TldrawUiDropdownMenuContent\n\t\t\t\tclassName=\"tlui-cmt-menu\"\n\t\t\t\tside=\"bottom\"\n\t\t\t\talign=\"end\"\n\t\t\t\talignOffset={0}\n\t\t\t>\n\t\t\t\t<TldrawUiMenuContextProvider type=\"menu\" sourceId=\"menu\">\n\t\t\t\t\t<TldrawUiMenuGroup id=\"comments-filter\">\n\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\tid=\"show-all-pages\"\n\t\t\t\t\t\t\tlabel=\"comments.show-all-pages\"\n\t\t\t\t\t\t\tchecked={!filters.onlyCurrentPage}\n\t\t\t\t\t\t\tonSelect={() => toggle('onlyCurrentPage')}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{canFilterByAuthor && (\n\t\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\t\tid=\"show-all-threads\"\n\t\t\t\t\t\t\t\tlabel=\"comments.show-all-threads\"\n\t\t\t\t\t\t\t\tchecked={!filters.onlyMine}\n\t\t\t\t\t\t\t\tonSelect={() => toggle('onlyMine')}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{canFilterByUnread && (\n\t\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\t\tid=\"only-unread\"\n\t\t\t\t\t\t\t\tlabel=\"comments.only-unread\"\n\t\t\t\t\t\t\t\tchecked={filters.onlyUnread}\n\t\t\t\t\t\t\t\tonSelect={() => toggle('onlyUnread')}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\tid=\"show-resolved\"\n\t\t\t\t\t\t\tlabel=\"comments.show-resolved\"\n\t\t\t\t\t\t\tchecked={filters.showResolved}\n\t\t\t\t\t\t\tonSelect={() => toggle('showResolved')}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t\t</TldrawUiMenuContextProvider>\n\t\t\t</TldrawUiDropdownMenuContent>\n\t\t</TldrawUiDropdownMenuRoot>\n\t)\n}\n\nfunction FilterIcon() {\n\treturn (\n\t\t<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t<path\n\t\t\t\td=\"M5.5 3C4.67157 3 4 3.67157 4 4.5C4 5.32843 4.67157 6 5.5 6C6.32843 6 7 5.32843 7 4.5C7 3.67157 6.32843 3 5.5 3ZM3 5C3.01671 5 3.03323 4.99918 3.04952 4.99758C3.28022 6.1399 4.28967 7 5.5 7C6.71033 7 7.71978 6.1399 7.95048 4.99758C7.96677 4.99918 7.98329 5 8 5H13.5C13.7761 5 14 4.77614 14 4.5C14 4.22386 13.7761 4 13.5 4H8C7.98329 4 7.96677 4.00082 7.95048 4.00242C7.71978 2.86009 6.71033 2 5.5 2C4.28967 2 3.28022 2.86009 3.04952 4.00242C3.03323 4.00082 3.01671 4 3 4H1.5C1.22386 4 1 4.22386 1 4.5C1 4.77614 1.22386 5 1.5 5H3ZM11.9505 10.9976C11.7198 12.1399 10.7103 13 9.5 13C8.28967 13 7.28022 12.1399 7.04952 10.9976C7.03323 10.9992 7.01671 11 7 11H1.5C1.22386 11 1 10.7761 1 10.5C1 10.2239 1.22386 10 1.5 10H7C7.01671 10 7.03323 10.0008 7.04952 10.0024C7.28022 8.8601 8.28967 8 9.5 8C10.7103 8 11.7198 8.8601 11.9505 10.0024C11.9668 10.0008 11.9833 10 12 10H13.5C13.7761 10 14 10.2239 14 10.5C14 10.7761 13.7761 11 13.5 11H12C11.9833 11 11.9668 10.9992 11.9505 10.9976ZM8 10.5C8 9.67157 8.67157 9 9.5 9C10.3284 9 11 9.67157 11 10.5C11 11.3284 10.3284 12 9.5 12C8.67157 12 8 11.3284 8 10.5Z\"\n\t\t\t\tfill=\"currentColor\"\n\t\t\t\tfillRule=\"evenodd\"\n\t\t\t\tclipRule=\"evenodd\"\n\t\t\t/>\n\t\t</svg>\n\t)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwCK;AAxCL,oBAUO;AACP,4BAA8B;AAE9B,mBAA+B;AAYxB,SAAS,mBAAmB;AAAA,EAClC;AAAA,EACA;AACD,GAA4B;AAC3B,QAAM,aAAS,yBAAU;AACzB,QAAM,UAAM,8BAAe;AAC3B,QAAM,cAAU,wBAAS,mBAAmB,MAAM,4BAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AACtF,QAAM,SAAS,CAAC,QAA8B;AAC7C,gCAAe,OAAO,QAAQ,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE;AAAA,EAChE;AAEA,SACC,6CAAC,0CAAyB,IAAG,mBAC5B;AAAA,gDAAC,6CACA,sDAAC,uCAAc,SAAS,IAAI,iBAAiB,GAAG,WAAU,uBACzD,sDAAC,cAAW,GACb,GACD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,MAAK;AAAA,QACL,OAAM;AAAA,QACN,aAAa;AAAA,QAEb,sDAAC,6CAA4B,MAAK,QAAO,UAAS,QACjD,uDAAC,mCAAkB,IAAG,mBACrB;AAAA;AAAA,YAAC;AAAA;AAAA,cACA,IAAG;AAAA,cACH,OAAM;AAAA,cACN,SAAS,CAAC,QAAQ;AAAA,cAClB,UAAU,MAAM,OAAO,iBAAiB;AAAA;AAAA,UACzC;AAAA,UACC,qBACA;AAAA,YAAC;AAAA;AAAA,cACA,IAAG;AAAA,cACH,OAAM;AAAA,cACN,SAAS,CAAC,QAAQ;AAAA,cAClB,UAAU,MAAM,OAAO,UAAU;AAAA;AAAA,UAClC;AAAA,UAEA,qBACA;AAAA,YAAC;AAAA;AAAA,cACA,IAAG;AAAA,cACH,OAAM;AAAA,cACN,SAAS,QAAQ;AAAA,cACjB,UAAU,MAAM,OAAO,YAAY;AAAA;AAAA,UACpC;AAAA,UAED;AAAA,YAAC;AAAA;AAAA,cACA,IAAG;AAAA,cACH,OAAM;AAAA,cACN,SAAS,QAAQ;AAAA,cACjB,UAAU,MAAM,OAAO,cAAc;AAAA;AAAA,UACtC;AAAA,WACD,GACD;AAAA;AAAA,IACD;AAAA,KACD;AAEF;AAEA,SAAS,aAAa;AACrB,SACC,4CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,eAAY,QACvE;AAAA,IAAC;AAAA;AAAA,MACA,GAAE;AAAA,MACF,MAAK;AAAA,MACL,UAAS;AAAA,MACT,UAAS;AAAA;AAAA,EACV,GACD;AAEF;",
6
6
  "names": []
7
7
  }
@@ -23,6 +23,7 @@ __export(comments_overflow_menu_exports, {
23
23
  module.exports = __toCommonJS(comments_overflow_menu_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
25
  var import_tldraw = require("tldraw");
26
+ var import_tooltip_button = require("../ui/tooltip-button");
26
27
  var import_state = require("./state");
27
28
  const HIDE_SHORTCUT = "\u21E7C";
28
29
  function CommentsOverflowMenu() {
@@ -30,35 +31,28 @@ function CommentsOverflowMenu() {
30
31
  const msg = (0, import_tldraw.useTranslation)();
31
32
  const hidden = (0, import_tldraw.useValue)("comments hidden", () => import_state.commentsHidden.get(editor), [editor]);
32
33
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_tldraw.TldrawUiDropdownMenuRoot, { id: "comments-overflow", children: [
33
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
34
- "button",
34
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tooltip_button.TooltipButton, { tooltip: msg("comments.more-options"), className: "tlui-cmt-header-btn", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiIcon, { icon: "dots-vertical", label: msg("comments.more-options"), small: true }) }) }),
35
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
36
+ import_tldraw.TldrawUiDropdownMenuContent,
35
37
  {
36
- type: "button",
37
- className: "cmt-header-btn",
38
- title: msg("comments.more-options"),
39
- "aria-label": msg("comments.more-options"),
40
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MoreIcon, {})
38
+ className: "tlui-cmt-menu",
39
+ side: "bottom",
40
+ align: "end",
41
+ alignOffset: 0,
42
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuGroup, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuItem, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
43
+ "button",
44
+ {
45
+ type: "button",
46
+ className: "tlui-cmt-menu-item",
47
+ onClick: () => (0, import_state.toggleCommentsHidden)(editor),
48
+ children: [
49
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: hidden ? msg("comments.show") : msg("comments.hide") }),
50
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tlui-cmt-menu-item__shortcut", children: HIDE_SHORTCUT })
51
+ ]
52
+ }
53
+ ) }) })
41
54
  }
42
- ) }),
43
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuContent, { side: "bottom", align: "end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuGroup, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tldraw.TldrawUiDropdownMenuItem, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
44
- "button",
45
- {
46
- type: "button",
47
- className: "cmt-menu-item",
48
- onClick: () => (0, import_state.toggleCommentsHidden)(editor),
49
- children: [
50
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: hidden ? msg("comments.show") : msg("comments.hide") }),
51
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cmt-menu-item__shortcut", children: HIDE_SHORTCUT })
52
- ]
53
- }
54
- ) }) }) })
55
- ] });
56
- }
57
- function MoreIcon() {
58
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", children: [
59
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "3", cy: "8", r: "1.4" }),
60
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "1.4" }),
61
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "13", cy: "8", r: "1.4" })
55
+ )
62
56
  ] });
63
57
  }
64
58
  //# sourceMappingURL=comments-overflow-menu.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/canvas/comments-overflow-menu.tsx"],
4
- "sourcesContent": ["import {\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuGroup,\n\tTldrawUiDropdownMenuItem,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { commentsHidden, toggleCommentsHidden } from './state'\n\n// A keyboard-shortcut glyph, not translatable copy \u2014 kept out of JSX as a constant.\nconst HIDE_SHORTCUT = '\u21E7C'\n\n/** The overflow (\u22EF) dropdown in the sidebar header. For now it holds the hide/show-comments\n * toggle; it's the home for later comment-wide controls (notifications, mark all as read).\n * @public @react */\nexport function CommentsOverflowMenu() {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-overflow\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName=\"cmt-header-btn\"\n\t\t\t\t\ttitle={msg('comments.more-options')}\n\t\t\t\t\taria-label={msg('comments.more-options')}\n\t\t\t\t>\n\t\t\t\t\t<MoreIcon />\n\t\t\t\t</button>\n\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t<TldrawUiDropdownMenuContent side=\"bottom\" align=\"end\">\n\t\t\t\t<TldrawUiDropdownMenuGroup>\n\t\t\t\t\t<TldrawUiDropdownMenuItem>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"cmt-menu-item\"\n\t\t\t\t\t\t\tonClick={() => toggleCommentsHidden(editor)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span>{hidden ? msg('comments.show') : msg('comments.hide')}</span>\n\t\t\t\t\t\t\t<span className=\"cmt-menu-item__shortcut\">{HIDE_SHORTCUT}</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</TldrawUiDropdownMenuItem>\n\t\t\t\t</TldrawUiDropdownMenuGroup>\n\t\t\t</TldrawUiDropdownMenuContent>\n\t\t</TldrawUiDropdownMenuRoot>\n\t)\n}\n\nfunction MoreIcon() {\n\treturn (\n\t\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n\t\t\t<circle cx=\"3\" cy=\"8\" r=\"1.4\" />\n\t\t\t<circle cx=\"8\" cy=\"8\" r=\"1.4\" />\n\t\t\t<circle cx=\"13\" cy=\"8\" r=\"1.4\" />\n\t\t</svg>\n\t)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCK;AAhCL,oBASO;AACP,mBAAqD;AAGrD,MAAM,gBAAgB;AAKf,SAAS,uBAAuB;AACtC,QAAM,aAAS,yBAAU;AACzB,QAAM,UAAM,8BAAe;AAC3B,QAAM,aAAS,wBAAS,mBAAmB,MAAM,4BAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AAErF,SACC,6CAAC,0CAAyB,IAAG,qBAC5B;AAAA,gDAAC,6CACA;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,WAAU;AAAA,QACV,OAAO,IAAI,uBAAuB;AAAA,QAClC,cAAY,IAAI,uBAAuB;AAAA,QAEvC,sDAAC,YAAS;AAAA;AAAA,IACX,GACD;AAAA,IACA,4CAAC,6CAA4B,MAAK,UAAS,OAAM,OAChD,sDAAC,2CACA,sDAAC,0CACA;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS,UAAM,mCAAqB,MAAM;AAAA,QAE1C;AAAA,sDAAC,UAAM,mBAAS,IAAI,eAAe,IAAI,IAAI,eAAe,GAAE;AAAA,UAC5D,4CAAC,UAAK,WAAU,2BAA2B,yBAAc;AAAA;AAAA;AAAA,IAC1D,GACD,GACD,GACD;AAAA,KACD;AAEF;AAEA,SAAS,WAAW;AACnB,SACC,6CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,gBAAe,eAAY,QAC/E;AAAA,gDAAC,YAAO,IAAG,KAAI,IAAG,KAAI,GAAE,OAAM;AAAA,IAC9B,4CAAC,YAAO,IAAG,KAAI,IAAG,KAAI,GAAE,OAAM;AAAA,IAC9B,4CAAC,YAAO,IAAG,MAAK,IAAG,KAAI,GAAE,OAAM;AAAA,KAChC;AAEF;",
4
+ "sourcesContent": ["import {\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuGroup,\n\tTldrawUiDropdownMenuItem,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tTldrawUiIcon,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { TooltipButton } from '../ui/tooltip-button'\nimport { commentsHidden, toggleCommentsHidden } from './state'\n\n// A keyboard-shortcut glyph, not translatable copy \u2014 kept out of JSX as a constant.\nconst HIDE_SHORTCUT = '\u21E7C'\n\n/** The overflow (\u22EF) dropdown in the sidebar header. For now it holds the hide/show-comments\n * toggle; it's the home for later comment-wide controls (notifications, mark all as read).\n * @public @react */\nexport function CommentsOverflowMenu() {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-overflow\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<TooltipButton tooltip={msg('comments.more-options')} className=\"tlui-cmt-header-btn\">\n\t\t\t\t\t<TldrawUiIcon icon=\"dots-vertical\" label={msg('comments.more-options')} small />\n\t\t\t\t</TooltipButton>\n\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t<TldrawUiDropdownMenuContent\n\t\t\t\tclassName=\"tlui-cmt-menu\"\n\t\t\t\tside=\"bottom\"\n\t\t\t\talign=\"end\"\n\t\t\t\talignOffset={0}\n\t\t\t>\n\t\t\t\t<TldrawUiDropdownMenuGroup>\n\t\t\t\t\t<TldrawUiDropdownMenuItem>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"tlui-cmt-menu-item\"\n\t\t\t\t\t\t\tonClick={() => toggleCommentsHidden(editor)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span>{hidden ? msg('comments.show') : msg('comments.hide')}</span>\n\t\t\t\t\t\t\t<span className=\"tlui-cmt-menu-item__shortcut\">{HIDE_SHORTCUT}</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</TldrawUiDropdownMenuItem>\n\t\t\t\t</TldrawUiDropdownMenuGroup>\n\t\t\t</TldrawUiDropdownMenuContent>\n\t\t</TldrawUiDropdownMenuRoot>\n\t)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA6BK;AA7BL,oBAUO;AACP,4BAA8B;AAC9B,mBAAqD;AAGrD,MAAM,gBAAgB;AAKf,SAAS,uBAAuB;AACtC,QAAM,aAAS,yBAAU;AACzB,QAAM,UAAM,8BAAe;AAC3B,QAAM,aAAS,wBAAS,mBAAmB,MAAM,4BAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AAErF,SACC,6CAAC,0CAAyB,IAAG,qBAC5B;AAAA,gDAAC,6CACA,sDAAC,uCAAc,SAAS,IAAI,uBAAuB,GAAG,WAAU,uBAC/D,sDAAC,8BAAa,MAAK,iBAAgB,OAAO,IAAI,uBAAuB,GAAG,OAAK,MAAC,GAC/E,GACD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,MAAK;AAAA,QACL,OAAM;AAAA,QACN,aAAa;AAAA,QAEb,sDAAC,2CACA,sDAAC,0CACA;AAAA,UAAC;AAAA;AAAA,YACA,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,UAAM,mCAAqB,MAAM;AAAA,YAE1C;AAAA,0DAAC,UAAM,mBAAS,IAAI,eAAe,IAAI,IAAI,eAAe,GAAE;AAAA,cAC5D,4CAAC,UAAK,WAAU,gCAAgC,yBAAc;AAAA;AAAA;AAAA,QAC/D,GACD,GACD;AAAA;AAAA,IACD;AAAA,KACD;AAEF;",
6
6
  "names": []
7
7
  }