@embedpdf/viewer-chrome 3.0.0-next.13 → 3.0.0-next.15

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/README.md +175 -132
  2. package/dist/annotation-fonts-BqoLZ4Mp.js +80 -0
  3. package/dist/annotation-fonts-BqoLZ4Mp.js.map +1 -0
  4. package/dist/annotation-fonts-Dt0IQXhz.cjs +91 -0
  5. package/dist/annotation-fonts-Dt0IQXhz.cjs.map +1 -0
  6. package/dist/annotation-style--rPIZsuP.js +879 -0
  7. package/dist/annotation-style--rPIZsuP.js.map +1 -0
  8. package/dist/annotation-style-Bzvvp8-N.cjs +879 -0
  9. package/dist/annotation-style-Bzvvp8-N.cjs.map +1 -0
  10. package/dist/ar-D9XKAmDx.js +567 -0
  11. package/dist/ar-D9XKAmDx.js.map +1 -0
  12. package/dist/ar-DnEj5vbP.cjs +567 -0
  13. package/dist/ar-DnEj5vbP.cjs.map +1 -0
  14. package/dist/chrome.cjs +378 -221
  15. package/dist/chrome.cjs.map +1 -1
  16. package/dist/chrome.d.cts +5 -34
  17. package/dist/chrome.d.ts +5 -34
  18. package/dist/chrome.js +377 -218
  19. package/dist/chrome.js.map +1 -1
  20. package/dist/commands-BmgxhJ8B.js +953 -0
  21. package/dist/commands-BmgxhJ8B.js.map +1 -0
  22. package/dist/commands-CYt4SFUX.cjs +1060 -0
  23. package/dist/commands-CYt4SFUX.cjs.map +1 -0
  24. package/dist/commands.cjs +12 -595
  25. package/dist/commands.d.cts +29 -4
  26. package/dist/commands.d.ts +29 -4
  27. package/dist/commands.js +2 -593
  28. package/dist/comments-panel-MUVgLCBI.js +514 -0
  29. package/dist/comments-panel-MUVgLCBI.js.map +1 -0
  30. package/dist/comments-panel-pEcQg4Vs.cjs +514 -0
  31. package/dist/comments-panel-pEcQg4Vs.cjs.map +1 -0
  32. package/dist/de-Bdr4aWzm.js +566 -0
  33. package/dist/de-Bdr4aWzm.js.map +1 -0
  34. package/dist/de-Dv6zvbnd.cjs +566 -0
  35. package/dist/de-Dv6zvbnd.cjs.map +1 -0
  36. package/dist/dialog-B5GBisKv.js +55 -0
  37. package/dist/dialog-B5GBisKv.js.map +1 -0
  38. package/dist/dialog-t8F13BL_.cjs +60 -0
  39. package/dist/dialog-t8F13BL_.cjs.map +1 -0
  40. package/dist/es-BP09DR9w.cjs +566 -0
  41. package/dist/es-BP09DR9w.cjs.map +1 -0
  42. package/dist/es-BSXz5tBv.js +566 -0
  43. package/dist/es-BSXz5tBv.js.map +1 -0
  44. package/dist/form-design-panel-B5MXTZ96.cjs +418 -0
  45. package/dist/form-design-panel-B5MXTZ96.cjs.map +1 -0
  46. package/dist/form-design-panel-vWWU9uB-.js +418 -0
  47. package/dist/form-design-panel-vWWU9uB-.js.map +1 -0
  48. package/dist/fr-BBQQn7tJ.cjs +566 -0
  49. package/dist/fr-BBQQn7tJ.cjs.map +1 -0
  50. package/dist/fr-Ckn3K9v1.js +566 -0
  51. package/dist/fr-Ckn3K9v1.js.map +1 -0
  52. package/dist/he-DRsG7bH7.cjs +567 -0
  53. package/dist/he-DRsG7bH7.cjs.map +1 -0
  54. package/dist/he-wdGU5PkU.js +567 -0
  55. package/dist/he-wdGU5PkU.js.map +1 -0
  56. package/dist/icons-BrK5iVL8.js +856 -0
  57. package/dist/icons-BrK5iVL8.js.map +1 -0
  58. package/dist/icons-x0Tc05bs.cjs +1010 -0
  59. package/dist/icons-x0Tc05bs.cjs.map +1 -0
  60. package/dist/index.cjs +3162 -5106
  61. package/dist/index.cjs.map +1 -1
  62. package/dist/index.d.cts +636 -143
  63. package/dist/index.d.ts +636 -143
  64. package/dist/index.js +3083 -4975
  65. package/dist/index.js.map +1 -1
  66. package/dist/it-BuP4o0xQ.cjs +566 -0
  67. package/dist/it-BuP4o0xQ.cjs.map +1 -0
  68. package/dist/it-Us-VmvIu.js +566 -0
  69. package/dist/it-Us-VmvIu.js.map +1 -0
  70. package/dist/ja-Cizj0-5O.js +566 -0
  71. package/dist/ja-Cizj0-5O.js.map +1 -0
  72. package/dist/ja-DovEd6ts.cjs +566 -0
  73. package/dist/ja-DovEd6ts.cjs.map +1 -0
  74. package/dist/ko-B-qJEol2.cjs +566 -0
  75. package/dist/ko-B-qJEol2.cjs.map +1 -0
  76. package/dist/ko-DlrXliE1.js +566 -0
  77. package/dist/ko-DlrXliE1.js.map +1 -0
  78. package/dist/labels-BiM9wp9K.cjs +21 -0
  79. package/dist/labels-BiM9wp9K.cjs.map +1 -0
  80. package/dist/labels-C6_i87Oj.js +16 -0
  81. package/dist/labels-C6_i87Oj.js.map +1 -0
  82. package/dist/link-editor-CSjqPnj1.cjs +125 -0
  83. package/dist/link-editor-CSjqPnj1.cjs.map +1 -0
  84. package/dist/link-editor-SxM1nnJU.js +125 -0
  85. package/dist/link-editor-SxM1nnJU.js.map +1 -0
  86. package/dist/measurement-Bs61XWbJ.js +325 -0
  87. package/dist/measurement-Bs61XWbJ.js.map +1 -0
  88. package/dist/measurement-CcCsHPOX.cjs +326 -0
  89. package/dist/measurement-CcCsHPOX.cjs.map +1 -0
  90. package/dist/nl-0aF7M_0h.js +566 -0
  91. package/dist/nl-0aF7M_0h.js.map +1 -0
  92. package/dist/nl-D3KZDDiJ.cjs +566 -0
  93. package/dist/nl-D3KZDDiJ.cjs.map +1 -0
  94. package/dist/pl-BW--AaCI.js +566 -0
  95. package/dist/pl-BW--AaCI.js.map +1 -0
  96. package/dist/pl-DSFhAUqx.cjs +566 -0
  97. package/dist/pl-DSFhAUqx.cjs.map +1 -0
  98. package/dist/pt-C5S-8MC5.cjs +566 -0
  99. package/dist/pt-C5S-8MC5.cjs.map +1 -0
  100. package/dist/pt-DIhpU5pT.js +566 -0
  101. package/dist/pt-DIhpU5pT.js.map +1 -0
  102. package/dist/redact-confirm-B6j36TPw.js +83 -0
  103. package/dist/redact-confirm-B6j36TPw.js.map +1 -0
  104. package/dist/redact-confirm-CX_SY2ZN.cjs +83 -0
  105. package/dist/redact-confirm-CX_SY2ZN.cjs.map +1 -0
  106. package/dist/redaction-panel-BmoIl-Ll.cjs +96 -0
  107. package/dist/redaction-panel-BmoIl-Ll.cjs.map +1 -0
  108. package/dist/redaction-panel-IJDshSJI.js +96 -0
  109. package/dist/redaction-panel-IJDshSJI.js.map +1 -0
  110. package/dist/search-panel-HfIhJm1M.js +249 -0
  111. package/dist/search-panel-HfIhJm1M.js.map +1 -0
  112. package/dist/search-panel-s9IoiOtv.cjs +249 -0
  113. package/dist/search-panel-s9IoiOtv.cjs.map +1 -0
  114. package/dist/sheet-BDsa_Sup.cjs +442 -0
  115. package/dist/sheet-BDsa_Sup.cjs.map +1 -0
  116. package/dist/sheet-R6XDgyEf.js +365 -0
  117. package/dist/sheet-R6XDgyEf.js.map +1 -0
  118. package/dist/sign-dialog-CgnR7akj.cjs +155 -0
  119. package/dist/sign-dialog-CgnR7akj.cjs.map +1 -0
  120. package/dist/sign-dialog-GDqCoQhA.js +155 -0
  121. package/dist/sign-dialog-GDqCoQhA.js.map +1 -0
  122. package/dist/signature-inspector-DyO313Qf.cjs +166 -0
  123. package/dist/signature-inspector-DyO313Qf.cjs.map +1 -0
  124. package/dist/signature-inspector-okFz8do6.js +166 -0
  125. package/dist/signature-inspector-okFz8do6.js.map +1 -0
  126. package/dist/signature-maker-BhCmYyHD.js +393 -0
  127. package/dist/signature-maker-BhCmYyHD.js.map +1 -0
  128. package/dist/signature-maker-BoYa8vgA.cjs +393 -0
  129. package/dist/signature-maker-BoYa8vgA.cjs.map +1 -0
  130. package/dist/signatures-panel-B56PC6d9.cjs +300 -0
  131. package/dist/signatures-panel-B56PC6d9.cjs.map +1 -0
  132. package/dist/signatures-panel-CgnFfhFm.js +300 -0
  133. package/dist/signatures-panel-CgnFfhFm.js.map +1 -0
  134. package/dist/stamp-store-BHvRs-Do.cjs +147 -0
  135. package/dist/stamp-store-BHvRs-Do.cjs.map +1 -0
  136. package/dist/stamp-store-WuF-L3Jj.js +112 -0
  137. package/dist/stamp-store-WuF-L3Jj.js.map +1 -0
  138. package/dist/stamps-panel-B9NYGayn.cjs +231 -0
  139. package/dist/stamps-panel-B9NYGayn.cjs.map +1 -0
  140. package/dist/stamps-panel-CDO0FPyK.js +231 -0
  141. package/dist/stamps-panel-CDO0FPyK.js.map +1 -0
  142. package/dist/toolbar-CJu2wKTU.cjs +1659 -0
  143. package/dist/toolbar-CJu2wKTU.cjs.map +1 -0
  144. package/dist/toolbar-z9od3tKl.js +1564 -0
  145. package/dist/toolbar-z9od3tKl.js.map +1 -0
  146. package/dist/zh-cn-3tmMeHqv.cjs +566 -0
  147. package/dist/zh-cn-3tmMeHqv.cjs.map +1 -0
  148. package/dist/zh-cn-CxkUUBct.js +566 -0
  149. package/dist/zh-cn-CxkUUBct.js.map +1 -0
  150. package/package.json +6 -4
  151. package/src/styles.css +153 -89
  152. package/dist/commands.cjs.map +0 -1
  153. package/dist/commands.js.map +0 -1
  154. package/dist/es-DYwzfxyt.cjs +0 -293
  155. package/dist/es-DYwzfxyt.cjs.map +0 -1
  156. package/dist/es-sN6uqBgD.js +0 -293
  157. package/dist/es-sN6uqBgD.js.map +0 -1
@@ -0,0 +1,514 @@
1
+ const require_icons = require("./icons-x0Tc05bs.cjs");
2
+ let react = require("react");
3
+ let _embedpdf_react_runtime = require("@embedpdf/react/runtime");
4
+ let _embedpdf_react_stage = require("@embedpdf/react/stage");
5
+ let _embedpdf_react_annotation = require("@embedpdf/react/annotation");
6
+ let _embedpdf_react_i18n = require("@embedpdf/react/i18n");
7
+ let _embedpdf_react_commands = require("@embedpdf/react/commands");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ //#region src/ui/comment-config.ts
10
+ const TYPE_CONFIG = {
11
+ text: {
12
+ icon: "message",
13
+ labelKey: "annotation.comment",
14
+ label: "Comment"
15
+ },
16
+ highlight: {
17
+ icon: "highlight",
18
+ labelKey: "annotation.highlight",
19
+ label: "Highlight"
20
+ },
21
+ underline: {
22
+ icon: "underline",
23
+ labelKey: "annotation.underline",
24
+ label: "Underline"
25
+ },
26
+ squiggly: {
27
+ icon: "squiggly",
28
+ labelKey: "annotation.squiggly",
29
+ label: "Squiggly"
30
+ },
31
+ strikeout: {
32
+ icon: "strikethrough",
33
+ labelKey: "annotation.strikeout",
34
+ label: "Strikethrough"
35
+ },
36
+ square: {
37
+ icon: "square",
38
+ labelKey: "annotation.square",
39
+ label: "Square"
40
+ },
41
+ circle: {
42
+ icon: "circle",
43
+ labelKey: "annotation.circle",
44
+ label: "Circle"
45
+ },
46
+ line: {
47
+ icon: "line",
48
+ labelKey: "annotation.line",
49
+ label: "Line"
50
+ },
51
+ polygon: {
52
+ icon: "polygon",
53
+ labelKey: "annotation.polygon",
54
+ label: "Polygon"
55
+ },
56
+ polyline: {
57
+ icon: "zigzag",
58
+ labelKey: "annotation.polyline",
59
+ label: "Polyline"
60
+ },
61
+ ink: {
62
+ icon: "pencilMarker",
63
+ labelKey: "annotation.ink",
64
+ label: "Ink"
65
+ },
66
+ "free-text": {
67
+ icon: "freeText",
68
+ labelKey: "annotation.freeText",
69
+ label: "Text"
70
+ },
71
+ stamp: {
72
+ icon: "rubberStamp",
73
+ labelKey: "annotation.stamp",
74
+ label: "Stamp"
75
+ },
76
+ caret: {
77
+ icon: "insertText",
78
+ labelKey: "annotation.caret",
79
+ label: "Caret"
80
+ },
81
+ redact: {
82
+ icon: "redact",
83
+ labelKey: "annotation.redact",
84
+ label: "Redact"
85
+ },
86
+ "file-attachment": {
87
+ icon: "paperclip",
88
+ labelKey: "annotation.fileAttachment",
89
+ label: "Attachment"
90
+ }
91
+ };
92
+ const FALLBACK = {
93
+ icon: "message",
94
+ labelKey: "annotation.comment",
95
+ label: "Comment"
96
+ };
97
+ const commentTypeConfig = (dto) => TYPE_CONFIG[dto.subtype] ?? FALLBACK;
98
+ /**
99
+ * The glyph's tint slots, read off the annotation itself — this is what makes
100
+ * a card recognizable as "that yellow highlight on page 3". Free-text prefers
101
+ * its `fontColor` override, since that is the color a reader actually sees.
102
+ */
103
+ const commentIconAccent = (dto) => {
104
+ const anyA = dto;
105
+ const primary = anyA.fontColor ?? anyA.color;
106
+ const secondary = anyA.interiorColor ?? void 0;
107
+ return {
108
+ ...primary ? { primary } : {},
109
+ ...secondary ? { secondary } : {}
110
+ };
111
+ };
112
+ //#endregion
113
+ //#region src/ui/comments-panel.tsx
114
+ /**
115
+ * The comments sidebar (right panel): a live view over the conversation
116
+ * plane — page-grouped sections, and a card that identifies its annotation at
117
+ * a glance by the type's glyph tinted with that annotation's own colors, plus
118
+ * the ISO 32000 §12.5.6.3 review status per thread.
119
+ *
120
+ * There is no per-page loading dance: the plugin hydrates the whole document
121
+ * at open (`annotations.list()`), so the list is complete the moment `hydration()`
122
+ * says so, whether or not a page was ever scrolled to.
123
+ *
124
+ * Selection is two-way: clicking a card selects the annotation and
125
+ * flies the camera to it (`stage.reveal`, the same verb search hits use);
126
+ * selecting in the document scrolls that card into view here.
127
+ *
128
+ * Every action routes through the `comments` verbs — plain annotation writes
129
+ * on the one optimistic pipeline — and every control is gated by
130
+ * `permissionsFor`, the engine's own collab-resolver mirrors: what shows here
131
+ * is what the engine will allow. With writing turned off (`comments.writing`,
132
+ * which `readOnly` turns off) the threads are only read: no composer, no
133
+ * status, no edit and no delete.
134
+ */
135
+ const REVIEW_STATES = [
136
+ "none",
137
+ "accepted",
138
+ "rejected",
139
+ "cancelled",
140
+ "completed"
141
+ ];
142
+ const dateLabel = (iso) => {
143
+ if (!iso) return "";
144
+ const date = new Date(iso);
145
+ if (Number.isNaN(date.getTime())) return "";
146
+ return date.toLocaleDateString(void 0, {
147
+ month: "short",
148
+ day: "numeric"
149
+ });
150
+ };
151
+ /** Whether people write here: the `comments.writing` feature is on. */
152
+ function useWriting() {
153
+ const commands = (0, _embedpdf_react_commands.useCommands)();
154
+ (0, _embedpdf_react_commands.useCommandsSettings)((settings) => settings.disabledCategories);
155
+ return !commands.isCategoryDisabled("comments.writing");
156
+ }
157
+ function CommentsPanel() {
158
+ const t = (0, _embedpdf_react_i18n.useT)();
159
+ const writing = useWriting();
160
+ const status = (0, _embedpdf_react_annotation.useAnnotationState)((state) => state.status);
161
+ const comments = (0, _embedpdf_react_annotation.useComments)();
162
+ const threads = (0, _embedpdf_react_annotation.useCommentThreads)();
163
+ const anno = (0, _embedpdf_react_runtime.useCapability)(_embedpdf_react_annotation.AnnotationToken);
164
+ const stage = (0, _embedpdf_react_runtime.useCapability)(_embedpdf_react_stage.StageToken);
165
+ const selected = (0, _embedpdf_react_annotation.useAnnotationState)((state) => state.selected);
166
+ const selection = (0, react.useMemo)(() => selected.map((annotation) => annotation.ref), [selected]);
167
+ const scrollRef = (0, react.useRef)(null);
168
+ const cardRefs = (0, react.useRef)({});
169
+ /** Which thread the document's selection points at — any member counts, so
170
+ * selecting a reply's parent shape highlights the same card. */
171
+ const selectedThreadKey = (0, react.useMemo)(() => {
172
+ for (const ref of selection) {
173
+ const thread = comments.getThread(ref);
174
+ if (thread) return (0, _embedpdf_react_annotation.annotationKey)(thread.root.ref);
175
+ }
176
+ return null;
177
+ }, [selection, comments]);
178
+ (0, react.useEffect)(() => {
179
+ if (!selectedThreadKey || !scrollRef.current) return;
180
+ const element = cardRefs.current[selectedThreadKey];
181
+ if (!element) return;
182
+ const container = scrollRef.current;
183
+ const top = element.offsetTop - container.clientHeight / 2 + element.clientHeight / 2 - container.offsetTop;
184
+ container.scrollTo({
185
+ top,
186
+ behavior: "smooth"
187
+ });
188
+ }, [selectedThreadKey]);
189
+ /** Click a card: select the annotation and fly the camera to it. */
190
+ const goTo = (view) => {
191
+ anno.selection.set([view.root.ref]);
192
+ if (view.pageIndex < 0) return;
193
+ stage?.reveal(view.pageIndex, {
194
+ rect: view.root.rect,
195
+ anchor: {
196
+ x: "center",
197
+ y: .35
198
+ },
199
+ behavior: "smooth"
200
+ });
201
+ };
202
+ const byPage = (0, react.useMemo)(() => {
203
+ const groups = /* @__PURE__ */ new Map();
204
+ for (const view of threads) {
205
+ const arr = groups.get(view.pageIndex);
206
+ if (arr) arr.push(view);
207
+ else groups.set(view.pageIndex, [view]);
208
+ }
209
+ return [...groups.entries()].sort((left, right) => left[0] - right[0]);
210
+ }, [threads]);
211
+ if (status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Empty, {
212
+ icon: "comment",
213
+ text: t("demo.commentsLoading")
214
+ });
215
+ if (status === "forbidden") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Empty, {
216
+ icon: "lock",
217
+ text: t("demo.commentsForbidden")
218
+ });
219
+ if (status === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Empty, {
220
+ icon: "alertTriangle",
221
+ text: t("demo.commentsError"),
222
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
223
+ type: "button",
224
+ onClick: () => void anno.refresh(),
225
+ className: "bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm font-medium",
226
+ children: t("demo.commentsRetry")
227
+ })
228
+ });
229
+ if (threads.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Empty, {
230
+ icon: "comment",
231
+ text: t(writing ? "demo.commentsEmpty" : "demo.commentsEmptyReadOnly")
232
+ });
233
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
234
+ ref: scrollRef,
235
+ className: "min-h-0 flex-1 overflow-y-auto",
236
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
237
+ className: "flex flex-col gap-5 p-3",
238
+ children: byPage.map(([pageIndex, group]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
239
+ className: "flex flex-col gap-2",
240
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
241
+ className: "bg-surface sticky top-0 z-10",
242
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
243
+ className: "border-border-subtle border-b pb-1.5",
244
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
245
+ className: "text-fg text-sm font-semibold",
246
+ children: t("demo.commentsPage", { params: { page: group[0].pageLabel } })
247
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
248
+ className: "text-fg-muted text-xs",
249
+ children: t(group.length === 1 ? "demo.commentCount" : "demo.commentCountPlural", { params: { count: String(group.length) } })
250
+ })]
251
+ })
252
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
253
+ className: "flex flex-col gap-2",
254
+ children: group.map((view) => {
255
+ const key = (0, _embedpdf_react_annotation.annotationKey)(view.root.ref);
256
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreadCard, {
257
+ view,
258
+ writing,
259
+ selected: selectedThreadKey === key,
260
+ onSelect: () => goTo(view),
261
+ cardRef: (element) => {
262
+ cardRefs.current[key] = element;
263
+ }
264
+ }, key);
265
+ })
266
+ })]
267
+ }, pageIndex))
268
+ })
269
+ });
270
+ }
271
+ function Empty({ icon, text, children }) {
272
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
273
+ className: "flex flex-col items-center gap-3 px-4 py-10 text-center",
274
+ children: [
275
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
276
+ name: icon,
277
+ size: 32,
278
+ className: "text-fg-muted"
279
+ }),
280
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
281
+ className: "text-fg-muted text-sm",
282
+ children: text
283
+ }),
284
+ children
285
+ ]
286
+ });
287
+ }
288
+ function ThreadCard({ view, writing, selected, onSelect, cardRef }) {
289
+ const t = (0, _embedpdf_react_i18n.useT)();
290
+ const comments = (0, _embedpdf_react_annotation.useComments)();
291
+ const [reply, setReply] = (0, react.useState)("");
292
+ const root = view.root.ref;
293
+ const perms = {
294
+ canDeleteThread: writing && comments.canDeleteThread(root),
295
+ canSetStatus: writing && comments.canSetStatus(root),
296
+ canReply: writing && comments.canReply(root)
297
+ };
298
+ const config = commentTypeConfig(view.root);
299
+ const status = view.review.mine?.state ?? "none";
300
+ const latest = view.review.lastChange;
301
+ const sendReply = () => {
302
+ const text = reply.trim();
303
+ if (!text) return;
304
+ setReply("");
305
+ comments.reply(view.root.ref, text);
306
+ };
307
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
308
+ ref: cardRef,
309
+ onClick: onSelect,
310
+ className: `bg-surface cursor-pointer rounded-lg border shadow-sm transition-all hover:shadow-md ${selected ? "border-accent ring-accent/40 ring-2" : "border-border-subtle"}`,
311
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
312
+ className: "flex flex-col gap-2 p-3",
313
+ children: [
314
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
315
+ className: "flex items-start gap-2.5",
316
+ children: [
317
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
318
+ className: "bg-hover mt-0.5 grid h-8 w-8 shrink-0 place-items-center rounded-full",
319
+ title: t(config.labelKey, { fallback: config.label }),
320
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
321
+ name: config.icon,
322
+ size: 18,
323
+ accent: commentIconAccent(view.root)
324
+ })
325
+ }),
326
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
327
+ className: "min-w-0 flex-1",
328
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
329
+ className: "flex items-baseline gap-2",
330
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
331
+ className: "text-fg truncate text-sm font-medium",
332
+ children: view.root.author ?? t("demo.commentsAnonymous")
333
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
334
+ className: "text-fg-muted ms-auto shrink-0 text-xs",
335
+ children: dateLabel(view.root.modifiedAt ?? view.root.createdAt)
336
+ })]
337
+ }), latest && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
338
+ className: "bg-accent-light text-accent mt-1 inline-block rounded px-1.5 py-0.5 text-[11px] font-medium",
339
+ children: [statusLabel(t, latest.state), latest.by ? ` — ${latest.by}` : ""]
340
+ })]
341
+ }),
342
+ perms.canDeleteThread && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
343
+ type: "button",
344
+ onClick: (event) => {
345
+ event.stopPropagation();
346
+ comments.deleteThread(view.root.ref);
347
+ },
348
+ className: "text-fg-muted hover:text-fg grid h-6 w-6 shrink-0 place-items-center rounded",
349
+ title: t("demo.commentsDeleteThread"),
350
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
351
+ name: "trash",
352
+ size: 14
353
+ })
354
+ })
355
+ ]
356
+ }),
357
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommentBody, {
358
+ annotationRef: view.root.ref,
359
+ text: view.root.contents ?? "",
360
+ deletable: false,
361
+ writing
362
+ }),
363
+ view.replies.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
364
+ className: "border-border-subtle flex flex-col gap-2 border-t pt-2",
365
+ children: view.replies.map((replyDto) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Reply, {
366
+ dto: replyDto,
367
+ writing
368
+ }, (0, _embedpdf_react_annotation.annotationKey)(replyDto.ref)))
369
+ }),
370
+ (perms.canSetStatus || perms.canReply) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
371
+ className: "border-border-subtle flex flex-col gap-1.5 border-t pt-2",
372
+ children: [perms.canSetStatus && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
373
+ className: "text-fg-muted flex items-center gap-2 text-xs",
374
+ onClick: (event) => event.stopPropagation(),
375
+ children: [t("demo.commentsStatus"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
376
+ value: status,
377
+ onChange: (event) => void comments.setStatus(view.root.ref, event.target.value),
378
+ className: "border-border bg-surface text-fg flex-1 rounded-md border px-1.5 py-1 text-xs outline-none",
379
+ children: REVIEW_STATES.map((state) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
380
+ value: state,
381
+ children: statusLabel(t, state)
382
+ }, state))
383
+ })]
384
+ }), perms.canReply && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
385
+ className: "flex items-center gap-1.5",
386
+ onClick: (event) => event.stopPropagation(),
387
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
388
+ type: "text",
389
+ value: reply,
390
+ placeholder: view.root.contents || view.replies.length ? t("demo.commentsReplyPlaceholder") : t("demo.commentsAddPlaceholder"),
391
+ onChange: (event) => setReply(event.target.value),
392
+ onKeyDown: (event) => event.key === "Enter" && sendReply(),
393
+ className: "border-border bg-surface text-fg focus:border-accent w-full min-w-0 flex-1 rounded-md border px-2 py-1 text-sm outline-none"
394
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
395
+ type: "button",
396
+ onClick: sendReply,
397
+ disabled: !reply.trim(),
398
+ className: "text-accent disabled:text-fg-muted grid h-7 w-7 shrink-0 place-items-center rounded-md",
399
+ title: t("demo.commentsReply"),
400
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
401
+ name: "arrowForwardUp",
402
+ size: 16
403
+ })
404
+ })]
405
+ })]
406
+ })
407
+ ]
408
+ })
409
+ });
410
+ }
411
+ function Reply({ dto, writing }) {
412
+ const t = (0, _embedpdf_react_i18n.useT)();
413
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
414
+ className: "border-border-subtle ms-1 border-s-2 ps-2",
415
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
416
+ className: "flex items-baseline gap-2",
417
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
418
+ className: "text-fg-secondary text-xs font-medium",
419
+ children: dto.author ?? t("demo.commentsAnonymous")
420
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
421
+ className: "text-fg-muted text-[11px]",
422
+ children: dateLabel(dto.modifiedAt ?? dto.createdAt)
423
+ })]
424
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommentBody, {
425
+ annotationRef: dto.ref,
426
+ text: dto.contents ?? "",
427
+ deletable: true,
428
+ writing
429
+ })]
430
+ });
431
+ }
432
+ /** One comment's text with inline edit (own, unlocked) and delete (replies), while people write. */
433
+ function CommentBody({ annotationRef, text, deletable, writing }) {
434
+ const t = (0, _embedpdf_react_i18n.useT)();
435
+ const comments = (0, _embedpdf_react_annotation.useComments)();
436
+ const perms = {
437
+ canEditText: writing && comments.canSetText(annotationRef),
438
+ canDelete: writing && comments.canDelete(annotationRef)
439
+ };
440
+ const [draft, setDraft] = (0, react.useState)(null);
441
+ if (draft !== null) {
442
+ const save = () => {
443
+ const next = draft.trim();
444
+ setDraft(null);
445
+ if (next && next !== text) comments.setText(annotationRef, next);
446
+ };
447
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
448
+ value: draft,
449
+ autoFocus: true,
450
+ rows: 2,
451
+ onClick: (event) => event.stopPropagation(),
452
+ onChange: (event) => setDraft(event.target.value),
453
+ onBlur: save,
454
+ onKeyDown: (event) => {
455
+ if (event.key === "Enter" && !event.shiftKey) {
456
+ event.preventDefault();
457
+ save();
458
+ }
459
+ if (event.key === "Escape") setDraft(null);
460
+ },
461
+ className: "border-accent bg-surface text-fg w-full resize-none rounded-md border px-2 py-1 text-sm outline-none"
462
+ });
463
+ }
464
+ if (!text && !perms.canEditText && !deletable) return null;
465
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
466
+ className: "group/body flex items-start gap-1",
467
+ children: [
468
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
469
+ className: "text-fg min-w-0 flex-1 whitespace-pre-wrap text-sm",
470
+ children: text
471
+ }),
472
+ perms.canEditText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
473
+ type: "button",
474
+ onClick: (event) => {
475
+ event.stopPropagation();
476
+ setDraft(text);
477
+ },
478
+ className: "text-fg-muted hover:text-fg grid h-5 w-5 shrink-0 place-items-center rounded opacity-0 group-hover/body:opacity-100",
479
+ title: t("demo.commentsEdit"),
480
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
481
+ name: "pencilMarker",
482
+ size: 12
483
+ })
484
+ }),
485
+ deletable && perms.canDelete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
486
+ type: "button",
487
+ onClick: (event) => {
488
+ event.stopPropagation();
489
+ comments.delete(annotationRef);
490
+ },
491
+ className: "text-fg-muted hover:text-fg grid h-5 w-5 shrink-0 place-items-center rounded opacity-0 group-hover/body:opacity-100",
492
+ title: t("demo.commentsDelete"),
493
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
494
+ name: "trash",
495
+ size: 12
496
+ })
497
+ })
498
+ ]
499
+ });
500
+ }
501
+ function statusLabel(t, state) {
502
+ switch (state) {
503
+ case "none": return t("demo.commentsStatusNone");
504
+ case "accepted": return t("demo.commentsStatusAccepted");
505
+ case "rejected": return t("demo.commentsStatusRejected");
506
+ case "cancelled": return t("demo.commentsStatusCancelled");
507
+ case "completed": return t("demo.commentsStatusCompleted");
508
+ default: return state;
509
+ }
510
+ }
511
+ //#endregion
512
+ exports.CommentsPanel = CommentsPanel;
513
+
514
+ //# sourceMappingURL=comments-panel-pEcQg4Vs.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comments-panel-pEcQg4Vs.cjs","names":["AnnotationToken","StageToken","Icon"],"sources":["../src/ui/comment-config.ts","../src/ui/comments-panel.tsx"],"sourcesContent":["import type { Annotation, Color } from '@embedpdf/react/annotation';\n\n/**\n * How a comment card identifies its annotation at a glance: the type's glyph,\n * tinted with that annotation's own colors. The icon names are the chrome icon\n * set's registry names and accent slots. The color model follows the spec:\n * there is exactly one `/C` (\"color\") and one `/IC` (\"interiorColor\"), so\n * `primary` is the annotation's `/C` and `secondary` its interior fill where\n * the family has one.\n */\n\nexport interface IconAccent {\n primary?: string;\n secondary?: string;\n}\n\nexport interface CommentTypeConfig {\n /** Icon name in the chrome registry. */\n icon: string;\n /** i18n key for the type's display name (tooltip). */\n labelKey: string;\n /** Fallback when the locale has no entry. */\n label: string;\n}\n\nconst TYPE_CONFIG: Record<string, CommentTypeConfig> = {\n text: { icon: 'message', labelKey: 'annotation.comment', label: 'Comment' },\n highlight: { icon: 'highlight', labelKey: 'annotation.highlight', label: 'Highlight' },\n underline: { icon: 'underline', labelKey: 'annotation.underline', label: 'Underline' },\n squiggly: { icon: 'squiggly', labelKey: 'annotation.squiggly', label: 'Squiggly' },\n strikeout: { icon: 'strikethrough', labelKey: 'annotation.strikeout', label: 'Strikethrough' },\n square: { icon: 'square', labelKey: 'annotation.square', label: 'Square' },\n circle: { icon: 'circle', labelKey: 'annotation.circle', label: 'Circle' },\n line: { icon: 'line', labelKey: 'annotation.line', label: 'Line' },\n polygon: { icon: 'polygon', labelKey: 'annotation.polygon', label: 'Polygon' },\n polyline: { icon: 'zigzag', labelKey: 'annotation.polyline', label: 'Polyline' },\n ink: { icon: 'pencilMarker', labelKey: 'annotation.ink', label: 'Ink' },\n 'free-text': { icon: 'freeText', labelKey: 'annotation.freeText', label: 'Text' },\n stamp: { icon: 'rubberStamp', labelKey: 'annotation.stamp', label: 'Stamp' },\n caret: { icon: 'insertText', labelKey: 'annotation.caret', label: 'Caret' },\n redact: { icon: 'redact', labelKey: 'annotation.redact', label: 'Redact' },\n 'file-attachment': {\n icon: 'paperclip',\n labelKey: 'annotation.fileAttachment',\n label: 'Attachment',\n },\n};\n\nconst FALLBACK: CommentTypeConfig = {\n icon: 'message',\n labelKey: 'annotation.comment',\n label: 'Comment',\n};\n\nexport const commentTypeConfig = (dto: Annotation): CommentTypeConfig =>\n TYPE_CONFIG[dto.subtype] ?? FALLBACK;\n\n/**\n * The glyph's tint slots, read off the annotation itself — this is what makes\n * a card recognizable as \"that yellow highlight on page 3\". Free-text prefers\n * its `fontColor` override, since that is the color a reader actually sees.\n */\nexport const commentIconAccent = (dto: Annotation): IconAccent => {\n // Engine colors are `'#rrggbb'`, which CSS takes as they are.\n const anyA = dto as { color?: Color; interiorColor?: Color | null; fontColor?: Color };\n const primary = anyA.fontColor ?? anyA.color;\n const secondary = anyA.interiorColor ?? undefined;\n return {\n ...(primary ? { primary } : {}),\n ...(secondary ? { secondary } : {}),\n };\n};\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { useCapability } from '@embedpdf/react/runtime';\nimport {\n AnnotationToken,\n annotationKey,\n useAnnotationState,\n useComments,\n useCommentThreads,\n type Annotation,\n type AnnotationRef,\n type CommentThreadView,\n} from '@embedpdf/react/annotation';\nimport { StageToken } from '@embedpdf/react/stage';\nimport { useCommands, useCommandsSettings } from '@embedpdf/react/commands';\nimport { useT } from '@embedpdf/react/i18n';\nimport { Icon } from './icons';\nimport { commentIconAccent, commentTypeConfig } from './comment-config';\n\n/**\n * The comments sidebar (right panel): a live view over the conversation\n * plane — page-grouped sections, and a card that identifies its annotation at\n * a glance by the type's glyph tinted with that annotation's own colors, plus\n * the ISO 32000 §12.5.6.3 review status per thread.\n *\n * There is no per-page loading dance: the plugin hydrates the whole document\n * at open (`annotations.list()`), so the list is complete the moment `hydration()`\n * says so, whether or not a page was ever scrolled to.\n *\n * Selection is two-way: clicking a card selects the annotation and\n * flies the camera to it (`stage.reveal`, the same verb search hits use);\n * selecting in the document scrolls that card into view here.\n *\n * Every action routes through the `comments` verbs — plain annotation writes\n * on the one optimistic pipeline — and every control is gated by\n * `permissionsFor`, the engine's own collab-resolver mirrors: what shows here\n * is what the engine will allow. With writing turned off (`comments.writing`,\n * which `readOnly` turns off) the threads are only read: no composer, no\n * status, no edit and no delete.\n */\n\nconst REVIEW_STATES = ['none', 'accepted', 'rejected', 'cancelled', 'completed'] as const;\n\nconst dateLabel = (iso: string | null): string => {\n if (!iso) return '';\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) return '';\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n};\n\n/** Whether people write here: the `comments.writing` feature is on. */\nfunction useWriting(): boolean {\n const commands = useCommands();\n // Read again whenever what's turned off changes.\n useCommandsSettings((settings) => settings.disabledCategories);\n return !commands.isCategoryDisabled('comments.writing');\n}\n\nexport function CommentsPanel() {\n const t = useT();\n const writing = useWriting();\n const status = useAnnotationState((state) => state.status);\n const comments = useComments();\n const threads = useCommentThreads();\n const anno = useCapability(AnnotationToken);\n const stage = useCapability(StageToken);\n const selected = useAnnotationState((state) => state.selected);\n const selection = useMemo(() => selected.map((annotation) => annotation.ref), [selected]);\n const scrollRef = useRef<HTMLDivElement>(null);\n const cardRefs = useRef<Record<string, HTMLLIElement | null>>({});\n\n /** Which thread the document's selection points at — any member counts, so\n * selecting a reply's parent shape highlights the same card. */\n const selectedThreadKey = useMemo(() => {\n for (const ref of selection) {\n const thread = comments.getThread(ref);\n if (thread) return annotationKey(thread.root.ref);\n }\n return null;\n // `selection` identity changes on every selection write — the right key.\n }, [selection, comments]);\n\n // Reverse sync: the selected card scrolls itself into view.\n useEffect(() => {\n if (!selectedThreadKey || !scrollRef.current) return;\n const element = cardRefs.current[selectedThreadKey];\n if (!element) return;\n const container = scrollRef.current;\n const top =\n element.offsetTop -\n container.clientHeight / 2 +\n element.clientHeight / 2 -\n container.offsetTop;\n container.scrollTo({ top, behavior: 'smooth' });\n }, [selectedThreadKey]);\n\n /** Click a card: select the annotation and fly the camera to it. */\n const goTo = (view: CommentThreadView) => {\n anno.selection.set([view.root.ref]);\n if (view.pageIndex < 0) return;\n // Anchor values are viewport fractions (0–1), not percentages:\n // the annotation lands a third down the viewport, the find-bar feel.\n stage?.reveal(view.pageIndex, {\n rect: view.root.rect,\n anchor: { x: 'center', y: 0.35 },\n behavior: 'smooth',\n });\n };\n\n // Page-grouped, preserving the lens's display order within each page.\n const byPage = useMemo(() => {\n const groups = new Map<number, CommentThreadView[]>();\n for (const view of threads) {\n const arr = groups.get(view.pageIndex);\n if (arr) arr.push(view);\n else groups.set(view.pageIndex, [view]);\n }\n return [...groups.entries()].sort((left, right) => left[0] - right[0]);\n }, [threads]);\n\n if (status === 'loading') return <Empty icon=\"comment\" text={t('demo.commentsLoading')} />;\n if (status === 'forbidden') return <Empty icon=\"lock\" text={t('demo.commentsForbidden')} />;\n if (status === 'error') {\n return (\n <Empty icon=\"alertTriangle\" text={t('demo.commentsError')}>\n <button\n type=\"button\"\n onClick={() => void anno.refresh()}\n className=\"bg-accent text-on-accent rounded-md px-3 py-1.5 text-sm font-medium\"\n >\n {t('demo.commentsRetry')}\n </button>\n </Empty>\n );\n }\n if (threads.length === 0) {\n return (\n <Empty\n icon=\"comment\"\n text={t(writing ? 'demo.commentsEmpty' : 'demo.commentsEmptyReadOnly')}\n />\n );\n }\n\n return (\n <div ref={scrollRef} className=\"min-h-0 flex-1 overflow-y-auto\">\n <div className=\"flex flex-col gap-5 p-3\">\n {byPage.map(([pageIndex, group]) => (\n <div key={pageIndex} className=\"flex flex-col gap-2\">\n <div className=\"bg-surface sticky top-0 z-10\">\n <div className=\"border-border-subtle border-b pb-1.5\">\n <h3 className=\"text-fg text-sm font-semibold\">\n {t('demo.commentsPage', { params: { page: group[0]!.pageLabel } })}\n </h3>\n <p className=\"text-fg-muted text-xs\">\n {t(group.length === 1 ? 'demo.commentCount' : 'demo.commentCountPlural', {\n params: { count: String(group.length) },\n })}\n </p>\n </div>\n </div>\n <ul className=\"flex flex-col gap-2\">\n {group.map((view) => {\n const key = annotationKey(view.root.ref);\n return (\n <ThreadCard\n key={key}\n view={view}\n writing={writing}\n selected={selectedThreadKey === key}\n onSelect={() => goTo(view)}\n cardRef={(element) => {\n cardRefs.current[key] = element;\n }}\n />\n );\n })}\n </ul>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nfunction Empty({\n icon,\n text,\n children,\n}: {\n icon: string;\n text: string;\n children?: React.ReactNode;\n}) {\n return (\n <div className=\"flex flex-col items-center gap-3 px-4 py-10 text-center\">\n <Icon name={icon} size={32} className=\"text-fg-muted\" />\n <p className=\"text-fg-muted text-sm\">{text}</p>\n {children}\n </div>\n );\n}\n\nfunction ThreadCard({\n view,\n writing,\n selected,\n onSelect,\n cardRef,\n}: {\n view: CommentThreadView;\n /** Whether people write here; without it the thread is only read. */\n writing: boolean;\n selected: boolean;\n onSelect: () => void;\n cardRef: (element: HTMLLIElement | null) => void;\n}) {\n const t = useT();\n const comments = useComments();\n const [reply, setReply] = useState('');\n const root = view.root.ref;\n const perms = {\n canDeleteThread: writing && comments.canDeleteThread(root),\n canSetStatus: writing && comments.canSetStatus(root),\n canReply: writing && comments.canReply(root),\n };\n const config = commentTypeConfig(view.root);\n const status = view.review.mine?.state ?? 'none';\n const latest = view.review.lastChange;\n\n const sendReply = () => {\n const text = reply.trim();\n if (!text) return;\n setReply('');\n void comments.reply(view.root.ref, text);\n };\n\n return (\n <li\n ref={cardRef}\n onClick={onSelect}\n className={`bg-surface cursor-pointer rounded-lg border shadow-sm transition-all hover:shadow-md ${\n selected ? 'border-accent ring-accent/40 ring-2' : 'border-border-subtle'\n }`}\n >\n <div className=\"flex flex-col gap-2 p-3\">\n {/* identity row: the annotation's own glyph + color is the anchor */}\n <div className=\"flex items-start gap-2.5\">\n <div\n className=\"bg-hover mt-0.5 grid h-8 w-8 shrink-0 place-items-center rounded-full\"\n title={t(config.labelKey, { fallback: config.label })}\n >\n <Icon name={config.icon} size={18} accent={commentIconAccent(view.root)} />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-baseline gap-2\">\n <span className=\"text-fg truncate text-sm font-medium\">\n {view.root.author ?? t('demo.commentsAnonymous')}\n </span>\n <span className=\"text-fg-muted ms-auto shrink-0 text-xs\">\n {dateLabel(view.root.modifiedAt ?? view.root.createdAt)}\n </span>\n </div>\n {latest && (\n <span className=\"bg-accent-light text-accent mt-1 inline-block rounded px-1.5 py-0.5 text-[11px] font-medium\">\n {statusLabel(t, latest.state)}\n {latest.by ? ` — ${latest.by}` : ''}\n </span>\n )}\n </div>\n {perms.canDeleteThread && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n void comments.deleteThread(view.root.ref);\n }}\n className=\"text-fg-muted hover:text-fg grid h-6 w-6 shrink-0 place-items-center rounded\"\n title={t('demo.commentsDeleteThread')}\n >\n <Icon name=\"trash\" size={14} />\n </button>\n )}\n </div>\n\n <CommentBody\n annotationRef={view.root.ref}\n text={view.root.contents ?? ''}\n deletable={false}\n writing={writing}\n />\n\n {view.replies.length > 0 && (\n <div className=\"border-border-subtle flex flex-col gap-2 border-t pt-2\">\n {view.replies.map((replyDto) => (\n <Reply key={annotationKey(replyDto.ref)} dto={replyDto} writing={writing} />\n ))}\n </div>\n )}\n\n {(perms.canSetStatus || perms.canReply) && (\n <div className=\"border-border-subtle flex flex-col gap-1.5 border-t pt-2\">\n {perms.canSetStatus && (\n <label\n className=\"text-fg-muted flex items-center gap-2 text-xs\"\n onClick={(event) => event.stopPropagation()}\n >\n {t('demo.commentsStatus')}\n <select\n value={status}\n onChange={(event) => void comments.setStatus(view.root.ref, event.target.value)}\n className=\"border-border bg-surface text-fg flex-1 rounded-md border px-1.5 py-1 text-xs outline-none\"\n >\n {REVIEW_STATES.map((state) => (\n <option key={state} value={state}>\n {statusLabel(t, state)}\n </option>\n ))}\n </select>\n </label>\n )}\n {perms.canReply && (\n <div\n className=\"flex items-center gap-1.5\"\n onClick={(event) => event.stopPropagation()}\n >\n <input\n type=\"text\"\n value={reply}\n placeholder={\n view.root.contents || view.replies.length\n ? t('demo.commentsReplyPlaceholder')\n : t('demo.commentsAddPlaceholder')\n }\n onChange={(event) => setReply(event.target.value)}\n onKeyDown={(event) => event.key === 'Enter' && sendReply()}\n className=\"border-border bg-surface text-fg focus:border-accent w-full min-w-0 flex-1 rounded-md border px-2 py-1 text-sm outline-none\"\n />\n <button\n type=\"button\"\n onClick={sendReply}\n disabled={!reply.trim()}\n className=\"text-accent disabled:text-fg-muted grid h-7 w-7 shrink-0 place-items-center rounded-md\"\n title={t('demo.commentsReply')}\n >\n <Icon name=\"arrowForwardUp\" size={16} />\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n </li>\n );\n}\n\nfunction Reply({ dto, writing }: { dto: Annotation; writing: boolean }) {\n const t = useT();\n return (\n <div className=\"border-border-subtle ms-1 border-s-2 ps-2\">\n <div className=\"flex items-baseline gap-2\">\n <span className=\"text-fg-secondary text-xs font-medium\">\n {dto.author ?? t('demo.commentsAnonymous')}\n </span>\n <span className=\"text-fg-muted text-[11px]\">\n {dateLabel(dto.modifiedAt ?? dto.createdAt)}\n </span>\n </div>\n <CommentBody annotationRef={dto.ref} text={dto.contents ?? ''} deletable writing={writing} />\n </div>\n );\n}\n\n/** One comment's text with inline edit (own, unlocked) and delete (replies), while people write. */\nfunction CommentBody({\n annotationRef,\n text,\n deletable,\n writing,\n}: {\n annotationRef: AnnotationRef;\n text: string;\n deletable: boolean;\n writing: boolean;\n}) {\n const t = useT();\n const comments = useComments();\n const perms = {\n canEditText: writing && comments.canSetText(annotationRef),\n canDelete: writing && comments.canDelete(annotationRef),\n };\n const [draft, setDraft] = useState<string | null>(null);\n\n if (draft !== null) {\n const save = () => {\n const next = draft.trim();\n setDraft(null);\n if (next && next !== text) void comments.setText(annotationRef, next);\n };\n return (\n <textarea\n value={draft}\n autoFocus\n rows={2}\n onClick={(event) => event.stopPropagation()}\n onChange={(event) => setDraft(event.target.value)}\n onBlur={save}\n onKeyDown={(event) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n save();\n }\n if (event.key === 'Escape') setDraft(null);\n }}\n className=\"border-accent bg-surface text-fg w-full resize-none rounded-md border px-2 py-1 text-sm outline-none\"\n />\n );\n }\n if (!text && !perms.canEditText && !deletable) return null;\n return (\n <div className=\"group/body flex items-start gap-1\">\n <p className=\"text-fg min-w-0 flex-1 whitespace-pre-wrap text-sm\">{text}</p>\n {perms.canEditText && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n setDraft(text);\n }}\n className=\"text-fg-muted hover:text-fg grid h-5 w-5 shrink-0 place-items-center rounded opacity-0 group-hover/body:opacity-100\"\n title={t('demo.commentsEdit')}\n >\n <Icon name=\"pencilMarker\" size={12} />\n </button>\n )}\n {deletable && perms.canDelete && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n void comments.delete(annotationRef);\n }}\n className=\"text-fg-muted hover:text-fg grid h-5 w-5 shrink-0 place-items-center rounded opacity-0 group-hover/body:opacity-100\"\n title={t('demo.commentsDelete')}\n >\n <Icon name=\"trash\" size={12} />\n </button>\n )}\n </div>\n );\n}\n\nfunction statusLabel(t: ReturnType<typeof useT>, state: string): string {\n switch (state) {\n case 'none':\n return t('demo.commentsStatusNone');\n case 'accepted':\n return t('demo.commentsStatusAccepted');\n case 'rejected':\n return t('demo.commentsStatusRejected');\n case 'cancelled':\n return t('demo.commentsStatusCancelled');\n case 'completed':\n return t('demo.commentsStatusCompleted');\n default:\n return state; // custom vocabularies render verbatim\n }\n}\n"],"mappings":"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"}