@flanksource/clicky-ui 0.3.30 → 0.3.32

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 (157) hide show
  1. package/dist/comments/CommentCard.cjs +156 -273
  2. package/dist/comments/CommentCard.cjs.map +1 -1
  3. package/dist/comments/CommentCard.d.ts +5 -1
  4. package/dist/comments/CommentCard.d.ts.map +1 -1
  5. package/dist/comments/CommentCard.js +151 -268
  6. package/dist/comments/CommentCard.js.map +1 -1
  7. package/dist/comments/CommentCardParts.cjs +115 -0
  8. package/dist/comments/CommentCardParts.cjs.map +1 -0
  9. package/dist/comments/CommentCardParts.d.ts +21 -0
  10. package/dist/comments/CommentCardParts.d.ts.map +1 -0
  11. package/dist/comments/CommentCardParts.js +115 -0
  12. package/dist/comments/CommentCardParts.js.map +1 -0
  13. package/dist/comments/CommentProvider.cjs +3 -0
  14. package/dist/comments/CommentProvider.cjs.map +1 -1
  15. package/dist/comments/CommentProvider.d.ts +1 -1
  16. package/dist/comments/CommentProvider.d.ts.map +1 -1
  17. package/dist/comments/CommentProvider.js +3 -0
  18. package/dist/comments/CommentProvider.js.map +1 -1
  19. package/dist/comments/CommentSidePanel.cjs +55 -22
  20. package/dist/comments/CommentSidePanel.cjs.map +1 -1
  21. package/dist/comments/CommentSidePanel.d.ts +3 -1
  22. package/dist/comments/CommentSidePanel.d.ts.map +1 -1
  23. package/dist/comments/CommentSidePanel.js +56 -23
  24. package/dist/comments/CommentSidePanel.js.map +1 -1
  25. package/dist/comments/CommentThread.cjs +2 -0
  26. package/dist/comments/CommentThread.cjs.map +1 -1
  27. package/dist/comments/CommentThread.d.ts +2 -1
  28. package/dist/comments/CommentThread.d.ts.map +1 -1
  29. package/dist/comments/CommentThread.js +2 -0
  30. package/dist/comments/CommentThread.js.map +1 -1
  31. package/dist/comments/CommentThreadList.cjs +52 -34
  32. package/dist/comments/CommentThreadList.cjs.map +1 -1
  33. package/dist/comments/CommentThreadList.d.ts +5 -2
  34. package/dist/comments/CommentThreadList.d.ts.map +1 -1
  35. package/dist/comments/CommentThreadList.js +52 -34
  36. package/dist/comments/CommentThreadList.js.map +1 -1
  37. package/dist/comments/comment-types.cjs +16 -4
  38. package/dist/comments/comment-types.cjs.map +1 -1
  39. package/dist/comments/comment-types.d.ts +11 -1
  40. package/dist/comments/comment-types.d.ts.map +1 -1
  41. package/dist/comments/comment-types.js +16 -4
  42. package/dist/comments/comment-types.js.map +1 -1
  43. package/dist/comments/comment-utils.cjs +31 -1
  44. package/dist/comments/comment-utils.cjs.map +1 -1
  45. package/dist/comments/comment-utils.d.ts +9 -2
  46. package/dist/comments/comment-utils.d.ts.map +1 -1
  47. package/dist/comments/comment-utils.js +31 -1
  48. package/dist/comments/comment-utils.js.map +1 -1
  49. package/dist/comments.cjs +3 -0
  50. package/dist/comments.cjs.map +1 -1
  51. package/dist/comments.js +4 -1
  52. package/dist/components/ComboboxMenu.cjs +4 -1
  53. package/dist/components/ComboboxMenu.cjs.map +1 -1
  54. package/dist/components/ComboboxMenu.d.ts.map +1 -1
  55. package/dist/components/ComboboxMenu.js +5 -2
  56. package/dist/components/ComboboxMenu.js.map +1 -1
  57. package/dist/components/ComboboxTags.cjs +5 -1
  58. package/dist/components/ComboboxTags.cjs.map +1 -1
  59. package/dist/components/ComboboxTags.d.ts.map +1 -1
  60. package/dist/components/ComboboxTags.js +6 -2
  61. package/dist/components/ComboboxTags.js.map +1 -1
  62. package/dist/components/ConnectionLoggingPolicy.cjs +24 -35
  63. package/dist/components/ConnectionLoggingPolicy.cjs.map +1 -1
  64. package/dist/components/ConnectionLoggingPolicy.d.ts.map +1 -1
  65. package/dist/components/ConnectionLoggingPolicy.js +24 -35
  66. package/dist/components/ConnectionLoggingPolicy.js.map +1 -1
  67. package/dist/components/MultiSelect.cjs.map +1 -1
  68. package/dist/components/MultiSelect.d.ts +7 -0
  69. package/dist/components/MultiSelect.d.ts.map +1 -1
  70. package/dist/components/MultiSelect.js.map +1 -1
  71. package/dist/components/filter-bar-multi.cjs +10 -1
  72. package/dist/components/filter-bar-multi.cjs.map +1 -1
  73. package/dist/components/filter-bar-multi.d.ts.map +1 -1
  74. package/dist/components/filter-bar-multi.js +11 -2
  75. package/dist/components/filter-bar-multi.js.map +1 -1
  76. package/dist/data/ai/SessionViewer.model.cjs +23 -8
  77. package/dist/data/ai/SessionViewer.model.cjs.map +1 -1
  78. package/dist/data/ai/SessionViewer.model.d.ts +8 -0
  79. package/dist/data/ai/SessionViewer.model.d.ts.map +1 -1
  80. package/dist/data/ai/SessionViewer.model.js +23 -8
  81. package/dist/data/ai/SessionViewer.model.js.map +1 -1
  82. package/dist/data/ai/SessionViewer.rows.cjs +9 -1
  83. package/dist/data/ai/SessionViewer.rows.cjs.map +1 -1
  84. package/dist/data/ai/SessionViewer.rows.js +9 -1
  85. package/dist/data/ai/SessionViewer.rows.js.map +1 -1
  86. package/dist/data/ai/SpecRuntimeEditor/VerifySection.cjs +24 -2
  87. package/dist/data/ai/SpecRuntimeEditor/VerifySection.cjs.map +1 -1
  88. package/dist/data/ai/SpecRuntimeEditor/VerifySection.d.ts.map +1 -1
  89. package/dist/data/ai/SpecRuntimeEditor/VerifySection.js +25 -3
  90. package/dist/data/ai/SpecRuntimeEditor/VerifySection.js.map +1 -1
  91. package/dist/data/ai/SpecRuntimeEditor/summaries.cjs.map +1 -1
  92. package/dist/data/ai/SpecRuntimeEditor/summaries.d.ts.map +1 -1
  93. package/dist/data/ai/SpecRuntimeEditor/summaries.js.map +1 -1
  94. package/dist/data/ai/SpecRuntimeEditor.model.cjs +4 -0
  95. package/dist/data/ai/SpecRuntimeEditor.model.cjs.map +1 -1
  96. package/dist/data/ai/SpecRuntimeEditor.model.d.ts +4 -0
  97. package/dist/data/ai/SpecRuntimeEditor.model.d.ts.map +1 -1
  98. package/dist/data/ai/SpecRuntimeEditor.model.js +4 -0
  99. package/dist/data/ai/SpecRuntimeEditor.model.js.map +1 -1
  100. package/dist/data/test-runner/TestRunner.cjs +6 -4
  101. package/dist/data/test-runner/TestRunner.cjs.map +1 -1
  102. package/dist/data/test-runner/TestRunner.d.ts +10 -1
  103. package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
  104. package/dist/data/test-runner/TestRunner.js +6 -4
  105. package/dist/data/test-runner/TestRunner.js.map +1 -1
  106. package/dist/data/test-runner/status.cjs +1 -1
  107. package/dist/data/test-runner/status.cjs.map +1 -1
  108. package/dist/data/test-runner/status.js +1 -1
  109. package/dist/data/test-runner/status.js.map +1 -1
  110. package/dist/data/test-runner/types.d.ts +6 -0
  111. package/dist/data/test-runner/types.d.ts.map +1 -1
  112. package/dist/data/verification/VerificationResults.cjs +99 -0
  113. package/dist/data/verification/VerificationResults.cjs.map +1 -0
  114. package/dist/data/verification/VerificationResults.d.ts +27 -0
  115. package/dist/data/verification/VerificationResults.d.ts.map +1 -0
  116. package/dist/data/verification/VerificationResults.js +99 -0
  117. package/dist/data/verification/VerificationResults.js.map +1 -0
  118. package/dist/data/verification/adapterViews.cjs +116 -0
  119. package/dist/data/verification/adapterViews.cjs.map +1 -0
  120. package/dist/data/verification/adapterViews.d.ts +13 -0
  121. package/dist/data/verification/adapterViews.d.ts.map +1 -0
  122. package/dist/data/verification/adapterViews.js +116 -0
  123. package/dist/data/verification/adapterViews.js.map +1 -0
  124. package/dist/data/verification/adapters.cjs +26 -0
  125. package/dist/data/verification/adapters.cjs.map +1 -0
  126. package/dist/data/verification/adapters.d.ts +6 -0
  127. package/dist/data/verification/adapters.d.ts.map +1 -0
  128. package/dist/data/verification/adapters.js +26 -0
  129. package/dist/data/verification/adapters.js.map +1 -0
  130. package/dist/data/verification/index.d.ts +5 -0
  131. package/dist/data/verification/index.d.ts.map +1 -0
  132. package/dist/data/verification/verify-report.cjs +58 -0
  133. package/dist/data/verification/verify-report.cjs.map +1 -0
  134. package/dist/data/verification/verify-report.d.ts +96 -0
  135. package/dist/data/verification/verify-report.d.ts.map +1 -0
  136. package/dist/data/verification/verify-report.js +58 -0
  137. package/dist/data/verification/verify-report.js.map +1 -0
  138. package/dist/data/verification/verifyReportTests.cjs +84 -0
  139. package/dist/data/verification/verifyReportTests.cjs.map +1 -0
  140. package/dist/data/verification/verifyReportTests.d.ts +13 -0
  141. package/dist/data/verification/verifyReportTests.d.ts.map +1 -0
  142. package/dist/data/verification/verifyReportTests.js +84 -0
  143. package/dist/data/verification/verifyReportTests.js.map +1 -0
  144. package/dist/data/version-info.cjs +3 -3
  145. package/dist/data/version-info.js +3 -3
  146. package/dist/data.cjs +10 -0
  147. package/dist/data.cjs.map +1 -1
  148. package/dist/data.d.ts +1 -0
  149. package/dist/data.d.ts.map +1 -1
  150. package/dist/data.js +10 -0
  151. package/dist/data.js.map +1 -1
  152. package/dist/index.cjs +13 -0
  153. package/dist/index.cjs.map +1 -1
  154. package/dist/index.js +14 -1
  155. package/dist/index.js.map +1 -1
  156. package/dist/styles.css +1 -1
  157. package/package.json +1 -1
@@ -3,227 +3,114 @@ 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
13
  const UiCheck = require("../icons/components/UiCheck.cjs");
14
- const UiFullscreen = require("../icons/components/UiFullscreen.cjs");
14
+ const UiLock = require("../icons/components/UiLock.cjs");
15
+ const UiRestart = require("../icons/components/UiRestart.cjs");
15
16
  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
17
  const UiTrash = require("../icons/components/UiTrash.cjs");
20
- function completionStatus(config) {
21
- for (const completionName of ["resolved", "closed"]) {
22
- const status = config.statuses.find(
23
- (candidate) => candidate.value.toLowerCase() === completionName || candidate.label.toLowerCase() === completionName
24
- );
25
- if (status) return status;
26
- }
27
- return void 0;
28
- }
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;
89
- }
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) {
18
+ const UiCopy = require("../icons/components/UiCopy.cjs");
19
+ const UiFullscreen = require("../icons/components/UiFullscreen.cjs");
20
+ function actionMenuItems(isReply, opts) {
133
21
  const items = [];
22
+ if (opts.onDelete) {
23
+ items.push({ label: "Delete", icon: UiTrash.UiTrash, onSelect: opts.onDelete });
24
+ }
25
+ if (!isReply) items.push(...opts.statusActions ?? []);
26
+ if (opts.onCopy) {
27
+ items.push({ label: "Copy", icon: UiCopy.UiCopy, onSelect: opts.onCopy });
28
+ }
134
29
  if (opts.onMaximize) {
135
30
  items.push({
136
- label: "Maximize",
31
+ label: "Maximise",
137
32
  icon: UiFullscreen.UiFullscreen,
138
33
  onSelect: opts.onMaximize
139
34
  });
140
35
  }
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
36
  return items;
173
37
  }
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
38
  function CommentBody({
196
39
  comment,
197
40
  config,
198
41
  renderBody,
199
42
  onUpdateStatus,
43
+ onClose,
200
44
  onDelete,
45
+ onCopy,
201
46
  onReply,
202
47
  onChecklistToggle,
203
- onCollapse,
204
48
  onMaximize
205
49
  }) {
206
- const [statusError, setStatusError] = react.useState("");
50
+ const [actionError, setActionError] = react.useState("");
207
51
  const isReply = Boolean(comment.parentId);
208
52
  const date = timestampFormat.parseTimestamp(comment.createdAt);
209
- const completion = completionStatus(config);
210
- const canComplete = !isReply && onUpdateStatus != null && completion != null && commentUtils.isUnresolved(config, comment.status);
53
+ const resolved = commentUtils.statusForCommentStage(config, "resolved");
54
+ const active = commentUtils.statusForCommentStage(config, "active");
55
+ const closed = commentUtils.statusForCommentStage(config, "closed");
56
+ const stage = commentUtils.resolveCommentStage(config, comment.status);
211
57
  const updateStatus = async (status) => {
212
- setStatusError("");
58
+ setActionError("");
213
59
  try {
214
60
  await (onUpdateStatus == null ? void 0 : onUpdateStatus(status));
215
61
  } catch (error) {
216
- setStatusError(
217
- error instanceof Error ? error.message : "Unexpected error"
62
+ setActionError(
63
+ `Couldn't update comment: ${error instanceof Error ? error.message : "Unexpected error"}`
218
64
  );
219
65
  }
220
66
  };
221
- const menuItems = statusMenuItems(comment, config, isReply, {
222
- ...onUpdateStatus ? { onUpdateStatus: (status) => void updateStatus(status) } : {},
67
+ const close = async () => {
68
+ setActionError("");
69
+ try {
70
+ await (onClose == null ? void 0 : onClose());
71
+ } catch (error) {
72
+ setActionError(
73
+ `Couldn't close comment: ${error instanceof Error ? error.message : "Unexpected error"}`
74
+ );
75
+ }
76
+ };
77
+ const copy = async () => {
78
+ setActionError("");
79
+ try {
80
+ await (onCopy == null ? void 0 : onCopy());
81
+ } catch (error) {
82
+ setActionError(
83
+ `Couldn't copy comment: ${error instanceof Error ? error.message : "Unexpected error"}`
84
+ );
85
+ }
86
+ };
87
+ const menuItems = actionMenuItems(isReply, {
223
88
  ...onDelete ? { onDelete } : {},
224
- ...onCollapse ? { onCollapse } : {},
225
- ...onMaximize ? { onMaximize } : {},
226
- ...completion ? { completionStatus: completion.value } : {}
89
+ statusActions: [
90
+ ...stage === "active" && onUpdateStatus && resolved ? [
91
+ {
92
+ label: "Resolve",
93
+ icon: UiCheck.UiCheck,
94
+ onSelect: () => void updateStatus(resolved.value)
95
+ }
96
+ ] : [],
97
+ ...stage === "resolved" && onClose && closed ? [
98
+ {
99
+ label: "Close",
100
+ icon: UiLock.UiLock,
101
+ onSelect: () => void close()
102
+ }
103
+ ] : [],
104
+ ...(stage === "resolved" || stage === "closed") && onUpdateStatus && active ? [
105
+ {
106
+ label: "Reopen",
107
+ icon: UiRestart.UiRestart,
108
+ onSelect: () => void updateStatus(active.value)
109
+ }
110
+ ] : []
111
+ ],
112
+ ...onCopy ? { onCopy: () => void copy() } : {},
113
+ ...onMaximize ? { onMaximize } : {}
227
114
  });
228
115
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2.5", children: [
229
116
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2.5", children: [
@@ -241,60 +128,47 @@ function CommentBody({
241
128
  )
242
129
  ] }),
243
130
  !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 })
131
+ /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.StatusChip, { comment, config }),
132
+ /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.RatingChip, { rating: comment.rating }),
133
+ /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.FacetBadges, { comment, config })
246
134
  ] })
247
135
  ] }),
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
- ] })
136
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-0.5", children: menuItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
137
+ DropdownMenu.DropdownMenu,
138
+ {
139
+ align: "right",
140
+ menuClassName: "min-w-[150px]",
141
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(
142
+ "span",
143
+ {
144
+ role: "button",
145
+ tabIndex: 0,
146
+ "aria-label": "Comment actions",
147
+ className: "inline-flex rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
148
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiDotsVertical.UiDotsVertical, className: "text-sm" })
149
+ }
150
+ ),
151
+ items: menuItems
152
+ }
153
+ ) })
277
154
  ] }),
278
155
  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(
156
+ actionError && /* @__PURE__ */ jsxRuntime.jsx(
280
157
  "div",
281
158
  {
282
159
  role: "alert",
283
160
  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
- ]
161
+ children: actionError
288
162
  }
289
163
  ),
290
164
  !isReply && /* @__PURE__ */ jsxRuntime.jsx(
291
- ChecklistChips,
165
+ CommentCardParts.ChecklistChips,
292
166
  {
293
167
  comment,
294
168
  ...onChecklistToggle ? { onChecklistToggle } : {}
295
169
  }
296
170
  ),
297
- !isReply && /* @__PURE__ */ jsxRuntime.jsx(RefChips, { comment }),
171
+ !isReply && /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.RefChips, { comment }),
298
172
  onReply && /* @__PURE__ */ jsxRuntime.jsx(
299
173
  "button",
300
174
  {
@@ -307,31 +181,41 @@ function CommentBody({
307
181
  ] });
308
182
  }
309
183
  function CommentCard(props) {
310
- const { comment, config, compact, defaultExpanded, onMaximize } = props;
184
+ const { comment, config, compact, defaultExpanded } = props;
311
185
  const [expanded, setExpanded] = react.useState(defaultExpanded ?? false);
312
- const [collapsedStatusError, setCollapsedStatusError] = react.useState("");
186
+ const [collapsedActionError, setCollapsedActionError] = react.useState("");
313
187
  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 () => {
317
- var _a;
318
- if (!completion) return;
319
- setCollapsedStatusError("");
188
+ const body = /* @__PURE__ */ jsxRuntime.jsx(CommentBody, { ...props });
189
+ async function runCollapsedAction(action) {
190
+ var _a, _b;
191
+ setCollapsedActionError("");
320
192
  try {
321
- await ((_a = props.onUpdateStatus) == null ? void 0 : _a.call(props, completion.value));
193
+ if (action === "close") {
194
+ await ((_a = props.onClose) == null ? void 0 : _a.call(props));
195
+ return;
196
+ }
197
+ const next = commentUtils.statusForCommentStage(
198
+ config,
199
+ action === "resolve" ? "resolved" : "active"
200
+ );
201
+ if (next) await ((_b = props.onUpdateStatus) == null ? void 0 : _b.call(props, next.value));
322
202
  } catch (error) {
323
- setCollapsedStatusError(
324
- error instanceof Error ? error.message : "Unexpected error"
203
+ setCollapsedActionError(
204
+ `${action === "close" ? "Couldn't close comment" : "Couldn't update comment"}: ${error instanceof Error ? error.message : "Unexpected error"}`
325
205
  );
326
206
  }
327
- };
328
- const body = /* @__PURE__ */ jsxRuntime.jsx(
329
- CommentBody,
330
- {
331
- ...props,
332
- ...expanded ? { onCollapse: () => setExpanded(false) } : {}
207
+ }
208
+ async function copyCollapsed() {
209
+ var _a;
210
+ setCollapsedActionError("");
211
+ try {
212
+ await ((_a = props.onCopy) == null ? void 0 : _a.call(props));
213
+ } catch (error) {
214
+ setCollapsedActionError(
215
+ `Couldn't copy comment: ${error instanceof Error ? error.message : "Unexpected error"}`
216
+ );
333
217
  }
334
- );
218
+ }
335
219
  if (!expanded) {
336
220
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
337
221
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -358,27 +242,11 @@ function CommentCard(props) {
358
242
  /* @__PURE__ */ jsxRuntime.jsx(CommentAuthor.CommentAuthorAvatar, { author: comment.author, bare: true }),
359
243
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-[11px] font-semibold text-foreground", children: commentUtils.authorDisplayName(comment.author) }),
360
244
  !isReply && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
361
- /* @__PURE__ */ jsxRuntime.jsx(RatingChip, { rating: comment.rating }),
362
- /* @__PURE__ */ jsxRuntime.jsx(FacetBadges, { comment, config, compact: true })
245
+ /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.StatusChip, { comment, config }),
246
+ /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.RatingChip, { rating: comment.rating }),
247
+ /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.FacetBadges, { comment, config, compact: true })
363
248
  ] }),
364
249
  /* @__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
250
  /* @__PURE__ */ jsxRuntime.jsx(
383
251
  "span",
384
252
  {
@@ -399,16 +267,34 @@ function CommentCard(props) {
399
267
  children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiDotsVertical.UiDotsVertical, className: "text-sm" })
400
268
  }
401
269
  ),
402
- items: [
403
- { label: "Expand", onSelect: () => setExpanded(true) },
404
- ...onMaximize ? [
405
- {
406
- label: "Maximize",
407
- icon: UiFullscreen.UiFullscreen,
408
- onSelect: onMaximize
409
- }
410
- ] : []
411
- ]
270
+ items: actionMenuItems(isReply, {
271
+ ...props.onDelete ? { onDelete: props.onDelete } : {},
272
+ statusActions: [
273
+ ...commentUtils.resolveCommentStage(config, comment.status) === "active" && props.onUpdateStatus && commentUtils.statusForCommentStage(config, "resolved") ? [
274
+ {
275
+ label: "Resolve",
276
+ icon: UiCheck.UiCheck,
277
+ onSelect: () => void runCollapsedAction("resolve")
278
+ }
279
+ ] : [],
280
+ ...commentUtils.resolveCommentStage(config, comment.status) === "resolved" && props.onClose && commentUtils.statusForCommentStage(config, "closed") ? [
281
+ {
282
+ label: "Close",
283
+ icon: UiLock.UiLock,
284
+ onSelect: () => void runCollapsedAction("close")
285
+ }
286
+ ] : [],
287
+ ...(commentUtils.resolveCommentStage(config, comment.status) === "resolved" || commentUtils.resolveCommentStage(config, comment.status) === "closed") && props.onUpdateStatus && commentUtils.statusForCommentStage(config, "active") ? [
288
+ {
289
+ label: "Reopen",
290
+ icon: UiRestart.UiRestart,
291
+ onSelect: () => void runCollapsedAction("reopen")
292
+ }
293
+ ] : []
294
+ ],
295
+ ...props.onCopy ? { onCopy: () => void copyCollapsed() } : {},
296
+ ...props.onMaximize ? { onMaximize: props.onMaximize } : {}
297
+ })
412
298
  }
413
299
  )
414
300
  }
@@ -416,15 +302,12 @@ function CommentCard(props) {
416
302
  ]
417
303
  }
418
304
  ),
419
- collapsedStatusError && /* @__PURE__ */ jsxRuntime.jsxs(
305
+ collapsedActionError && /* @__PURE__ */ jsxRuntime.jsx(
420
306
  "div",
421
307
  {
422
308
  role: "alert",
423
309
  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
- ]
310
+ children: collapsedActionError
428
311
  }
429
312
  )
430
313
  ] });