@tldraw/commenting 0.0.0-bootstrap → 5.3.0-internal.1640468db8fd

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 (184) 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 +65 -12
  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 +304 -112
  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/options.js +1 -0
  21. package/dist-cjs/canvas/options.js.map +2 -2
  22. package/dist-cjs/canvas/sidebar-filters.js +2 -2
  23. package/dist-cjs/canvas/sidebar-filters.js.map +2 -2
  24. package/dist-cjs/canvas/state.js +11 -0
  25. package/dist-cjs/canvas/state.js.map +2 -2
  26. package/dist-cjs/canvas/thread-state.js +8 -1
  27. package/dist-cjs/canvas/thread-state.js.map +2 -2
  28. package/dist-cjs/clustering/runtime.js +119 -7
  29. package/dist-cjs/clustering/runtime.js.map +2 -2
  30. package/dist-cjs/index.d.ts +126 -41
  31. package/dist-cjs/index.js +7 -1
  32. package/dist-cjs/index.js.map +2 -2
  33. package/dist-cjs/ui/avatar.js +14 -6
  34. package/dist-cjs/ui/avatar.js.map +2 -2
  35. package/dist-cjs/ui/byline.js +4 -4
  36. package/dist-cjs/ui/byline.js.map +2 -2
  37. package/dist-cjs/ui/comment-author.js +17 -0
  38. package/dist-cjs/ui/comment-author.js.map +7 -0
  39. package/dist-cjs/ui/comment-card.js +4 -4
  40. package/dist-cjs/ui/comment-card.js.map +2 -2
  41. package/dist-cjs/ui/comment-composer.js +92 -29
  42. package/dist-cjs/ui/comment-composer.js.map +2 -2
  43. package/dist-cjs/ui/comment-mention.js +1 -1
  44. package/dist-cjs/ui/comment-mention.js.map +2 -2
  45. package/dist-cjs/ui/comment-pin.js +23 -3
  46. package/dist-cjs/ui/comment-pin.js.map +2 -2
  47. package/dist-cjs/ui/comment-text.js +1 -1
  48. package/dist-cjs/ui/comment-text.js.map +2 -2
  49. package/dist-cjs/ui/comment-thread.js +6 -6
  50. package/dist-cjs/ui/comment-thread.js.map +2 -2
  51. package/dist-cjs/ui/comments-list.js +12 -12
  52. package/dist-cjs/ui/comments-list.js.map +2 -2
  53. package/dist-cjs/ui/count-badge.js +1 -1
  54. package/dist-cjs/ui/count-badge.js.map +2 -2
  55. package/dist-cjs/ui/empty-state.js +3 -3
  56. package/dist-cjs/ui/empty-state.js.map +2 -2
  57. package/dist-cjs/ui/format-time.js +3 -0
  58. package/dist-cjs/ui/format-time.js.map +2 -2
  59. package/dist-cjs/ui/mention-list.js +8 -8
  60. package/dist-cjs/ui/mention-list.js.map +2 -2
  61. package/dist-cjs/ui/mention-suggestion.js +3 -3
  62. package/dist-cjs/ui/mention-suggestion.js.map +2 -2
  63. package/dist-cjs/ui/mention.js +1 -1
  64. package/dist-cjs/ui/mention.js.map +2 -2
  65. package/dist-cjs/ui/reaction.js +11 -4
  66. package/dist-cjs/ui/reaction.js.map +2 -2
  67. package/dist-cjs/ui/reactions.js +10 -2
  68. package/dist-cjs/ui/reactions.js.map +2 -2
  69. package/dist-cjs/ui/send-button.js +1 -1
  70. package/dist-cjs/ui/send-button.js.map +2 -2
  71. package/dist-cjs/ui/tooltip-button.js +32 -0
  72. package/dist-cjs/ui/tooltip-button.js.map +7 -0
  73. package/dist-esm/canvas/comment-body.mjs +1 -1
  74. package/dist-esm/canvas/comment-body.mjs.map +2 -2
  75. package/dist-esm/canvas/comment-drafts.mjs +34 -0
  76. package/dist-esm/canvas/comment-drafts.mjs.map +7 -0
  77. package/dist-esm/canvas/comment-render.mjs +2 -0
  78. package/dist-esm/canvas/comment-render.mjs.map +2 -2
  79. package/dist-esm/canvas/comment-tool.mjs +67 -14
  80. package/dist-esm/canvas/comment-tool.mjs.map +2 -2
  81. package/dist-esm/canvas/comments-filter-menu.mjs +54 -63
  82. package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
  83. package/dist-esm/canvas/comments-overflow-menu.mjs +22 -27
  84. package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
  85. package/dist-esm/canvas/comments-overlay.mjs +322 -114
  86. package/dist-esm/canvas/comments-overlay.mjs.map +2 -2
  87. package/dist-esm/canvas/comments-sidebar.mjs +15 -21
  88. package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
  89. package/dist-esm/canvas/comments-visibility-toggle.mjs +46 -0
  90. package/dist-esm/canvas/comments-visibility-toggle.mjs.map +7 -0
  91. package/dist-esm/canvas/options.mjs +1 -0
  92. package/dist-esm/canvas/options.mjs.map +2 -2
  93. package/dist-esm/canvas/sidebar-filters.mjs +2 -2
  94. package/dist-esm/canvas/sidebar-filters.mjs.map +2 -2
  95. package/dist-esm/canvas/state.mjs +11 -0
  96. package/dist-esm/canvas/state.mjs.map +2 -2
  97. package/dist-esm/canvas/thread-state.mjs +8 -1
  98. package/dist-esm/canvas/thread-state.mjs.map +2 -2
  99. package/dist-esm/clustering/runtime.mjs +119 -7
  100. package/dist-esm/clustering/runtime.mjs.map +2 -2
  101. package/dist-esm/index.d.mts +126 -41
  102. package/dist-esm/index.mjs +11 -1
  103. package/dist-esm/index.mjs.map +2 -2
  104. package/dist-esm/ui/avatar.mjs +14 -6
  105. package/dist-esm/ui/avatar.mjs.map +2 -2
  106. package/dist-esm/ui/byline.mjs +4 -4
  107. package/dist-esm/ui/byline.mjs.map +2 -2
  108. package/dist-esm/ui/comment-author.mjs +1 -0
  109. package/dist-esm/ui/comment-author.mjs.map +7 -0
  110. package/dist-esm/ui/comment-card.mjs +4 -4
  111. package/dist-esm/ui/comment-card.mjs.map +2 -2
  112. package/dist-esm/ui/comment-composer.mjs +93 -30
  113. package/dist-esm/ui/comment-composer.mjs.map +2 -2
  114. package/dist-esm/ui/comment-mention.mjs +1 -1
  115. package/dist-esm/ui/comment-mention.mjs.map +2 -2
  116. package/dist-esm/ui/comment-pin.mjs +23 -3
  117. package/dist-esm/ui/comment-pin.mjs.map +2 -2
  118. package/dist-esm/ui/comment-text.mjs +1 -1
  119. package/dist-esm/ui/comment-text.mjs.map +2 -2
  120. package/dist-esm/ui/comment-thread.mjs +6 -6
  121. package/dist-esm/ui/comment-thread.mjs.map +2 -2
  122. package/dist-esm/ui/comments-list.mjs +12 -12
  123. package/dist-esm/ui/comments-list.mjs.map +2 -2
  124. package/dist-esm/ui/count-badge.mjs +1 -1
  125. package/dist-esm/ui/count-badge.mjs.map +2 -2
  126. package/dist-esm/ui/empty-state.mjs +3 -3
  127. package/dist-esm/ui/empty-state.mjs.map +2 -2
  128. package/dist-esm/ui/format-time.mjs +3 -0
  129. package/dist-esm/ui/format-time.mjs.map +2 -2
  130. package/dist-esm/ui/mention-list.mjs +8 -8
  131. package/dist-esm/ui/mention-list.mjs.map +2 -2
  132. package/dist-esm/ui/mention-suggestion.mjs +3 -3
  133. package/dist-esm/ui/mention-suggestion.mjs.map +2 -2
  134. package/dist-esm/ui/mention.mjs +1 -1
  135. package/dist-esm/ui/mention.mjs.map +2 -2
  136. package/dist-esm/ui/reaction.mjs +11 -4
  137. package/dist-esm/ui/reaction.mjs.map +2 -2
  138. package/dist-esm/ui/reactions.mjs +10 -2
  139. package/dist-esm/ui/reactions.mjs.map +2 -2
  140. package/dist-esm/ui/send-button.mjs +1 -1
  141. package/dist-esm/ui/send-button.mjs.map +2 -2
  142. package/dist-esm/ui/tooltip-button.mjs +12 -0
  143. package/dist-esm/ui/tooltip-button.mjs.map +7 -0
  144. package/package.json +3 -3
  145. package/src/canvas/canvas.css +79 -36
  146. package/src/canvas/comment-body.tsx +1 -1
  147. package/src/canvas/comment-drafts.test.ts +55 -0
  148. package/src/canvas/comment-drafts.ts +46 -0
  149. package/src/canvas/comment-render.test.ts +1 -1
  150. package/src/canvas/comment-render.ts +7 -2
  151. package/src/canvas/comment-tool.tsx +98 -17
  152. package/src/canvas/comments-filter-menu.tsx +27 -33
  153. package/src/canvas/comments-overflow-menu.tsx +13 -21
  154. package/src/canvas/comments-overlay.tsx +441 -145
  155. package/src/canvas/comments-sidebar.tsx +27 -28
  156. package/src/canvas/comments-visibility-toggle.tsx +50 -0
  157. package/src/canvas/options.ts +25 -0
  158. package/src/canvas/sidebar-filters.ts +4 -3
  159. package/src/canvas/state.ts +22 -0
  160. package/src/canvas/thread-state.ts +16 -3
  161. package/src/clustering/runtime.test.ts +183 -7
  162. package/src/clustering/runtime.ts +164 -15
  163. package/src/index.ts +7 -0
  164. package/src/ui/avatar.tsx +15 -11
  165. package/src/ui/byline.tsx +6 -5
  166. package/src/ui/comment-author.ts +15 -0
  167. package/src/ui/comment-card.tsx +6 -5
  168. package/src/ui/comment-composer.tsx +122 -33
  169. package/src/ui/comment-mention.ts +2 -2
  170. package/src/ui/comment-pin.tsx +35 -4
  171. package/src/ui/comment-text.tsx +1 -1
  172. package/src/ui/comment-thread.tsx +8 -6
  173. package/src/ui/comments-list.tsx +19 -14
  174. package/src/ui/comments.css +205 -117
  175. package/src/ui/count-badge.tsx +1 -1
  176. package/src/ui/empty-state.tsx +3 -3
  177. package/src/ui/format-time.ts +5 -0
  178. package/src/ui/mention-list.tsx +13 -16
  179. package/src/ui/mention-suggestion.tsx +3 -3
  180. package/src/ui/mention.tsx +1 -1
  181. package/src/ui/reaction.tsx +6 -3
  182. package/src/ui/reactions.tsx +6 -2
  183. package/src/ui/send-button.tsx +1 -1
  184. package/src/ui/tooltip-button.tsx +20 -0
@@ -22,7 +22,6 @@ __export(comment_composer_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(comment_composer_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
- var import_core = require("@tiptap/core");
26
25
  var import_react = require("@tiptap/react");
27
26
  var import_react2 = require("react");
28
27
  var import_tldraw = require("tldraw");
@@ -57,26 +56,48 @@ function CommentComposer({
57
56
  const renderMentionSuggestionRef = (0, import_react2.useRef)(renderMentionSuggestion);
58
57
  renderMentionSuggestionRef.current = renderMentionSuggestion;
59
58
  const [isEmpty, setIsEmpty] = (0, import_react2.useState)(() => !value || (0, import_comment_extensions.isCommentEmpty)(value));
60
- const submitExtension = (0, import_react2.useMemo)(
61
- () => import_core.Extension.create({
62
- name: "commentComposerSubmit",
63
- priority: 1e3,
64
- addKeyboardShortcuts() {
65
- return {
66
- Enter: () => {
67
- if ((0, import_mention_suggestion.isMentionPickerOpen)()) return false;
68
- if (!disabledRef.current) onSubmitRef.current?.();
69
- return true;
70
- }
71
- };
72
- }
73
- }),
74
- []
75
- );
59
+ const [expanded, setExpanded] = (0, import_react2.useState)(false);
60
+ const expandedRef = (0, import_react2.useRef)(expanded);
61
+ expandedRef.current = expanded;
62
+ const inputWrapRef = (0, import_react2.useRef)(null);
63
+ const mirrorRef = (0, import_react2.useRef)(null);
64
+ const remeasure = () => {
65
+ const wrap = inputWrapRef.current;
66
+ const mirror = mirrorRef.current;
67
+ const editor2 = editorRef.current;
68
+ if (!wrap || !mirror || !editor2) return;
69
+ if (editor2.isEmpty) {
70
+ if (expandedRef.current) setExpanded(false);
71
+ return;
72
+ }
73
+ if (wrap.clientWidth === 0) return;
74
+ const doc = editor2.state.doc;
75
+ const multiBlock = doc.childCount > 1 || doc.firstChild !== null && doc.firstChild.type.name !== "paragraph";
76
+ if (multiBlock) {
77
+ if (!expandedRef.current) setExpanded(true);
78
+ return;
79
+ }
80
+ const field = wrap.parentElement;
81
+ const send = field ? field.querySelector(".tlui-cmt-send") : null;
82
+ const input = wrap.querySelector(".tlui-cmt-input");
83
+ if (!send || !input) return;
84
+ mirror.innerHTML = input.innerHTML;
85
+ const textWidth = mirror.offsetWidth;
86
+ const collapsedAvailable = wrap.clientWidth - (expandedRef.current ? send.offsetWidth + 6 : 0);
87
+ if (!expandedRef.current && textWidth > collapsedAvailable - 8) {
88
+ setExpanded(true);
89
+ } else if (expandedRef.current && textWidth < collapsedAvailable - 24) {
90
+ setExpanded(false);
91
+ }
92
+ };
93
+ const remeasureRef = (0, import_react2.useRef)(remeasure);
94
+ remeasureRef.current = remeasure;
95
+ const editorRef = (0, import_react2.useRef)(null);
96
+ const replayingEnter = (0, import_react2.useRef)(false);
76
97
  const mentionsEnabled = !!getMentionSuggestions;
77
98
  const hasCustomRow = !!renderMentionSuggestion;
78
99
  const extensions = (0, import_react2.useMemo)(() => {
79
- const list = [...import_comment_extensions.commentTipTapExtensions, submitExtension];
100
+ const list = [...import_comment_extensions.commentTipTapExtensions];
80
101
  if (mentionsEnabled) {
81
102
  const resolveSuggestions = (query) => getMentionSuggestionsRef.current?.(query) ?? [];
82
103
  const renderRow = hasCustomRow ? (member) => renderMentionSuggestionRef.current?.(member) : void 0;
@@ -92,7 +113,7 @@ function CommentComposer({
92
113
  list.push((0, import_comment_mention.commentMention)({ suggestion: { char: "@", allow: () => false } }));
93
114
  }
94
115
  return list;
95
- }, [submitExtension, mentionsEnabled, hasCustomRow, tlEditor]);
116
+ }, [mentionsEnabled, hasCustomRow, tlEditor]);
96
117
  const editor = (0, import_react.useEditor)(
97
118
  {
98
119
  extensions,
@@ -103,34 +124,76 @@ function CommentComposer({
103
124
  // mirrors RichTextArea's setup.
104
125
  enableCoreExtensions: { textDirection: false },
105
126
  textDirection: "auto",
106
- editorProps: { attributes: { class: "cmt-input" } },
127
+ editorProps: {
128
+ attributes: { class: "tlui-cmt-input" },
129
+ // Runs before every keymap plugin, so it can distinguish Shift+Enter from Enter — an
130
+ // `Enter` keymap binding also fires on Shift+Enter and would otherwise swallow it.
131
+ handleKeyDown: (_view, event) => {
132
+ if (replayingEnter.current) return false;
133
+ if (event.key !== "Enter" || event.isComposing) return false;
134
+ if ((0, import_mention_suggestion.isMentionPickerOpen)()) return false;
135
+ if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey) {
136
+ replayingEnter.current = true;
137
+ try {
138
+ editorRef.current?.commands.enter();
139
+ } finally {
140
+ replayingEnter.current = false;
141
+ }
142
+ return true;
143
+ }
144
+ if (!disabledRef.current) onSubmitRef.current?.();
145
+ return true;
146
+ }
147
+ },
107
148
  onUpdate: ({ editor: editor2 }) => {
108
149
  setIsEmpty(editor2.isEmpty);
150
+ remeasureRef.current();
109
151
  onChangeRef.current?.(editor2.getJSON());
110
152
  }
111
153
  },
112
154
  [interactive]
113
155
  );
156
+ editorRef.current = editor;
114
157
  (0, import_react2.useEffect)(() => {
115
158
  if (!editor || value === void 0) return;
116
159
  if ((0, import_tldraw.isEqual)(editor.getJSON(), value)) return;
117
160
  editor.commands.setContent(value);
118
161
  setIsEmpty(editor.isEmpty);
119
162
  }, [editor, value]);
163
+ (0, import_react2.useLayoutEffect)(() => {
164
+ remeasureRef.current();
165
+ }, [editor, value]);
120
166
  (0, import_react2.useEffect)(() => {
121
167
  if (!autoFocus || !editor) return;
122
168
  const raf = requestAnimationFrame(() => editor.commands.focus("end"));
123
169
  return () => cancelAnimationFrame(raf);
124
170
  }, [autoFocus, editor]);
125
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "cmt-composer", children: [
126
- leading ?? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_avatar.Avatar, { name: author }),
127
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "cmt-composer__field", children: [
128
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "cmt-composer__input-wrap", children: [
129
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.EditorContent, { editor }),
130
- isEmpty && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cmt-input__placeholder", "aria-hidden": "true", children: placeholder })
131
- ] }),
132
- interactive && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_send_button.SendButton, { label: sendLabel, onClick: onSubmit, disabled })
133
- ] })
171
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-cmt-composer", children: [
172
+ leading ?? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_avatar.Avatar, { author }),
173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
174
+ "div",
175
+ {
176
+ className: [
177
+ "tlui-cmt-composer__field",
178
+ interactive && expanded && "tlui-cmt-composer__field--expanded"
179
+ ].filter(Boolean).join(" "),
180
+ children: [
181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-cmt-composer__input-wrap", ref: inputWrapRef, children: [
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.EditorContent, { editor }),
183
+ isEmpty && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-cmt-input__placeholder", "aria-hidden": "true", children: placeholder }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
185
+ "div",
186
+ {
187
+ className: "tlui-cmt-composer__mirror tlui-cmt-input",
188
+ ref: mirrorRef,
189
+ "aria-hidden": "true"
190
+ }
191
+ )
192
+ ] }),
193
+ interactive && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_send_button.SendButton, { label: sendLabel, onClick: onSubmit, disabled })
194
+ ]
195
+ }
196
+ )
134
197
  ] });
135
198
  }
136
199
  //# sourceMappingURL=comment-composer.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/comment-composer.tsx"],
4
- "sourcesContent": ["import { Extension, JSONContent } from '@tiptap/core'\nimport { EditorContent, useEditor } from '@tiptap/react'\nimport { ReactNode, useEffect, useMemo, useRef, useState } from 'react'\nimport { isEqual, TLRichText, useMaybeEditor } from 'tldraw'\nimport { Avatar } from './avatar'\nimport { commentTipTapExtensions, EMPTY_COMMENT, isCommentEmpty } from './comment-extensions'\nimport { commentMention } from './comment-mention'\nimport { MentionMember } from './mention-list'\nimport { createMentionSuggestion, isMentionPickerOpen } from './mention-suggestion'\nimport { SendButton } from './send-button'\n\n/** @public */\nexport interface CommentComposerProps {\n\tauthor: string\n\tplaceholder: string\n\t/** Controlled rich-text value. Omit for the presentational (display-only) composer. */\n\tvalue?: TLRichText\n\tonChange?(value: TLRichText): void\n\t/** Called on Send click or Enter. When set, the composer is interactive. */\n\tonSubmit?(): void\n\tsendLabel?: string\n\tdisabled?: boolean\n\tautoFocus?: boolean\n\t/** The leading element before the field. Defaults to the author's avatar. */\n\tleading?: ReactNode\n\t/** Resolve the members matching an `@`-query (sync or async). Provide to enable mentions. */\n\tgetMentionSuggestions?(query: string): MentionMember[] | Promise<MentionMember[]>\n\t/** Override a picker row's content. Defaults to avatar + name (+ secondary). */\n\trenderMentionSuggestion?(member: MentionMember): ReactNode\n}\n\n/**\n * The input for writing a comment: a TipTap rich-text editor restricted to the comment extension\n * set (bold, italic, lists, links, code, highlight \u2014 no headings), with a Send button. Formatting\n * is applied through markdown and keyboard shortcuts (e.g. `**bold**`, `- `, Cmd+B); there's no\n * floating toolbar. Presentational by default; pass value/onChange/onSubmit to drive it as a form.\n * @public @react\n */\nexport function CommentComposer({\n\tauthor,\n\tplaceholder,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tsendLabel = 'Send',\n\tdisabled,\n\tautoFocus,\n\tleading,\n\tgetMentionSuggestions,\n\trenderMentionSuggestion,\n}: CommentComposerProps) {\n\tconst interactive = !!onChange || !!onSubmit\n\t// The canvas editor the composer lives in, if any \u2014 lets the mention popup track the camera. Null\n\t// when the composer is used outside a tldraw editor (e.g. an isolated demo).\n\tconst tlEditor = useMaybeEditor()\n\n\t// Callbacks are read through refs so the editor instance doesn't need to be recreated when they\n\t// change identity between renders.\n\tconst onChangeRef = useRef(onChange)\n\tonChangeRef.current = onChange\n\tconst onSubmitRef = useRef(onSubmit)\n\tonSubmitRef.current = onSubmit\n\tconst disabledRef = useRef(disabled)\n\tdisabledRef.current = disabled\n\tconst getMentionSuggestionsRef = useRef(getMentionSuggestions)\n\tgetMentionSuggestionsRef.current = getMentionSuggestions\n\tconst renderMentionSuggestionRef = useRef(renderMentionSuggestion)\n\trenderMentionSuggestionRef.current = renderMentionSuggestion\n\n\tconst [isEmpty, setIsEmpty] = useState(() => !value || isCommentEmpty(value))\n\n\t// Enter submits the comment. Shift+Enter (and Cmd/Ctrl+Enter) keep their default behavior \u2014 a\n\t// new line \u2014 for the occasional multi-line comment.\n\tconst submitExtension = useMemo(\n\t\t() =>\n\t\t\tExtension.create({\n\t\t\t\tname: 'commentComposerSubmit',\n\t\t\t\tpriority: 1000,\n\t\t\t\taddKeyboardShortcuts() {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tEnter: () => {\n\t\t\t\t\t\t\t// While the @-mention picker is open, Enter selects the highlighted member.\n\t\t\t\t\t\t\t// This extension outranks the mention plugin (priority 1000), so defer to the\n\t\t\t\t\t\t\t// picker here or Enter would submit before the member could be inserted.\n\t\t\t\t\t\t\tif (isMentionPickerOpen()) return false\n\t\t\t\t\t\t\tif (!disabledRef.current) onSubmitRef.current?.()\n\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t}),\n\t\t[]\n\t)\n\n\t// The suggestion plugin is built once (the editor is recreated only on `interactive`) and runs\n\t// outside React, so it must read the mention callbacks through refs \u2014 like onChange/onSubmit \u2014\n\t// or it queries the roster present at mount forever, never seeing a member who loads or joins\n\t// later. Whether mentions (and a custom picker row) are wired at all is fixed at mount; only the\n\t// callbacks themselves are live.\n\tconst mentionsEnabled = !!getMentionSuggestions\n\tconst hasCustomRow = !!renderMentionSuggestion\n\tconst extensions = useMemo(() => {\n\t\tconst list = [...commentTipTapExtensions, submitExtension]\n\t\t// Always register the mention node so an existing body that contains a mention keeps it on\n\t\t// edit (an unregistered node would be stripped by ProseMirror when the content loads). The `@`\n\t\t// picker itself only turns on when the host provides a resolver; otherwise the node is present\n\t\t// but its trigger is disabled.\n\t\tif (mentionsEnabled) {\n\t\t\tconst resolveSuggestions = (query: string) => getMentionSuggestionsRef.current?.(query) ?? []\n\t\t\tconst renderRow = hasCustomRow\n\t\t\t\t? (member: MentionMember) => renderMentionSuggestionRef.current?.(member)\n\t\t\t\t: undefined\n\t\t\tlist.push(\n\t\t\t\tcommentMention({\n\t\t\t\t\tsuggestion: createMentionSuggestion(resolveSuggestions, {\n\t\t\t\t\t\trenderMember: renderRow,\n\t\t\t\t\t\teditor: tlEditor,\n\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t)\n\t\t} else {\n\t\t\tlist.push(commentMention({ suggestion: { char: '@', allow: () => false } }))\n\t\t}\n\t\treturn list\n\t}, [submitExtension, mentionsEnabled, hasCustomRow, tlEditor])\n\n\tconst editor = useEditor(\n\t\t{\n\t\t\textensions,\n\t\t\tcontent: (value ?? EMPTY_COMMENT) as JSONContent,\n\t\t\teditable: interactive,\n\t\t\t// tldraw's default extensions add their own TextDirection extension (so it can be\n\t\t\t// overridden), so disable TipTap's core one to avoid a duplicate-extension warning \u2014\n\t\t\t// mirrors RichTextArea's setup.\n\t\t\tenableCoreExtensions: { textDirection: false },\n\t\t\ttextDirection: 'auto',\n\t\t\teditorProps: { attributes: { class: 'cmt-input' } },\n\t\t\tonUpdate: ({ editor }) => {\n\t\t\t\tsetIsEmpty(editor.isEmpty)\n\t\t\t\tonChangeRef.current?.(editor.getJSON() as TLRichText)\n\t\t\t},\n\t\t},\n\t\t[interactive]\n\t)\n\n\t// Sync controlled resets (e.g. the parent clearing to EMPTY_COMMENT after posting) into the\n\t// editor without echoing back the value the editor itself just emitted.\n\tuseEffect(() => {\n\t\tif (!editor || value === undefined) return\n\t\tif (isEqual(editor.getJSON(), value)) return\n\t\teditor.commands.setContent(value as JSONContent)\n\t\tsetIsEmpty(editor.isEmpty)\n\t}, [editor, value])\n\n\t// Focus on the next frame rather than via TipTap's autofocus: the composer often mounts from a\n\t// canvas pointer event whose default focus handling would otherwise steal it back.\n\tuseEffect(() => {\n\t\tif (!autoFocus || !editor) return\n\t\tconst raf = requestAnimationFrame(() => editor.commands.focus('end'))\n\t\treturn () => cancelAnimationFrame(raf)\n\t}, [autoFocus, editor])\n\n\treturn (\n\t\t<div className=\"cmt-composer\">\n\t\t\t{leading ?? <Avatar name={author} />}\n\t\t\t<div className=\"cmt-composer__field\">\n\t\t\t\t<div className=\"cmt-composer__input-wrap\">\n\t\t\t\t\t<EditorContent editor={editor} />\n\t\t\t\t\t{isEmpty && (\n\t\t\t\t\t\t<div className=\"cmt-input__placeholder\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t{placeholder}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t{interactive && <SendButton label={sendLabel} onClick={onSubmit} disabled={disabled} />}\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { JSONContent } from '@tiptap/core'\nimport { EditorContent, useEditor } from '@tiptap/react'\nimport { ReactNode, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'\nimport { isEqual, TLRichText, useMaybeEditor } from 'tldraw'\nimport { Avatar } from './avatar'\nimport { CommentAuthor } from './comment-author'\nimport { commentTipTapExtensions, EMPTY_COMMENT, isCommentEmpty } from './comment-extensions'\nimport { commentMention } from './comment-mention'\nimport { MentionMember } from './mention-list'\nimport { createMentionSuggestion, isMentionPickerOpen } from './mention-suggestion'\nimport { SendButton } from './send-button'\n\n/** @public */\nexport interface CommentComposerProps {\n\tauthor: CommentAuthor\n\tplaceholder: string\n\t/** Controlled rich-text value. Omit for the presentational (display-only) composer. */\n\tvalue?: TLRichText\n\tonChange?(value: TLRichText): void\n\t/** Called on Send click or Enter. When set, the composer is interactive. */\n\tonSubmit?(): void\n\tsendLabel?: string\n\tdisabled?: boolean\n\tautoFocus?: boolean\n\t/** The leading element before the field. Defaults to the author's avatar. */\n\tleading?: ReactNode\n\t/** Resolve the members matching an `@`-query (sync or async). Provide to enable mentions. */\n\tgetMentionSuggestions?(query: string): MentionMember[] | Promise<MentionMember[]>\n\t/** Override a picker row's content. Defaults to avatar + name (+ secondary). */\n\trenderMentionSuggestion?(member: MentionMember): ReactNode\n}\n\n/**\n * The input for writing a comment: a TipTap rich-text editor restricted to the comment extension\n * set (bold, italic, lists, links, code, highlight \u2014 no headings), with a Send button. Formatting\n * is applied through markdown and keyboard shortcuts (e.g. `**bold**`, `- `, Cmd+B); there's no\n * floating toolbar. Presentational by default; pass value/onChange/onSubmit to drive it as a form.\n * @public @react\n */\nexport function CommentComposer({\n\tauthor,\n\tplaceholder,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tsendLabel = 'Send',\n\tdisabled,\n\tautoFocus,\n\tleading,\n\tgetMentionSuggestions,\n\trenderMentionSuggestion,\n}: CommentComposerProps) {\n\tconst interactive = !!onChange || !!onSubmit\n\t// The canvas editor the composer lives in, if any \u2014 lets the mention popup track the camera. Null\n\t// when the composer is used outside a tldraw editor (e.g. an isolated demo).\n\tconst tlEditor = useMaybeEditor()\n\n\t// Callbacks are read through refs so the editor instance doesn't need to be recreated when they\n\t// change identity between renders.\n\tconst onChangeRef = useRef(onChange)\n\tonChangeRef.current = onChange\n\tconst onSubmitRef = useRef(onSubmit)\n\tonSubmitRef.current = onSubmit\n\tconst disabledRef = useRef(disabled)\n\tdisabledRef.current = disabled\n\tconst getMentionSuggestionsRef = useRef(getMentionSuggestions)\n\tgetMentionSuggestionsRef.current = getMentionSuggestions\n\tconst renderMentionSuggestionRef = useRef(renderMentionSuggestion)\n\trenderMentionSuggestionRef.current = renderMentionSuggestion\n\n\tconst [isEmpty, setIsEmpty] = useState(() => !value || isCommentEmpty(value))\n\t// Whether the field has grown to two rows: input across the full width, send button below.\n\t// Flips when the (single-line) content width reaches the space left beside the send button.\n\tconst [expanded, setExpanded] = useState(false)\n\tconst expandedRef = useRef(expanded)\n\texpandedRef.current = expanded\n\tconst inputWrapRef = useRef<HTMLDivElement>(null)\n\tconst mirrorRef = useRef<HTMLDivElement>(null)\n\n\t// Measure whether the content still fits on one line beside the send button. The mirror is a\n\t// hidden, nowrap clone of the editor's rendered content, so marks and mention chips measure at\n\t// their true width. A small dead zone between the expand and collapse thresholds keeps the\n\t// layout from flapping while typing at the boundary.\n\tconst remeasure = () => {\n\t\tconst wrap = inputWrapRef.current\n\t\tconst mirror = mirrorRef.current\n\t\tconst editor = editorRef.current\n\t\tif (!wrap || !mirror || !editor) return\n\t\t// Empty always fits \u2014 and measuring before TipTap's DOM has laid out reads zero widths,\n\t\t// which would flash the expanded layout for a frame on mount.\n\t\tif (editor.isEmpty) {\n\t\t\tif (expandedRef.current) setExpanded(false)\n\t\t\treturn\n\t\t}\n\t\tif (wrap.clientWidth === 0) return\n\t\tconst doc = editor.state.doc\n\t\tconst multiBlock =\n\t\t\tdoc.childCount > 1 || (doc.firstChild !== null && doc.firstChild.type.name !== 'paragraph')\n\t\tif (multiBlock) {\n\t\t\tif (!expandedRef.current) setExpanded(true)\n\t\t\treturn\n\t\t}\n\t\tconst field = wrap.parentElement\n\t\tconst send = field ? field.querySelector<HTMLElement>('.tlui-cmt-send') : null\n\t\tconst input = wrap.querySelector('.tlui-cmt-input')\n\t\tif (!send || !input) return\n\t\tmirror.innerHTML = input.innerHTML\n\t\tconst textWidth = mirror.offsetWidth\n\t\t// The single-line space the input has beside the send button: when already expanded the\n\t\t// wrap spans the full field, so subtract the button (plus the field's 6px gap) back out.\n\t\tconst collapsedAvailable = wrap.clientWidth - (expandedRef.current ? send.offsetWidth + 6 : 0)\n\t\tif (!expandedRef.current && textWidth > collapsedAvailable - 8) {\n\t\t\tsetExpanded(true)\n\t\t} else if (expandedRef.current && textWidth < collapsedAvailable - 24) {\n\t\t\tsetExpanded(false)\n\t\t}\n\t}\n\tconst remeasureRef = useRef(remeasure)\n\tremeasureRef.current = remeasure\n\n\t// The editor instance, reachable from `handleKeyDown` (which is created before `useEditor`\n\t// returns). Updated on every render so the ref never points at a stale editor.\n\tconst editorRef = useRef<ReturnType<typeof useEditor>>(null)\n\n\t// Set while we replay Enter through the keymaps for a Shift+Enter newline: `commands.enter()`\n\t// re-dispatches Enter through `handleKeyDown`, and without this guard our own handler would catch\n\t// that synthetic Enter and submit instead.\n\tconst replayingEnter = useRef(false)\n\n\t// Enter and Cmd/Ctrl+Enter submit the comment; Shift+Enter inserts a new line for the occasional\n\t// multi-line comment. This is handled through `editorProps.handleKeyDown` (below) rather than a\n\t// keyboard-shortcut extension: ProseMirror runs `handleKeyDown` before every keymap plugin, so it\n\t// can tell Shift+Enter apart from Enter \u2014 an `Enter` keymap binding also fires on Shift/Cmd+Enter\n\t// and would otherwise swallow the newline.\n\n\t// The suggestion plugin is built once (the editor is recreated only on `interactive`) and runs\n\t// outside React, so it must read the mention callbacks through refs \u2014 like onChange/onSubmit \u2014\n\t// or it queries the roster present at mount forever, never seeing a member who loads or joins\n\t// later. Whether mentions (and a custom picker row) are wired at all is fixed at mount; only the\n\t// callbacks themselves are live.\n\tconst mentionsEnabled = !!getMentionSuggestions\n\tconst hasCustomRow = !!renderMentionSuggestion\n\tconst extensions = useMemo(() => {\n\t\tconst list = [...commentTipTapExtensions]\n\t\t// Always register the mention node so an existing body that contains a mention keeps it on\n\t\t// edit (an unregistered node would be stripped by ProseMirror when the content loads). The `@`\n\t\t// picker itself only turns on when the host provides a resolver; otherwise the node is present\n\t\t// but its trigger is disabled.\n\t\tif (mentionsEnabled) {\n\t\t\tconst resolveSuggestions = (query: string) => getMentionSuggestionsRef.current?.(query) ?? []\n\t\t\tconst renderRow = hasCustomRow\n\t\t\t\t? (member: MentionMember) => renderMentionSuggestionRef.current?.(member)\n\t\t\t\t: undefined\n\t\t\tlist.push(\n\t\t\t\tcommentMention({\n\t\t\t\t\tsuggestion: createMentionSuggestion(resolveSuggestions, {\n\t\t\t\t\t\trenderMember: renderRow,\n\t\t\t\t\t\teditor: tlEditor,\n\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t)\n\t\t} else {\n\t\t\tlist.push(commentMention({ suggestion: { char: '@', allow: () => false } }))\n\t\t}\n\t\treturn list\n\t}, [mentionsEnabled, hasCustomRow, tlEditor])\n\n\tconst editor = useEditor(\n\t\t{\n\t\t\textensions,\n\t\t\tcontent: (value ?? EMPTY_COMMENT) as JSONContent,\n\t\t\teditable: interactive,\n\t\t\t// tldraw's default extensions add their own TextDirection extension (so it can be\n\t\t\t// overridden), so disable TipTap's core one to avoid a duplicate-extension warning \u2014\n\t\t\t// mirrors RichTextArea's setup.\n\t\t\tenableCoreExtensions: { textDirection: false },\n\t\t\ttextDirection: 'auto',\n\t\t\teditorProps: {\n\t\t\t\tattributes: { class: 'tlui-cmt-input' },\n\t\t\t\t// Runs before every keymap plugin, so it can distinguish Shift+Enter from Enter \u2014 an\n\t\t\t\t// `Enter` keymap binding also fires on Shift+Enter and would otherwise swallow it.\n\t\t\t\thandleKeyDown: (_view, event) => {\n\t\t\t\t\t// Let the keymaps handle the synthetic Enter we replay for a Shift+Enter newline.\n\t\t\t\t\tif (replayingEnter.current) return false\n\t\t\t\t\tif (event.key !== 'Enter' || event.isComposing) return false\n\t\t\t\t\t// While the @-mention picker is open, Enter selects the highlighted member \u2014 defer.\n\t\t\t\t\tif (isMentionPickerOpen()) return false\n\t\t\t\t\t// Shift+Enter inserts a new line. Replay a plain Enter through the keymaps (guarded so\n\t\t\t\t\t// we don't re-enter and submit) to reuse the editor's list-aware Enter handling \u2014 a new\n\t\t\t\t\t// list item in a list, a new paragraph otherwise. tldraw doesn't do soft breaks.\n\t\t\t\t\tif (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey) {\n\t\t\t\t\t\treplayingEnter.current = true\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\teditorRef.current?.commands.enter()\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\treplayingEnter.current = false\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\t// Enter, Cmd+Enter, and Ctrl+Enter submit.\n\t\t\t\t\tif (!disabledRef.current) onSubmitRef.current?.()\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t},\n\t\t\tonUpdate: ({ editor }) => {\n\t\t\t\tsetIsEmpty(editor.isEmpty)\n\t\t\t\t// Same-value state sets don't re-render, so measure here \u2014 the editor's DOM is\n\t\t\t\t// already updated when onUpdate fires.\n\t\t\t\tremeasureRef.current()\n\t\t\t\tonChangeRef.current?.(editor.getJSON() as TLRichText)\n\t\t\t},\n\t\t},\n\t\t[interactive]\n\t)\n\teditorRef.current = editor\n\n\t// Sync controlled resets (e.g. the parent clearing to EMPTY_COMMENT after posting) into the\n\t// editor without echoing back the value the editor itself just emitted.\n\tuseEffect(() => {\n\t\tif (!editor || value === undefined) return\n\t\tif (isEqual(editor.getJSON(), value)) return\n\t\teditor.commands.setContent(value as JSONContent)\n\t\tsetIsEmpty(editor.isEmpty)\n\t}, [editor, value])\n\n\t// Measure once the editor's content is in the DOM, so a composer that mounts pre-filled (the\n\t// edit-in-place composer) starts in the right layout.\n\tuseLayoutEffect(() => {\n\t\tremeasureRef.current()\n\t}, [editor, value])\n\n\t// Focus on the next frame rather than via TipTap's autofocus: the composer often mounts from a\n\t// canvas pointer event whose default focus handling would otherwise steal it back.\n\tuseEffect(() => {\n\t\tif (!autoFocus || !editor) return\n\t\tconst raf = requestAnimationFrame(() => editor.commands.focus('end'))\n\t\treturn () => cancelAnimationFrame(raf)\n\t}, [autoFocus, editor])\n\n\treturn (\n\t\t<div className=\"tlui-cmt-composer\">\n\t\t\t{leading ?? <Avatar author={author} />}\n\t\t\t<div\n\t\t\t\tclassName={[\n\t\t\t\t\t'tlui-cmt-composer__field',\n\t\t\t\t\tinteractive && expanded && 'tlui-cmt-composer__field--expanded',\n\t\t\t\t]\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.join(' ')}\n\t\t\t>\n\t\t\t\t<div className=\"tlui-cmt-composer__input-wrap\" ref={inputWrapRef}>\n\t\t\t\t\t<EditorContent editor={editor} />\n\t\t\t\t\t{isEmpty && (\n\t\t\t\t\t\t<div className=\"tlui-cmt-input__placeholder\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t{placeholder}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"tlui-cmt-composer__mirror tlui-cmt-input\"\n\t\t\t\t\t\tref={mirrorRef}\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t{interactive && <SendButton label={sendLabel} onClick={onSubmit} disabled={disabled} />}\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["import_react", "editor"]
7
7
  }
@@ -29,7 +29,7 @@ function mentionName(attrs, resolveName) {
29
29
  }
30
30
  function commentMention({ resolveName, suggestion } = {}) {
31
31
  return import_extension_mention.Mention.configure({
32
- HTMLAttributes: { class: "cmt-mention" },
32
+ HTMLAttributes: { class: "tlui-cmt-mention" },
33
33
  renderText: ({ node }) => `@${mentionName(node.attrs, resolveName)}`,
34
34
  renderHTML: ({ node, options }) => [
35
35
  "span",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/comment-mention.ts"],
4
- "sourcesContent": ["import { mergeAttributes } from '@tiptap/core'\nimport { Mention, type MentionNodeAttrs, type MentionOptions } from '@tiptap/extension-mention'\n\n/**\n * A mention node's display name: the member's current name, resolved live from its id when a\n * resolver is given (so a rename shows through), falling back to the label captured at insert time\n * when no source can name the id \u2014 e.g. a deleted account. The stored id is always the source of\n * truth; live resolution only freshens the displayed name over that captured label.\n */\nfunction mentionName(\n\tattrs: MentionNodeAttrs,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tconst resolved = attrs.id && resolveName ? resolveName(attrs.id) : undefined\n\treturn resolved ?? attrs.label ?? attrs.id ?? ''\n}\n\nexport interface CommentMentionOptions {\n\t/**\n\t * Resolve a member id to its current display name \u2014 for the read-only render paths. Returns\n\t * `undefined` when the id can't be resolved, so the render falls back to the mention's stored label.\n\t */\n\tresolveName?(id: string): string | undefined\n\t/** The `@`-picker suggestion config \u2014 for the composer (omit on the render paths). */\n\tsuggestion?: MentionOptions['suggestion']\n}\n\n/**\n * The comment @-mention node \u2014 TipTap's `Mention` configured to render as a `.cmt-mention` pill.\n *\n * A factory rather than a shared constant because it's configured differently per context: the\n * read-only render paths pass `resolveName` (so the stored `{ id }` node always shows the member's\n * current name, not a copy frozen at insert time), while the composer passes a `suggestion` (the\n * `@` picker). The node schema is identical either way \u2014 only these two levers differ.\n */\nexport function commentMention({ resolveName, suggestion }: CommentMentionOptions = {}) {\n\treturn Mention.configure({\n\t\tHTMLAttributes: { class: 'cmt-mention' },\n\t\trenderText: ({ node }) => `@${mentionName(node.attrs as MentionNodeAttrs, resolveName)}`,\n\t\trenderHTML: ({ node, options }) => [\n\t\t\t'span',\n\t\t\tmergeAttributes(options.HTMLAttributes, { 'data-mention-id': node.attrs.id }),\n\t\t\t`@${mentionName(node.attrs as MentionNodeAttrs, resolveName)}`,\n\t\t],\n\t\t...(suggestion ? { suggestion } : {}),\n\t})\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAgC;AAChC,+BAAoE;AAQpE,SAAS,YACR,OACA,aACS;AACT,QAAM,WAAW,MAAM,MAAM,cAAc,YAAY,MAAM,EAAE,IAAI;AACnE,SAAO,YAAY,MAAM,SAAS,MAAM,MAAM;AAC/C;AAoBO,SAAS,eAAe,EAAE,aAAa,WAAW,IAA2B,CAAC,GAAG;AACvF,SAAO,iCAAQ,UAAU;AAAA,IACxB,gBAAgB,EAAE,OAAO,cAAc;AAAA,IACvC,YAAY,CAAC,EAAE,KAAK,MAAM,IAAI,YAAY,KAAK,OAA2B,WAAW,CAAC;AAAA,IACtF,YAAY,CAAC,EAAE,MAAM,QAAQ,MAAM;AAAA,MAClC;AAAA,UACA,6BAAgB,QAAQ,gBAAgB,EAAE,mBAAmB,KAAK,MAAM,GAAG,CAAC;AAAA,MAC5E,IAAI,YAAY,KAAK,OAA2B,WAAW,CAAC;AAAA,IAC7D;AAAA,IACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,EACpC,CAAC;AACF;",
4
+ "sourcesContent": ["import { mergeAttributes } from '@tiptap/core'\nimport { Mention, type MentionNodeAttrs, type MentionOptions } from '@tiptap/extension-mention'\n\n/**\n * A mention node's display name: the member's current name, resolved live from its id when a\n * resolver is given (so a rename shows through), falling back to the label captured at insert time\n * when no source can name the id \u2014 e.g. a deleted account. The stored id is always the source of\n * truth; live resolution only freshens the displayed name over that captured label.\n */\nfunction mentionName(\n\tattrs: MentionNodeAttrs,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tconst resolved = attrs.id && resolveName ? resolveName(attrs.id) : undefined\n\treturn resolved ?? attrs.label ?? attrs.id ?? ''\n}\n\nexport interface CommentMentionOptions {\n\t/**\n\t * Resolve a member id to its current display name \u2014 for the read-only render paths. Returns\n\t * `undefined` when the id can't be resolved, so the render falls back to the mention's stored label.\n\t */\n\tresolveName?(id: string): string | undefined\n\t/** The `@`-picker suggestion config \u2014 for the composer (omit on the render paths). */\n\tsuggestion?: MentionOptions['suggestion']\n}\n\n/**\n * The comment @-mention node \u2014 TipTap's `Mention` configured to render as a `.tlui-cmt-mention` pill.\n *\n * A factory rather than a shared constant because it's configured differently per context: the\n * read-only render paths pass `resolveName` (so the stored `{ id }` node always shows the member's\n * current name, not a copy frozen at insert time), while the composer passes a `suggestion` (the\n * `@` picker). The node schema is identical either way \u2014 only these two levers differ.\n */\nexport function commentMention({ resolveName, suggestion }: CommentMentionOptions = {}) {\n\treturn Mention.configure({\n\t\tHTMLAttributes: { class: 'tlui-cmt-mention' },\n\t\trenderText: ({ node }) => `@${mentionName(node.attrs as MentionNodeAttrs, resolveName)}`,\n\t\trenderHTML: ({ node, options }) => [\n\t\t\t'span',\n\t\t\tmergeAttributes(options.HTMLAttributes, { 'data-mention-id': node.attrs.id }),\n\t\t\t`@${mentionName(node.attrs as MentionNodeAttrs, resolveName)}`,\n\t\t],\n\t\t...(suggestion ? { suggestion } : {}),\n\t})\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAgC;AAChC,+BAAoE;AAQpE,SAAS,YACR,OACA,aACS;AACT,QAAM,WAAW,MAAM,MAAM,cAAc,YAAY,MAAM,EAAE,IAAI;AACnE,SAAO,YAAY,MAAM,SAAS,MAAM,MAAM;AAC/C;AAoBO,SAAS,eAAe,EAAE,aAAa,WAAW,IAA2B,CAAC,GAAG;AACvF,SAAO,iCAAQ,UAAU;AAAA,IACxB,gBAAgB,EAAE,OAAO,mBAAmB;AAAA,IAC5C,YAAY,CAAC,EAAE,KAAK,MAAM,IAAI,YAAY,KAAK,OAA2B,WAAW,CAAC;AAAA,IACtF,YAAY,CAAC,EAAE,MAAM,QAAQ,MAAM;AAAA,MAClC;AAAA,UACA,6BAAgB,QAAQ,gBAAgB,EAAE,mBAAmB,KAAK,MAAM,GAAG,CAAC;AAAA,MAC5E,IAAI,YAAY,KAAK,OAA2B,WAAW,CAAC;AAAA,IAC7D;AAAA,IACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,EACpC,CAAC;AACF;",
6
6
  "names": []
7
7
  }
@@ -22,8 +22,28 @@ __export(comment_pin_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(comment_pin_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
- function CommentPin({ children, resolved, open }) {
26
- const className = ["cmt-pin", resolved && "cmt-pin--resolved", open && "cmt-pin--open"].filter(Boolean).join(" ");
27
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, children: resolved ? "\u2713" : children });
25
+ const resolvedCheck = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
26
+ "svg",
27
+ {
28
+ viewBox: "0 0 24 24",
29
+ width: "15",
30
+ height: "15",
31
+ fill: "none",
32
+ stroke: "currentColor",
33
+ strokeWidth: "2.5",
34
+ strokeLinecap: "round",
35
+ strokeLinejoin: "round",
36
+ "aria-hidden": "true",
37
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 12.5l5 5L20 6.5" })
38
+ }
39
+ );
40
+ function CommentPin({ children, resolved, open, color }) {
41
+ const className = [
42
+ "tlui-cmt-pin",
43
+ resolved && "tlui-cmt-pin--resolved",
44
+ open && "tlui-cmt-pin--open"
45
+ ].filter(Boolean).join(" ");
46
+ const style = color && !resolved ? { "--tlui-cmt-pin-color": color } : void 0;
47
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, style, children: resolved ? resolvedCheck : children });
28
48
  }
29
49
  //# sourceMappingURL=comment-pin.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/comment-pin.tsx"],
4
- "sourcesContent": ["import { ReactNode } from 'react'\n\n/** @public */\nexport interface CommentPinProps {\n\t/** What the pin shows when unresolved \u2014 a number, an author initial, an <Avatar>, etc.\n\t * Left as a lever so consumers aren't locked into a count. */\n\tchildren?: ReactNode\n\tresolved?: boolean\n\t/** The pin's thread is open \u2014 shows the active/selected indicator state. */\n\topen?: boolean\n}\n\n/** A canvas comment marker: shows its `children` (or a check when resolved). Purely\n * presentational \u2014 it reflects open/resolved state via CSS; wrap it to make it clickable.\n * @public @react */\nexport function CommentPin({ children, resolved, open }: CommentPinProps) {\n\tconst className = ['cmt-pin', resolved && 'cmt-pin--resolved', open && 'cmt-pin--open']\n\t\t.filter(Boolean)\n\t\t.join(' ')\n\treturn <div className={className}>{resolved ? '\u2713' : children}</div>\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBQ;AAJD,SAAS,WAAW,EAAE,UAAU,UAAU,KAAK,GAAoB;AACzE,QAAM,YAAY,CAAC,WAAW,YAAY,qBAAqB,QAAQ,eAAe,EACpF,OAAO,OAAO,EACd,KAAK,GAAG;AACV,SAAO,4CAAC,SAAI,WAAuB,qBAAW,WAAM,UAAS;AAC9D;",
4
+ "sourcesContent": ["import { CSSProperties, ReactNode } from 'react'\n\n/** @public */\nexport interface CommentPinProps {\n\t/** What the pin shows when unresolved \u2014 a number, an author initial, an <Avatar>, etc.\n\t * Left as a lever so consumers aren't locked into a count. */\n\tchildren?: ReactNode\n\tresolved?: boolean\n\t/** The pin's thread is open \u2014 shows the active/selected indicator state. */\n\topen?: boolean\n\t/** Background color (any CSS color). Falls back to the default pin tint.\n\t * Ignored while resolved \u2014 resolved pins are always grey. */\n\tcolor?: string\n}\n\n/* An inline check, not a text glyph \u2014 the '\u2713' character sits off-baseline and varies by font. */\nconst resolvedCheck = (\n\t<svg\n\t\tviewBox=\"0 0 24 24\"\n\t\twidth=\"15\"\n\t\theight=\"15\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstrokeWidth=\"2.5\"\n\t\tstrokeLinecap=\"round\"\n\t\tstrokeLinejoin=\"round\"\n\t\taria-hidden=\"true\"\n\t>\n\t\t<path d=\"M4 12.5l5 5L20 6.5\" />\n\t</svg>\n)\n\n/** A canvas comment marker: shows its `children` (or a check when resolved). Purely\n * presentational \u2014 it reflects open/resolved state via CSS; wrap it to make it clickable.\n * @public @react */\nexport function CommentPin({ children, resolved, open, color }: CommentPinProps) {\n\tconst className = [\n\t\t'tlui-cmt-pin',\n\t\tresolved && 'tlui-cmt-pin--resolved',\n\t\topen && 'tlui-cmt-pin--open',\n\t]\n\t\t.filter(Boolean)\n\t\t.join(' ')\n\t// A custom property rather than backgroundColor, so the open ring follows the tint too.\n\tconst style =\n\t\tcolor && !resolved ? ({ '--tlui-cmt-pin-color': color } as CSSProperties) : undefined\n\treturn (\n\t\t<div className={className} style={style}>\n\t\t\t{resolved ? resolvedCheck : children}\n\t\t</div>\n\t)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BE;AAZF,MAAM,gBACL;AAAA,EAAC;AAAA;AAAA,IACA,SAAQ;AAAA,IACR,OAAM;AAAA,IACN,QAAO;AAAA,IACP,MAAK;AAAA,IACL,QAAO;AAAA,IACP,aAAY;AAAA,IACZ,eAAc;AAAA,IACd,gBAAe;AAAA,IACf,eAAY;AAAA,IAEZ,sDAAC,UAAK,GAAE,sBAAqB;AAAA;AAC9B;AAMM,SAAS,WAAW,EAAE,UAAU,UAAU,MAAM,MAAM,GAAoB;AAChF,QAAM,YAAY;AAAA,IACjB;AAAA,IACA,YAAY;AAAA,IACZ,QAAQ;AAAA,EACT,EACE,OAAO,OAAO,EACd,KAAK,GAAG;AAEV,QAAM,QACL,SAAS,CAAC,WAAY,EAAE,wBAAwB,MAAM,IAAsB;AAC7E,SACC,4CAAC,SAAI,WAAsB,OACzB,qBAAW,gBAAgB,UAC7B;AAEF;",
6
6
  "names": []
7
7
  }
@@ -24,6 +24,6 @@ module.exports = __toCommonJS(comment_text_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
25
  var import_render_markdown = require("./render-markdown");
26
26
  function CommentText({ text }) {
27
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cmt-text", children: (0, import_render_markdown.renderMarkdown)(text) });
27
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-cmt-text", children: (0, import_render_markdown.renderMarkdown)(text) });
28
28
  }
29
29
  //# sourceMappingURL=comment-text.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/comment-text.tsx"],
4
- "sourcesContent": ["import { renderMarkdown } from './render-markdown'\n\n/** @public */\nexport interface CommentTextProps {\n\t/** Rich text body, authored as markdown. */\n\ttext: string\n}\n\n/** A comment's body, rendered as markdown (bold, italic, code, links, lists). @public @react */\nexport function CommentText({ text }: CommentTextProps) {\n\treturn <div className=\"cmt-text\">{renderMarkdown(text)}</div>\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUQ;AAVR,6BAA+B;AASxB,SAAS,YAAY,EAAE,KAAK,GAAqB;AACvD,SAAO,4CAAC,SAAI,WAAU,YAAY,qDAAe,IAAI,GAAE;AACxD;",
4
+ "sourcesContent": ["import { renderMarkdown } from './render-markdown'\n\n/** @public */\nexport interface CommentTextProps {\n\t/** Rich text body, authored as markdown. */\n\ttext: string\n}\n\n/** A comment's body, rendered as markdown (bold, italic, code, links, lists). @public @react */\nexport function CommentText({ text }: CommentTextProps) {\n\treturn <div className=\"tlui-cmt-text\">{renderMarkdown(text)}</div>\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUQ;AAVR,6BAA+B;AASxB,SAAS,YAAY,EAAE,KAAK,GAAqB;AACvD,SAAO,4CAAC,SAAI,WAAU,iBAAiB,qDAAe,IAAI,GAAE;AAC7D;",
6
6
  "names": []
7
7
  }
@@ -32,13 +32,13 @@ function CommentThread({
32
32
  composer,
33
33
  renderComment
34
34
  }) {
35
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "cmt-thread", children: [
36
- (header !== void 0 || headerActions !== void 0) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "cmt-thread__header", children: [
37
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cmt-thread__title", children: header }),
38
- headerActions !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cmt-thread__actions", children: headerActions })
35
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-cmt-thread", children: [
36
+ (header !== void 0 || headerActions !== void 0) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-cmt-thread__header", children: [
37
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tlui-cmt-thread__title", children: header }),
38
+ headerActions !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-cmt-thread__actions", children: headerActions })
39
39
  ] }),
40
- resolvedBanner !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cmt-thread__resolved", children: resolvedBanner }),
41
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cmt-thread__list", children: comments.map((comment, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: renderComment ? renderComment(comment, i) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_comment_card.CommentCard, { ...comment }) }, i)) }),
40
+ resolvedBanner !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-cmt-thread__resolved", children: resolvedBanner }),
41
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-cmt-thread__list", children: comments.map((comment, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: renderComment ? renderComment(comment, i) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_comment_card.CommentCard, { ...comment }) }, i)) }),
42
42
  composer !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_comment_composer.CommentComposer, { ...composer })
43
43
  ] });
44
44
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/comment-thread.tsx"],
4
- "sourcesContent": ["import { ReactNode } from 'react'\nimport { CommentCard, CommentCardProps } from './comment-card'\nimport { CommentComposer, CommentComposerProps } from './comment-composer'\n\n/** @public */\nexport interface CommentThreadProps {\n\t/** The thread's comments, already adapted to card props (oldest first). */\n\tcomments: CommentCardProps[]\n\t/** Optional header, e.g. \"Thread\". Omit for no header. */\n\theader?: ReactNode\n\t/** Action controls shown at the right of the header (resolve, delete, dismiss\u2026). */\n\theaderActions?: ReactNode\n\t/** When set, shows a banner above the comments (e.g. \"Resolved by Jess\"). */\n\tresolvedBanner?: ReactNode\n\t/** Reply composer props. Omit for a read-only thread (no composer). */\n\tcomposer?: CommentComposerProps\n\t/** Override how each comment renders. Defaults to `<CommentCard>`. */\n\trenderComment?(comment: CommentCardProps, index: number): ReactNode\n}\n\n/**\n * A comment thread: an optional header, an optional resolved banner, the comments, and an\n * optional reply composer. Presentational \u2014 you supply the comments (as card props) and the\n * composer's handlers; how each comment renders is overridable via `renderComment`.\n * @public @react\n */\nexport function CommentThread({\n\tcomments,\n\theader,\n\theaderActions,\n\tresolvedBanner,\n\tcomposer,\n\trenderComment,\n}: CommentThreadProps) {\n\treturn (\n\t\t<div className=\"cmt-thread\">\n\t\t\t{(header !== undefined || headerActions !== undefined) && (\n\t\t\t\t<div className=\"cmt-thread__header\">\n\t\t\t\t\t<span className=\"cmt-thread__title\">{header}</span>\n\t\t\t\t\t{headerActions !== undefined && (\n\t\t\t\t\t\t<div className=\"cmt-thread__actions\">{headerActions}</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{resolvedBanner !== undefined && <div className=\"cmt-thread__resolved\">{resolvedBanner}</div>}\n\t\t\t<div className=\"cmt-thread__list\">\n\t\t\t\t{comments.map((comment, i) => (\n\t\t\t\t\t<div key={i}>\n\t\t\t\t\t\t{renderComment ? renderComment(comment, i) : <CommentCard {...comment} />}\n\t\t\t\t\t</div>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t\t{composer !== undefined && <CommentComposer {...composer} />}\n\t\t</div>\n\t)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAqCI;AApCJ,0BAA8C;AAC9C,8BAAsD;AAwB/C,SAAS,cAAc;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAuB;AACtB,SACC,6CAAC,SAAI,WAAU,cACZ;AAAA,gBAAW,UAAa,kBAAkB,WAC3C,6CAAC,SAAI,WAAU,sBACd;AAAA,kDAAC,UAAK,WAAU,qBAAqB,kBAAO;AAAA,MAC3C,kBAAkB,UAClB,4CAAC,SAAI,WAAU,uBAAuB,yBAAc;AAAA,OAEtD;AAAA,IAEA,mBAAmB,UAAa,4CAAC,SAAI,WAAU,wBAAwB,0BAAe;AAAA,IACvF,4CAAC,SAAI,WAAU,oBACb,mBAAS,IAAI,CAAC,SAAS,MACvB,4CAAC,SACC,0BAAgB,cAAc,SAAS,CAAC,IAAI,4CAAC,mCAAa,GAAG,SAAS,KAD9D,CAEV,CACA,GACF;AAAA,IACC,aAAa,UAAa,4CAAC,2CAAiB,GAAG,UAAU;AAAA,KAC3D;AAEF;",
4
+ "sourcesContent": ["import { ReactNode } from 'react'\nimport { CommentCard, CommentCardProps } from './comment-card'\nimport { CommentComposer, CommentComposerProps } from './comment-composer'\n\n/** @public */\nexport interface CommentThreadProps {\n\t/** The thread's comments, already adapted to card props (oldest first). */\n\tcomments: CommentCardProps[]\n\t/** Optional header, e.g. \"Thread\". Omit for no header. */\n\theader?: ReactNode\n\t/** Action controls shown at the right of the header (resolve, delete, dismiss\u2026). */\n\theaderActions?: ReactNode\n\t/** When set, shows a banner above the comments (e.g. \"Resolved by Jess\"). */\n\tresolvedBanner?: ReactNode\n\t/** Reply composer props. Omit for a read-only thread (no composer). */\n\tcomposer?: CommentComposerProps\n\t/** Override how each comment renders. Defaults to `<CommentCard>`. */\n\trenderComment?(comment: CommentCardProps, index: number): ReactNode\n}\n\n/**\n * A comment thread: an optional header, an optional resolved banner, the comments, and an\n * optional reply composer. Presentational \u2014 you supply the comments (as card props) and the\n * composer's handlers; how each comment renders is overridable via `renderComment`.\n * @public @react\n */\nexport function CommentThread({\n\tcomments,\n\theader,\n\theaderActions,\n\tresolvedBanner,\n\tcomposer,\n\trenderComment,\n}: CommentThreadProps) {\n\treturn (\n\t\t<div className=\"tlui-cmt-thread\">\n\t\t\t{(header !== undefined || headerActions !== undefined) && (\n\t\t\t\t<div className=\"tlui-cmt-thread__header\">\n\t\t\t\t\t<span className=\"tlui-cmt-thread__title\">{header}</span>\n\t\t\t\t\t{headerActions !== undefined && (\n\t\t\t\t\t\t<div className=\"tlui-cmt-thread__actions\">{headerActions}</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{resolvedBanner !== undefined && (\n\t\t\t\t<div className=\"tlui-cmt-thread__resolved\">{resolvedBanner}</div>\n\t\t\t)}\n\t\t\t<div className=\"tlui-cmt-thread__list\">\n\t\t\t\t{comments.map((comment, i) => (\n\t\t\t\t\t<div key={i}>\n\t\t\t\t\t\t{renderComment ? renderComment(comment, i) : <CommentCard {...comment} />}\n\t\t\t\t\t</div>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t\t{composer !== undefined && <CommentComposer {...composer} />}\n\t\t</div>\n\t)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAqCI;AApCJ,0BAA8C;AAC9C,8BAAsD;AAwB/C,SAAS,cAAc;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAuB;AACtB,SACC,6CAAC,SAAI,WAAU,mBACZ;AAAA,gBAAW,UAAa,kBAAkB,WAC3C,6CAAC,SAAI,WAAU,2BACd;AAAA,kDAAC,UAAK,WAAU,0BAA0B,kBAAO;AAAA,MAChD,kBAAkB,UAClB,4CAAC,SAAI,WAAU,4BAA4B,yBAAc;AAAA,OAE3D;AAAA,IAEA,mBAAmB,UACnB,4CAAC,SAAI,WAAU,6BAA6B,0BAAe;AAAA,IAE5D,4CAAC,SAAI,WAAU,yBACb,mBAAS,IAAI,CAAC,SAAS,MACvB,4CAAC,SACC,0BAAgB,cAAc,SAAS,CAAC,IAAI,4CAAC,mCAAa,GAAG,SAAS,KAD9D,CAEV,CACA,GACF;AAAA,IACC,aAAa,UAAa,4CAAC,2CAAiB,GAAG,UAAU;AAAA,KAC3D;AAEF;",
6
6
  "names": []
7
7
  }
@@ -33,12 +33,12 @@ function CommentsList({
33
33
  resolvedLabel,
34
34
  renderItem
35
35
  }) {
36
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "cmt-list", children: [
37
- (header !== void 0 || headerAction) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "cmt-list__header", children: [
38
- header !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cmt-list__header-title", children: header }),
36
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-cmt-list", children: [
37
+ (header !== void 0 || headerAction) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-cmt-list__header", children: [
38
+ header !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tlui-cmt-list__header-title", children: header }),
39
39
  headerAction
40
40
  ] }),
41
- items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cmt-list__empty", children: empty }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cmt-list__items", children: items.map(
41
+ items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-cmt-list__empty", children: empty }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-cmt-list__items", children: items.map(
42
42
  (item) => renderItem ? renderItem(item) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
43
43
  CommentListItem,
44
44
  {
@@ -70,23 +70,23 @@ function CommentListItem({
70
70
  "button",
71
71
  {
72
72
  type: "button",
73
- className: selected ? "cmt-list__item cmt-list__item--selected" : "cmt-list__item",
73
+ className: selected ? "tlui-cmt-list__item tlui-cmt-list__item--selected" : "tlui-cmt-list__item",
74
74
  "data-resolved": resolved || void 0,
75
75
  onClick: handleClick,
76
76
  children: [
77
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_avatar.Avatar, { name: author }),
78
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "cmt-list__item-body", children: [
77
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_avatar.Avatar, { author }),
78
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-cmt-list__item-body", children: [
79
79
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_byline.Byline, { author, date }),
80
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cmt-list__item-preview", children: preview }),
81
- (resolved || page !== void 0) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "cmt-list__item-meta", children: [
82
- resolved && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "cmt-list__item-resolved", children: [
80
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-cmt-list__item-preview", children: preview }),
81
+ (resolved || page !== void 0) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-cmt-list__item-meta", children: [
82
+ resolved && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "tlui-cmt-list__item-resolved", children: [
83
83
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckIcon, {}),
84
84
  resolvedLabel
85
85
  ] }),
86
- page !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cmt-list__item-page", children: page })
86
+ page !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tlui-cmt-list__item-page", children: page })
87
87
  ] })
88
88
  ] }),
89
- count !== void 0 && count > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cmt-list__item-count", children: count })
89
+ count !== void 0 && count > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tlui-cmt-list__item-count", children: count })
90
90
  ]
91
91
  }
92
92
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/comments-list.tsx"],
4
- "sourcesContent": ["import { ReactNode } from 'react'\nimport { Avatar } from './avatar'\nimport { Byline } from './byline'\n\n/** @public */\nexport interface CommentListItemProps {\n\tid: string\n\tauthor: string\n\t/** A short preview of the thread \u2014 e.g. the first comment's body. */\n\tpreview: ReactNode\n\t/** ISO datetime of the thread's first comment. */\n\tdate: string\n\tresolved?: boolean\n\t/** Name of the page the thread lives on, shown as a small label. Omit to hide. */\n\tpage?: string\n\t/** Total comments in the thread. */\n\tcount?: number\n\t/** Whether this thread is the open one. */\n\tselected?: boolean\n}\n\n/** @public */\nexport interface CommentsListProps {\n\titems: CommentListItemProps[]\n\t/** Called with a thread id when an item is chosen. */\n\tonSelect?(id: string): void\n\t/** Shown above the list (e.g. \"Comments\"). Omit for none. */\n\theader?: ReactNode\n\t/** Rendered at the right of the header row \u2014 e.g. a filter menu. */\n\theaderAction?: ReactNode\n\t/** Shown in place of the list when there are no threads. */\n\tempty?: ReactNode\n\t/** Label for a resolved thread's marker on its row. Defaults to \"Resolved\". */\n\tresolvedLabel?: string\n\t/** Override how each item renders. Defaults to `<CommentListItem>`. */\n\trenderItem?(item: CommentListItemProps): ReactNode\n}\n\n/**\n * A scrollable list of comment threads \u2014 each an avatar, byline, and a one-line preview.\n * Presentational: you supply the items (already summarised) and an `onSelect` handler; the canvas\n * `CanvasCommentsSidebar` wires it to the store, but a consumer can build their own list from this.\n * @public @react\n */\nexport function CommentsList({\n\titems,\n\tonSelect,\n\theader,\n\theaderAction,\n\tempty,\n\tresolvedLabel,\n\trenderItem,\n}: CommentsListProps) {\n\treturn (\n\t\t<div className=\"cmt-list\">\n\t\t\t{(header !== undefined || headerAction) && (\n\t\t\t\t<div className=\"cmt-list__header\">\n\t\t\t\t\t{header !== undefined && <span className=\"cmt-list__header-title\">{header}</span>}\n\t\t\t\t\t{headerAction}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{items.length === 0 ? (\n\t\t\t\t<div className=\"cmt-list__empty\">{empty}</div>\n\t\t\t) : (\n\t\t\t\t<div className=\"cmt-list__items\">\n\t\t\t\t\t{items.map((item) =>\n\t\t\t\t\t\trenderItem ? (\n\t\t\t\t\t\t\trenderItem(item)\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<CommentListItem\n\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t{...item}\n\t\t\t\t\t\t\t\tresolvedLabel={resolvedLabel}\n\t\t\t\t\t\t\t\tonSelect={onSelect}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n\nfunction CommentListItem({\n\tid,\n\tauthor,\n\tpreview,\n\tdate,\n\tresolved,\n\tpage,\n\tcount,\n\tselected,\n\tresolvedLabel = 'Resolved',\n\tonSelect,\n}: CommentListItemProps & { resolvedLabel?: string; onSelect?(id: string): void }) {\n\tconst handleClick = () => {\n\t\tif (onSelect) onSelect(id)\n\t}\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tclassName={selected ? 'cmt-list__item cmt-list__item--selected' : 'cmt-list__item'}\n\t\t\tdata-resolved={resolved || undefined}\n\t\t\tonClick={handleClick}\n\t\t>\n\t\t\t<Avatar name={author} />\n\t\t\t<div className=\"cmt-list__item-body\">\n\t\t\t\t<Byline author={author} date={date} />\n\t\t\t\t<div className=\"cmt-list__item-preview\">{preview}</div>\n\t\t\t\t{(resolved || page !== undefined) && (\n\t\t\t\t\t<div className=\"cmt-list__item-meta\">\n\t\t\t\t\t\t{resolved && (\n\t\t\t\t\t\t\t<span className=\"cmt-list__item-resolved\">\n\t\t\t\t\t\t\t\t<CheckIcon />\n\t\t\t\t\t\t\t\t{resolvedLabel}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{page !== undefined && <span className=\"cmt-list__item-page\">{page}</span>}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t{count !== undefined && count > 1 && <span className=\"cmt-list__item-count\">{count}</span>}\n\t\t</button>\n\t)\n}\n\nfunction CheckIcon() {\n\treturn (\n\t\t<svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t<path\n\t\t\t\td=\"M2.5 6.2 4.7 8.4 9.5 3.6\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.5\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwDI;AAvDJ,oBAAuB;AACvB,oBAAuB;AA0ChB,SAAS,aAAa;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAsB;AACrB,SACC,6CAAC,SAAI,WAAU,YACZ;AAAA,gBAAW,UAAa,iBACzB,6CAAC,SAAI,WAAU,oBACb;AAAA,iBAAW,UAAa,4CAAC,UAAK,WAAU,0BAA0B,kBAAO;AAAA,MACzE;AAAA,OACF;AAAA,IAEA,MAAM,WAAW,IACjB,4CAAC,SAAI,WAAU,mBAAmB,iBAAM,IAExC,4CAAC,SAAI,WAAU,mBACb,gBAAM;AAAA,MAAI,CAAC,SACX,aACC,WAAW,IAAI,IAEf;AAAA,QAAC;AAAA;AAAA,UAEC,GAAG;AAAA,UACJ;AAAA,UACA;AAAA;AAAA,QAHK,KAAK;AAAA,MAIX;AAAA,IAEF,GACD;AAAA,KAEF;AAEF;AAEA,SAAS,gBAAgB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AACD,GAAmF;AAClF,QAAM,cAAc,MAAM;AACzB,QAAI,SAAU,UAAS,EAAE;AAAA,EAC1B;AACA,SACC;AAAA,IAAC;AAAA;AAAA,MACA,MAAK;AAAA,MACL,WAAW,WAAW,4CAA4C;AAAA,MAClE,iBAAe,YAAY;AAAA,MAC3B,SAAS;AAAA,MAET;AAAA,oDAAC,wBAAO,MAAM,QAAQ;AAAA,QACtB,6CAAC,SAAI,WAAU,uBACd;AAAA,sDAAC,wBAAO,QAAgB,MAAY;AAAA,UACpC,4CAAC,SAAI,WAAU,0BAA0B,mBAAQ;AAAA,WAC/C,YAAY,SAAS,WACtB,6CAAC,SAAI,WAAU,uBACb;AAAA,wBACA,6CAAC,UAAK,WAAU,2BACf;AAAA,0DAAC,aAAU;AAAA,cACV;AAAA,eACF;AAAA,YAEA,SAAS,UAAa,4CAAC,UAAK,WAAU,uBAAuB,gBAAK;AAAA,aACpE;AAAA,WAEF;AAAA,QACC,UAAU,UAAa,QAAQ,KAAK,4CAAC,UAAK,WAAU,wBAAwB,iBAAM;AAAA;AAAA;AAAA,EACpF;AAEF;AAEA,SAAS,YAAY;AACpB,SACC,4CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,eAAY,QACvE;AAAA,IAAC;AAAA;AAAA,MACA,GAAE;AAAA,MACF,QAAO;AAAA,MACP,aAAY;AAAA,MACZ,eAAc;AAAA,MACd,gBAAe;AAAA;AAAA,EAChB,GACD;AAEF;",
4
+ "sourcesContent": ["import { ReactNode } from 'react'\nimport { Avatar } from './avatar'\nimport { Byline } from './byline'\nimport { CommentAuthor } from './comment-author'\n\n/** @public */\nexport interface CommentListItemProps {\n\tid: string\n\tauthor: CommentAuthor\n\t/** A short preview of the thread \u2014 e.g. the first comment's body. */\n\tpreview: ReactNode\n\t/** ISO datetime of the thread's first comment. */\n\tdate: string\n\tresolved?: boolean\n\t/** Name of the page the thread lives on, shown as a small label. Omit to hide. */\n\tpage?: string\n\t/** Total comments in the thread. */\n\tcount?: number\n\t/** Whether this thread is the open one. */\n\tselected?: boolean\n}\n\n/** @public */\nexport interface CommentsListProps {\n\titems: CommentListItemProps[]\n\t/** Called with a thread id when an item is chosen. */\n\tonSelect?(id: string): void\n\t/** Shown above the list (e.g. \"Comments\"). Omit for none. */\n\theader?: ReactNode\n\t/** Rendered at the right of the header row \u2014 e.g. a filter menu. */\n\theaderAction?: ReactNode\n\t/** Shown in place of the list when there are no threads. */\n\tempty?: ReactNode\n\t/** Label for a resolved thread's marker on its row. Defaults to \"Resolved\". */\n\tresolvedLabel?: string\n\t/** Override how each item renders. Defaults to `<CommentListItem>`. */\n\trenderItem?(item: CommentListItemProps): ReactNode\n}\n\n/**\n * A scrollable list of comment threads \u2014 each an avatar, byline, and a one-line preview.\n * Presentational: you supply the items (already summarised) and an `onSelect` handler; the canvas\n * `CanvasCommentsSidebar` wires it to the store, but a consumer can build their own list from this.\n * @public @react\n */\nexport function CommentsList({\n\titems,\n\tonSelect,\n\theader,\n\theaderAction,\n\tempty,\n\tresolvedLabel,\n\trenderItem,\n}: CommentsListProps) {\n\treturn (\n\t\t<div className=\"tlui-cmt-list\">\n\t\t\t{(header !== undefined || headerAction) && (\n\t\t\t\t<div className=\"tlui-cmt-list__header\">\n\t\t\t\t\t{header !== undefined && <span className=\"tlui-cmt-list__header-title\">{header}</span>}\n\t\t\t\t\t{headerAction}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{items.length === 0 ? (\n\t\t\t\t<div className=\"tlui-cmt-list__empty\">{empty}</div>\n\t\t\t) : (\n\t\t\t\t<div className=\"tlui-cmt-list__items\">\n\t\t\t\t\t{items.map((item) =>\n\t\t\t\t\t\trenderItem ? (\n\t\t\t\t\t\t\trenderItem(item)\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<CommentListItem\n\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t{...item}\n\t\t\t\t\t\t\t\tresolvedLabel={resolvedLabel}\n\t\t\t\t\t\t\t\tonSelect={onSelect}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n\nfunction CommentListItem({\n\tid,\n\tauthor,\n\tpreview,\n\tdate,\n\tresolved,\n\tpage,\n\tcount,\n\tselected,\n\tresolvedLabel = 'Resolved',\n\tonSelect,\n}: CommentListItemProps & { resolvedLabel?: string; onSelect?(id: string): void }) {\n\tconst handleClick = () => {\n\t\tif (onSelect) onSelect(id)\n\t}\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tclassName={\n\t\t\t\tselected ? 'tlui-cmt-list__item tlui-cmt-list__item--selected' : 'tlui-cmt-list__item'\n\t\t\t}\n\t\t\tdata-resolved={resolved || undefined}\n\t\t\tonClick={handleClick}\n\t\t>\n\t\t\t<Avatar author={author} />\n\t\t\t<div className=\"tlui-cmt-list__item-body\">\n\t\t\t\t<Byline author={author} date={date} />\n\t\t\t\t<div className=\"tlui-cmt-list__item-preview\">{preview}</div>\n\t\t\t\t{(resolved || page !== undefined) && (\n\t\t\t\t\t<div className=\"tlui-cmt-list__item-meta\">\n\t\t\t\t\t\t{resolved && (\n\t\t\t\t\t\t\t<span className=\"tlui-cmt-list__item-resolved\">\n\t\t\t\t\t\t\t\t<CheckIcon />\n\t\t\t\t\t\t\t\t{resolvedLabel}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{page !== undefined && <span className=\"tlui-cmt-list__item-page\">{page}</span>}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t{count !== undefined && count > 1 && (\n\t\t\t\t<span className=\"tlui-cmt-list__item-count\">{count}</span>\n\t\t\t)}\n\t\t</button>\n\t)\n}\n\nfunction CheckIcon() {\n\treturn (\n\t\t<svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t<path\n\t\t\t\td=\"M2.5 6.2 4.7 8.4 9.5 3.6\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.5\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAyDI;AAxDJ,oBAAuB;AACvB,oBAAuB;AA2ChB,SAAS,aAAa;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAsB;AACrB,SACC,6CAAC,SAAI,WAAU,iBACZ;AAAA,gBAAW,UAAa,iBACzB,6CAAC,SAAI,WAAU,yBACb;AAAA,iBAAW,UAAa,4CAAC,UAAK,WAAU,+BAA+B,kBAAO;AAAA,MAC9E;AAAA,OACF;AAAA,IAEA,MAAM,WAAW,IACjB,4CAAC,SAAI,WAAU,wBAAwB,iBAAM,IAE7C,4CAAC,SAAI,WAAU,wBACb,gBAAM;AAAA,MAAI,CAAC,SACX,aACC,WAAW,IAAI,IAEf;AAAA,QAAC;AAAA;AAAA,UAEC,GAAG;AAAA,UACJ;AAAA,UACA;AAAA;AAAA,QAHK,KAAK;AAAA,MAIX;AAAA,IAEF,GACD;AAAA,KAEF;AAEF;AAEA,SAAS,gBAAgB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AACD,GAAmF;AAClF,QAAM,cAAc,MAAM;AACzB,QAAI,SAAU,UAAS,EAAE;AAAA,EAC1B;AACA,SACC;AAAA,IAAC;AAAA;AAAA,MACA,MAAK;AAAA,MACL,WACC,WAAW,sDAAsD;AAAA,MAElE,iBAAe,YAAY;AAAA,MAC3B,SAAS;AAAA,MAET;AAAA,oDAAC,wBAAO,QAAgB;AAAA,QACxB,6CAAC,SAAI,WAAU,4BACd;AAAA,sDAAC,wBAAO,QAAgB,MAAY;AAAA,UACpC,4CAAC,SAAI,WAAU,+BAA+B,mBAAQ;AAAA,WACpD,YAAY,SAAS,WACtB,6CAAC,SAAI,WAAU,4BACb;AAAA,wBACA,6CAAC,UAAK,WAAU,gCACf;AAAA,0DAAC,aAAU;AAAA,cACV;AAAA,eACF;AAAA,YAEA,SAAS,UAAa,4CAAC,UAAK,WAAU,4BAA4B,gBAAK;AAAA,aACzE;AAAA,WAEF;AAAA,QACC,UAAU,UAAa,QAAQ,KAC/B,4CAAC,UAAK,WAAU,6BAA6B,iBAAM;AAAA;AAAA;AAAA,EAErD;AAEF;AAEA,SAAS,YAAY;AACpB,SACC,4CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,eAAY,QACvE;AAAA,IAAC;AAAA;AAAA,MACA,GAAE;AAAA,MACF,QAAO;AAAA,MACP,aAAY;AAAA,MACZ,eAAc;AAAA,MACd,gBAAe;AAAA;AAAA,EAChB,GACD;AAEF;",
6
6
  "names": []
7
7
  }
@@ -23,6 +23,6 @@ __export(count_badge_exports, {
23
23
  module.exports = __toCommonJS(count_badge_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
25
  function CountBadge({ count }) {
26
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cmt-count-badge", children: count });
26
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-cmt-count-badge", children: count });
27
27
  }
28
28
  //# sourceMappingURL=count-badge.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/count-badge.tsx"],
4
- "sourcesContent": ["/** @public */\nexport interface CountBadgeProps {\n\tcount: number\n}\n\n/** @public @react */\nexport function CountBadge({ count }: CountBadgeProps) {\n\treturn <div className=\"cmt-count-badge\">{count}</div>\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOQ;AADD,SAAS,WAAW,EAAE,MAAM,GAAoB;AACtD,SAAO,4CAAC,SAAI,WAAU,mBAAmB,iBAAM;AAChD;",
4
+ "sourcesContent": ["/** @public */\nexport interface CountBadgeProps {\n\tcount: number\n}\n\n/** @public @react */\nexport function CountBadge({ count }: CountBadgeProps) {\n\treturn <div className=\"tlui-cmt-count-badge\">{count}</div>\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOQ;AADD,SAAS,WAAW,EAAE,MAAM,GAAoB;AACtD,SAAO,4CAAC,SAAI,WAAU,wBAAwB,iBAAM;AACrD;",
6
6
  "names": []
7
7
  }
@@ -23,9 +23,9 @@ __export(empty_state_exports, {
23
23
  module.exports = __toCommonJS(empty_state_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
25
  function EmptyState({ message }) {
26
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "cmt-empty", children: [
27
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cmt-empty__icon", "aria-hidden": "true", children: "\u{1F4AC}" }),
28
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "cmt-empty__message", children: message })
26
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-cmt-empty", children: [
27
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-cmt-empty__icon", "aria-hidden": "true", children: "\u{1F4AC}" }),
28
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "tlui-cmt-empty__message", children: message })
29
29
  ] });
30
30
  }
31
31
  //# sourceMappingURL=empty-state.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/empty-state.tsx"],
4
- "sourcesContent": ["/** @public */\nexport interface EmptyStateProps {\n\tmessage: string\n}\n\n/** The empty state shown when a thread has no comments yet. @public @react */\nexport function EmptyState({ message }: EmptyStateProps) {\n\treturn (\n\t\t<div className=\"cmt-empty\">\n\t\t\t<div className=\"cmt-empty__icon\" aria-hidden=\"true\">\n\t\t\t\t\uD83D\uDCAC\n\t\t\t</div>\n\t\t\t<p className=\"cmt-empty__message\">{message}</p>\n\t\t</div>\n\t)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAQE;AAFK,SAAS,WAAW,EAAE,QAAQ,GAAoB;AACxD,SACC,6CAAC,SAAI,WAAU,aACd;AAAA,gDAAC,SAAI,WAAU,mBAAkB,eAAY,QAAO,uBAEpD;AAAA,IACA,4CAAC,OAAE,WAAU,sBAAsB,mBAAQ;AAAA,KAC5C;AAEF;",
4
+ "sourcesContent": ["/** @public */\nexport interface EmptyStateProps {\n\tmessage: string\n}\n\n/** The empty state shown when a thread has no comments yet. @public @react */\nexport function EmptyState({ message }: EmptyStateProps) {\n\treturn (\n\t\t<div className=\"tlui-cmt-empty\">\n\t\t\t<div className=\"tlui-cmt-empty__icon\" aria-hidden=\"true\">\n\t\t\t\t\uD83D\uDCAC\n\t\t\t</div>\n\t\t\t<p className=\"tlui-cmt-empty__message\">{message}</p>\n\t\t</div>\n\t)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAQE;AAFK,SAAS,WAAW,EAAE,QAAQ,GAAoB;AACxD,SACC,6CAAC,SAAI,WAAU,kBACd;AAAA,gDAAC,SAAI,WAAU,wBAAuB,eAAY,QAAO,uBAEzD;AAAA,IACA,4CAAC,OAAE,WAAU,2BAA2B,mBAAQ;AAAA,KACjD;AAEF;",
6
6
  "names": []
7
7
  }
@@ -35,6 +35,9 @@ function formatRelativeTime(iso, locale = "en") {
35
35
  if (Number.isNaN(then)) return "";
36
36
  const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
37
37
  let duration = (then - Date.now()) / 1e3;
38
+ if (Math.abs(duration) < 60) {
39
+ return rtf.format(0, "second");
40
+ }
38
41
  for (const division of DIVISIONS) {
39
42
  if (Math.abs(duration) < division.amount) {
40
43
  return rtf.format(Math.round(duration), division.unit);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ui/format-time.ts"],
4
- "sourcesContent": ["const DIVISIONS = [\n\t{ amount: 60, unit: 'second' },\n\t{ amount: 60, unit: 'minute' },\n\t{ amount: 24, unit: 'hour' },\n\t{ amount: 7, unit: 'day' },\n\t{ amount: 4.34524, unit: 'week' },\n\t{ amount: 12, unit: 'month' },\n\t{ amount: Number.POSITIVE_INFINITY, unit: 'year' },\n] as const\n\n/**\n * Format an ISO datetime as relative time (\"2 hours ago\", \"yesterday\", \"last week\").\n * Locale-aware via Intl.RelativeTimeFormat.\n * @public\n */\nexport function formatRelativeTime(iso: string, locale = 'en'): string {\n\tconst then = new Date(iso).getTime()\n\tif (Number.isNaN(then)) return ''\n\n\tconst rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' })\n\tlet duration = (then - Date.now()) / 1000\n\tfor (const division of DIVISIONS) {\n\t\tif (Math.abs(duration) < division.amount) {\n\t\t\treturn rtf.format(Math.round(duration), division.unit)\n\t\t}\n\t\tduration /= division.amount\n\t}\n\treturn ''\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAM,YAAY;AAAA,EACjB,EAAE,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC7B,EAAE,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC7B,EAAE,QAAQ,IAAI,MAAM,OAAO;AAAA,EAC3B,EAAE,QAAQ,GAAG,MAAM,MAAM;AAAA,EACzB,EAAE,QAAQ,SAAS,MAAM,OAAO;AAAA,EAChC,EAAE,QAAQ,IAAI,MAAM,QAAQ;AAAA,EAC5B,EAAE,QAAQ,OAAO,mBAAmB,MAAM,OAAO;AAClD;AAOO,SAAS,mBAAmB,KAAa,SAAS,MAAc;AACtE,QAAM,OAAO,IAAI,KAAK,GAAG,EAAE,QAAQ;AACnC,MAAI,OAAO,MAAM,IAAI,EAAG,QAAO;AAE/B,QAAM,MAAM,IAAI,KAAK,mBAAmB,QAAQ,EAAE,SAAS,OAAO,CAAC;AACnE,MAAI,YAAY,OAAO,KAAK,IAAI,KAAK;AACrC,aAAW,YAAY,WAAW;AACjC,QAAI,KAAK,IAAI,QAAQ,IAAI,SAAS,QAAQ;AACzC,aAAO,IAAI,OAAO,KAAK,MAAM,QAAQ,GAAG,SAAS,IAAI;AAAA,IACtD;AACA,gBAAY,SAAS;AAAA,EACtB;AACA,SAAO;AACR;",
4
+ "sourcesContent": ["const DIVISIONS = [\n\t{ amount: 60, unit: 'second' },\n\t{ amount: 60, unit: 'minute' },\n\t{ amount: 24, unit: 'hour' },\n\t{ amount: 7, unit: 'day' },\n\t{ amount: 4.34524, unit: 'week' },\n\t{ amount: 12, unit: 'month' },\n\t{ amount: Number.POSITIVE_INFINITY, unit: 'year' },\n] as const\n\n/**\n * Format an ISO datetime as relative time (\"2 hours ago\", \"yesterday\", \"last week\").\n * Locale-aware via Intl.RelativeTimeFormat.\n * @public\n */\nexport function formatRelativeTime(iso: string, locale = 'en'): string {\n\tconst then = new Date(iso).getTime()\n\tif (Number.isNaN(then)) return ''\n\n\tconst rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' })\n\tlet duration = (then - Date.now()) / 1000\n\t// Under a minute is just \"now\" \u2014 with second granularity the label visibly ticks while\n\t// typing a reply (every keystroke re-renders the card).\n\tif (Math.abs(duration) < 60) {\n\t\treturn rtf.format(0, 'second')\n\t}\n\tfor (const division of DIVISIONS) {\n\t\tif (Math.abs(duration) < division.amount) {\n\t\t\treturn rtf.format(Math.round(duration), division.unit)\n\t\t}\n\t\tduration /= division.amount\n\t}\n\treturn ''\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAM,YAAY;AAAA,EACjB,EAAE,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC7B,EAAE,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC7B,EAAE,QAAQ,IAAI,MAAM,OAAO;AAAA,EAC3B,EAAE,QAAQ,GAAG,MAAM,MAAM;AAAA,EACzB,EAAE,QAAQ,SAAS,MAAM,OAAO;AAAA,EAChC,EAAE,QAAQ,IAAI,MAAM,QAAQ;AAAA,EAC5B,EAAE,QAAQ,OAAO,mBAAmB,MAAM,OAAO;AAClD;AAOO,SAAS,mBAAmB,KAAa,SAAS,MAAc;AACtE,QAAM,OAAO,IAAI,KAAK,GAAG,EAAE,QAAQ;AACnC,MAAI,OAAO,MAAM,IAAI,EAAG,QAAO;AAE/B,QAAM,MAAM,IAAI,KAAK,mBAAmB,QAAQ,EAAE,SAAS,OAAO,CAAC;AACnE,MAAI,YAAY,OAAO,KAAK,IAAI,KAAK;AAGrC,MAAI,KAAK,IAAI,QAAQ,IAAI,IAAI;AAC5B,WAAO,IAAI,OAAO,GAAG,QAAQ;AAAA,EAC9B;AACA,aAAW,YAAY,WAAW;AACjC,QAAI,KAAK,IAAI,QAAQ,IAAI,SAAS,QAAQ;AACzC,aAAO,IAAI,OAAO,KAAK,MAAM,QAAQ,GAAG,SAAS,IAAI;AAAA,IACtD;AACA,gBAAY,SAAS;AAAA,EACtB;AACA,SAAO;AACR;",
6
6
  "names": []
7
7
  }