@flanksource/clicky-ui 0.3.30 → 0.3.31

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 (130) hide show
  1. package/dist/comments/CommentCard.cjs +110 -262
  2. package/dist/comments/CommentCard.cjs.map +1 -1
  3. package/dist/comments/CommentCard.d.ts +3 -1
  4. package/dist/comments/CommentCard.d.ts.map +1 -1
  5. package/dist/comments/CommentCard.js +105 -257
  6. package/dist/comments/CommentCard.js.map +1 -1
  7. package/dist/comments/CommentCardParts.cjs +95 -0
  8. package/dist/comments/CommentCardParts.cjs.map +1 -0
  9. package/dist/comments/CommentCardParts.d.ts +17 -0
  10. package/dist/comments/CommentCardParts.d.ts.map +1 -0
  11. package/dist/comments/CommentCardParts.js +95 -0
  12. package/dist/comments/CommentCardParts.js.map +1 -0
  13. package/dist/comments/CommentSidePanel.cjs +14 -7
  14. package/dist/comments/CommentSidePanel.cjs.map +1 -1
  15. package/dist/comments/CommentSidePanel.d.ts +3 -1
  16. package/dist/comments/CommentSidePanel.d.ts.map +1 -1
  17. package/dist/comments/CommentSidePanel.js +14 -7
  18. package/dist/comments/CommentSidePanel.js.map +1 -1
  19. package/dist/comments/CommentThread.cjs +2 -0
  20. package/dist/comments/CommentThread.cjs.map +1 -1
  21. package/dist/comments/CommentThread.d.ts +2 -1
  22. package/dist/comments/CommentThread.d.ts.map +1 -1
  23. package/dist/comments/CommentThread.js +2 -0
  24. package/dist/comments/CommentThread.js.map +1 -1
  25. package/dist/comments/CommentThreadList.cjs +50 -34
  26. package/dist/comments/CommentThreadList.cjs.map +1 -1
  27. package/dist/comments/CommentThreadList.d.ts +3 -1
  28. package/dist/comments/CommentThreadList.d.ts.map +1 -1
  29. package/dist/comments/CommentThreadList.js +50 -34
  30. package/dist/comments/CommentThreadList.js.map +1 -1
  31. package/dist/components/ComboboxMenu.cjs +4 -1
  32. package/dist/components/ComboboxMenu.cjs.map +1 -1
  33. package/dist/components/ComboboxMenu.d.ts.map +1 -1
  34. package/dist/components/ComboboxMenu.js +5 -2
  35. package/dist/components/ComboboxMenu.js.map +1 -1
  36. package/dist/components/ComboboxTags.cjs +5 -1
  37. package/dist/components/ComboboxTags.cjs.map +1 -1
  38. package/dist/components/ComboboxTags.d.ts.map +1 -1
  39. package/dist/components/ComboboxTags.js +6 -2
  40. package/dist/components/ComboboxTags.js.map +1 -1
  41. package/dist/components/ConnectionLoggingPolicy.cjs +24 -35
  42. package/dist/components/ConnectionLoggingPolicy.cjs.map +1 -1
  43. package/dist/components/ConnectionLoggingPolicy.d.ts.map +1 -1
  44. package/dist/components/ConnectionLoggingPolicy.js +24 -35
  45. package/dist/components/ConnectionLoggingPolicy.js.map +1 -1
  46. package/dist/components/MultiSelect.cjs.map +1 -1
  47. package/dist/components/MultiSelect.d.ts +7 -0
  48. package/dist/components/MultiSelect.d.ts.map +1 -1
  49. package/dist/components/MultiSelect.js.map +1 -1
  50. package/dist/components/filter-bar-multi.cjs +10 -1
  51. package/dist/components/filter-bar-multi.cjs.map +1 -1
  52. package/dist/components/filter-bar-multi.d.ts.map +1 -1
  53. package/dist/components/filter-bar-multi.js +11 -2
  54. package/dist/components/filter-bar-multi.js.map +1 -1
  55. package/dist/data/ai/SessionViewer.model.cjs +23 -8
  56. package/dist/data/ai/SessionViewer.model.cjs.map +1 -1
  57. package/dist/data/ai/SessionViewer.model.d.ts +8 -0
  58. package/dist/data/ai/SessionViewer.model.d.ts.map +1 -1
  59. package/dist/data/ai/SessionViewer.model.js +23 -8
  60. package/dist/data/ai/SessionViewer.model.js.map +1 -1
  61. package/dist/data/ai/SessionViewer.rows.cjs +9 -1
  62. package/dist/data/ai/SessionViewer.rows.cjs.map +1 -1
  63. package/dist/data/ai/SessionViewer.rows.js +9 -1
  64. package/dist/data/ai/SessionViewer.rows.js.map +1 -1
  65. package/dist/data/ai/SpecRuntimeEditor/VerifySection.cjs +24 -2
  66. package/dist/data/ai/SpecRuntimeEditor/VerifySection.cjs.map +1 -1
  67. package/dist/data/ai/SpecRuntimeEditor/VerifySection.d.ts.map +1 -1
  68. package/dist/data/ai/SpecRuntimeEditor/VerifySection.js +25 -3
  69. package/dist/data/ai/SpecRuntimeEditor/VerifySection.js.map +1 -1
  70. package/dist/data/ai/SpecRuntimeEditor/summaries.cjs.map +1 -1
  71. package/dist/data/ai/SpecRuntimeEditor/summaries.d.ts.map +1 -1
  72. package/dist/data/ai/SpecRuntimeEditor/summaries.js.map +1 -1
  73. package/dist/data/ai/SpecRuntimeEditor.model.cjs +4 -0
  74. package/dist/data/ai/SpecRuntimeEditor.model.cjs.map +1 -1
  75. package/dist/data/ai/SpecRuntimeEditor.model.d.ts +4 -0
  76. package/dist/data/ai/SpecRuntimeEditor.model.d.ts.map +1 -1
  77. package/dist/data/ai/SpecRuntimeEditor.model.js +4 -0
  78. package/dist/data/ai/SpecRuntimeEditor.model.js.map +1 -1
  79. package/dist/data/test-runner/TestRunner.cjs +6 -4
  80. package/dist/data/test-runner/TestRunner.cjs.map +1 -1
  81. package/dist/data/test-runner/TestRunner.d.ts +10 -1
  82. package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
  83. package/dist/data/test-runner/TestRunner.js +6 -4
  84. package/dist/data/test-runner/TestRunner.js.map +1 -1
  85. package/dist/data/verification/VerificationResults.cjs +99 -0
  86. package/dist/data/verification/VerificationResults.cjs.map +1 -0
  87. package/dist/data/verification/VerificationResults.d.ts +27 -0
  88. package/dist/data/verification/VerificationResults.d.ts.map +1 -0
  89. package/dist/data/verification/VerificationResults.js +99 -0
  90. package/dist/data/verification/VerificationResults.js.map +1 -0
  91. package/dist/data/verification/adapterViews.cjs +116 -0
  92. package/dist/data/verification/adapterViews.cjs.map +1 -0
  93. package/dist/data/verification/adapterViews.d.ts +13 -0
  94. package/dist/data/verification/adapterViews.d.ts.map +1 -0
  95. package/dist/data/verification/adapterViews.js +116 -0
  96. package/dist/data/verification/adapterViews.js.map +1 -0
  97. package/dist/data/verification/adapters.cjs +26 -0
  98. package/dist/data/verification/adapters.cjs.map +1 -0
  99. package/dist/data/verification/adapters.d.ts +6 -0
  100. package/dist/data/verification/adapters.d.ts.map +1 -0
  101. package/dist/data/verification/adapters.js +26 -0
  102. package/dist/data/verification/adapters.js.map +1 -0
  103. package/dist/data/verification/index.d.ts +5 -0
  104. package/dist/data/verification/index.d.ts.map +1 -0
  105. package/dist/data/verification/verify-report.cjs +58 -0
  106. package/dist/data/verification/verify-report.cjs.map +1 -0
  107. package/dist/data/verification/verify-report.d.ts +96 -0
  108. package/dist/data/verification/verify-report.d.ts.map +1 -0
  109. package/dist/data/verification/verify-report.js +58 -0
  110. package/dist/data/verification/verify-report.js.map +1 -0
  111. package/dist/data/verification/verifyReportTests.cjs +83 -0
  112. package/dist/data/verification/verifyReportTests.cjs.map +1 -0
  113. package/dist/data/verification/verifyReportTests.d.ts +13 -0
  114. package/dist/data/verification/verifyReportTests.d.ts.map +1 -0
  115. package/dist/data/verification/verifyReportTests.js +83 -0
  116. package/dist/data/verification/verifyReportTests.js.map +1 -0
  117. package/dist/data/version-info.cjs +3 -3
  118. package/dist/data/version-info.js +3 -3
  119. package/dist/data.cjs +10 -0
  120. package/dist/data.cjs.map +1 -1
  121. package/dist/data.d.ts +1 -0
  122. package/dist/data.d.ts.map +1 -1
  123. package/dist/data.js +10 -0
  124. package/dist/data.js.map +1 -1
  125. package/dist/index.cjs +10 -0
  126. package/dist/index.cjs.map +1 -1
  127. package/dist/index.js +10 -0
  128. package/dist/index.js.map +1 -1
  129. package/dist/styles.css +1 -1
  130. package/package.json +1 -1
@@ -3,20 +3,19 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const react = require("react");
5
5
  const utils = require("../lib/utils.cjs");
6
- const Badge = require("../data/Badge.cjs");
7
6
  const Icon = require("../data/Icon.cjs");
8
7
  const DropdownMenu = require("../overlay/DropdownMenu.cjs");
9
8
  const timestampFormat = require("../data/cells/timestamp-format.cjs");
10
9
  const CommentAuthor = require("./CommentAuthor.cjs");
10
+ const CommentCardParts = require("./CommentCardParts.cjs");
11
11
  const CommentMarkdown = require("./CommentMarkdown.cjs");
12
12
  const commentUtils = require("./comment-utils.cjs");
13
- const UiCheck = require("../icons/components/UiCheck.cjs");
14
- const UiFullscreen = require("../icons/components/UiFullscreen.cjs");
15
13
  const UiDotsVertical = require("../icons/components/UiDotsVertical.cjs");
16
- const UiThumbsUp = require("../icons/components/UiThumbsUp.cjs");
17
- const UiThumbsDown = require("../icons/components/UiThumbsDown.cjs");
18
- const UiClose = require("../icons/components/UiClose.cjs");
19
14
  const UiTrash = require("../icons/components/UiTrash.cjs");
15
+ const UiCheck = require("../icons/components/UiCheck.cjs");
16
+ const UiRestart = require("../icons/components/UiRestart.cjs");
17
+ const UiCopy = require("../icons/components/UiCopy.cjs");
18
+ const UiFullscreen = require("../icons/components/UiFullscreen.cjs");
20
19
  function completionStatus(config) {
21
20
  for (const completionName of ["resolved", "closed"]) {
22
21
  const status = config.statuses.find(
@@ -26,204 +25,82 @@ function completionStatus(config) {
26
25
  }
27
26
  return void 0;
28
27
  }
29
- function completionActionLabel(status) {
30
- return status.value.toLowerCase() === "closed" || status.label.toLowerCase() === "closed" ? "Close comment" : "Resolve comment";
31
- }
32
- function RatingChip({ rating }) {
33
- if (!rating) return null;
34
- const positive = rating === "positive";
35
- return /* @__PURE__ */ jsxRuntime.jsx(
36
- Badge.Badge,
37
- {
38
- variant: "soft",
39
- tone: positive ? "success" : "danger",
40
- size: "xs",
41
- icon: positive ? UiThumbsUp.UiThumbsUp : UiThumbsDown.UiThumbsDown,
42
- children: positive ? "Positive rating" : "Negative rating"
43
- }
44
- );
45
- }
46
- function StatusChip({
47
- status,
48
- config,
49
- showLabel
50
- }) {
51
- const cfg = commentUtils.resolveStatusConfig(config, status);
52
- if (!cfg) return null;
53
- return /* @__PURE__ */ jsxRuntime.jsx(
54
- Badge.Badge,
55
- {
56
- variant: "soft",
57
- tone: commentUtils.toneToBadgeTone(cfg.tone),
58
- size: "xs",
59
- ...cfg.icon ? { icon: cfg.icon } : {},
60
- children: showLabel ? cfg.label : null
61
- }
62
- );
63
- }
64
- function FacetBadges({
65
- comment,
66
- config,
67
- compact
68
- }) {
69
- const facets = config.facets ?? [];
70
- const chips = facets.flatMap((facet) => {
71
- var _a;
72
- const value = (_a = comment.facets) == null ? void 0 : _a[facet.key];
73
- const option = commentUtils.resolveFacetOption(facet, value);
74
- if (!option) return [];
75
- return [
76
- /* @__PURE__ */ jsxRuntime.jsx(
77
- Badge.Badge,
78
- {
79
- variant: "soft",
80
- tone: commentUtils.toneToBadgeTone(option.tone),
81
- size: "xs",
82
- children: compact ? option.short ?? option.label : option.label
83
- },
84
- facet.key
85
- )
86
- ];
87
- });
88
- return chips.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: chips }) : null;
28
+ function reopenStatus(config) {
29
+ return config.statuses.find((status) => status.unresolved);
89
30
  }
90
- function ChecklistChips({
91
- comment,
92
- onChecklistToggle
93
- }) {
94
- if (!comment.checklist || comment.checklist.length === 0) return null;
95
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: comment.checklist.map((item, i) => {
96
- const done = /done|complete|resolved/i.test(item.status);
97
- const active = /progress|doing/i.test(item.status);
98
- return /* @__PURE__ */ jsxRuntime.jsx(
99
- "button",
100
- {
101
- type: "button",
102
- onClick: () => onChecklistToggle == null ? void 0 : onChecklistToggle(i),
103
- disabled: !onChecklistToggle,
104
- className: utils.cn(
105
- "inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] transition-opacity",
106
- onChecklistToggle && "cursor-pointer hover:opacity-80",
107
- done ? "bg-green-100 text-green-800 line-through dark:bg-green-500/20 dark:text-green-300" : active ? "bg-yellow-100 text-yellow-800 dark:bg-yellow-500/20 dark:text-yellow-300" : "bg-muted text-muted-foreground"
108
- ),
109
- children: item.label
110
- },
111
- i
112
- );
113
- }) });
114
- }
115
- function RefChips({ comment }) {
116
- if (!comment.refs || comment.refs.length === 0) return null;
117
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5", children: comment.refs.map((group, i) => /* @__PURE__ */ jsxRuntime.jsxs(
118
- "span",
119
- {
120
- className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground",
121
- children: [
122
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
123
- group.label,
124
- ":"
125
- ] }),
126
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: group.mono ? "font-mono" : void 0, children: group.items.join(", ") })
127
- ]
128
- },
129
- i
130
- )) });
131
- }
132
- function statusMenuItems(comment, config, isReply, opts) {
31
+ function actionMenuItems(isReply, opts) {
133
32
  const items = [];
33
+ if (opts.onDelete) {
34
+ items.push({ label: "Delete", icon: UiTrash.UiTrash, onSelect: opts.onDelete });
35
+ }
36
+ if (!isReply && opts.onToggleStatus && opts.toggleStatusLabel) {
37
+ items.push({
38
+ label: opts.toggleStatusLabel,
39
+ icon: opts.toggleStatusLabel === "Resolve" ? UiCheck.UiCheck : UiRestart.UiRestart,
40
+ onSelect: opts.onToggleStatus
41
+ });
42
+ }
43
+ if (opts.onCopy) {
44
+ items.push({ label: "Copy", icon: UiCopy.UiCopy, onSelect: opts.onCopy });
45
+ }
134
46
  if (opts.onMaximize) {
135
47
  items.push({
136
- label: "Maximize",
48
+ label: "Maximise",
137
49
  icon: UiFullscreen.UiFullscreen,
138
50
  onSelect: opts.onMaximize
139
51
  });
140
52
  }
141
- if (opts.onCollapse) {
142
- items.push({ label: "Collapse", icon: UiClose.UiClose, onSelect: opts.onCollapse });
143
- }
144
- if (!isReply && opts.onUpdateStatus) {
145
- for (const status of config.statuses) {
146
- if (status.value === opts.completionStatus) continue;
147
- const current = status.value === comment.status;
148
- items.push({
149
- label: /* @__PURE__ */ jsxRuntime.jsxs(
150
- "span",
151
- {
152
- className: utils.cn(
153
- "inline-flex items-center gap-1.5",
154
- current && "font-semibold"
155
- ),
156
- children: [
157
- /* @__PURE__ */ jsxRuntime.jsx(StatusChip, { status: status.value, config }),
158
- status.label
159
- ]
160
- }
161
- ),
162
- onSelect: () => {
163
- var _a;
164
- return (_a = opts.onUpdateStatus) == null ? void 0 : _a.call(opts, status.value);
165
- }
166
- });
167
- }
168
- }
169
- if (opts.onDelete) {
170
- items.push({ label: "Delete", icon: UiTrash.UiTrash, onSelect: opts.onDelete });
171
- }
172
53
  return items;
173
54
  }
174
- function IconAction({
175
- icon,
176
- label,
177
- onClick,
178
- className
179
- }) {
180
- return /* @__PURE__ */ jsxRuntime.jsx(
181
- "button",
182
- {
183
- type: "button",
184
- onClick,
185
- "aria-label": label,
186
- title: label,
187
- className: utils.cn(
188
- "rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
189
- className
190
- ),
191
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon, className: "text-sm" })
192
- }
193
- );
194
- }
195
55
  function CommentBody({
196
56
  comment,
197
57
  config,
198
58
  renderBody,
199
59
  onUpdateStatus,
200
60
  onDelete,
61
+ onCopy,
201
62
  onReply,
202
63
  onChecklistToggle,
203
- onCollapse,
204
64
  onMaximize
205
65
  }) {
206
- const [statusError, setStatusError] = react.useState("");
66
+ const [actionError, setActionError] = react.useState("");
207
67
  const isReply = Boolean(comment.parentId);
208
68
  const date = timestampFormat.parseTimestamp(comment.createdAt);
209
69
  const completion = completionStatus(config);
210
- const canComplete = !isReply && onUpdateStatus != null && completion != null && commentUtils.isUnresolved(config, comment.status);
70
+ const reopen = reopenStatus(config);
71
+ const unresolved = comment.status == null || commentUtils.isUnresolved(config, comment.status);
211
72
  const updateStatus = async (status) => {
212
- setStatusError("");
73
+ setActionError("");
213
74
  try {
214
75
  await (onUpdateStatus == null ? void 0 : onUpdateStatus(status));
215
76
  } catch (error) {
216
- setStatusError(
217
- error instanceof Error ? error.message : "Unexpected error"
77
+ setActionError(
78
+ `Couldn't update comment: ${error instanceof Error ? error.message : "Unexpected error"}`
218
79
  );
219
80
  }
220
81
  };
221
- const menuItems = statusMenuItems(comment, config, isReply, {
222
- ...onUpdateStatus ? { onUpdateStatus: (status) => void updateStatus(status) } : {},
82
+ const copy = async () => {
83
+ setActionError("");
84
+ try {
85
+ await (onCopy == null ? void 0 : onCopy());
86
+ } catch (error) {
87
+ setActionError(
88
+ `Couldn't copy comment: ${error instanceof Error ? error.message : "Unexpected error"}`
89
+ );
90
+ }
91
+ };
92
+ const menuItems = actionMenuItems(isReply, {
223
93
  ...onDelete ? { onDelete } : {},
224
- ...onCollapse ? { onCollapse } : {},
225
- ...onMaximize ? { onMaximize } : {},
226
- ...completion ? { completionStatus: completion.value } : {}
94
+ ...!isReply && onUpdateStatus && completion && unresolved ? {
95
+ onToggleStatus: () => void updateStatus(completion.value),
96
+ toggleStatusLabel: "Resolve"
97
+ } : {},
98
+ ...!isReply && onUpdateStatus && reopen && !unresolved ? {
99
+ onToggleStatus: () => void updateStatus(reopen.value),
100
+ toggleStatusLabel: "Reopen"
101
+ } : {},
102
+ ...onCopy ? { onCopy: () => void copy() } : {},
103
+ ...onMaximize ? { onMaximize } : {}
227
104
  });
228
105
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2.5", children: [
229
106
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2.5", children: [
@@ -241,60 +118,46 @@ function CommentBody({
241
118
  )
242
119
  ] }),
243
120
  !isReply && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex flex-wrap gap-1", children: [
244
- /* @__PURE__ */ jsxRuntime.jsx(RatingChip, { rating: comment.rating }),
245
- /* @__PURE__ */ jsxRuntime.jsx(FacetBadges, { comment, config })
121
+ /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.RatingChip, { rating: comment.rating }),
122
+ /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.FacetBadges, { comment, config })
246
123
  ] })
247
124
  ] }),
248
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [
249
- canComplete && /* @__PURE__ */ jsxRuntime.jsx(
250
- IconAction,
251
- {
252
- icon: UiCheck.UiCheck,
253
- label: completionActionLabel(completion),
254
- onClick: () => void updateStatus(completion.value),
255
- className: "hover:bg-green-100 hover:text-green-700 dark:hover:bg-green-500/20 dark:hover:text-green-300"
256
- }
257
- ),
258
- menuItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
259
- DropdownMenu.DropdownMenu,
260
- {
261
- align: "right",
262
- menuClassName: "min-w-[150px]",
263
- trigger: /* @__PURE__ */ jsxRuntime.jsx(
264
- "span",
265
- {
266
- role: "button",
267
- tabIndex: 0,
268
- "aria-label": "Comment actions",
269
- className: "inline-flex rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
270
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiDotsVertical.UiDotsVertical, className: "text-sm" })
271
- }
272
- ),
273
- items: menuItems
274
- }
275
- )
276
- ] })
125
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-0.5", children: menuItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
126
+ DropdownMenu.DropdownMenu,
127
+ {
128
+ align: "right",
129
+ menuClassName: "min-w-[150px]",
130
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(
131
+ "span",
132
+ {
133
+ role: "button",
134
+ tabIndex: 0,
135
+ "aria-label": "Comment actions",
136
+ className: "inline-flex rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
137
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiDotsVertical.UiDotsVertical, className: "text-sm" })
138
+ }
139
+ ),
140
+ items: menuItems
141
+ }
142
+ ) })
277
143
  ] }),
278
144
  comment.body.trim() && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[13px] leading-5 text-foreground", children: renderBody ? renderBody(comment.body) : /* @__PURE__ */ jsxRuntime.jsx(CommentMarkdown.CommentMarkdown, { text: comment.body }) }),
279
- statusError && /* @__PURE__ */ jsxRuntime.jsxs(
145
+ actionError && /* @__PURE__ */ jsxRuntime.jsx(
280
146
  "div",
281
147
  {
282
148
  role: "alert",
283
149
  className: "rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive",
284
- children: [
285
- "Couldn't update comment: ",
286
- statusError
287
- ]
150
+ children: actionError
288
151
  }
289
152
  ),
290
153
  !isReply && /* @__PURE__ */ jsxRuntime.jsx(
291
- ChecklistChips,
154
+ CommentCardParts.ChecklistChips,
292
155
  {
293
156
  comment,
294
157
  ...onChecklistToggle ? { onChecklistToggle } : {}
295
158
  }
296
159
  ),
297
- !isReply && /* @__PURE__ */ jsxRuntime.jsx(RefChips, { comment }),
160
+ !isReply && /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.RefChips, { comment }),
298
161
  onReply && /* @__PURE__ */ jsxRuntime.jsx(
299
162
  "button",
300
163
  {
@@ -307,31 +170,37 @@ function CommentBody({
307
170
  ] });
308
171
  }
309
172
  function CommentCard(props) {
310
- const { comment, config, compact, defaultExpanded, onMaximize } = props;
173
+ const { comment, config, compact, defaultExpanded } = props;
311
174
  const [expanded, setExpanded] = react.useState(defaultExpanded ?? false);
312
- const [collapsedStatusError, setCollapsedStatusError] = react.useState("");
175
+ const [collapsedActionError, setCollapsedActionError] = react.useState("");
313
176
  const isReply = Boolean(comment.parentId);
314
- const completion = completionStatus(config);
315
- const canComplete = !isReply && props.onUpdateStatus != null && completion != null && commentUtils.isUnresolved(config, comment.status);
316
- const completeCollapsed = async () => {
177
+ const body = /* @__PURE__ */ jsxRuntime.jsx(CommentBody, { ...props });
178
+ async function updateCollapsedStatus() {
317
179
  var _a;
318
- if (!completion) return;
319
- setCollapsedStatusError("");
180
+ const completion = completionStatus(config);
181
+ const unresolved = comment.status == null || commentUtils.isUnresolved(config, comment.status);
182
+ const next = unresolved ? completion : reopenStatus(config);
183
+ if (!next) return;
184
+ setCollapsedActionError("");
320
185
  try {
321
- await ((_a = props.onUpdateStatus) == null ? void 0 : _a.call(props, completion.value));
186
+ await ((_a = props.onUpdateStatus) == null ? void 0 : _a.call(props, next.value));
322
187
  } catch (error) {
323
- setCollapsedStatusError(
324
- error instanceof Error ? error.message : "Unexpected error"
188
+ setCollapsedActionError(
189
+ `Couldn't update comment: ${error instanceof Error ? error.message : "Unexpected error"}`
325
190
  );
326
191
  }
327
- };
328
- const body = /* @__PURE__ */ jsxRuntime.jsx(
329
- CommentBody,
330
- {
331
- ...props,
332
- ...expanded ? { onCollapse: () => setExpanded(false) } : {}
192
+ }
193
+ async function copyCollapsed() {
194
+ var _a;
195
+ setCollapsedActionError("");
196
+ try {
197
+ await ((_a = props.onCopy) == null ? void 0 : _a.call(props));
198
+ } catch (error) {
199
+ setCollapsedActionError(
200
+ `Couldn't copy comment: ${error instanceof Error ? error.message : "Unexpected error"}`
201
+ );
333
202
  }
334
- );
203
+ }
335
204
  if (!expanded) {
336
205
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
337
206
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -358,27 +227,10 @@ function CommentCard(props) {
358
227
  /* @__PURE__ */ jsxRuntime.jsx(CommentAuthor.CommentAuthorAvatar, { author: comment.author, bare: true }),
359
228
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-[11px] font-semibold text-foreground", children: commentUtils.authorDisplayName(comment.author) }),
360
229
  !isReply && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
361
- /* @__PURE__ */ jsxRuntime.jsx(RatingChip, { rating: comment.rating }),
362
- /* @__PURE__ */ jsxRuntime.jsx(FacetBadges, { comment, config, compact: true })
230
+ /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.RatingChip, { rating: comment.rating }),
231
+ /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.FacetBadges, { comment, config, compact: true })
363
232
  ] }),
364
233
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-[12px] text-foreground/80", children: comment.body.trim() ? commentUtils.truncatePlain(comment.body) : comment.rating === "positive" ? "Positive rating" : comment.rating === "negative" ? "Negative rating" : "Empty comment" }),
365
- canComplete && /* @__PURE__ */ jsxRuntime.jsx(
366
- "span",
367
- {
368
- role: "presentation",
369
- onClick: (e) => e.stopPropagation(),
370
- onKeyDown: (e) => e.stopPropagation(),
371
- children: /* @__PURE__ */ jsxRuntime.jsx(
372
- IconAction,
373
- {
374
- icon: UiCheck.UiCheck,
375
- label: completionActionLabel(completion),
376
- onClick: () => void completeCollapsed(),
377
- className: "hover:bg-green-100 hover:text-green-700 dark:hover:bg-green-500/20 dark:hover:text-green-300"
378
- }
379
- )
380
- }
381
- ),
382
234
  /* @__PURE__ */ jsxRuntime.jsx(
383
235
  "span",
384
236
  {
@@ -399,16 +251,15 @@ function CommentCard(props) {
399
251
  children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiDotsVertical.UiDotsVertical, className: "text-sm" })
400
252
  }
401
253
  ),
402
- items: [
403
- { label: "Expand", onSelect: () => setExpanded(true) },
404
- ...onMaximize ? [
405
- {
406
- label: "Maximize",
407
- icon: UiFullscreen.UiFullscreen,
408
- onSelect: onMaximize
409
- }
410
- ] : []
411
- ]
254
+ items: actionMenuItems(isReply, {
255
+ ...props.onDelete ? { onDelete: props.onDelete } : {},
256
+ ...!isReply && props.onUpdateStatus && completionStatus(config) ? {
257
+ onToggleStatus: () => void updateCollapsedStatus(),
258
+ toggleStatusLabel: comment.status == null || commentUtils.isUnresolved(config, comment.status) ? "Resolve" : "Reopen"
259
+ } : {},
260
+ ...props.onCopy ? { onCopy: () => void copyCollapsed() } : {},
261
+ ...props.onMaximize ? { onMaximize: props.onMaximize } : {}
262
+ })
412
263
  }
413
264
  )
414
265
  }
@@ -416,15 +267,12 @@ function CommentCard(props) {
416
267
  ]
417
268
  }
418
269
  ),
419
- collapsedStatusError && /* @__PURE__ */ jsxRuntime.jsxs(
270
+ collapsedActionError && /* @__PURE__ */ jsxRuntime.jsx(
420
271
  "div",
421
272
  {
422
273
  role: "alert",
423
274
  className: "rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive",
424
- children: [
425
- "Couldn't update comment: ",
426
- collapsedStatusError
427
- ]
275
+ children: collapsedActionError
428
276
  }
429
277
  )
430
278
  ] });
@@ -1 +1 @@
1
- {"version":3,"file":"CommentCard.cjs","sources":["../../src/comments/CommentCard.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Badge } from \"../data/Badge\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { DropdownMenu, type DropdownMenuItem } from \"../overlay/DropdownMenu\";\nimport {\n parseTimestamp,\n formatRelativeTime,\n} from \"../data/cells/timestamp-format\";\nimport {\n UiCheck,\n UiClose,\n UiDotsVertical,\n UiFullscreen,\n UiThumbsDown,\n UiThumbsUp,\n UiTrash,\n} from \"../icons\";\nimport { CommentAuthorAvatar } from \"./CommentAuthor\";\nimport { CommentMarkdown } from \"./CommentMarkdown\";\nimport {\n authorDisplayName,\n isUnresolved,\n resolveFacetOption,\n resolveStatusConfig,\n toneToBadgeTone,\n truncatePlain,\n} from \"./comment-utils\";\nimport type {\n Comment,\n CommentConfig,\n CommentRating,\n CommentStatusConfig,\n} from \"./comment-types\";\n\nfunction completionStatus(config: CommentConfig) {\n for (const completionName of [\"resolved\", \"closed\"]) {\n const status = config.statuses.find(\n (candidate) =>\n candidate.value.toLowerCase() === completionName ||\n candidate.label.toLowerCase() === completionName,\n );\n if (status) return status;\n }\n return undefined;\n}\n\nfunction completionActionLabel(status: CommentStatusConfig) {\n return status.value.toLowerCase() === \"closed\" ||\n status.label.toLowerCase() === \"closed\"\n ? \"Close comment\"\n : \"Resolve comment\";\n}\n\nexport type CommentCardProps = {\n comment: Comment;\n config: CommentConfig;\n /** Tighter padding for dense rails. */\n compact?: boolean;\n /** Render expanded on first mount. */\n defaultExpanded?: boolean;\n /** Custom body renderer; defaults to the lightweight CommentMarkdown. */\n renderBody?: (body: string) => ReactNode;\n /** Change this comment's status (roots only). */\n onUpdateStatus?: (status: string) => void | Promise<void>;\n /** Delete this comment. */\n onDelete?: () => void;\n /** Begin a reply to this comment. */\n onReply?: () => void;\n /** Advance the checklist item at `index` to its next status. */\n onChecklistToggle?: (index: number) => void;\n /**\n * Open this card's thread in a larger view. Owned by the caller because a\n * card knows only itself, while maximizing shows the root and its replies.\n * Omit to drop the action (e.g. inside an already-maximized thread).\n */\n onMaximize?: () => void;\n};\n\nfunction RatingChip({ rating }: { rating: CommentRating | undefined }) {\n if (!rating) return null;\n const positive = rating === \"positive\";\n return (\n <Badge\n variant=\"soft\"\n tone={positive ? \"success\" : \"danger\"}\n size=\"xs\"\n icon={positive ? UiThumbsUp : UiThumbsDown}\n >\n {positive ? \"Positive rating\" : \"Negative rating\"}\n </Badge>\n );\n}\n\nfunction StatusChip({\n status,\n config,\n showLabel,\n}: {\n status: string | undefined;\n config: CommentConfig;\n showLabel?: boolean;\n}) {\n const cfg = resolveStatusConfig(config, status);\n if (!cfg) return null;\n return (\n <Badge\n variant=\"soft\"\n tone={toneToBadgeTone(cfg.tone)}\n size=\"xs\"\n {...(cfg.icon ? { icon: cfg.icon } : {})}\n >\n {showLabel ? cfg.label : null}\n </Badge>\n );\n}\n\nfunction FacetBadges({\n comment,\n config,\n compact,\n}: {\n comment: Comment;\n config: CommentConfig;\n compact?: boolean;\n}) {\n const facets = config.facets ?? [];\n const chips = facets.flatMap((facet) => {\n const value = comment.facets?.[facet.key];\n const option = resolveFacetOption(facet, value);\n if (!option) return [];\n return [\n <Badge\n key={facet.key}\n variant=\"soft\"\n tone={toneToBadgeTone(option.tone)}\n size=\"xs\"\n >\n {compact ? (option.short ?? option.label) : option.label}\n </Badge>,\n ];\n });\n return chips.length > 0 ? <>{chips}</> : null;\n}\n\nfunction ChecklistChips({\n comment,\n onChecklistToggle,\n}: {\n comment: Comment;\n onChecklistToggle?: (index: number) => void;\n}) {\n if (!comment.checklist || comment.checklist.length === 0) return null;\n return (\n <div className=\"flex flex-wrap gap-1\">\n {comment.checklist.map((item, i) => {\n const done = /done|complete|resolved/i.test(item.status);\n const active = /progress|doing/i.test(item.status);\n return (\n <button\n key={i}\n type=\"button\"\n onClick={() => onChecklistToggle?.(i)}\n disabled={!onChecklistToggle}\n className={cn(\n \"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] transition-opacity\",\n onChecklistToggle && \"cursor-pointer hover:opacity-80\",\n done\n ? \"bg-green-100 text-green-800 line-through dark:bg-green-500/20 dark:text-green-300\"\n : active\n ? \"bg-yellow-100 text-yellow-800 dark:bg-yellow-500/20 dark:text-yellow-300\"\n : \"bg-muted text-muted-foreground\",\n )}\n >\n {item.label}\n </button>\n );\n })}\n </div>\n );\n}\n\nfunction RefChips({ comment }: { comment: Comment }) {\n if (!comment.refs || comment.refs.length === 0) return null;\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {comment.refs.map((group, i) => (\n <span\n key={i}\n className=\"inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground\"\n >\n <span className=\"font-medium\">{group.label}:</span>\n <span className={group.mono ? \"font-mono\" : undefined}>\n {group.items.join(\", \")}\n </span>\n </span>\n ))}\n </div>\n );\n}\n\nfunction statusMenuItems(\n comment: Comment,\n config: CommentConfig,\n isReply: boolean,\n opts: {\n onUpdateStatus?: (status: string) => void;\n onDelete?: () => void;\n onCollapse?: () => void;\n onMaximize?: () => void;\n completionStatus?: string;\n },\n): DropdownMenuItem[] {\n const items: DropdownMenuItem[] = [];\n if (opts.onMaximize) {\n items.push({\n label: \"Maximize\",\n icon: UiFullscreen,\n onSelect: opts.onMaximize,\n });\n }\n if (opts.onCollapse) {\n items.push({ label: \"Collapse\", icon: UiClose, onSelect: opts.onCollapse });\n }\n if (!isReply && opts.onUpdateStatus) {\n for (const status of config.statuses) {\n if (status.value === opts.completionStatus) continue;\n const current = status.value === comment.status;\n items.push({\n label: (\n <span\n className={cn(\n \"inline-flex items-center gap-1.5\",\n current && \"font-semibold\",\n )}\n >\n <StatusChip status={status.value} config={config} />\n {status.label}\n </span>\n ),\n onSelect: () => opts.onUpdateStatus?.(status.value),\n });\n }\n }\n if (opts.onDelete) {\n items.push({ label: \"Delete\", icon: UiTrash, onSelect: opts.onDelete });\n }\n return items;\n}\n\nfunction IconAction({\n icon,\n label,\n onClick,\n className,\n}: {\n icon: StaticIconComponent;\n label: string;\n onClick: () => void;\n className?: string;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={label}\n title={label}\n className={cn(\n \"rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\",\n className,\n )}\n >\n <Icon icon={icon} className=\"text-sm\" />\n </button>\n );\n}\n\nfunction CommentBody({\n comment,\n config,\n renderBody,\n onUpdateStatus,\n onDelete,\n onReply,\n onChecklistToggle,\n onCollapse,\n onMaximize,\n}: CommentCardProps & { onCollapse?: () => void }) {\n const [statusError, setStatusError] = useState(\"\");\n const isReply = Boolean(comment.parentId);\n const date = parseTimestamp(comment.createdAt);\n const completion = completionStatus(config);\n const canComplete =\n !isReply &&\n onUpdateStatus != null &&\n completion != null &&\n isUnresolved(config, comment.status);\n const updateStatus = async (status: string) => {\n setStatusError(\"\");\n try {\n await onUpdateStatus?.(status);\n } catch (error) {\n setStatusError(\n error instanceof Error ? error.message : \"Unexpected error\",\n );\n }\n };\n const menuItems = statusMenuItems(comment, config, isReply, {\n ...(onUpdateStatus\n ? { onUpdateStatus: (status) => void updateStatus(status) }\n : {}),\n ...(onDelete ? { onDelete } : {}),\n ...(onCollapse ? { onCollapse } : {}),\n ...(onMaximize ? { onMaximize } : {}),\n ...(completion ? { completionStatus: completion.value } : {}),\n });\n\n return (\n <div className=\"space-y-2.5\">\n <div className=\"flex items-start gap-2.5\">\n <CommentAuthorAvatar author={comment.author} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-baseline gap-1.5\">\n <span className=\"truncate text-xs font-semibold text-foreground\">\n {authorDisplayName(comment.author)}\n </span>\n {date && (\n <span\n title={date.toLocaleString()}\n className=\"shrink-0 text-[10px] text-muted-foreground\"\n >\n {formatRelativeTime(date)}\n </span>\n )}\n </div>\n {!isReply && (\n <div className=\"mt-1 flex flex-wrap gap-1\">\n <RatingChip rating={comment.rating} />\n <FacetBadges comment={comment} config={config} />\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-0.5\">\n {canComplete && (\n <IconAction\n icon={UiCheck}\n label={completionActionLabel(completion)}\n onClick={() => void updateStatus(completion.value)}\n className=\"hover:bg-green-100 hover:text-green-700 dark:hover:bg-green-500/20 dark:hover:text-green-300\"\n />\n )}\n {menuItems.length > 0 && (\n <DropdownMenu\n align=\"right\"\n menuClassName=\"min-w-[150px]\"\n trigger={\n <span\n role=\"button\"\n tabIndex={0}\n aria-label=\"Comment actions\"\n className=\"inline-flex rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </span>\n }\n items={menuItems}\n />\n )}\n </div>\n </div>\n {comment.body.trim() && (\n <div className=\"text-[13px] leading-5 text-foreground\">\n {renderBody ? (\n renderBody(comment.body)\n ) : (\n <CommentMarkdown text={comment.body} />\n )}\n </div>\n )}\n {statusError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive\"\n >\n Couldn't update comment: {statusError}\n </div>\n )}\n {!isReply && (\n <ChecklistChips\n comment={comment}\n {...(onChecklistToggle ? { onChecklistToggle } : {})}\n />\n )}\n {!isReply && <RefChips comment={comment} />}\n {onReply && (\n <button\n type=\"button\"\n onClick={onReply}\n className=\"-ml-2 rounded px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-primary/10\"\n >\n Reply\n </button>\n )}\n </div>\n );\n}\n\n/**\n * A single comment, collapsible between a compact one-line preview and a full\n * card. Offers a Maximize action when `onMaximize` is supplied — the owner\n * enlarges the whole thread. Fully controlled — all mutations are delegated to\n * the supplied callbacks.\n */\nexport function CommentCard(props: CommentCardProps) {\n const { comment, config, compact, defaultExpanded, onMaximize } = props;\n const [expanded, setExpanded] = useState(defaultExpanded ?? false);\n const [collapsedStatusError, setCollapsedStatusError] = useState(\"\");\n const isReply = Boolean(comment.parentId);\n const completion = completionStatus(config);\n const canComplete =\n !isReply &&\n props.onUpdateStatus != null &&\n completion != null &&\n isUnresolved(config, comment.status);\n const completeCollapsed = async () => {\n if (!completion) return;\n setCollapsedStatusError(\"\");\n try {\n await props.onUpdateStatus?.(completion.value);\n } catch (error) {\n setCollapsedStatusError(\n error instanceof Error ? error.message : \"Unexpected error\",\n );\n }\n };\n\n const body = (\n <CommentBody\n {...props}\n {...(expanded ? { onCollapse: () => setExpanded(false) } : {})}\n />\n );\n\n if (!expanded) {\n return (\n <>\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => setExpanded(true)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n setExpanded(true);\n }\n }}\n data-testid=\"comment-card\"\n data-comment-kind={isReply ? \"reply\" : \"root\"}\n data-comment-id={comment.id}\n className={cn(\n \"group/comment flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-xs transition-colors hover:bg-muted/50\",\n !isReply && \"border border-border bg-background shadow-sm\",\n isReply && \"ml-4 border-l-2 border-l-border bg-muted/30\",\n )}\n >\n <CommentAuthorAvatar author={comment.author} bare />\n <span className=\"shrink-0 text-[11px] font-semibold text-foreground\">\n {authorDisplayName(comment.author)}\n </span>\n {!isReply && (\n <>\n <RatingChip rating={comment.rating} />\n <FacetBadges comment={comment} config={config} compact />\n </>\n )}\n <span className=\"min-w-0 flex-1 truncate text-[12px] text-foreground/80\">\n {comment.body.trim()\n ? truncatePlain(comment.body)\n : comment.rating === \"positive\"\n ? \"Positive rating\"\n : comment.rating === \"negative\"\n ? \"Negative rating\"\n : \"Empty comment\"}\n </span>\n {canComplete && (\n <span\n role=\"presentation\"\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n >\n <IconAction\n icon={UiCheck}\n label={completionActionLabel(completion)}\n onClick={() => void completeCollapsed()}\n className=\"hover:bg-green-100 hover:text-green-700 dark:hover:bg-green-500/20 dark:hover:text-green-300\"\n />\n </span>\n )}\n <span\n role=\"presentation\"\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n >\n <DropdownMenu\n align=\"right\"\n trigger={\n <span\n role=\"button\"\n tabIndex={0}\n aria-label=\"Comment actions\"\n className=\"rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:opacity-100 group-hover/comment:opacity-70\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </span>\n }\n items={[\n { label: \"Expand\", onSelect: () => setExpanded(true) },\n ...(onMaximize\n ? [\n {\n label: \"Maximize\",\n icon: UiFullscreen,\n onSelect: onMaximize,\n },\n ]\n : []),\n ]}\n />\n </span>\n </div>\n {collapsedStatusError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive\"\n >\n Couldn't update comment: {collapsedStatusError}\n </div>\n )}\n </>\n );\n }\n\n return (\n <>\n <div\n data-testid=\"comment-card\"\n data-comment-kind={isReply ? \"reply\" : \"root\"}\n data-comment-id={comment.id}\n className={cn(\n \"group/comment rounded-xl bg-background px-3.5 py-3 text-xs\",\n !isReply &&\n \"border border-border/80 shadow-sm transition-shadow hover:shadow-md\",\n compact && \"px-2.5 py-2.5\",\n isReply && \"ml-4 border-l-2 border-l-border bg-muted/20 shadow-none\",\n )}\n >\n {body}\n </div>\n </>\n );\n}\n"],"names":["jsx","Badge","UiThumbsUp","UiThumbsDown","resolveStatusConfig","toneToBadgeTone","resolveFacetOption","Fragment","cn","jsxs","UiFullscreen","UiClose","UiTrash","Icon","useState","parseTimestamp","isUnresolved","CommentAuthorAvatar","authorDisplayName","UiCheck","DropdownMenu","UiDotsVertical","CommentMarkdown","truncatePlain"],"mappings":"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"}
1
+ {"version":3,"file":"CommentCard.cjs","sources":["../../src/comments/CommentCard.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { DropdownMenu, type DropdownMenuItem } from \"../overlay/DropdownMenu\";\nimport {\n parseTimestamp,\n formatRelativeTime,\n} from \"../data/cells/timestamp-format\";\nimport {\n UiCheck,\n UiCopy,\n UiDotsVertical,\n UiFullscreen,\n UiRestart,\n UiTrash,\n} from \"../icons\";\nimport { CommentAuthorAvatar } from \"./CommentAuthor\";\nimport {\n ChecklistChips,\n FacetBadges,\n RatingChip,\n RefChips,\n} from \"./CommentCardParts\";\nimport { CommentMarkdown } from \"./CommentMarkdown\";\nimport {\n authorDisplayName,\n isUnresolved,\n truncatePlain,\n} from \"./comment-utils\";\nimport type { Comment, CommentConfig } from \"./comment-types\";\n\nfunction completionStatus(config: CommentConfig) {\n for (const completionName of [\"resolved\", \"closed\"]) {\n const status = config.statuses.find(\n (candidate) =>\n candidate.value.toLowerCase() === completionName ||\n candidate.label.toLowerCase() === completionName,\n );\n if (status) return status;\n }\n return undefined;\n}\n\nfunction reopenStatus(config: CommentConfig) {\n return config.statuses.find((status) => status.unresolved);\n}\n\nexport type CommentCardProps = {\n comment: Comment;\n config: CommentConfig;\n /** Tighter padding for dense rails. */\n compact?: boolean;\n /** Render expanded on first mount. */\n defaultExpanded?: boolean;\n /** Custom body renderer; defaults to the lightweight CommentMarkdown. */\n renderBody?: (body: string) => ReactNode;\n /** Change this comment's status (roots only). */\n onUpdateStatus?: (status: string) => void | Promise<void>;\n /** Delete this comment. */\n onDelete?: () => void;\n /** Copy this comment's whole thread in the caller's canonical format. */\n onCopy?: () => void | Promise<void>;\n /** Begin a reply to this comment. */\n onReply?: () => void;\n /** Advance the checklist item at `index` to its next status. */\n onChecklistToggle?: (index: number) => void;\n /**\n * Open this card's thread in a larger view. Owned by the caller because a\n * card knows only itself, while maximizing shows the root and its replies.\n * Omit to drop the action (e.g. inside an already-maximized thread).\n */\n onMaximize?: () => void;\n};\n\nfunction actionMenuItems(\n isReply: boolean,\n opts: {\n onDelete?: () => void;\n onToggleStatus?: () => void;\n toggleStatusLabel?: \"Resolve\" | \"Reopen\";\n onCopy?: () => void;\n onMaximize?: () => void;\n },\n): DropdownMenuItem[] {\n const items: DropdownMenuItem[] = [];\n if (opts.onDelete) {\n items.push({ label: \"Delete\", icon: UiTrash, onSelect: opts.onDelete });\n }\n if (!isReply && opts.onToggleStatus && opts.toggleStatusLabel) {\n items.push({\n label: opts.toggleStatusLabel,\n icon: opts.toggleStatusLabel === \"Resolve\" ? UiCheck : UiRestart,\n onSelect: opts.onToggleStatus,\n });\n }\n if (opts.onCopy) {\n items.push({ label: \"Copy\", icon: UiCopy, onSelect: opts.onCopy });\n }\n if (opts.onMaximize) {\n items.push({\n label: \"Maximise\",\n icon: UiFullscreen,\n onSelect: opts.onMaximize,\n });\n }\n return items;\n}\n\nfunction CommentBody({\n comment,\n config,\n renderBody,\n onUpdateStatus,\n onDelete,\n onCopy,\n onReply,\n onChecklistToggle,\n onMaximize,\n}: CommentCardProps) {\n const [actionError, setActionError] = useState(\"\");\n const isReply = Boolean(comment.parentId);\n const date = parseTimestamp(comment.createdAt);\n const completion = completionStatus(config);\n const reopen = reopenStatus(config);\n const unresolved =\n comment.status == null || isUnresolved(config, comment.status);\n const updateStatus = async (status: string) => {\n setActionError(\"\");\n try {\n await onUpdateStatus?.(status);\n } catch (error) {\n setActionError(\n `Couldn't update comment: ${error instanceof Error ? error.message : \"Unexpected error\"}`,\n );\n }\n };\n const copy = async () => {\n setActionError(\"\");\n try {\n await onCopy?.();\n } catch (error) {\n setActionError(\n `Couldn't copy comment: ${error instanceof Error ? error.message : \"Unexpected error\"}`,\n );\n }\n };\n const menuItems = actionMenuItems(isReply, {\n ...(onDelete ? { onDelete } : {}),\n ...(!isReply && onUpdateStatus && completion && unresolved\n ? {\n onToggleStatus: () => void updateStatus(completion.value),\n toggleStatusLabel: \"Resolve\" as const,\n }\n : {}),\n ...(!isReply && onUpdateStatus && reopen && !unresolved\n ? {\n onToggleStatus: () => void updateStatus(reopen.value),\n toggleStatusLabel: \"Reopen\" as const,\n }\n : {}),\n ...(onCopy ? { onCopy: () => void copy() } : {}),\n ...(onMaximize ? { onMaximize } : {}),\n });\n\n return (\n <div className=\"space-y-2.5\">\n <div className=\"flex items-start gap-2.5\">\n <CommentAuthorAvatar author={comment.author} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-baseline gap-1.5\">\n <span className=\"truncate text-xs font-semibold text-foreground\">\n {authorDisplayName(comment.author)}\n </span>\n {date && (\n <span\n title={date.toLocaleString()}\n className=\"shrink-0 text-[10px] text-muted-foreground\"\n >\n {formatRelativeTime(date)}\n </span>\n )}\n </div>\n {!isReply && (\n <div className=\"mt-1 flex flex-wrap gap-1\">\n <RatingChip rating={comment.rating} />\n <FacetBadges comment={comment} config={config} />\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-0.5\">\n {menuItems.length > 0 && (\n <DropdownMenu\n align=\"right\"\n menuClassName=\"min-w-[150px]\"\n trigger={\n <span\n role=\"button\"\n tabIndex={0}\n aria-label=\"Comment actions\"\n className=\"inline-flex rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </span>\n }\n items={menuItems}\n />\n )}\n </div>\n </div>\n {comment.body.trim() && (\n <div className=\"text-[13px] leading-5 text-foreground\">\n {renderBody ? (\n renderBody(comment.body)\n ) : (\n <CommentMarkdown text={comment.body} />\n )}\n </div>\n )}\n {actionError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive\"\n >\n {actionError}\n </div>\n )}\n {!isReply && (\n <ChecklistChips\n comment={comment}\n {...(onChecklistToggle ? { onChecklistToggle } : {})}\n />\n )}\n {!isReply && <RefChips comment={comment} />}\n {onReply && (\n <button\n type=\"button\"\n onClick={onReply}\n className=\"-ml-2 rounded px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-primary/10\"\n >\n Reply\n </button>\n )}\n </div>\n );\n}\n\n/**\n * A single comment, collapsible between a compact one-line preview and a full\n * card. Offers a Maximise action when `onMaximize` is supplied — the owner\n * enlarges the whole thread. Fully controlled — all mutations are delegated to\n * the supplied callbacks.\n */\nexport function CommentCard(props: CommentCardProps) {\n const { comment, config, compact, defaultExpanded } = props;\n const [expanded, setExpanded] = useState(defaultExpanded ?? false);\n const [collapsedActionError, setCollapsedActionError] = useState(\"\");\n const isReply = Boolean(comment.parentId);\n const body = <CommentBody {...props} />;\n\n async function updateCollapsedStatus() {\n const completion = completionStatus(config);\n const unresolved =\n comment.status == null || isUnresolved(config, comment.status);\n const next = unresolved ? completion : reopenStatus(config);\n if (!next) return;\n setCollapsedActionError(\"\");\n try {\n await props.onUpdateStatus?.(next.value);\n } catch (error) {\n setCollapsedActionError(\n `Couldn't update comment: ${error instanceof Error ? error.message : \"Unexpected error\"}`,\n );\n }\n }\n\n async function copyCollapsed() {\n setCollapsedActionError(\"\");\n try {\n await props.onCopy?.();\n } catch (error) {\n setCollapsedActionError(\n `Couldn't copy comment: ${error instanceof Error ? error.message : \"Unexpected error\"}`,\n );\n }\n }\n\n if (!expanded) {\n return (\n <>\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => setExpanded(true)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n setExpanded(true);\n }\n }}\n data-testid=\"comment-card\"\n data-comment-kind={isReply ? \"reply\" : \"root\"}\n data-comment-id={comment.id}\n className={cn(\n \"group/comment flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-xs transition-colors hover:bg-muted/50\",\n !isReply && \"border border-border bg-background shadow-sm\",\n isReply && \"ml-4 border-l-2 border-l-border bg-muted/30\",\n )}\n >\n <CommentAuthorAvatar author={comment.author} bare />\n <span className=\"shrink-0 text-[11px] font-semibold text-foreground\">\n {authorDisplayName(comment.author)}\n </span>\n {!isReply && (\n <>\n <RatingChip rating={comment.rating} />\n <FacetBadges comment={comment} config={config} compact />\n </>\n )}\n <span className=\"min-w-0 flex-1 truncate text-[12px] text-foreground/80\">\n {comment.body.trim()\n ? truncatePlain(comment.body)\n : comment.rating === \"positive\"\n ? \"Positive rating\"\n : comment.rating === \"negative\"\n ? \"Negative rating\"\n : \"Empty comment\"}\n </span>\n <span\n role=\"presentation\"\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n >\n <DropdownMenu\n align=\"right\"\n trigger={\n <span\n role=\"button\"\n tabIndex={0}\n aria-label=\"Comment actions\"\n className=\"rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:opacity-100 group-hover/comment:opacity-70\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </span>\n }\n items={actionMenuItems(isReply, {\n ...(props.onDelete ? { onDelete: props.onDelete } : {}),\n ...(!isReply && props.onUpdateStatus && completionStatus(config)\n ? {\n onToggleStatus: () => void updateCollapsedStatus(),\n toggleStatusLabel:\n comment.status == null ||\n isUnresolved(config, comment.status)\n ? \"Resolve\"\n : \"Reopen\",\n }\n : {}),\n ...(props.onCopy ? { onCopy: () => void copyCollapsed() } : {}),\n ...(props.onMaximize ? { onMaximize: props.onMaximize } : {}),\n })}\n />\n </span>\n </div>\n {collapsedActionError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive\"\n >\n {collapsedActionError}\n </div>\n )}\n </>\n );\n }\n\n return (\n <>\n <div\n data-testid=\"comment-card\"\n data-comment-kind={isReply ? \"reply\" : \"root\"}\n data-comment-id={comment.id}\n className={cn(\n \"group/comment rounded-xl bg-background px-3.5 py-3 text-xs\",\n !isReply &&\n \"border border-border/80 shadow-sm transition-shadow hover:shadow-md\",\n compact && \"px-2.5 py-2.5\",\n isReply && \"ml-4 border-l-2 border-l-border bg-muted/20 shadow-none\",\n )}\n >\n {body}\n </div>\n </>\n );\n}\n"],"names":["UiTrash","UiCheck","UiRestart","UiCopy","UiFullscreen","useState","parseTimestamp","isUnresolved","jsxs","jsx","CommentAuthorAvatar","authorDisplayName","RatingChip","FacetBadges","DropdownMenu","Icon","UiDotsVertical","CommentMarkdown","ChecklistChips","RefChips","Fragment","cn","truncatePlain"],"mappings":";;;;;;;;;;;;;;;;;;AA+BA,SAAS,iBAAiB,QAAuB;AAC/C,aAAW,kBAAkB,CAAC,YAAY,QAAQ,GAAG;AACnD,UAAM,SAAS,OAAO,SAAS;AAAA,MAC7B,CAAC,cACC,UAAU,MAAM,YAAA,MAAkB,kBAClC,UAAU,MAAM,kBAAkB;AAAA,IAAA;AAEtC,QAAI,OAAQ,QAAO;AAAA,EACrB;AACA,SAAO;AACT;AAEA,SAAS,aAAa,QAAuB;AAC3C,SAAO,OAAO,SAAS,KAAK,CAAC,WAAW,OAAO,UAAU;AAC3D;AA6BA,SAAS,gBACP,SACA,MAOoB;AACpB,QAAM,QAA4B,CAAA;AAClC,MAAI,KAAK,UAAU;AACjB,UAAM,KAAK,EAAE,OAAO,UAAU,MAAMA,QAAAA,SAAS,UAAU,KAAK,UAAU;AAAA,EACxE;AACA,MAAI,CAAC,WAAW,KAAK,kBAAkB,KAAK,mBAAmB;AAC7D,UAAM,KAAK;AAAA,MACT,OAAO,KAAK;AAAA,MACZ,MAAM,KAAK,sBAAsB,YAAYC,QAAAA,UAAUC,UAAAA;AAAAA,MACvD,UAAU,KAAK;AAAA,IAAA,CAChB;AAAA,EACH;AACA,MAAI,KAAK,QAAQ;AACf,UAAM,KAAK,EAAE,OAAO,QAAQ,MAAMC,OAAAA,QAAQ,UAAU,KAAK,QAAQ;AAAA,EACnE;AACA,MAAI,KAAK,YAAY;AACnB,UAAM,KAAK;AAAA,MACT,OAAO;AAAA,MACP,MAAMC,aAAAA;AAAAA,MACN,UAAU,KAAK;AAAA,IAAA,CAChB;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,CAAC,aAAa,cAAc,IAAIC,MAAAA,SAAS,EAAE;AACjD,QAAM,UAAU,QAAQ,QAAQ,QAAQ;AACxC,QAAM,OAAOC,gBAAAA,eAAe,QAAQ,SAAS;AAC7C,QAAM,aAAa,iBAAiB,MAAM;AAC1C,QAAM,SAAS,aAAa,MAAM;AAClC,QAAM,aACJ,QAAQ,UAAU,QAAQC,aAAAA,aAAa,QAAQ,QAAQ,MAAM;AAC/D,QAAM,eAAe,OAAO,WAAmB;AAC7C,mBAAe,EAAE;AACjB,QAAI;AACF,aAAM,iDAAiB;AAAA,IACzB,SAAS,OAAO;AACd;AAAA,QACE,4BAA4B,iBAAiB,QAAQ,MAAM,UAAU,kBAAkB;AAAA,MAAA;AAAA,IAE3F;AAAA,EACF;AACA,QAAM,OAAO,YAAY;AACvB,mBAAe,EAAE;AACjB,QAAI;AACF,aAAM;AAAA,IACR,SAAS,OAAO;AACd;AAAA,QACE,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,kBAAkB;AAAA,MAAA;AAAA,IAEzF;AAAA,EACF;AACA,QAAM,YAAY,gBAAgB,SAAS;AAAA,IACzC,GAAI,WAAW,EAAE,SAAA,IAAa,CAAA;AAAA,IAC9B,GAAI,CAAC,WAAW,kBAAkB,cAAc,aAC5C;AAAA,MACE,gBAAgB,MAAM,KAAK,aAAa,WAAW,KAAK;AAAA,MACxD,mBAAmB;AAAA,IAAA,IAErB,CAAA;AAAA,IACJ,GAAI,CAAC,WAAW,kBAAkB,UAAU,CAAC,aACzC;AAAA,MACE,gBAAgB,MAAM,KAAK,aAAa,OAAO,KAAK;AAAA,MACpD,mBAAmB;AAAA,IAAA,IAErB,CAAA;AAAA,IACJ,GAAI,SAAS,EAAE,QAAQ,MAAM,KAAK,KAAA,EAAK,IAAM,CAAA;AAAA,IAC7C,GAAI,aAAa,EAAE,eAAe,CAAA;AAAA,EAAC,CACpC;AAED,SACEC,2BAAAA,KAAC,OAAA,EAAI,WAAU,eACb,UAAA;AAAA,IAAAA,2BAAAA,KAAC,OAAA,EAAI,WAAU,4BACb,UAAA;AAAA,MAAAC,2BAAAA,IAACC,cAAAA,qBAAA,EAAoB,QAAQ,QAAQ,OAAA,CAAQ;AAAA,MAC7CF,2BAAAA,KAAC,OAAA,EAAI,WAAU,kBACb,UAAA;AAAA,QAAAA,2BAAAA,KAAC,OAAA,EAAI,WAAU,uCACb,UAAA;AAAA,UAAAC,+BAAC,UAAK,WAAU,kDACb,UAAAE,+BAAkB,QAAQ,MAAM,GACnC;AAAA,UACC,QACCF,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,OAAO,KAAK,eAAA;AAAA,cACZ,WAAU;AAAA,cAET,6CAAmB,IAAI;AAAA,YAAA;AAAA,UAAA;AAAA,QAC1B,GAEJ;AAAA,QACC,CAAC,WACAD,2BAAAA,KAAC,OAAA,EAAI,WAAU,6BACb,UAAA;AAAA,UAAAC,2BAAAA,IAACG,iBAAAA,YAAA,EAAW,QAAQ,QAAQ,OAAA,CAAQ;AAAA,UACpCH,2BAAAA,IAACI,iBAAAA,aAAA,EAAY,SAAkB,OAAA,CAAgB;AAAA,QAAA,EAAA,CACjD;AAAA,MAAA,GAEJ;AAAA,qCACC,OAAA,EAAI,WAAU,sCACZ,UAAA,UAAU,SAAS,KAClBJ,2BAAAA;AAAAA,QAACK,aAAAA;AAAAA,QAAA;AAAA,UACC,OAAM;AAAA,UACN,eAAc;AAAA,UACd,SACEL,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,UAAU;AAAA,cACV,cAAW;AAAA,cACX,WAAU;AAAA,cAEV,UAAAA,2BAAAA,IAACM,WAAA,EAAK,MAAMC,eAAAA,gBAAgB,WAAU,UAAA,CAAU;AAAA,YAAA;AAAA,UAAA;AAAA,UAGpD,OAAO;AAAA,QAAA;AAAA,MAAA,EACT,CAEJ;AAAA,IAAA,GACF;AAAA,IACC,QAAQ,KAAK,KAAA,KACZP,2BAAAA,IAAC,OAAA,EAAI,WAAU,yCACZ,UAAA,aACC,WAAW,QAAQ,IAAI,IAEvBA,2BAAAA,IAACQ,gBAAAA,mBAAgB,MAAM,QAAQ,MAAM,GAEzC;AAAA,IAED,eACCR,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QAET,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAGJ,CAAC,WACAA,2BAAAA;AAAAA,MAACS,iBAAAA;AAAAA,MAAA;AAAA,QACC;AAAA,QACC,GAAI,oBAAoB,EAAE,sBAAsB,CAAA;AAAA,MAAC;AAAA,IAAA;AAAA,IAGrD,CAAC,WAAWT,2BAAAA,IAACU,2BAAA,EAAS,QAAA,CAAkB;AAAA,IACxC,WACCV,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QACX,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAED,GAEJ;AAEJ;AAQO,SAAS,YAAY,OAAyB;AACnD,QAAM,EAAE,SAAS,QAAQ,SAAS,oBAAoB;AACtD,QAAM,CAAC,UAAU,WAAW,IAAIJ,MAAAA,SAAS,mBAAmB,KAAK;AACjE,QAAM,CAAC,sBAAsB,uBAAuB,IAAIA,MAAAA,SAAS,EAAE;AACnE,QAAM,UAAU,QAAQ,QAAQ,QAAQ;AACxC,QAAM,OAAOI,2BAAAA,IAAC,aAAA,EAAa,GAAG,MAAA,CAAO;AAErC,iBAAe,wBAAwB;;AACrC,UAAM,aAAa,iBAAiB,MAAM;AAC1C,UAAM,aACJ,QAAQ,UAAU,QAAQF,aAAAA,aAAa,QAAQ,QAAQ,MAAM;AAC/D,UAAM,OAAO,aAAa,aAAa,aAAa,MAAM;AAC1D,QAAI,CAAC,KAAM;AACX,4BAAwB,EAAE;AAC1B,QAAI;AACF,cAAM,WAAM,mBAAN,+BAAuB,KAAK;AAAA,IACpC,SAAS,OAAO;AACd;AAAA,QACE,4BAA4B,iBAAiB,QAAQ,MAAM,UAAU,kBAAkB;AAAA,MAAA;AAAA,IAE3F;AAAA,EACF;AAEA,iBAAe,gBAAgB;;AAC7B,4BAAwB,EAAE;AAC1B,QAAI;AACF,cAAM,WAAM,WAAN;AAAA,IACR,SAAS,OAAO;AACd;AAAA,QACE,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,kBAAkB;AAAA,MAAA;AAAA,IAEzF;AAAA,EACF;AAEA,MAAI,CAAC,UAAU;AACb,WACEC,2BAAAA,KAAAY,qBAAA,EACE,UAAA;AAAA,MAAAZ,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,UAAU;AAAA,UACV,SAAS,MAAM,YAAY,IAAI;AAAA,UAC/B,WAAW,CAAC,MAAM;AAChB,gBAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,gBAAE,eAAA;AACF,0BAAY,IAAI;AAAA,YAClB;AAAA,UACF;AAAA,UACA,eAAY;AAAA,UACZ,qBAAmB,UAAU,UAAU;AAAA,UACvC,mBAAiB,QAAQ;AAAA,UACzB,WAAWa,MAAAA;AAAAA,YACT;AAAA,YACA,CAAC,WAAW;AAAA,YACZ,WAAW;AAAA,UAAA;AAAA,UAGb,UAAA;AAAA,YAAAZ,2BAAAA,IAACC,cAAAA,qBAAA,EAAoB,QAAQ,QAAQ,QAAQ,MAAI,MAAC;AAAA,2CACjD,QAAA,EAAK,WAAU,sDACb,UAAAC,+BAAkB,QAAQ,MAAM,GACnC;AAAA,YACC,CAAC,WACAH,2BAAAA,KAAAY,WAAAA,UAAA,EACE,UAAA;AAAA,cAAAX,2BAAAA,IAACG,iBAAAA,YAAA,EAAW,QAAQ,QAAQ,OAAA,CAAQ;AAAA,cACpCH,2BAAAA,IAACI,iBAAAA,aAAA,EAAY,SAAkB,QAAgB,SAAO,KAAA,CAAC;AAAA,YAAA,GACzD;AAAA,YAEFJ,+BAAC,UAAK,WAAU,0DACb,kBAAQ,KAAK,KAAA,IACVa,2BAAc,QAAQ,IAAI,IAC1B,QAAQ,WAAW,aACjB,oBACA,QAAQ,WAAW,aACjB,oBACA,iBACV;AAAA,YACAb,2BAAAA;AAAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,CAAC,MAAM,EAAE,gBAAA;AAAA,gBAClB,WAAW,CAAC,MAAM,EAAE,gBAAA;AAAA,gBAEpB,UAAAA,2BAAAA;AAAAA,kBAACK,aAAAA;AAAAA,kBAAA;AAAA,oBACC,OAAM;AAAA,oBACN,SACEL,2BAAAA;AAAAA,sBAAC;AAAA,sBAAA;AAAA,wBACC,MAAK;AAAA,wBACL,UAAU;AAAA,wBACV,cAAW;AAAA,wBACX,WAAU;AAAA,wBAEV,UAAAA,2BAAAA,IAACM,WAAA,EAAK,MAAMC,eAAAA,gBAAgB,WAAU,UAAA,CAAU;AAAA,sBAAA;AAAA,oBAAA;AAAA,oBAGpD,OAAO,gBAAgB,SAAS;AAAA,sBAC9B,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAA,IAAa,CAAA;AAAA,sBACpD,GAAI,CAAC,WAAW,MAAM,kBAAkB,iBAAiB,MAAM,IAC3D;AAAA,wBACE,gBAAgB,MAAM,KAAK,sBAAA;AAAA,wBAC3B,mBACE,QAAQ,UAAU,QAClBT,aAAAA,aAAa,QAAQ,QAAQ,MAAM,IAC/B,YACA;AAAA,sBAAA,IAER,CAAA;AAAA,sBACJ,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,KAAK,cAAA,EAAc,IAAM,CAAA;AAAA,sBAC5D,GAAI,MAAM,aAAa,EAAE,YAAY,MAAM,WAAA,IAAe,CAAA;AAAA,oBAAC,CAC5D;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACH;AAAA,YAAA;AAAA,UACF;AAAA,QAAA;AAAA,MAAA;AAAA,MAED,wBACCE,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UAET,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IACH,GAEJ;AAAA,EAEJ;AAEA,SACEA,+BAAAW,WAAAA,UAAA,EACE,UAAAX,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,eAAY;AAAA,MACZ,qBAAmB,UAAU,UAAU;AAAA,MACvC,mBAAiB,QAAQ;AAAA,MACzB,WAAWY,MAAAA;AAAAA,QACT;AAAA,QACA,CAAC,WACC;AAAA,QACF,WAAW;AAAA,QACX,WAAW;AAAA,MAAA;AAAA,MAGZ,UAAA;AAAA,IAAA;AAAA,EAAA,GAEL;AAEJ;;"}
@@ -13,6 +13,8 @@ export type CommentCardProps = {
13
13
  onUpdateStatus?: (status: string) => void | Promise<void>;
14
14
  /** Delete this comment. */
15
15
  onDelete?: () => void;
16
+ /** Copy this comment's whole thread in the caller's canonical format. */
17
+ onCopy?: () => void | Promise<void>;
16
18
  /** Begin a reply to this comment. */
17
19
  onReply?: () => void;
18
20
  /** Advance the checklist item at `index` to its next status. */
@@ -26,7 +28,7 @@ export type CommentCardProps = {
26
28
  };
27
29
  /**
28
30
  * A single comment, collapsible between a compact one-line preview and a full
29
- * card. Offers a Maximize action when `onMaximize` is supplied — the owner
31
+ * card. Offers a Maximise action when `onMaximize` is supplied — the owner
30
32
  * enlarges the whole thread. Fully controlled — all mutations are delegated to
31
33
  * the supplied callbacks.
32
34
  */
@@ -1 +1 @@
1
- {"version":3,"file":"CommentCard.d.ts","sourceRoot":"","sources":["../../src/comments/CommentCard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA4BjD,OAAO,KAAK,EACV,OAAO,EACP,aAAa,EAGd,MAAM,iBAAiB,CAAC;AAqBzB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;IACtB,uCAAuC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,yEAAyE;IACzE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;IACzC,iDAAiD;IACjD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC;AA0UF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BAmJlD"}
1
+ {"version":3,"file":"CommentCard.d.ts","sourceRoot":"","sources":["../../src/comments/CommentCard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA6BjD,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAkB9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;IACtB,uCAAuC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,yEAAyE;IACzE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;IACzC,iDAAiD;IACjD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC;AA8KF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BA4IlD"}