@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
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const Badge = require("../data/Badge.cjs");
5
+ const utils = require("../lib/utils.cjs");
6
+ const commentUtils = require("./comment-utils.cjs");
7
+ const UiThumbsUp = require("../icons/components/UiThumbsUp.cjs");
8
+ const UiThumbsDown = require("../icons/components/UiThumbsDown.cjs");
9
+ function RatingChip({ rating }) {
10
+ if (!rating) return null;
11
+ const positive = rating === "positive";
12
+ return /* @__PURE__ */ jsxRuntime.jsx(
13
+ Badge.Badge,
14
+ {
15
+ variant: "soft",
16
+ tone: positive ? "success" : "danger",
17
+ size: "xs",
18
+ icon: positive ? UiThumbsUp.UiThumbsUp : UiThumbsDown.UiThumbsDown,
19
+ children: positive ? "Positive rating" : "Negative rating"
20
+ }
21
+ );
22
+ }
23
+ function StatusChip({
24
+ comment,
25
+ config
26
+ }) {
27
+ const status = commentUtils.resolveStatusConfig(config, comment.status);
28
+ if (!status) return null;
29
+ const closedBy = status.stage === "closed" ? comment.closedBy : void 0;
30
+ return /* @__PURE__ */ jsxRuntime.jsx(
31
+ Badge.Badge,
32
+ {
33
+ variant: "soft",
34
+ tone: commentUtils.toneToBadgeTone(status.tone),
35
+ size: "xs",
36
+ ...status.icon ? { icon: status.icon } : {},
37
+ ...comment.closedAt ? { title: new Date(comment.closedAt).toLocaleString() } : {},
38
+ children: closedBy ? `Closed by ${closedBy.name}` : status.label
39
+ }
40
+ );
41
+ }
42
+ function FacetBadges({
43
+ comment,
44
+ config,
45
+ compact
46
+ }) {
47
+ const chips = (config.facets ?? []).flatMap((facet) => {
48
+ var _a;
49
+ const option = commentUtils.resolveFacetOption(facet, (_a = comment.facets) == null ? void 0 : _a[facet.key]);
50
+ if (!option) return [];
51
+ return [
52
+ /* @__PURE__ */ jsxRuntime.jsx(
53
+ Badge.Badge,
54
+ {
55
+ variant: "soft",
56
+ tone: commentUtils.toneToBadgeTone(option.tone),
57
+ size: "xs",
58
+ children: compact ? option.short ?? option.label : option.label
59
+ },
60
+ facet.key
61
+ )
62
+ ];
63
+ });
64
+ return chips.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: chips }) : null;
65
+ }
66
+ function ChecklistChips({
67
+ comment,
68
+ onChecklistToggle
69
+ }) {
70
+ var _a;
71
+ if (!((_a = comment.checklist) == null ? void 0 : _a.length)) return null;
72
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: comment.checklist.map((item, index) => {
73
+ const done = /done|complete|resolved/i.test(item.status);
74
+ const active = /progress|doing/i.test(item.status);
75
+ return /* @__PURE__ */ jsxRuntime.jsx(
76
+ "button",
77
+ {
78
+ type: "button",
79
+ onClick: () => onChecklistToggle == null ? void 0 : onChecklistToggle(index),
80
+ disabled: !onChecklistToggle,
81
+ className: utils.cn(
82
+ "inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] transition-opacity",
83
+ onChecklistToggle && "cursor-pointer hover:opacity-80",
84
+ 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"
85
+ ),
86
+ children: item.label
87
+ },
88
+ index
89
+ );
90
+ }) });
91
+ }
92
+ function RefChips({ comment }) {
93
+ var _a;
94
+ if (!((_a = comment.refs) == null ? void 0 : _a.length)) return null;
95
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5", children: comment.refs.map((group, index) => /* @__PURE__ */ jsxRuntime.jsxs(
96
+ "span",
97
+ {
98
+ className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground",
99
+ children: [
100
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
101
+ group.label,
102
+ ":"
103
+ ] }),
104
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: group.mono ? "font-mono" : void 0, children: group.items.join(", ") })
105
+ ]
106
+ },
107
+ index
108
+ )) });
109
+ }
110
+ exports.ChecklistChips = ChecklistChips;
111
+ exports.FacetBadges = FacetBadges;
112
+ exports.RatingChip = RatingChip;
113
+ exports.RefChips = RefChips;
114
+ exports.StatusChip = StatusChip;
115
+ //# sourceMappingURL=CommentCardParts.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommentCardParts.cjs","sources":["../../src/comments/CommentCardParts.tsx"],"sourcesContent":["import { Badge } from \"../data/Badge\";\nimport { cn } from \"../lib/utils\";\nimport { UiThumbsDown, UiThumbsUp } from \"../icons\";\nimport {\n resolveFacetOption,\n resolveStatusConfig,\n toneToBadgeTone,\n} from \"./comment-utils\";\nimport type { Comment, CommentConfig, CommentRating } from \"./comment-types\";\n\nexport function 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\nexport function StatusChip({\n comment,\n config,\n}: {\n comment: Comment;\n config: CommentConfig;\n}) {\n const status = resolveStatusConfig(config, comment.status);\n if (!status) return null;\n const closedBy = status.stage === \"closed\" ? comment.closedBy : undefined;\n return (\n <Badge\n variant=\"soft\"\n tone={toneToBadgeTone(status.tone)}\n size=\"xs\"\n {...(status.icon ? { icon: status.icon } : {})}\n {...(comment.closedAt\n ? { title: new Date(comment.closedAt).toLocaleString() }\n : {})}\n >\n {closedBy ? `Closed by ${closedBy.name}` : status.label}\n </Badge>\n );\n}\n\nexport function FacetBadges({\n comment,\n config,\n compact,\n}: {\n comment: Comment;\n config: CommentConfig;\n compact?: boolean;\n}) {\n const chips = (config.facets ?? []).flatMap((facet) => {\n const option = resolveFacetOption(facet, comment.facets?.[facet.key]);\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\nexport function ChecklistChips({\n comment,\n onChecklistToggle,\n}: {\n comment: Comment;\n onChecklistToggle?: (index: number) => void;\n}) {\n if (!comment.checklist?.length) return null;\n return (\n <div className=\"flex flex-wrap gap-1\">\n {comment.checklist.map((item, index) => {\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={index}\n type=\"button\"\n onClick={() => onChecklistToggle?.(index)}\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\nexport function RefChips({ comment }: { comment: Comment }) {\n if (!comment.refs?.length) return null;\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {comment.refs.map((group, index) => (\n <span\n key={index}\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"],"names":["jsx","Badge","UiThumbsUp","UiThumbsDown","resolveStatusConfig","toneToBadgeTone","resolveFacetOption","Fragment","cn","jsxs"],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { Comment, CommentConfig, CommentRating } from './comment-types';
2
+ export declare function RatingChip({ rating }: {
3
+ rating: CommentRating | undefined;
4
+ }): import("react").JSX.Element | null;
5
+ export declare function StatusChip({ comment, config, }: {
6
+ comment: Comment;
7
+ config: CommentConfig;
8
+ }): import("react").JSX.Element | null;
9
+ export declare function FacetBadges({ comment, config, compact, }: {
10
+ comment: Comment;
11
+ config: CommentConfig;
12
+ compact?: boolean;
13
+ }): import("react").JSX.Element | null;
14
+ export declare function ChecklistChips({ comment, onChecklistToggle, }: {
15
+ comment: Comment;
16
+ onChecklistToggle?: (index: number) => void;
17
+ }): import("react").JSX.Element | null;
18
+ export declare function RefChips({ comment }: {
19
+ comment: Comment;
20
+ }): import("react").JSX.Element | null;
21
+ //# sourceMappingURL=CommentCardParts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommentCardParts.d.ts","sourceRoot":"","sources":["../../src/comments/CommentCardParts.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE7E,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,aAAa,GAAG,SAAS,CAAA;CAAE,sCAa3E;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,MAAM,GACP,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;CACvB,sCAiBA;AAED,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,MAAM,EACN,OAAO,GACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,sCAgBA;AAED,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,iBAAiB,GAClB,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C,sCA6BA;AAED,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,sCAiBzD"}
@@ -0,0 +1,115 @@
1
+ import { jsx, Fragment, jsxs } from "react/jsx-runtime";
2
+ import { Badge } from "../data/Badge.js";
3
+ import { cn } from "../lib/utils.js";
4
+ import { resolveStatusConfig, toneToBadgeTone, resolveFacetOption } from "./comment-utils.js";
5
+ import { UiThumbsUp } from "../icons/components/UiThumbsUp.js";
6
+ import { UiThumbsDown } from "../icons/components/UiThumbsDown.js";
7
+ function RatingChip({ rating }) {
8
+ if (!rating) return null;
9
+ const positive = rating === "positive";
10
+ return /* @__PURE__ */ jsx(
11
+ Badge,
12
+ {
13
+ variant: "soft",
14
+ tone: positive ? "success" : "danger",
15
+ size: "xs",
16
+ icon: positive ? UiThumbsUp : UiThumbsDown,
17
+ children: positive ? "Positive rating" : "Negative rating"
18
+ }
19
+ );
20
+ }
21
+ function StatusChip({
22
+ comment,
23
+ config
24
+ }) {
25
+ const status = resolveStatusConfig(config, comment.status);
26
+ if (!status) return null;
27
+ const closedBy = status.stage === "closed" ? comment.closedBy : void 0;
28
+ return /* @__PURE__ */ jsx(
29
+ Badge,
30
+ {
31
+ variant: "soft",
32
+ tone: toneToBadgeTone(status.tone),
33
+ size: "xs",
34
+ ...status.icon ? { icon: status.icon } : {},
35
+ ...comment.closedAt ? { title: new Date(comment.closedAt).toLocaleString() } : {},
36
+ children: closedBy ? `Closed by ${closedBy.name}` : status.label
37
+ }
38
+ );
39
+ }
40
+ function FacetBadges({
41
+ comment,
42
+ config,
43
+ compact
44
+ }) {
45
+ const chips = (config.facets ?? []).flatMap((facet) => {
46
+ var _a;
47
+ const option = resolveFacetOption(facet, (_a = comment.facets) == null ? void 0 : _a[facet.key]);
48
+ if (!option) return [];
49
+ return [
50
+ /* @__PURE__ */ jsx(
51
+ Badge,
52
+ {
53
+ variant: "soft",
54
+ tone: toneToBadgeTone(option.tone),
55
+ size: "xs",
56
+ children: compact ? option.short ?? option.label : option.label
57
+ },
58
+ facet.key
59
+ )
60
+ ];
61
+ });
62
+ return chips.length > 0 ? /* @__PURE__ */ jsx(Fragment, { children: chips }) : null;
63
+ }
64
+ function ChecklistChips({
65
+ comment,
66
+ onChecklistToggle
67
+ }) {
68
+ var _a;
69
+ if (!((_a = comment.checklist) == null ? void 0 : _a.length)) return null;
70
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: comment.checklist.map((item, index) => {
71
+ const done = /done|complete|resolved/i.test(item.status);
72
+ const active = /progress|doing/i.test(item.status);
73
+ return /* @__PURE__ */ jsx(
74
+ "button",
75
+ {
76
+ type: "button",
77
+ onClick: () => onChecklistToggle == null ? void 0 : onChecklistToggle(index),
78
+ disabled: !onChecklistToggle,
79
+ className: cn(
80
+ "inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] transition-opacity",
81
+ onChecklistToggle && "cursor-pointer hover:opacity-80",
82
+ 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"
83
+ ),
84
+ children: item.label
85
+ },
86
+ index
87
+ );
88
+ }) });
89
+ }
90
+ function RefChips({ comment }) {
91
+ var _a;
92
+ if (!((_a = comment.refs) == null ? void 0 : _a.length)) return null;
93
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: comment.refs.map((group, index) => /* @__PURE__ */ jsxs(
94
+ "span",
95
+ {
96
+ className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground",
97
+ children: [
98
+ /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
99
+ group.label,
100
+ ":"
101
+ ] }),
102
+ /* @__PURE__ */ jsx("span", { className: group.mono ? "font-mono" : void 0, children: group.items.join(", ") })
103
+ ]
104
+ },
105
+ index
106
+ )) });
107
+ }
108
+ export {
109
+ ChecklistChips,
110
+ FacetBadges,
111
+ RatingChip,
112
+ RefChips,
113
+ StatusChip
114
+ };
115
+ //# sourceMappingURL=CommentCardParts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommentCardParts.js","sources":["../../src/comments/CommentCardParts.tsx"],"sourcesContent":["import { Badge } from \"../data/Badge\";\nimport { cn } from \"../lib/utils\";\nimport { UiThumbsDown, UiThumbsUp } from \"../icons\";\nimport {\n resolveFacetOption,\n resolveStatusConfig,\n toneToBadgeTone,\n} from \"./comment-utils\";\nimport type { Comment, CommentConfig, CommentRating } from \"./comment-types\";\n\nexport function 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\nexport function StatusChip({\n comment,\n config,\n}: {\n comment: Comment;\n config: CommentConfig;\n}) {\n const status = resolveStatusConfig(config, comment.status);\n if (!status) return null;\n const closedBy = status.stage === \"closed\" ? comment.closedBy : undefined;\n return (\n <Badge\n variant=\"soft\"\n tone={toneToBadgeTone(status.tone)}\n size=\"xs\"\n {...(status.icon ? { icon: status.icon } : {})}\n {...(comment.closedAt\n ? { title: new Date(comment.closedAt).toLocaleString() }\n : {})}\n >\n {closedBy ? `Closed by ${closedBy.name}` : status.label}\n </Badge>\n );\n}\n\nexport function FacetBadges({\n comment,\n config,\n compact,\n}: {\n comment: Comment;\n config: CommentConfig;\n compact?: boolean;\n}) {\n const chips = (config.facets ?? []).flatMap((facet) => {\n const option = resolveFacetOption(facet, comment.facets?.[facet.key]);\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\nexport function ChecklistChips({\n comment,\n onChecklistToggle,\n}: {\n comment: Comment;\n onChecklistToggle?: (index: number) => void;\n}) {\n if (!comment.checklist?.length) return null;\n return (\n <div className=\"flex flex-wrap gap-1\">\n {comment.checklist.map((item, index) => {\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={index}\n type=\"button\"\n onClick={() => onChecklistToggle?.(index)}\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\nexport function RefChips({ comment }: { comment: Comment }) {\n if (!comment.refs?.length) return null;\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {comment.refs.map((group, index) => (\n <span\n key={index}\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"],"names":[],"mappings":";;;;;;AAUO,SAAS,WAAW,EAAE,UAAiD;AAC5E,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,WAAW,WAAW;AAC5B,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,SAAQ;AAAA,MACR,MAAM,WAAW,YAAY;AAAA,MAC7B,MAAK;AAAA,MACL,MAAM,WAAW,aAAa;AAAA,MAE7B,qBAAW,oBAAoB;AAAA,IAAA;AAAA,EAAA;AAGtC;AAEO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AACF,GAGG;AACD,QAAM,SAAS,oBAAoB,QAAQ,QAAQ,MAAM;AACzD,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,WAAW,OAAO,UAAU,WAAW,QAAQ,WAAW;AAChE,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,SAAQ;AAAA,MACR,MAAM,gBAAgB,OAAO,IAAI;AAAA,MACjC,MAAK;AAAA,MACJ,GAAI,OAAO,OAAO,EAAE,MAAM,OAAO,KAAA,IAAS,CAAA;AAAA,MAC1C,GAAI,QAAQ,WACT,EAAE,OAAO,IAAI,KAAK,QAAQ,QAAQ,EAAE,eAAA,EAAe,IACnD,CAAA;AAAA,MAEH,UAAA,WAAW,aAAa,SAAS,IAAI,KAAK,OAAO;AAAA,IAAA;AAAA,EAAA;AAGxD;AAEO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,SAAS,OAAO,UAAU,CAAA,GAAI,QAAQ,CAAC,UAAU;;AACrD,UAAM,SAAS,mBAAmB,QAAO,aAAQ,WAAR,mBAAiB,MAAM,IAAI;AACpE,QAAI,CAAC,OAAQ,QAAO,CAAA;AACpB,WAAO;AAAA,MACL;AAAA,QAAC;AAAA,QAAA;AAAA,UAEC,SAAQ;AAAA,UACR,MAAM,gBAAgB,OAAO,IAAI;AAAA,UACjC,MAAK;AAAA,UAEJ,UAAA,UAAW,OAAO,SAAS,OAAO,QAAS,OAAO;AAAA,QAAA;AAAA,QAL9C,MAAM;AAAA,MAAA;AAAA,IAMb;AAAA,EAEJ,CAAC;AACD,SAAO,MAAM,SAAS,IAAI,oBAAA,UAAA,EAAG,iBAAM,IAAM;AAC3C;AAEO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AACF,GAGG;;AACD,MAAI,GAAC,aAAQ,cAAR,mBAAmB,QAAQ,QAAO;AACvC,SACE,oBAAC,SAAI,WAAU,wBACZ,kBAAQ,UAAU,IAAI,CAAC,MAAM,UAAU;AACtC,UAAM,OAAO,0BAA0B,KAAK,KAAK,MAAM;AACvD,UAAM,SAAS,kBAAkB,KAAK,KAAK,MAAM;AACjD,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC,MAAK;AAAA,QACL,SAAS,MAAM,uDAAoB;AAAA,QACnC,UAAU,CAAC;AAAA,QACX,WAAW;AAAA,UACT;AAAA,UACA,qBAAqB;AAAA,UACrB,OACI,sFACA,SACE,6EACA;AAAA,QAAA;AAAA,QAGP,UAAA,KAAK;AAAA,MAAA;AAAA,MAdD;AAAA,IAAA;AAAA,EAiBX,CAAC,EAAA,CACH;AAEJ;AAEO,SAAS,SAAS,EAAE,WAAiC;;AAC1D,MAAI,GAAC,aAAQ,SAAR,mBAAc,QAAQ,QAAO;AAClC,SACE,oBAAC,SAAI,WAAU,0BACZ,kBAAQ,KAAK,IAAI,CAAC,OAAO,UACxB;AAAA,IAAC;AAAA,IAAA;AAAA,MAEC,WAAU;AAAA,MAEV,UAAA;AAAA,QAAA,qBAAC,QAAA,EAAK,WAAU,eAAe,UAAA;AAAA,UAAA,MAAM;AAAA,UAAM;AAAA,QAAA,GAAC;AAAA,QAC5C,oBAAC,QAAA,EAAK,WAAW,MAAM,OAAO,cAAc,QACzC,UAAA,MAAM,MAAM,KAAK,IAAI,EAAA,CACxB;AAAA,MAAA;AAAA,IAAA;AAAA,IANK;AAAA,EAAA,CAQR,GACH;AAEJ;"}
@@ -13,6 +13,7 @@ function CommentProvider({
13
13
  onCreate,
14
14
  onReply,
15
15
  onUpdateStatus,
16
+ onClose,
16
17
  onUpdateRating,
17
18
  onDelete,
18
19
  onChecklistToggle,
@@ -113,6 +114,7 @@ function CommentProvider({
113
114
  ...onCreate ? { onCreate } : {},
114
115
  ...onReply ? { onReply } : {},
115
116
  ...onUpdateStatus ? { onUpdateStatus } : {},
117
+ ...onClose ? { onClose } : {},
116
118
  ...onUpdateRating ? { onUpdateRating } : {},
117
119
  ...onDelete ? { onDelete } : {},
118
120
  ...onChecklistToggle ? { onChecklistToggle } : {},
@@ -122,6 +124,7 @@ function CommentProvider({
122
124
  onCreate,
123
125
  onReply,
124
126
  onUpdateStatus,
127
+ onClose,
125
128
  onUpdateRating,
126
129
  onDelete,
127
130
  onChecklistToggle,
@@ -1 +1 @@
1
- {"version":3,"file":"CommentProvider.cjs","sources":["../../src/comments/CommentProvider.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { deriveAnchorCounts, deriveAnchorMeta } from \"./comment-utils\";\nimport {\n CommentContext,\n CommentAnchorActionsContext,\n CommentAnchorStoreContext,\n createCommentAnchorStore,\n exactAnchorResolver,\n type AnchorResolver,\n type CommentAnchorActions,\n type CommentContextValue,\n type CommentRailMode,\n type CommentScrollOptions,\n} from \"./comment-context\";\nimport {\n type Comment,\n type CommentAnchor,\n type CommentCallbacks,\n type CommentConfig,\n} from \"./comment-types\";\n\nexport type CommentProviderProps = CommentCallbacks & {\n children: ReactNode;\n /** All comments for the document; counts and metadata are derived from these. */\n comments: Comment[];\n config: CommentConfig;\n /** Strategy to map a requested anchor onto a registered one. Defaults to exact. */\n resolveAnchor?: AnchorResolver;\n /** Reports rail actions so a host can synchronize external visibility state. */\n onRailModeChange?: (mode: CommentRailMode) => void;\n /**\n * Rail mode on mount. Use \"all\" where the host already decided the rail is\n * visible, so the reader is not asked to open it a second time.\n */\n initialRailMode?: CommentRailMode;\n};\n\n/**\n * Supplies comment state to a document subtree: derived per-anchor counts and\n * metadata, rail open/focus state, and a generic anchor registry (cells call\n * `registerAnchor` so the side panel can scroll-align to them). Owns no\n * fetching — `comments` and the callbacks are controlled by the consumer.\n */\nexport function CommentProvider({\n children,\n comments,\n config,\n resolveAnchor = exactAnchorResolver,\n onRailModeChange,\n onCreate,\n onReply,\n onUpdateStatus,\n onUpdateRating,\n onDelete,\n onChecklistToggle,\n onMention,\n initialRailMode = \"closed\",\n}: CommentProviderProps) {\n const [railMode, setRailMode] = useState<CommentRailMode>(initialRailMode);\n const [focusedAnchor, setFocusedAnchor] = useState<CommentAnchor | null>(\n null,\n );\n const [highlightAnchor, setHighlightAnchor] = useState<CommentAnchor | null>(\n null,\n );\n const railModeRef = useRef<CommentRailMode>(initialRailMode);\n const onRailModeChangeRef = useRef(onRailModeChange);\n const resolveAnchorRef = useRef(resolveAnchor);\n const anchorEls = useRef<Record<string, HTMLElement>>({});\n const contentRef = useRef<HTMLDivElement | null>(null);\n\n const commentCounts = useMemo(() => deriveAnchorCounts(comments), [comments]);\n const commentMeta = useMemo(() => deriveAnchorMeta(comments), [comments]);\n const [anchorStore] = useState(createCommentAnchorStore);\n\n useLayoutEffect(() => {\n onRailModeChangeRef.current = onRailModeChange;\n resolveAnchorRef.current = resolveAnchor;\n }, [onRailModeChange, resolveAnchor]);\n\n const focusAnchor = useCallback((anchor: CommentAnchor) => {\n setFocusedAnchor(anchor);\n railModeRef.current = \"focused\";\n setRailMode(\"focused\");\n onRailModeChangeRef.current?.(\"focused\");\n }, []);\n\n const openCommentList = useCallback(() => {\n const next = railModeRef.current === \"all\" ? \"closed\" : \"all\";\n railModeRef.current = next;\n setRailMode(next);\n onRailModeChangeRef.current?.(next);\n }, []);\n\n const closeRail = useCallback(() => {\n railModeRef.current = \"closed\";\n setRailMode(\"closed\");\n onRailModeChangeRef.current?.(\"closed\");\n }, []);\n\n const registerAnchor = useCallback(\n (anchor: CommentAnchor, el: HTMLElement | null) => {\n if (!el) {\n delete anchorEls.current[anchor];\n return;\n }\n const registered = anchorEls.current[anchor];\n if (registered && registered !== el) {\n throw new Error(\n `Comment anchor \"${anchor}\" is registered by multiple elements`,\n );\n }\n anchorEls.current[anchor] = el;\n },\n [],\n );\n\n const getAnchorTop = useCallback((anchor: CommentAnchor): number | null => {\n const resolved = resolveAnchorRef.current(\n anchor,\n Object.keys(anchorEls.current),\n );\n const el = resolved ? anchorEls.current[resolved] : null;\n const container = contentRef.current;\n if (!el || !container) return null;\n return (\n el.getBoundingClientRect().top - container.getBoundingClientRect().top\n );\n }, []);\n\n const scrollToAnchor = useCallback(\n (anchor: CommentAnchor, options: CommentScrollOptions = {}): boolean => {\n const resolved = resolveAnchorRef.current(\n anchor,\n Object.keys(anchorEls.current),\n );\n const el = resolved ? anchorEls.current[resolved] : null;\n const container = contentRef.current;\n if (!el || !container) return false;\n const containerRect = container.getBoundingClientRect();\n const anchorRect = el.getBoundingClientRect();\n const offset = options.offset ?? 0;\n const top =\n options.block === \"center\"\n ? container.scrollTop +\n anchorRect.top -\n containerRect.top +\n anchorRect.height / 2 -\n container.clientHeight / 2 -\n offset\n : container.scrollTop + anchorRect.top - containerRect.top - offset;\n container.scrollTo({\n top: Math.max(0, top),\n behavior: options.behavior ?? \"smooth\",\n });\n return true;\n },\n [],\n );\n\n // A focused rail with no anchor selected has nothing to show.\n useEffect(() => {\n if (railMode === \"focused\" && !focusedAnchor) closeRail();\n }, [closeRail, railMode, focusedAnchor]);\n\n const callbacks = useMemo<CommentCallbacks>(\n () => ({\n ...(onCreate ? { onCreate } : {}),\n ...(onReply ? { onReply } : {}),\n ...(onUpdateStatus ? { onUpdateStatus } : {}),\n ...(onUpdateRating ? { onUpdateRating } : {}),\n ...(onDelete ? { onDelete } : {}),\n ...(onChecklistToggle ? { onChecklistToggle } : {}),\n ...(onMention ? { onMention } : {}),\n }),\n [\n onCreate,\n onReply,\n onUpdateStatus,\n onUpdateRating,\n onDelete,\n onChecklistToggle,\n onMention,\n ],\n );\n\n useLayoutEffect(() => {\n anchorStore.update(commentMeta, railMode, focusedAnchor, highlightAnchor);\n }, [anchorStore, commentMeta, railMode, focusedAnchor, highlightAnchor]);\n\n const [anchorActions] = useState<CommentAnchorActions>(() => ({\n focusAnchor,\n registerAnchor,\n scrollToAnchor,\n contentRef,\n }));\n\n const value = useMemo<CommentContextValue>(\n () => ({\n comments,\n config,\n callbacks,\n commentCounts,\n commentMeta,\n railMode,\n focusedAnchor,\n focusAnchor,\n openCommentList,\n closeRail,\n registerAnchor,\n getAnchorTop,\n scrollToAnchor,\n contentRef,\n highlightAnchor,\n setHighlightAnchor,\n }),\n [\n comments,\n config,\n callbacks,\n commentCounts,\n commentMeta,\n railMode,\n focusedAnchor,\n focusAnchor,\n openCommentList,\n closeRail,\n registerAnchor,\n getAnchorTop,\n scrollToAnchor,\n highlightAnchor,\n ],\n );\n\n return (\n <CommentAnchorStoreContext.Provider value={anchorStore}>\n <CommentAnchorActionsContext.Provider value={anchorActions}>\n <CommentContext.Provider value={value}>\n {children}\n </CommentContext.Provider>\n </CommentAnchorActionsContext.Provider>\n </CommentAnchorStoreContext.Provider>\n );\n}\n"],"names":["exactAnchorResolver","useState","useRef","useMemo","deriveAnchorCounts","deriveAnchorMeta","createCommentAnchorStore","useLayoutEffect","useCallback","useEffect","CommentAnchorStoreContext","CommentAnchorActionsContext","CommentContext"],"mappings":"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"}
1
+ {"version":3,"file":"CommentProvider.cjs","sources":["../../src/comments/CommentProvider.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { deriveAnchorCounts, deriveAnchorMeta } from \"./comment-utils\";\nimport {\n CommentContext,\n CommentAnchorActionsContext,\n CommentAnchorStoreContext,\n createCommentAnchorStore,\n exactAnchorResolver,\n type AnchorResolver,\n type CommentAnchorActions,\n type CommentContextValue,\n type CommentRailMode,\n type CommentScrollOptions,\n} from \"./comment-context\";\nimport {\n type Comment,\n type CommentAnchor,\n type CommentCallbacks,\n type CommentConfig,\n} from \"./comment-types\";\n\nexport type CommentProviderProps = CommentCallbacks & {\n children: ReactNode;\n /** All comments for the document; counts and metadata are derived from these. */\n comments: Comment[];\n config: CommentConfig;\n /** Strategy to map a requested anchor onto a registered one. Defaults to exact. */\n resolveAnchor?: AnchorResolver;\n /** Reports rail actions so a host can synchronize external visibility state. */\n onRailModeChange?: (mode: CommentRailMode) => void;\n /**\n * Rail mode on mount. Use \"all\" where the host already decided the rail is\n * visible, so the reader is not asked to open it a second time.\n */\n initialRailMode?: CommentRailMode;\n};\n\n/**\n * Supplies comment state to a document subtree: derived per-anchor counts and\n * metadata, rail open/focus state, and a generic anchor registry (cells call\n * `registerAnchor` so the side panel can scroll-align to them). Owns no\n * fetching — `comments` and the callbacks are controlled by the consumer.\n */\nexport function CommentProvider({\n children,\n comments,\n config,\n resolveAnchor = exactAnchorResolver,\n onRailModeChange,\n onCreate,\n onReply,\n onUpdateStatus,\n onClose,\n onUpdateRating,\n onDelete,\n onChecklistToggle,\n onMention,\n initialRailMode = \"closed\",\n}: CommentProviderProps) {\n const [railMode, setRailMode] = useState<CommentRailMode>(initialRailMode);\n const [focusedAnchor, setFocusedAnchor] = useState<CommentAnchor | null>(\n null,\n );\n const [highlightAnchor, setHighlightAnchor] = useState<CommentAnchor | null>(\n null,\n );\n const railModeRef = useRef<CommentRailMode>(initialRailMode);\n const onRailModeChangeRef = useRef(onRailModeChange);\n const resolveAnchorRef = useRef(resolveAnchor);\n const anchorEls = useRef<Record<string, HTMLElement>>({});\n const contentRef = useRef<HTMLDivElement | null>(null);\n\n const commentCounts = useMemo(() => deriveAnchorCounts(comments), [comments]);\n const commentMeta = useMemo(() => deriveAnchorMeta(comments), [comments]);\n const [anchorStore] = useState(createCommentAnchorStore);\n\n useLayoutEffect(() => {\n onRailModeChangeRef.current = onRailModeChange;\n resolveAnchorRef.current = resolveAnchor;\n }, [onRailModeChange, resolveAnchor]);\n\n const focusAnchor = useCallback((anchor: CommentAnchor) => {\n setFocusedAnchor(anchor);\n railModeRef.current = \"focused\";\n setRailMode(\"focused\");\n onRailModeChangeRef.current?.(\"focused\");\n }, []);\n\n const openCommentList = useCallback(() => {\n const next = railModeRef.current === \"all\" ? \"closed\" : \"all\";\n railModeRef.current = next;\n setRailMode(next);\n onRailModeChangeRef.current?.(next);\n }, []);\n\n const closeRail = useCallback(() => {\n railModeRef.current = \"closed\";\n setRailMode(\"closed\");\n onRailModeChangeRef.current?.(\"closed\");\n }, []);\n\n const registerAnchor = useCallback(\n (anchor: CommentAnchor, el: HTMLElement | null) => {\n if (!el) {\n delete anchorEls.current[anchor];\n return;\n }\n const registered = anchorEls.current[anchor];\n if (registered && registered !== el) {\n throw new Error(\n `Comment anchor \"${anchor}\" is registered by multiple elements`,\n );\n }\n anchorEls.current[anchor] = el;\n },\n [],\n );\n\n const getAnchorTop = useCallback((anchor: CommentAnchor): number | null => {\n const resolved = resolveAnchorRef.current(\n anchor,\n Object.keys(anchorEls.current),\n );\n const el = resolved ? anchorEls.current[resolved] : null;\n const container = contentRef.current;\n if (!el || !container) return null;\n return (\n el.getBoundingClientRect().top - container.getBoundingClientRect().top\n );\n }, []);\n\n const scrollToAnchor = useCallback(\n (anchor: CommentAnchor, options: CommentScrollOptions = {}): boolean => {\n const resolved = resolveAnchorRef.current(\n anchor,\n Object.keys(anchorEls.current),\n );\n const el = resolved ? anchorEls.current[resolved] : null;\n const container = contentRef.current;\n if (!el || !container) return false;\n const containerRect = container.getBoundingClientRect();\n const anchorRect = el.getBoundingClientRect();\n const offset = options.offset ?? 0;\n const top =\n options.block === \"center\"\n ? container.scrollTop +\n anchorRect.top -\n containerRect.top +\n anchorRect.height / 2 -\n container.clientHeight / 2 -\n offset\n : container.scrollTop + anchorRect.top - containerRect.top - offset;\n container.scrollTo({\n top: Math.max(0, top),\n behavior: options.behavior ?? \"smooth\",\n });\n return true;\n },\n [],\n );\n\n // A focused rail with no anchor selected has nothing to show.\n useEffect(() => {\n if (railMode === \"focused\" && !focusedAnchor) closeRail();\n }, [closeRail, railMode, focusedAnchor]);\n\n const callbacks = useMemo<CommentCallbacks>(\n () => ({\n ...(onCreate ? { onCreate } : {}),\n ...(onReply ? { onReply } : {}),\n ...(onUpdateStatus ? { onUpdateStatus } : {}),\n ...(onClose ? { onClose } : {}),\n ...(onUpdateRating ? { onUpdateRating } : {}),\n ...(onDelete ? { onDelete } : {}),\n ...(onChecklistToggle ? { onChecklistToggle } : {}),\n ...(onMention ? { onMention } : {}),\n }),\n [\n onCreate,\n onReply,\n onUpdateStatus,\n onClose,\n onUpdateRating,\n onDelete,\n onChecklistToggle,\n onMention,\n ],\n );\n\n useLayoutEffect(() => {\n anchorStore.update(commentMeta, railMode, focusedAnchor, highlightAnchor);\n }, [anchorStore, commentMeta, railMode, focusedAnchor, highlightAnchor]);\n\n const [anchorActions] = useState<CommentAnchorActions>(() => ({\n focusAnchor,\n registerAnchor,\n scrollToAnchor,\n contentRef,\n }));\n\n const value = useMemo<CommentContextValue>(\n () => ({\n comments,\n config,\n callbacks,\n commentCounts,\n commentMeta,\n railMode,\n focusedAnchor,\n focusAnchor,\n openCommentList,\n closeRail,\n registerAnchor,\n getAnchorTop,\n scrollToAnchor,\n contentRef,\n highlightAnchor,\n setHighlightAnchor,\n }),\n [\n comments,\n config,\n callbacks,\n commentCounts,\n commentMeta,\n railMode,\n focusedAnchor,\n focusAnchor,\n openCommentList,\n closeRail,\n registerAnchor,\n getAnchorTop,\n scrollToAnchor,\n highlightAnchor,\n ],\n );\n\n return (\n <CommentAnchorStoreContext.Provider value={anchorStore}>\n <CommentAnchorActionsContext.Provider value={anchorActions}>\n <CommentContext.Provider value={value}>\n {children}\n </CommentContext.Provider>\n </CommentAnchorActionsContext.Provider>\n </CommentAnchorStoreContext.Provider>\n );\n}\n"],"names":["exactAnchorResolver","useState","useRef","useMemo","deriveAnchorCounts","deriveAnchorMeta","createCommentAnchorStore","useLayoutEffect","useCallback","useEffect","CommentAnchorStoreContext","CommentAnchorActionsContext","CommentContext"],"mappings":"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"}
@@ -22,5 +22,5 @@ export type CommentProviderProps = CommentCallbacks & {
22
22
  * `registerAnchor` so the side panel can scroll-align to them). Owns no
23
23
  * fetching — `comments` and the callbacks are controlled by the consumer.
24
24
  */
25
- export declare function CommentProvider({ children, comments, config, resolveAnchor, onRailModeChange, onCreate, onReply, onUpdateStatus, onUpdateRating, onDelete, onChecklistToggle, onMention, initialRailMode, }: CommentProviderProps): import("react").JSX.Element;
25
+ export declare function CommentProvider({ children, comments, config, resolveAnchor, onRailModeChange, onCreate, onReply, onUpdateStatus, onClose, onUpdateRating, onDelete, onChecklistToggle, onMention, initialRailMode, }: CommentProviderProps): import("react").JSX.Element;
26
26
  //# sourceMappingURL=CommentProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CommentProvider.d.ts","sourceRoot":"","sources":["../../src/comments/CommentProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAML,KAAK,cAAc,EAGnB,KAAK,eAAe,EAErB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,OAAO,EAEZ,KAAK,gBAAgB,EACrB,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG;IACpD,QAAQ,EAAE,SAAS,CAAC;IACpB,iFAAiF;IACjF,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;IACtB,mFAAmF;IACnF,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD;;;OAGG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,aAAmC,EACnC,gBAAgB,EAChB,QAAQ,EACR,OAAO,EACP,cAAc,EACd,cAAc,EACd,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,eAA0B,GAC3B,EAAE,oBAAoB,+BA0LtB"}
1
+ {"version":3,"file":"CommentProvider.d.ts","sourceRoot":"","sources":["../../src/comments/CommentProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAML,KAAK,cAAc,EAGnB,KAAK,eAAe,EAErB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,OAAO,EAEZ,KAAK,gBAAgB,EACrB,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG;IACpD,QAAQ,EAAE,SAAS,CAAC;IACpB,iFAAiF;IACjF,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;IACtB,mFAAmF;IACnF,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD;;;OAGG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,aAAmC,EACnC,gBAAgB,EAChB,QAAQ,EACR,OAAO,EACP,cAAc,EACd,OAAO,EACP,cAAc,EACd,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,eAA0B,GAC3B,EAAE,oBAAoB,+BA4LtB"}
@@ -11,6 +11,7 @@ function CommentProvider({
11
11
  onCreate,
12
12
  onReply,
13
13
  onUpdateStatus,
14
+ onClose,
14
15
  onUpdateRating,
15
16
  onDelete,
16
17
  onChecklistToggle,
@@ -111,6 +112,7 @@ function CommentProvider({
111
112
  ...onCreate ? { onCreate } : {},
112
113
  ...onReply ? { onReply } : {},
113
114
  ...onUpdateStatus ? { onUpdateStatus } : {},
115
+ ...onClose ? { onClose } : {},
114
116
  ...onUpdateRating ? { onUpdateRating } : {},
115
117
  ...onDelete ? { onDelete } : {},
116
118
  ...onChecklistToggle ? { onChecklistToggle } : {},
@@ -120,6 +122,7 @@ function CommentProvider({
120
122
  onCreate,
121
123
  onReply,
122
124
  onUpdateStatus,
125
+ onClose,
123
126
  onUpdateRating,
124
127
  onDelete,
125
128
  onChecklistToggle,
@@ -1 +1 @@
1
- {"version":3,"file":"CommentProvider.js","sources":["../../src/comments/CommentProvider.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { deriveAnchorCounts, deriveAnchorMeta } from \"./comment-utils\";\nimport {\n CommentContext,\n CommentAnchorActionsContext,\n CommentAnchorStoreContext,\n createCommentAnchorStore,\n exactAnchorResolver,\n type AnchorResolver,\n type CommentAnchorActions,\n type CommentContextValue,\n type CommentRailMode,\n type CommentScrollOptions,\n} from \"./comment-context\";\nimport {\n type Comment,\n type CommentAnchor,\n type CommentCallbacks,\n type CommentConfig,\n} from \"./comment-types\";\n\nexport type CommentProviderProps = CommentCallbacks & {\n children: ReactNode;\n /** All comments for the document; counts and metadata are derived from these. */\n comments: Comment[];\n config: CommentConfig;\n /** Strategy to map a requested anchor onto a registered one. Defaults to exact. */\n resolveAnchor?: AnchorResolver;\n /** Reports rail actions so a host can synchronize external visibility state. */\n onRailModeChange?: (mode: CommentRailMode) => void;\n /**\n * Rail mode on mount. Use \"all\" where the host already decided the rail is\n * visible, so the reader is not asked to open it a second time.\n */\n initialRailMode?: CommentRailMode;\n};\n\n/**\n * Supplies comment state to a document subtree: derived per-anchor counts and\n * metadata, rail open/focus state, and a generic anchor registry (cells call\n * `registerAnchor` so the side panel can scroll-align to them). Owns no\n * fetching — `comments` and the callbacks are controlled by the consumer.\n */\nexport function CommentProvider({\n children,\n comments,\n config,\n resolveAnchor = exactAnchorResolver,\n onRailModeChange,\n onCreate,\n onReply,\n onUpdateStatus,\n onUpdateRating,\n onDelete,\n onChecklistToggle,\n onMention,\n initialRailMode = \"closed\",\n}: CommentProviderProps) {\n const [railMode, setRailMode] = useState<CommentRailMode>(initialRailMode);\n const [focusedAnchor, setFocusedAnchor] = useState<CommentAnchor | null>(\n null,\n );\n const [highlightAnchor, setHighlightAnchor] = useState<CommentAnchor | null>(\n null,\n );\n const railModeRef = useRef<CommentRailMode>(initialRailMode);\n const onRailModeChangeRef = useRef(onRailModeChange);\n const resolveAnchorRef = useRef(resolveAnchor);\n const anchorEls = useRef<Record<string, HTMLElement>>({});\n const contentRef = useRef<HTMLDivElement | null>(null);\n\n const commentCounts = useMemo(() => deriveAnchorCounts(comments), [comments]);\n const commentMeta = useMemo(() => deriveAnchorMeta(comments), [comments]);\n const [anchorStore] = useState(createCommentAnchorStore);\n\n useLayoutEffect(() => {\n onRailModeChangeRef.current = onRailModeChange;\n resolveAnchorRef.current = resolveAnchor;\n }, [onRailModeChange, resolveAnchor]);\n\n const focusAnchor = useCallback((anchor: CommentAnchor) => {\n setFocusedAnchor(anchor);\n railModeRef.current = \"focused\";\n setRailMode(\"focused\");\n onRailModeChangeRef.current?.(\"focused\");\n }, []);\n\n const openCommentList = useCallback(() => {\n const next = railModeRef.current === \"all\" ? \"closed\" : \"all\";\n railModeRef.current = next;\n setRailMode(next);\n onRailModeChangeRef.current?.(next);\n }, []);\n\n const closeRail = useCallback(() => {\n railModeRef.current = \"closed\";\n setRailMode(\"closed\");\n onRailModeChangeRef.current?.(\"closed\");\n }, []);\n\n const registerAnchor = useCallback(\n (anchor: CommentAnchor, el: HTMLElement | null) => {\n if (!el) {\n delete anchorEls.current[anchor];\n return;\n }\n const registered = anchorEls.current[anchor];\n if (registered && registered !== el) {\n throw new Error(\n `Comment anchor \"${anchor}\" is registered by multiple elements`,\n );\n }\n anchorEls.current[anchor] = el;\n },\n [],\n );\n\n const getAnchorTop = useCallback((anchor: CommentAnchor): number | null => {\n const resolved = resolveAnchorRef.current(\n anchor,\n Object.keys(anchorEls.current),\n );\n const el = resolved ? anchorEls.current[resolved] : null;\n const container = contentRef.current;\n if (!el || !container) return null;\n return (\n el.getBoundingClientRect().top - container.getBoundingClientRect().top\n );\n }, []);\n\n const scrollToAnchor = useCallback(\n (anchor: CommentAnchor, options: CommentScrollOptions = {}): boolean => {\n const resolved = resolveAnchorRef.current(\n anchor,\n Object.keys(anchorEls.current),\n );\n const el = resolved ? anchorEls.current[resolved] : null;\n const container = contentRef.current;\n if (!el || !container) return false;\n const containerRect = container.getBoundingClientRect();\n const anchorRect = el.getBoundingClientRect();\n const offset = options.offset ?? 0;\n const top =\n options.block === \"center\"\n ? container.scrollTop +\n anchorRect.top -\n containerRect.top +\n anchorRect.height / 2 -\n container.clientHeight / 2 -\n offset\n : container.scrollTop + anchorRect.top - containerRect.top - offset;\n container.scrollTo({\n top: Math.max(0, top),\n behavior: options.behavior ?? \"smooth\",\n });\n return true;\n },\n [],\n );\n\n // A focused rail with no anchor selected has nothing to show.\n useEffect(() => {\n if (railMode === \"focused\" && !focusedAnchor) closeRail();\n }, [closeRail, railMode, focusedAnchor]);\n\n const callbacks = useMemo<CommentCallbacks>(\n () => ({\n ...(onCreate ? { onCreate } : {}),\n ...(onReply ? { onReply } : {}),\n ...(onUpdateStatus ? { onUpdateStatus } : {}),\n ...(onUpdateRating ? { onUpdateRating } : {}),\n ...(onDelete ? { onDelete } : {}),\n ...(onChecklistToggle ? { onChecklistToggle } : {}),\n ...(onMention ? { onMention } : {}),\n }),\n [\n onCreate,\n onReply,\n onUpdateStatus,\n onUpdateRating,\n onDelete,\n onChecklistToggle,\n onMention,\n ],\n );\n\n useLayoutEffect(() => {\n anchorStore.update(commentMeta, railMode, focusedAnchor, highlightAnchor);\n }, [anchorStore, commentMeta, railMode, focusedAnchor, highlightAnchor]);\n\n const [anchorActions] = useState<CommentAnchorActions>(() => ({\n focusAnchor,\n registerAnchor,\n scrollToAnchor,\n contentRef,\n }));\n\n const value = useMemo<CommentContextValue>(\n () => ({\n comments,\n config,\n callbacks,\n commentCounts,\n commentMeta,\n railMode,\n focusedAnchor,\n focusAnchor,\n openCommentList,\n closeRail,\n registerAnchor,\n getAnchorTop,\n scrollToAnchor,\n contentRef,\n highlightAnchor,\n setHighlightAnchor,\n }),\n [\n comments,\n config,\n callbacks,\n commentCounts,\n commentMeta,\n railMode,\n focusedAnchor,\n focusAnchor,\n openCommentList,\n closeRail,\n registerAnchor,\n getAnchorTop,\n scrollToAnchor,\n highlightAnchor,\n ],\n );\n\n return (\n <CommentAnchorStoreContext.Provider value={anchorStore}>\n <CommentAnchorActionsContext.Provider value={anchorActions}>\n <CommentContext.Provider value={value}>\n {children}\n </CommentContext.Provider>\n </CommentAnchorActionsContext.Provider>\n </CommentAnchorStoreContext.Provider>\n );\n}\n"],"names":[],"mappings":";;;;AAmDO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AACpB,GAAyB;AACvB,QAAM,CAAC,UAAU,WAAW,IAAI,SAA0B,eAAe;AACzE,QAAM,CAAC,eAAe,gBAAgB,IAAI;AAAA,IACxC;AAAA,EAAA;AAEF,QAAM,CAAC,iBAAiB,kBAAkB,IAAI;AAAA,IAC5C;AAAA,EAAA;AAEF,QAAM,cAAc,OAAwB,eAAe;AAC3D,QAAM,sBAAsB,OAAO,gBAAgB;AACnD,QAAM,mBAAmB,OAAO,aAAa;AAC7C,QAAM,YAAY,OAAoC,EAAE;AACxD,QAAM,aAAa,OAA8B,IAAI;AAErD,QAAM,gBAAgB,QAAQ,MAAM,mBAAmB,QAAQ,GAAG,CAAC,QAAQ,CAAC;AAC5E,QAAM,cAAc,QAAQ,MAAM,iBAAiB,QAAQ,GAAG,CAAC,QAAQ,CAAC;AACxE,QAAM,CAAC,WAAW,IAAI,SAAS,wBAAwB;AAEvD,kBAAgB,MAAM;AACpB,wBAAoB,UAAU;AAC9B,qBAAiB,UAAU;AAAA,EAC7B,GAAG,CAAC,kBAAkB,aAAa,CAAC;AAEpC,QAAM,cAAc,YAAY,CAAC,WAA0B;;AACzD,qBAAiB,MAAM;AACvB,gBAAY,UAAU;AACtB,gBAAY,SAAS;AACrB,8BAAoB,YAApB,6CAA8B;AAAA,EAChC,GAAG,CAAA,CAAE;AAEL,QAAM,kBAAkB,YAAY,MAAM;;AACxC,UAAM,OAAO,YAAY,YAAY,QAAQ,WAAW;AACxD,gBAAY,UAAU;AACtB,gBAAY,IAAI;AAChB,8BAAoB,YAApB,6CAA8B;AAAA,EAChC,GAAG,CAAA,CAAE;AAEL,QAAM,YAAY,YAAY,MAAM;;AAClC,gBAAY,UAAU;AACtB,gBAAY,QAAQ;AACpB,8BAAoB,YAApB,6CAA8B;AAAA,EAChC,GAAG,CAAA,CAAE;AAEL,QAAM,iBAAiB;AAAA,IACrB,CAAC,QAAuB,OAA2B;AACjD,UAAI,CAAC,IAAI;AACP,eAAO,UAAU,QAAQ,MAAM;AAC/B;AAAA,MACF;AACA,YAAM,aAAa,UAAU,QAAQ,MAAM;AAC3C,UAAI,cAAc,eAAe,IAAI;AACnC,cAAM,IAAI;AAAA,UACR,mBAAmB,MAAM;AAAA,QAAA;AAAA,MAE7B;AACA,gBAAU,QAAQ,MAAM,IAAI;AAAA,IAC9B;AAAA,IACA,CAAA;AAAA,EAAC;AAGH,QAAM,eAAe,YAAY,CAAC,WAAyC;AACzE,UAAM,WAAW,iBAAiB;AAAA,MAChC;AAAA,MACA,OAAO,KAAK,UAAU,OAAO;AAAA,IAAA;AAE/B,UAAM,KAAK,WAAW,UAAU,QAAQ,QAAQ,IAAI;AACpD,UAAM,YAAY,WAAW;AAC7B,QAAI,CAAC,MAAM,CAAC,UAAW,QAAO;AAC9B,WACE,GAAG,sBAAA,EAAwB,MAAM,UAAU,wBAAwB;AAAA,EAEvE,GAAG,CAAA,CAAE;AAEL,QAAM,iBAAiB;AAAA,IACrB,CAAC,QAAuB,UAAgC,OAAgB;AACtE,YAAM,WAAW,iBAAiB;AAAA,QAChC;AAAA,QACA,OAAO,KAAK,UAAU,OAAO;AAAA,MAAA;AAE/B,YAAM,KAAK,WAAW,UAAU,QAAQ,QAAQ,IAAI;AACpD,YAAM,YAAY,WAAW;AAC7B,UAAI,CAAC,MAAM,CAAC,UAAW,QAAO;AAC9B,YAAM,gBAAgB,UAAU,sBAAA;AAChC,YAAM,aAAa,GAAG,sBAAA;AACtB,YAAM,SAAS,QAAQ,UAAU;AACjC,YAAM,MACJ,QAAQ,UAAU,WACd,UAAU,YACV,WAAW,MACX,cAAc,MACd,WAAW,SAAS,IACpB,UAAU,eAAe,IACzB,SACA,UAAU,YAAY,WAAW,MAAM,cAAc,MAAM;AACjE,gBAAU,SAAS;AAAA,QACjB,KAAK,KAAK,IAAI,GAAG,GAAG;AAAA,QACpB,UAAU,QAAQ,YAAY;AAAA,MAAA,CAC/B;AACD,aAAO;AAAA,IACT;AAAA,IACA,CAAA;AAAA,EAAC;AAIH,YAAU,MAAM;AACd,QAAI,aAAa,aAAa,CAAC,cAAe,WAAA;AAAA,EAChD,GAAG,CAAC,WAAW,UAAU,aAAa,CAAC;AAEvC,QAAM,YAAY;AAAA,IAChB,OAAO;AAAA,MACL,GAAI,WAAW,EAAE,SAAA,IAAa,CAAA;AAAA,MAC9B,GAAI,UAAU,EAAE,QAAA,IAAY,CAAA;AAAA,MAC5B,GAAI,iBAAiB,EAAE,eAAA,IAAmB,CAAA;AAAA,MAC1C,GAAI,iBAAiB,EAAE,eAAA,IAAmB,CAAA;AAAA,MAC1C,GAAI,WAAW,EAAE,SAAA,IAAa,CAAA;AAAA,MAC9B,GAAI,oBAAoB,EAAE,kBAAA,IAAsB,CAAA;AAAA,MAChD,GAAI,YAAY,EAAE,cAAc,CAAA;AAAA,IAAC;AAAA,IAEnC;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EACF;AAGF,kBAAgB,MAAM;AACpB,gBAAY,OAAO,aAAa,UAAU,eAAe,eAAe;AAAA,EAC1E,GAAG,CAAC,aAAa,aAAa,UAAU,eAAe,eAAe,CAAC;AAEvE,QAAM,CAAC,aAAa,IAAI,SAA+B,OAAO;AAAA,IAC5D;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,EACA;AAEF,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAEF;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EACF;AAGF,6BACG,0BAA0B,UAA1B,EAAmC,OAAO,aACzC,8BAAC,4BAA4B,UAA5B,EAAqC,OAAO,eAC3C,8BAAC,eAAe,UAAf,EAAwB,OACtB,SAAA,CACH,GACF,GACF;AAEJ;"}
1
+ {"version":3,"file":"CommentProvider.js","sources":["../../src/comments/CommentProvider.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { deriveAnchorCounts, deriveAnchorMeta } from \"./comment-utils\";\nimport {\n CommentContext,\n CommentAnchorActionsContext,\n CommentAnchorStoreContext,\n createCommentAnchorStore,\n exactAnchorResolver,\n type AnchorResolver,\n type CommentAnchorActions,\n type CommentContextValue,\n type CommentRailMode,\n type CommentScrollOptions,\n} from \"./comment-context\";\nimport {\n type Comment,\n type CommentAnchor,\n type CommentCallbacks,\n type CommentConfig,\n} from \"./comment-types\";\n\nexport type CommentProviderProps = CommentCallbacks & {\n children: ReactNode;\n /** All comments for the document; counts and metadata are derived from these. */\n comments: Comment[];\n config: CommentConfig;\n /** Strategy to map a requested anchor onto a registered one. Defaults to exact. */\n resolveAnchor?: AnchorResolver;\n /** Reports rail actions so a host can synchronize external visibility state. */\n onRailModeChange?: (mode: CommentRailMode) => void;\n /**\n * Rail mode on mount. Use \"all\" where the host already decided the rail is\n * visible, so the reader is not asked to open it a second time.\n */\n initialRailMode?: CommentRailMode;\n};\n\n/**\n * Supplies comment state to a document subtree: derived per-anchor counts and\n * metadata, rail open/focus state, and a generic anchor registry (cells call\n * `registerAnchor` so the side panel can scroll-align to them). Owns no\n * fetching — `comments` and the callbacks are controlled by the consumer.\n */\nexport function CommentProvider({\n children,\n comments,\n config,\n resolveAnchor = exactAnchorResolver,\n onRailModeChange,\n onCreate,\n onReply,\n onUpdateStatus,\n onClose,\n onUpdateRating,\n onDelete,\n onChecklistToggle,\n onMention,\n initialRailMode = \"closed\",\n}: CommentProviderProps) {\n const [railMode, setRailMode] = useState<CommentRailMode>(initialRailMode);\n const [focusedAnchor, setFocusedAnchor] = useState<CommentAnchor | null>(\n null,\n );\n const [highlightAnchor, setHighlightAnchor] = useState<CommentAnchor | null>(\n null,\n );\n const railModeRef = useRef<CommentRailMode>(initialRailMode);\n const onRailModeChangeRef = useRef(onRailModeChange);\n const resolveAnchorRef = useRef(resolveAnchor);\n const anchorEls = useRef<Record<string, HTMLElement>>({});\n const contentRef = useRef<HTMLDivElement | null>(null);\n\n const commentCounts = useMemo(() => deriveAnchorCounts(comments), [comments]);\n const commentMeta = useMemo(() => deriveAnchorMeta(comments), [comments]);\n const [anchorStore] = useState(createCommentAnchorStore);\n\n useLayoutEffect(() => {\n onRailModeChangeRef.current = onRailModeChange;\n resolveAnchorRef.current = resolveAnchor;\n }, [onRailModeChange, resolveAnchor]);\n\n const focusAnchor = useCallback((anchor: CommentAnchor) => {\n setFocusedAnchor(anchor);\n railModeRef.current = \"focused\";\n setRailMode(\"focused\");\n onRailModeChangeRef.current?.(\"focused\");\n }, []);\n\n const openCommentList = useCallback(() => {\n const next = railModeRef.current === \"all\" ? \"closed\" : \"all\";\n railModeRef.current = next;\n setRailMode(next);\n onRailModeChangeRef.current?.(next);\n }, []);\n\n const closeRail = useCallback(() => {\n railModeRef.current = \"closed\";\n setRailMode(\"closed\");\n onRailModeChangeRef.current?.(\"closed\");\n }, []);\n\n const registerAnchor = useCallback(\n (anchor: CommentAnchor, el: HTMLElement | null) => {\n if (!el) {\n delete anchorEls.current[anchor];\n return;\n }\n const registered = anchorEls.current[anchor];\n if (registered && registered !== el) {\n throw new Error(\n `Comment anchor \"${anchor}\" is registered by multiple elements`,\n );\n }\n anchorEls.current[anchor] = el;\n },\n [],\n );\n\n const getAnchorTop = useCallback((anchor: CommentAnchor): number | null => {\n const resolved = resolveAnchorRef.current(\n anchor,\n Object.keys(anchorEls.current),\n );\n const el = resolved ? anchorEls.current[resolved] : null;\n const container = contentRef.current;\n if (!el || !container) return null;\n return (\n el.getBoundingClientRect().top - container.getBoundingClientRect().top\n );\n }, []);\n\n const scrollToAnchor = useCallback(\n (anchor: CommentAnchor, options: CommentScrollOptions = {}): boolean => {\n const resolved = resolveAnchorRef.current(\n anchor,\n Object.keys(anchorEls.current),\n );\n const el = resolved ? anchorEls.current[resolved] : null;\n const container = contentRef.current;\n if (!el || !container) return false;\n const containerRect = container.getBoundingClientRect();\n const anchorRect = el.getBoundingClientRect();\n const offset = options.offset ?? 0;\n const top =\n options.block === \"center\"\n ? container.scrollTop +\n anchorRect.top -\n containerRect.top +\n anchorRect.height / 2 -\n container.clientHeight / 2 -\n offset\n : container.scrollTop + anchorRect.top - containerRect.top - offset;\n container.scrollTo({\n top: Math.max(0, top),\n behavior: options.behavior ?? \"smooth\",\n });\n return true;\n },\n [],\n );\n\n // A focused rail with no anchor selected has nothing to show.\n useEffect(() => {\n if (railMode === \"focused\" && !focusedAnchor) closeRail();\n }, [closeRail, railMode, focusedAnchor]);\n\n const callbacks = useMemo<CommentCallbacks>(\n () => ({\n ...(onCreate ? { onCreate } : {}),\n ...(onReply ? { onReply } : {}),\n ...(onUpdateStatus ? { onUpdateStatus } : {}),\n ...(onClose ? { onClose } : {}),\n ...(onUpdateRating ? { onUpdateRating } : {}),\n ...(onDelete ? { onDelete } : {}),\n ...(onChecklistToggle ? { onChecklistToggle } : {}),\n ...(onMention ? { onMention } : {}),\n }),\n [\n onCreate,\n onReply,\n onUpdateStatus,\n onClose,\n onUpdateRating,\n onDelete,\n onChecklistToggle,\n onMention,\n ],\n );\n\n useLayoutEffect(() => {\n anchorStore.update(commentMeta, railMode, focusedAnchor, highlightAnchor);\n }, [anchorStore, commentMeta, railMode, focusedAnchor, highlightAnchor]);\n\n const [anchorActions] = useState<CommentAnchorActions>(() => ({\n focusAnchor,\n registerAnchor,\n scrollToAnchor,\n contentRef,\n }));\n\n const value = useMemo<CommentContextValue>(\n () => ({\n comments,\n config,\n callbacks,\n commentCounts,\n commentMeta,\n railMode,\n focusedAnchor,\n focusAnchor,\n openCommentList,\n closeRail,\n registerAnchor,\n getAnchorTop,\n scrollToAnchor,\n contentRef,\n highlightAnchor,\n setHighlightAnchor,\n }),\n [\n comments,\n config,\n callbacks,\n commentCounts,\n commentMeta,\n railMode,\n focusedAnchor,\n focusAnchor,\n openCommentList,\n closeRail,\n registerAnchor,\n getAnchorTop,\n scrollToAnchor,\n highlightAnchor,\n ],\n );\n\n return (\n <CommentAnchorStoreContext.Provider value={anchorStore}>\n <CommentAnchorActionsContext.Provider value={anchorActions}>\n <CommentContext.Provider value={value}>\n {children}\n </CommentContext.Provider>\n </CommentAnchorActionsContext.Provider>\n </CommentAnchorStoreContext.Provider>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -85,7 +85,8 @@ function AllComments({
85
85
  ctx,
86
86
  comments,
87
87
  label,
88
- emptyLabel
88
+ emptyLabel,
89
+ threadToMarkdown
89
90
  }) {
90
91
  const ordered = react.useMemo(() => {
91
92
  const anchorOrder = new Map(orderedAnchors(ctx).map((a, i) => [a, i]));
@@ -141,7 +142,8 @@ function AllComments({
141
142
  onMouseLeave: () => ctx.setHighlightAnchor(null),
142
143
  onClick: (event) => activateThread(event, c.anchor ?? commentTypes.DOCUMENT_ANCHOR)
143
144
  }),
144
- ...handlers
145
+ ...handlers,
146
+ ...threadToMarkdown ? { threadToMarkdown } : {}
145
147
  }
146
148
  )
147
149
  }
@@ -152,7 +154,8 @@ function FocusedComments({
152
154
  comments: visible,
153
155
  anchor,
154
156
  label,
155
- compact
157
+ compact,
158
+ threadToMarkdown
156
159
  }) {
157
160
  const comments = commentUtils.selectAnchorThreads(visible, anchor);
158
161
  const hasComments = comments.length > 0;
@@ -173,7 +176,8 @@ function FocusedComments({
173
176
  autoFocusComposer: !hasComments,
174
177
  defaultExpanded: hasComments,
175
178
  composerPlaceholder: hasComments ? "Add another top-level comment…" : "Add a comment…",
176
- ...ctx.callbacks
179
+ ...ctx.callbacks,
180
+ ...threadToMarkdown ? { threadToMarkdown } : {}
177
181
  }
178
182
  )
179
183
  ] });
@@ -184,7 +188,7 @@ function CommentSidePanel(props) {
184
188
  const railRef = react.useRef(null);
185
189
  const focusedRef = react.useRef(null);
186
190
  const [focusedOffset, setFocusedOffset] = react.useState(null);
187
- const [showResolved, setShowResolved] = react.useState(false);
191
+ const [stage, setStage] = react.useState("active");
188
192
  const focusedAnchor = (ctx == null ? void 0 : ctx.railMode) === "focused" ? ctx.focusedAnchor : null;
189
193
  react.useEffect(() => {
190
194
  if (!ctx || props.focusedAlignment !== "anchor" || !focusedAnchor) {
@@ -219,11 +223,21 @@ function CommentSidePanel(props) {
219
223
  };
220
224
  }, [ctx, focusedAnchor, props.focusedAlignment]);
221
225
  if (!ctx) return null;
222
- const unresolved = commentUtils.selectUnresolvedThreads(ctx.comments, ctx.config);
223
- const visible = showResolved ? ctx.comments : unresolved;
224
- const total = commentUtils.getRoots(visible).length;
225
- const resolvedCount = commentUtils.getRoots(ctx.comments).length - commentUtils.getRoots(unresolved).length;
226
- if (ctx.railMode === "closed" && total === 0 && resolvedCount === 0) return null;
226
+ const counts = commentUtils.getRoots(ctx.comments).reduce(
227
+ (result, root) => {
228
+ const rootStage = commentUtils.resolveCommentStage(ctx.config, root.status);
229
+ if (rootStage) result[rootStage] += 1;
230
+ return result;
231
+ },
232
+ { active: 0, resolved: 0, closed: 0 }
233
+ );
234
+ const visible = commentUtils.selectCommentThreadsByStage(ctx.comments, ctx.config, stage);
235
+ if (ctx.railMode === "closed" && counts.active + counts.resolved + counts.closed === 0)
236
+ return null;
237
+ function selectStage(next) {
238
+ setStage(next);
239
+ if ((ctx == null ? void 0 : ctx.railMode) !== "all") ctx == null ? void 0 : ctx.openCommentList();
240
+ }
227
241
  return /* @__PURE__ */ jsxRuntime.jsxs(
228
242
  "aside",
229
243
  {
@@ -237,28 +251,45 @@ function CommentSidePanel(props) {
237
251
  "data-testid": "comment-rail-header",
238
252
  className: "sticky top-0 z-20 flex items-center gap-2 bg-background py-2",
239
253
  children: [
240
- (total > 0 || ctx.railMode === "all") && /* @__PURE__ */ jsxRuntime.jsx(
254
+ (counts.active > 0 || ctx.railMode === "all") && /* @__PURE__ */ jsxRuntime.jsx(
241
255
  RailToggle,
242
256
  {
243
- active: ctx.railMode === "all",
244
- onClick: ctx.openCommentList,
257
+ active: ctx.railMode === "all" && stage === "active",
258
+ onClick: () => selectStage("active"),
245
259
  testId: "comment-open-all",
246
- children: ctx.railMode === "closed" ? `Open comments (${total})` : `All comments (${total})`
260
+ children: ctx.railMode === "closed" ? `Open comments (${counts.active})` : ctx.railMode === "focused" ? `All comments (${counts.active})` : `Open (${counts.active})`
247
261
  }
248
262
  ),
249
- resolvedCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
263
+ counts.resolved > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
250
264
  "button",
251
265
  {
252
266
  type: "button",
253
- "aria-pressed": showResolved,
254
- onClick: () => setShowResolved((shown) => !shown),
267
+ "aria-pressed": ctx.railMode === "all" && stage === "resolved",
268
+ onClick: () => selectStage("resolved"),
255
269
  className: utils.cn(
256
270
  "rounded-full border px-2 py-1 text-xs font-medium transition-colors",
257
- showResolved ? "border-primary bg-primary text-primary-foreground" : "border-border bg-background text-muted-foreground hover:text-foreground"
271
+ ctx.railMode === "all" && stage === "resolved" ? "border-primary bg-primary text-primary-foreground" : "border-border bg-background text-muted-foreground hover:text-foreground"
258
272
  ),
259
273
  children: [
260
274
  "Resolved (",
261
- resolvedCount,
275
+ counts.resolved,
276
+ ")"
277
+ ]
278
+ }
279
+ ),
280
+ counts.closed > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
281
+ "button",
282
+ {
283
+ type: "button",
284
+ "aria-pressed": ctx.railMode === "all" && stage === "closed",
285
+ onClick: () => selectStage("closed"),
286
+ className: utils.cn(
287
+ "rounded-full border px-2 py-1 text-xs font-medium transition-colors",
288
+ ctx.railMode === "all" && stage === "closed" ? "border-primary bg-primary text-primary-foreground" : "border-border bg-background text-muted-foreground hover:text-foreground"
289
+ ),
290
+ children: [
291
+ "Closed (",
292
+ counts.closed,
262
293
  ")"
263
294
  ]
264
295
  }
@@ -281,7 +312,8 @@ function CommentSidePanel(props) {
281
312
  ctx,
282
313
  comments: visible,
283
314
  label,
284
- emptyLabel: resolvedCount > 0 ? "No open comments." : "No comments yet."
315
+ emptyLabel: stage === "active" ? "No open comments." : stage === "resolved" ? "No resolved comments." : "No closed comments.",
316
+ ...props.threadToMarkdown ? { threadToMarkdown: props.threadToMarkdown } : {}
285
317
  }
286
318
  ) : ctx.railMode === "focused" && ctx.focusedAnchor ? /* @__PURE__ */ jsxRuntime.jsx(
287
319
  "div",
@@ -294,10 +326,11 @@ function CommentSidePanel(props) {
294
326
  FocusedComments,
295
327
  {
296
328
  ctx,
297
- comments: visible,
329
+ comments: ctx.comments,
298
330
  anchor: ctx.focusedAnchor,
299
331
  label: label(ctx.focusedAnchor),
300
- ...props.compact !== void 0 ? { compact: props.compact } : {}
332
+ ...props.compact !== void 0 ? { compact: props.compact } : {},
333
+ ...props.threadToMarkdown ? { threadToMarkdown: props.threadToMarkdown } : {}
301
334
  }
302
335
  )
303
336
  }