annotation-kit 0.0.0-stage → 0.1.1

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 (121) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +75 -0
  3. package/README.md +114 -2
  4. package/README.zh-CN.md +112 -0
  5. package/dist/annotation-kit.css +157 -0
  6. package/dist/chunks/references-ukB-tSle.js +1126 -0
  7. package/dist/chunks/references-ukB-tSle.js.map +1 -0
  8. package/dist/electron/element-context.cjs +72 -0
  9. package/dist/electron/guest-preload.cjs +327 -0
  10. package/dist/electron.js +394 -0
  11. package/dist/electron.js.map +1 -0
  12. package/dist/index.js +910 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/react.js +2282 -0
  15. package/dist/react.js.map +1 -0
  16. package/dist/styles.d.ts +2 -0
  17. package/dist/types/address.d.ts +53 -0
  18. package/dist/types/address.d.ts.map +1 -0
  19. package/dist/types/client/Workbench.d.ts +11 -0
  20. package/dist/types/client/Workbench.d.ts.map +1 -0
  21. package/dist/types/client/composer/PacketPreview.d.ts +12 -0
  22. package/dist/types/client/composer/PacketPreview.d.ts.map +1 -0
  23. package/dist/types/client/composer/ReferenceCard.d.ts +12 -0
  24. package/dist/types/client/composer/ReferenceCard.d.ts.map +1 -0
  25. package/dist/types/client/composer/attachments.d.ts +10 -0
  26. package/dist/types/client/composer/attachments.d.ts.map +1 -0
  27. package/dist/types/client/composer/commit.d.ts +4 -0
  28. package/dist/types/client/composer/commit.d.ts.map +1 -0
  29. package/dist/types/client/composer/packets.d.ts +53 -0
  30. package/dist/types/client/composer/packets.d.ts.map +1 -0
  31. package/dist/types/client/composer/reference-presentation.d.ts +4 -0
  32. package/dist/types/client/composer/reference-presentation.d.ts.map +1 -0
  33. package/dist/types/client/error-copy.d.ts +69 -0
  34. package/dist/types/client/error-copy.d.ts.map +1 -0
  35. package/dist/types/client/errors.d.ts +10 -0
  36. package/dist/types/client/errors.d.ts.map +1 -0
  37. package/dist/types/client/files/FileSelectionAction.d.ts +7 -0
  38. package/dist/types/client/files/FileSelectionAction.d.ts.map +1 -0
  39. package/dist/types/client/files/SelectionCard.d.ts +14 -0
  40. package/dist/types/client/files/SelectionCard.d.ts.map +1 -0
  41. package/dist/types/client/files/document-workflow.d.ts +34 -0
  42. package/dist/types/client/files/document-workflow.d.ts.map +1 -0
  43. package/dist/types/client/files/pages/DocumentChrome.d.ts +30 -0
  44. package/dist/types/client/files/pages/DocumentChrome.d.ts.map +1 -0
  45. package/dist/types/client/files/pages/PageSelectionAction.d.ts +7 -0
  46. package/dist/types/client/files/pages/PageSelectionAction.d.ts.map +1 -0
  47. package/dist/types/client/files/pages/batches.d.ts +21 -0
  48. package/dist/types/client/files/pages/batches.d.ts.map +1 -0
  49. package/dist/types/client/files/pages/geometry.d.ts +16 -0
  50. package/dist/types/client/files/pages/geometry.d.ts.map +1 -0
  51. package/dist/types/client/files/pages/image-store.d.ts +8 -0
  52. package/dist/types/client/files/pages/image-store.d.ts.map +1 -0
  53. package/dist/types/client/files/pages/images.d.ts +41 -0
  54. package/dist/types/client/files/pages/images.d.ts.map +1 -0
  55. package/dist/types/client/files/pages/interaction.d.ts +55 -0
  56. package/dist/types/client/files/pages/interaction.d.ts.map +1 -0
  57. package/dist/types/client/files/pages/selection.d.ts +25 -0
  58. package/dist/types/client/files/pages/selection.d.ts.map +1 -0
  59. package/dist/types/client/files/pages/snapshot.d.ts +31 -0
  60. package/dist/types/client/files/pages/snapshot.d.ts.map +1 -0
  61. package/dist/types/client/files/references.d.ts +80 -0
  62. package/dist/types/client/files/references.d.ts.map +1 -0
  63. package/dist/types/client/files/selection.d.ts +11 -0
  64. package/dist/types/client/files/selection.d.ts.map +1 -0
  65. package/dist/types/client/files/sheets/SheetRecovery.d.ts +12 -0
  66. package/dist/types/client/files/sheets/SheetRecovery.d.ts.map +1 -0
  67. package/dist/types/client/files/sheets/SheetSurface.d.ts +27 -0
  68. package/dist/types/client/files/sheets/SheetSurface.d.ts.map +1 -0
  69. package/dist/types/client/files/sheets/geometry.d.ts +30 -0
  70. package/dist/types/client/files/sheets/geometry.d.ts.map +1 -0
  71. package/dist/types/client/files/sheets/navigation.d.ts +4 -0
  72. package/dist/types/client/files/sheets/navigation.d.ts.map +1 -0
  73. package/dist/types/client/files/sheets/snapshot.d.ts +45 -0
  74. package/dist/types/client/files/sheets/snapshot.d.ts.map +1 -0
  75. package/dist/types/client/locales.d.ts +148 -0
  76. package/dist/types/client/locales.d.ts.map +1 -0
  77. package/dist/types/client/messages/SentAnnotations.d.ts +18 -0
  78. package/dist/types/client/messages/SentAnnotations.d.ts.map +1 -0
  79. package/dist/types/client/messages/presentation.d.ts +39 -0
  80. package/dist/types/client/messages/presentation.d.ts.map +1 -0
  81. package/dist/types/client/model.d.ts +68 -0
  82. package/dist/types/client/model.d.ts.map +1 -0
  83. package/dist/types/client/quoted-context.d.ts +3 -0
  84. package/dist/types/client/quoted-context.d.ts.map +1 -0
  85. package/dist/types/client/responses/ResponseCard.d.ts +12 -0
  86. package/dist/types/client/responses/ResponseCard.d.ts.map +1 -0
  87. package/dist/types/client/responses/ResponseOverlay.d.ts +7 -0
  88. package/dist/types/client/responses/ResponseOverlay.d.ts.map +1 -0
  89. package/dist/types/client/responses/composer.d.ts +11 -0
  90. package/dist/types/client/responses/composer.d.ts.map +1 -0
  91. package/dist/types/client/responses/controller.d.ts +39 -0
  92. package/dist/types/client/responses/controller.d.ts.map +1 -0
  93. package/dist/types/client/responses/references.d.ts +62 -0
  94. package/dist/types/client/responses/references.d.ts.map +1 -0
  95. package/dist/types/client/responses/selection.d.ts +43 -0
  96. package/dist/types/client/responses/selection.d.ts.map +1 -0
  97. package/dist/types/electron/bridge.d.mts +45 -0
  98. package/dist/types/electron/bridge.d.mts.map +1 -0
  99. package/dist/types/electron/build-preload.d.mts +4 -0
  100. package/dist/types/electron/build-preload.d.mts.map +1 -0
  101. package/dist/types/electron/element-context.d.cts +7 -0
  102. package/dist/types/electron/element-context.d.cts.map +1 -0
  103. package/dist/types/electron/guest-preload.d.cts +2 -0
  104. package/dist/types/electron/guest-preload.d.cts.map +1 -0
  105. package/dist/types/electron/index.d.ts +5 -0
  106. package/dist/types/electron/index.d.ts.map +1 -0
  107. package/dist/types/host-contracts.d.ts +104 -0
  108. package/dist/types/host-contracts.d.ts.map +1 -0
  109. package/dist/types/index.d.ts +32 -0
  110. package/dist/types/index.d.ts.map +1 -0
  111. package/dist/types/react.d.ts +14 -0
  112. package/dist/types/react.d.ts.map +1 -0
  113. package/dist/types/shared/desktop.d.ts +75 -0
  114. package/dist/types/shared/desktop.d.ts.map +1 -0
  115. package/dist/types/shared/surface.d.ts +11 -0
  116. package/dist/types/shared/surface.d.ts.map +1 -0
  117. package/dist/types/ui/icons.d.ts +11 -0
  118. package/dist/types/ui/icons.d.ts.map +1 -0
  119. package/dist/types/ui/regions.d.ts +28 -0
  120. package/dist/types/ui/regions.d.ts.map +1 -0
  121. package/package.json +83 -3
package/dist/react.js ADDED
@@ -0,0 +1,2282 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { k as annotationErrorText, d as ANNOTATION_SOURCE, I as packetSpan, y as fileLabel, B as filePath, r as columnName, P as PageBatches, D as DocumentWorkflow, K as pageInteraction, O as projectAnchors, w as enclosing, W as readPageText, S as rangeAnchors, L as pageRegion, J as pageBounds, b as annotationFailure, o as capturePageRegion, N as parseFileAddress, V as readFileSelection, g as PaintedCells, X as readRange, a0 as registerSpreadsheet, A as AnnotationError, a7 as spreadsheetSnapshot, C as fileReference, U as readAnnotation, Z as readSentFile, _ as readSentResponse } from "./chunks/references-ukB-tSle.js";
3
+ import { useSyncExternalStore, useState, useRef, useLayoutEffect, useEffect, useId, forwardRef, useContext, createContext, useReducer, useMemo, startTransition } from "react";
4
+ import { createPortal } from "react-dom";
5
+ function icon$2(path, filled = false) {
6
+ return function Icon({ width = 18, height = 18, ...props }) {
7
+ return /* @__PURE__ */ jsx("svg", { width, height, viewBox: "0 0 24 24", fill: filled ? "currentColor" : "none", stroke: filled ? "none" : "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", ...props, children: /* @__PURE__ */ jsx("path", { d: path }) });
8
+ };
9
+ }
10
+ const Chat = icon$2("M20 11.5a8 8 0 0 1-8 8H5l-3 3v-11a9 9 0 0 1 18 0Z");
11
+ const ChatFilled = icon$2("M12 2a10 10 0 0 0-10 10v10l4-3h6a8.5 8.5 0 1 0 0-17Z", true);
12
+ const X = icon$2("m6 6 12 12M18 6 6 18");
13
+ const Trash = icon$2("M3 6h18M9 6V3h6v3M5 6l1 15h12l1-15M10 10v7M14 10v7");
14
+ const Pencil = icon$2("m15 4 5 5M4 20l5-1L21 7a2 2 0 0 0-5-5L4 14v6Z");
15
+ const EditPencil = Pencil;
16
+ const SettingsSlider = icon$2("M4 7h5m4 0h7M4 17h9m4 0h3M9 4v6m8 4v6");
17
+ const annotate = "_annotate_voyw3_1";
18
+ const comment$2 = "_comment_voyw3_1";
19
+ const notice = "_notice_voyw3_1";
20
+ const marker$2 = "_marker_voyw3_6";
21
+ const commentRow = "_commentRow_voyw3_8";
22
+ const icon$1 = "_icon_voyw3_11";
23
+ const actions$2 = "_actions_voyw3_13";
24
+ const cancel = "_cancel_voyw3_15";
25
+ const save$1 = "_save_voyw3_15";
26
+ const adjust = "_adjust_voyw3_20";
27
+ const error$2 = "_error_voyw3_23";
28
+ const toolbar = "_toolbar_voyw3_25";
29
+ const send$1 = "_send_voyw3_28";
30
+ const outline$1 = "_outline_voyw3_29";
31
+ const savedMarker = "_savedMarker_voyw3_30";
32
+ const css$7 = {
33
+ annotate,
34
+ comment: comment$2,
35
+ notice,
36
+ marker: marker$2,
37
+ commentRow,
38
+ icon: icon$1,
39
+ actions: actions$2,
40
+ "delete": "_delete_voyw3_14",
41
+ cancel,
42
+ save: save$1,
43
+ adjust,
44
+ error: error$2,
45
+ toolbar,
46
+ send: send$1,
47
+ outline: outline$1,
48
+ savedMarker
49
+ };
50
+ function AnnotationButton({ surface, model, t }) {
51
+ const state = useSyncExternalStore(model.subscribe, model.getSnapshot);
52
+ const active = state.drafts[surface.id]?.active ?? false;
53
+ return /* @__PURE__ */ jsxs(
54
+ "button",
55
+ {
56
+ type: "button",
57
+ className: css$7.annotate,
58
+ "aria-label": t("annotate"),
59
+ "aria-pressed": active,
60
+ disabled: !active && (!model.available(surface) || !model.hasTarget()),
61
+ title: active ? t("close") : t("pick"),
62
+ onClick: () => {
63
+ void model.toggle(surface);
64
+ },
65
+ children: [
66
+ /* @__PURE__ */ jsx(Chat, { width: 18, height: 18 }),
67
+ t("annotate")
68
+ ]
69
+ }
70
+ );
71
+ }
72
+ function AnnotationOverlay({ surface, model, t }) {
73
+ const state = useSyncExternalStore(model.subscribe, model.getSnapshot);
74
+ const draft2 = state.drafts[surface.id];
75
+ const selection = draft2?.active ? draft2.selection : void 0;
76
+ const [bounds, setBounds] = useState();
77
+ const [adjusting, setAdjusting] = useState(false);
78
+ const form = useRef(null);
79
+ const input = useRef(null);
80
+ useLayoutEffect(() => {
81
+ if (!draft2?.active) return;
82
+ const viewport2 = document.getElementById(surface.viewportId);
83
+ if (!viewport2) return;
84
+ const measure = () => setBounds(viewport2.getBoundingClientRect());
85
+ measure();
86
+ const observer = new ResizeObserver(measure);
87
+ observer.observe(viewport2);
88
+ window.addEventListener("resize", measure);
89
+ window.addEventListener("scroll", measure, true);
90
+ return () => {
91
+ observer.disconnect();
92
+ window.removeEventListener("resize", measure);
93
+ window.removeEventListener("scroll", measure, true);
94
+ };
95
+ }, [draft2?.active, surface.viewportId]);
96
+ useEffect(() => {
97
+ setAdjusting(false);
98
+ if (selection) input.current?.focus();
99
+ }, [selection]);
100
+ useLayoutEffect(() => {
101
+ if (!input.current) return;
102
+ input.current.style.height = "24px";
103
+ input.current.style.height = `${Math.min(144, input.current.scrollHeight)}px`;
104
+ }, [draft2?.comment, selection]);
105
+ useEffect(() => {
106
+ if (!draft2?.active) return;
107
+ const dismiss2 = (event) => {
108
+ if (event.key !== "Escape" || event.isComposing) return;
109
+ event.preventDefault();
110
+ if (selection) model.cancel(surface.id);
111
+ else void model.toggle(surface);
112
+ };
113
+ const outside = (event) => {
114
+ if (selection && event.target instanceof Node && !form.current?.contains(event.target)) model.cancel(surface.id);
115
+ };
116
+ window.addEventListener("keydown", dismiss2);
117
+ window.addEventListener("pointerdown", outside);
118
+ return () => {
119
+ window.removeEventListener("keydown", dismiss2);
120
+ window.removeEventListener("pointerdown", outside);
121
+ };
122
+ }, [draft2?.active, selection, surface, model]);
123
+ if (!draft2?.active || !bounds || bounds.width < 1 || bounds.height < 1) return null;
124
+ const controls = model.controls();
125
+ const markers2 = controls?.entries.filter((entry2) => entry2.surfaceId === surface.id && model.markerVisible(entry2.id)).map((entry2) => {
126
+ const point = entry2.selection.anchor ?? { x: entry2.selection.rect.x + 16, y: entry2.selection.rect.y };
127
+ return /* @__PURE__ */ jsxs(
128
+ "button",
129
+ {
130
+ type: "button",
131
+ className: css$7.savedMarker,
132
+ "aria-label": `${t("packet.edit")} ${entry2.id}`,
133
+ style: { left: bounds.left + point.x - 14, top: bounds.top + point.y - 28 },
134
+ onClick: () => controls.edit(entry2.id),
135
+ children: [
136
+ /* @__PURE__ */ jsx(ChatFilled, { width: 34, height: 34 }),
137
+ /* @__PURE__ */ jsx("span", { children: entry2.id })
138
+ ]
139
+ },
140
+ entry2.id
141
+ );
142
+ });
143
+ const toolbar2 = controls?.count ? /* @__PURE__ */ jsxs("div", { className: css$7.toolbar, style: { left: bounds.left + bounds.width / 2, top: bounds.bottom - 70 }, children: [
144
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: controls.review, children: [
145
+ t("annotating"),
146
+ " · ",
147
+ controls.count
148
+ ] }),
149
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": t("packet.remove"), title: t("packet.remove"), onClick: controls.clear, children: /* @__PURE__ */ jsx(Trash, { width: 18, height: 18 }) }),
150
+ /* @__PURE__ */ jsx("button", { type: "button", className: css$7.send, onClick: controls.send, children: t("send") }),
151
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": t("close"), title: t("close"), onClick: () => {
152
+ void model.toggle(surface);
153
+ }, children: /* @__PURE__ */ jsx(X, { width: 18, height: 18 }) })
154
+ ] }) : null;
155
+ if (!selection) return createPortal(/* @__PURE__ */ jsxs(Fragment, { children: [
156
+ markers2,
157
+ toolbar2,
158
+ draft2.error && /* @__PURE__ */ jsx(
159
+ "div",
160
+ {
161
+ role: "alert",
162
+ className: css$7.notice,
163
+ style: { left: bounds.left + 12, top: bounds.top + 12 },
164
+ children: annotationErrorText(draft2.error, t)
165
+ }
166
+ )
167
+ ] }), document.body);
168
+ const anchor = selection.anchor ?? { x: selection.rect.x + Math.min(32, selection.rect.width / 2), y: selection.rect.y };
169
+ const x = bounds.left + anchor.x * bounds.width / selection.viewport.width;
170
+ const y = bounds.top + anchor.y * bounds.height / selection.viewport.height;
171
+ const width = Math.min(296, bounds.width - 24);
172
+ const beside = x + width + 40 <= bounds.right - 12;
173
+ const left = beside ? x + 28 : Math.max(bounds.left + 12, Math.min(x - width / 2, bounds.right - width - 12));
174
+ const expanded2 = draft2.comment.length > 0;
175
+ const estimatedHeight = Math.max(56, (input.current?.scrollHeight ?? 24) + 32) + (expanded2 ? 44 : 0) + (draft2.error ? 56 : 0);
176
+ const top = Math.max(bounds.top + 12, Math.min(beside ? y - 40 : y + 16, bounds.bottom - estimatedHeight - 12));
177
+ return createPortal(/* @__PURE__ */ jsxs(Fragment, { children: [
178
+ markers2,
179
+ toolbar2,
180
+ draft2.editing && /* @__PURE__ */ jsx("div", { className: css$7.outline, style: {
181
+ left: bounds.left + selection.rect.x,
182
+ top: bounds.top + selection.rect.y,
183
+ width: selection.rect.width,
184
+ height: selection.rect.height
185
+ } }),
186
+ !draft2.editing && /* @__PURE__ */ jsx("span", { className: css$7.marker, style: { left: x - 16, top: y - 28 }, "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChatFilled, { width: 34, height: 34 }) }),
187
+ /* @__PURE__ */ jsxs(
188
+ "form",
189
+ {
190
+ ref: form,
191
+ className: css$7.comment,
192
+ style: { left, top, width },
193
+ "data-aione-annotation-comment": true,
194
+ "aria-label": t("comment"),
195
+ onSubmit: (event) => {
196
+ event.preventDefault();
197
+ void model.save(surface);
198
+ },
199
+ children: [
200
+ /* @__PURE__ */ jsxs("div", { className: css$7.commentRow, children: [
201
+ /* @__PURE__ */ jsx(
202
+ "button",
203
+ {
204
+ type: "button",
205
+ className: css$7.icon,
206
+ "aria-label": t("adjust"),
207
+ title: t("adjust"),
208
+ "aria-expanded": adjusting,
209
+ disabled: draft2.saving,
210
+ onClick: () => setAdjusting((value) => !value),
211
+ children: /* @__PURE__ */ jsx(SettingsSlider, { width: 16, height: 16 })
212
+ }
213
+ ),
214
+ /* @__PURE__ */ jsx(
215
+ "textarea",
216
+ {
217
+ ref: input,
218
+ rows: 1,
219
+ value: draft2.comment,
220
+ placeholder: t("placeholder"),
221
+ "aria-label": t("comment"),
222
+ disabled: draft2.saving,
223
+ onChange: (event) => model.setComment(surface.id, event.currentTarget.value),
224
+ onKeyDown: (event) => {
225
+ if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
226
+ event.preventDefault();
227
+ void model.save(surface);
228
+ }
229
+ }
230
+ }
231
+ )
232
+ ] }),
233
+ expanded2 && /* @__PURE__ */ jsxs("div", { className: css$7.actions, children: [
234
+ draft2.editing && /* @__PURE__ */ jsx(
235
+ "button",
236
+ {
237
+ type: "button",
238
+ className: `${css$7.icon} ${css$7.delete}`,
239
+ "aria-label": t("packet.remove"),
240
+ disabled: draft2.saving,
241
+ onClick: () => {
242
+ controls?.remove(draft2.editing.entry.id);
243
+ model.cancel(surface.id);
244
+ },
245
+ children: /* @__PURE__ */ jsx(Trash, { width: 18, height: 18 })
246
+ }
247
+ ),
248
+ /* @__PURE__ */ jsx("button", { type: "button", className: css$7.cancel, disabled: draft2.saving, onClick: () => model.cancel(surface.id), children: t("cancel") }),
249
+ /* @__PURE__ */ jsx(
250
+ "button",
251
+ {
252
+ type: "submit",
253
+ className: css$7.save,
254
+ "aria-label": t("save"),
255
+ disabled: draft2.saving || !draft2.comment.trim() || !model.hasTarget(),
256
+ children: draft2.saving ? t("adding") : t("save")
257
+ }
258
+ )
259
+ ] }),
260
+ draft2.error && /* @__PURE__ */ jsx("div", { className: css$7.error, role: "alert", children: annotationErrorText(draft2.error, t) }),
261
+ adjusting && /* @__PURE__ */ jsxs("div", { className: css$7.adjust, children: [
262
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => {
263
+ model.cancel(surface.id);
264
+ void model.setMode(surface, "element");
265
+ }, children: t("element") }),
266
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => {
267
+ model.cancel(surface.id);
268
+ void model.setMode(surface, "region");
269
+ }, children: t("region") }),
270
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => model.cancel(surface.id), children: [
271
+ /* @__PURE__ */ jsx(X, { width: 16, height: 16 }),
272
+ t("cancel")
273
+ ] })
274
+ ] })
275
+ ]
276
+ }
277
+ )
278
+ ] }), document.body);
279
+ }
280
+ const host = "_host_ptdo8_1";
281
+ const chip$1 = "_chip_ptdo8_2";
282
+ const open = "_open_ptdo8_3";
283
+ const removeAll$1 = "_removeAll_ptdo8_5";
284
+ const packet = "_packet_ptdo8_7";
285
+ const entry$2 = "_entry_ptdo8_8";
286
+ const heading$1 = "_heading_ptdo8_9";
287
+ const thumbnail = "_thumbnail_ptdo8_11";
288
+ const spacer = "_spacer_ptdo8_13";
289
+ const icon = "_icon_ptdo8_14";
290
+ const text = "_text_ptdo8_16";
291
+ const image = "_image_ptdo8_17";
292
+ const actions$1 = "_actions_ptdo8_19";
293
+ const dismiss = "_dismiss_ptdo8_21";
294
+ const css$6 = {
295
+ host,
296
+ chip: chip$1,
297
+ open,
298
+ removeAll: removeAll$1,
299
+ packet,
300
+ entry: entry$2,
301
+ heading: heading$1,
302
+ thumbnail,
303
+ spacer,
304
+ icon,
305
+ text,
306
+ image,
307
+ actions: actions$1,
308
+ dismiss
309
+ };
310
+ function PacketPreview({ packets, sessionId, input, actions: actions2, t, editAnnotation }) {
311
+ useSyncExternalStore(packets.subscribe, packets.getSnapshot);
312
+ const [open2, setOpen] = useState(false);
313
+ const [editing, setEditing] = useState();
314
+ const [comment2, setComment] = useState("");
315
+ const host2 = useRef(null);
316
+ const active = [...new Set(input.occurrences.filter((item) => item.source === ANNOTATION_SOURCE).map((item) => item.ref))].flatMap((id) => {
317
+ const packet2 = packets.get(id);
318
+ return packet2?.sessionId === sessionId ? [packet2] : [];
319
+ });
320
+ const count2 = active.reduce((sum, packet2) => sum + packet2.entries.length, 0);
321
+ const editable = input.phase === "plain";
322
+ useEffect(() => {
323
+ if (packets.opened?.sessionId === sessionId) setOpen(true);
324
+ }, [packets.opened, sessionId]);
325
+ useEffect(() => {
326
+ if (!open2) return;
327
+ const outside = (event) => {
328
+ if (event.target instanceof Node && !host2.current?.contains(event.target)) setOpen(false);
329
+ };
330
+ const escape = (event) => {
331
+ if (event.key === "Escape") setOpen(false);
332
+ };
333
+ window.addEventListener("pointerdown", outside);
334
+ window.addEventListener("keydown", escape);
335
+ return () => {
336
+ window.removeEventListener("pointerdown", outside);
337
+ window.removeEventListener("keydown", escape);
338
+ };
339
+ }, [open2]);
340
+ if (!count2) return null;
341
+ const label = `${count2} ${count2 === 1 ? t("packet.one") : t("packet.many")}`;
342
+ const remove2 = (id) => {
343
+ const span = packetSpan(input, id);
344
+ if (span) actions2.insertText("", span);
345
+ };
346
+ return /* @__PURE__ */ jsxs("div", { ref: host2, className: css$6.host, "data-aione-annotation-package": true, children: [
347
+ /* @__PURE__ */ jsxs("div", { className: css$6.chip, children: [
348
+ /* @__PURE__ */ jsxs("button", { type: "button", className: css$6.open, "aria-expanded": open2, "aria-label": label, onClick: () => setOpen((value) => !value), children: [
349
+ /* @__PURE__ */ jsx(Chat, { width: 16, height: 16 }),
350
+ /* @__PURE__ */ jsx("span", { children: label })
351
+ ] }),
352
+ /* @__PURE__ */ jsx(
353
+ "button",
354
+ {
355
+ type: "button",
356
+ className: css$6.removeAll,
357
+ "aria-label": t("packet.removeAll"),
358
+ disabled: !editable,
359
+ onClick: () => {
360
+ for (const packet2 of [...active].reverse()) {
361
+ const span = packetSpan(input, packet2.id);
362
+ if (span) actions2.insertText("", { ...span, draftRev: actions2.captureInsertion().draftRev });
363
+ }
364
+ },
365
+ children: /* @__PURE__ */ jsx(X, { width: 14, height: 14 })
366
+ }
367
+ )
368
+ ] }),
369
+ open2 && /* @__PURE__ */ jsxs("section", { className: css$6.packet, "data-aione-annotation-packet": true, "aria-label": t("packet.title"), children: [
370
+ active.flatMap((packet2) => packet2.entries.map((entry2, index) => /* @__PURE__ */ jsxs("article", { className: css$6.entry, children: [
371
+ /* @__PURE__ */ jsxs("div", { className: css$6.heading, children: [
372
+ /* @__PURE__ */ jsx(
373
+ "button",
374
+ {
375
+ type: "button",
376
+ className: css$6.thumbnail,
377
+ title: t("packet.screenshot"),
378
+ onClick: () => {
379
+ setEditing(editing === `image:${packet2.id}` ? void 0 : `image:${packet2.id}`);
380
+ },
381
+ children: /* @__PURE__ */ jsx("img", { src: packet2.previews[index], alt: `${t("packet.screenshot")} #${entry2.id}` })
382
+ }
383
+ ),
384
+ /* @__PURE__ */ jsx("code", { title: entry2.selection.selector ?? entry2.selection.url, children: entry2.selection.element?.role ?? entry2.selection.element?.tagName ?? entry2.selection.kind }),
385
+ /* @__PURE__ */ jsx("span", { className: css$6.spacer }),
386
+ /* @__PURE__ */ jsx(
387
+ "button",
388
+ {
389
+ type: "button",
390
+ className: css$6.icon,
391
+ "aria-label": t("packet.edit"),
392
+ disabled: !editable,
393
+ onClick: () => {
394
+ if (editAnnotation(entry2, (value) => packets.edit(packet2.id, index, value))) {
395
+ setOpen(false);
396
+ return;
397
+ }
398
+ setEditing(packet2.id);
399
+ setComment(entry2.comment);
400
+ },
401
+ children: /* @__PURE__ */ jsx(EditPencil, { width: 18, height: 18 })
402
+ }
403
+ ),
404
+ /* @__PURE__ */ jsx(
405
+ "button",
406
+ {
407
+ type: "button",
408
+ className: css$6.icon,
409
+ "aria-label": t("packet.remove"),
410
+ disabled: !editable,
411
+ onClick: () => remove2(packet2.id),
412
+ children: /* @__PURE__ */ jsx(Trash, { width: 18, height: 18 })
413
+ }
414
+ )
415
+ ] }),
416
+ editing === `image:${packet2.id}` && /* @__PURE__ */ jsx("img", { className: css$6.image, src: packet2.previews[index], alt: t("packet.screenshot") }),
417
+ editing === packet2.id ? /* @__PURE__ */ jsxs("form", { onSubmit: (event) => {
418
+ event.preventDefault();
419
+ packets.edit(packet2.id, index, comment2);
420
+ setEditing(void 0);
421
+ }, children: [
422
+ /* @__PURE__ */ jsx("textarea", { autoFocus: true, "aria-label": t("comment"), value: comment2, onChange: (event) => setComment(event.currentTarget.value) }),
423
+ /* @__PURE__ */ jsxs("div", { className: css$6.actions, children: [
424
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setEditing(void 0), children: t("cancel") }),
425
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: !comment2.trim(), children: t("packet.done") })
426
+ ] })
427
+ ] }) : /* @__PURE__ */ jsx("p", { className: css$6.text, children: entry2.comment })
428
+ ] }, `${packet2.id}:${entry2.id}`))),
429
+ /* @__PURE__ */ jsx("button", { type: "button", className: css$6.dismiss, "aria-label": t("packet.done"), onClick: () => setOpen(false), children: /* @__PURE__ */ jsx(X, { width: 16, height: 16 }) })
430
+ ] })
431
+ ] });
432
+ }
433
+ const draft = "_draft_ypxoz_1";
434
+ const sent = "_sent_ypxoz_2";
435
+ const chip = "_chip_ypxoz_3";
436
+ const summary$1 = "_summary_ypxoz_4";
437
+ const removeAll = "_removeAll_ypxoz_6";
438
+ const remove = "_remove_ypxoz_6";
439
+ const popover = "_popover_ypxoz_10";
440
+ const details = "_details_ypxoz_10";
441
+ const entry$1 = "_entry_ypxoz_13";
442
+ const heading = "_heading_ypxoz_14";
443
+ const source = "_source_ypxoz_15";
444
+ const quote = "_quote_ypxoz_18";
445
+ const hint = "_hint_ypxoz_19";
446
+ const tableScroll = "_tableScroll_ypxoz_20";
447
+ const cells = "_cells_ypxoz_21";
448
+ const css$5 = {
449
+ draft,
450
+ sent,
451
+ chip,
452
+ summary: summary$1,
453
+ removeAll,
454
+ remove,
455
+ popover,
456
+ details,
457
+ entry: entry$1,
458
+ heading,
459
+ source,
460
+ quote,
461
+ hint,
462
+ tableScroll,
463
+ cells
464
+ };
465
+ function ReferenceCard({ label, children, draft: draft2 = false, initiallyOpen = false, onClear, clearLabel, kind }) {
466
+ const [pinned, setPinned] = useState(initiallyOpen);
467
+ const [hovered, setHovered] = useState(false);
468
+ const host2 = useRef(null);
469
+ const trigger = useRef(null);
470
+ const closeTimer = useRef();
471
+ const panelId = useId();
472
+ const open2 = pinned || hovered;
473
+ useEffect(() => () => clearTimeout(closeTimer.current), []);
474
+ useLayoutEffect(() => {
475
+ const node = host2.current;
476
+ if (!draft2 || !open2 || !node) return;
477
+ const measure = () => {
478
+ let top = 0;
479
+ for (let parent = node.parentElement; parent; parent = parent.parentElement) {
480
+ if (/(auto|scroll|hidden|clip)/u.test(getComputedStyle(parent).overflowY)) top = Math.max(top, parent.getBoundingClientRect().top);
481
+ }
482
+ node.style.setProperty("--file-selection-space", `${Math.max(80, node.getBoundingClientRect().top - top - 8)}px`);
483
+ };
484
+ measure();
485
+ const observer = new ResizeObserver(measure);
486
+ observer.observe(node.closest("[data-composer-card]") ?? node);
487
+ window.addEventListener("resize", measure);
488
+ window.addEventListener("scroll", measure, true);
489
+ return () => {
490
+ observer.disconnect();
491
+ window.removeEventListener("resize", measure);
492
+ window.removeEventListener("scroll", measure, true);
493
+ };
494
+ }, [draft2, open2]);
495
+ useEffect(() => {
496
+ if (!open2) return;
497
+ const close = () => {
498
+ setPinned(false);
499
+ setHovered(false);
500
+ clearTimeout(closeTimer.current);
501
+ };
502
+ const outside = (event) => {
503
+ if (event.target instanceof Node && !host2.current?.contains(event.target)) close();
504
+ };
505
+ const escape = (event) => {
506
+ if (event.key !== "Escape" || event.isComposing) return;
507
+ if (host2.current?.contains(document.activeElement)) trigger.current?.focus();
508
+ close();
509
+ };
510
+ window.addEventListener("pointerdown", outside);
511
+ window.addEventListener("keydown", escape);
512
+ return () => {
513
+ window.removeEventListener("pointerdown", outside);
514
+ window.removeEventListener("keydown", escape);
515
+ };
516
+ }, [open2]);
517
+ return /* @__PURE__ */ jsxs(
518
+ "div",
519
+ {
520
+ ref: host2,
521
+ className: draft2 ? css$5.draft : css$5.sent,
522
+ "data-file-selection-card": kind === "file" ? draft2 ? "draft" : "sent" : void 0,
523
+ "data-response-annotation-card": kind === "response" ? draft2 ? "draft" : "sent" : void 0,
524
+ onPointerEnter: () => {
525
+ if (draft2) {
526
+ clearTimeout(closeTimer.current);
527
+ setHovered(true);
528
+ }
529
+ },
530
+ onPointerLeave: () => {
531
+ closeTimer.current = setTimeout(() => setHovered(false), 160);
532
+ },
533
+ children: [
534
+ /* @__PURE__ */ jsxs("div", { className: css$5.chip, children: [
535
+ /* @__PURE__ */ jsxs(
536
+ "button",
537
+ {
538
+ ref: trigger,
539
+ type: "button",
540
+ className: css$5.summary,
541
+ "aria-expanded": open2,
542
+ "aria-controls": panelId,
543
+ onClick: () => {
544
+ setPinned(!pinned);
545
+ setHovered(false);
546
+ },
547
+ children: [
548
+ /* @__PURE__ */ jsx(Chat, { width: 16, height: 16 }),
549
+ /* @__PURE__ */ jsx("span", { children: label })
550
+ ]
551
+ }
552
+ ),
553
+ onClear && /* @__PURE__ */ jsx("button", { type: "button", className: css$5.removeAll, "aria-label": clearLabel, onClick: onClear, children: /* @__PURE__ */ jsx(X, { width: 14, height: 14 }) })
554
+ ] }),
555
+ open2 && /* @__PURE__ */ jsx(
556
+ "section",
557
+ {
558
+ id: panelId,
559
+ className: draft2 ? css$5.popover : css$5.details,
560
+ "aria-label": label,
561
+ onFocusCapture: () => setPinned(true),
562
+ children: typeof children === "function" ? children() : children
563
+ }
564
+ )
565
+ ]
566
+ }
567
+ );
568
+ }
569
+ function SelectionCard({ items, draft: draft2 = false, initiallyOpen = false, onRemove, onClear, onOpen, renderImage, renderText = (text2) => text2, t }) {
570
+ const [openFailed, setOpenFailed] = useState(false);
571
+ if (!items.length) return null;
572
+ const annotated = items.every((item) => item.version !== 1 && item.comment);
573
+ const label = `${items.length} ${annotated ? items.length === 1 ? t("page.one") : t("page.many") : items.length === 1 ? t("file.one") : t("file.many")}`;
574
+ return /* @__PURE__ */ jsx(ReferenceCard, { kind: "file", draft: draft2, initiallyOpen, label, onClear, clearLabel: t("file.removeAll"), children: () => /* @__PURE__ */ jsxs(Fragment, { children: [
575
+ items.map((item, index) => /* @__PURE__ */ jsxs("article", { className: css$5.entry, children: [
576
+ /* @__PURE__ */ jsxs("div", { className: css$5.heading, children: [
577
+ onOpen ? /* @__PURE__ */ jsx(
578
+ "button",
579
+ {
580
+ type: "button",
581
+ className: css$5.source,
582
+ title: `${t("file.openSource")}: ${filePath(item)}`,
583
+ onClick: async () => {
584
+ try {
585
+ await onOpen(item);
586
+ setOpenFailed(false);
587
+ } catch {
588
+ setOpenFailed(true);
589
+ }
590
+ },
591
+ children: fileLabel(item)
592
+ }
593
+ ) : /* @__PURE__ */ jsx("span", { className: css$5.source, title: filePath(item), children: fileLabel(item) }),
594
+ onRemove && /* @__PURE__ */ jsx(
595
+ "button",
596
+ {
597
+ type: "button",
598
+ className: css$5.remove,
599
+ "aria-label": `${t("file.remove")} ${index + 1}`,
600
+ onClick: () => onRemove(index),
601
+ children: /* @__PURE__ */ jsx(X, { width: 14, height: 14 })
602
+ }
603
+ )
604
+ ] }),
605
+ item.kind === "markdown" && /* @__PURE__ */ jsx("span", { className: css$5.hint, children: t("file.sourceBlock") }),
606
+ item.version !== 1 && item.comment && /* @__PURE__ */ jsx("p", { className: css$5.quote, children: renderText(item.comment) }),
607
+ item.version === 3 ? /* @__PURE__ */ jsx("div", { className: css$5.tableScroll, children: /* @__PURE__ */ jsxs("table", { className: css$5.cells, "aria-label": item.a1, children: [
608
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
609
+ /* @__PURE__ */ jsx("th", { children: t("sheet.cell") }),
610
+ /* @__PURE__ */ jsx("th", { children: t("sheet.value") }),
611
+ item.cells.some((cell) => cell.formula) && /* @__PURE__ */ jsx("th", { children: t("sheet.formula") })
612
+ ] }) }),
613
+ /* @__PURE__ */ jsx("tbody", { children: item.cells.map((cell) => /* @__PURE__ */ jsxs("tr", { children: [
614
+ /* @__PURE__ */ jsxs("td", { children: [
615
+ columnName(cell.column),
616
+ cell.row + 1
617
+ ] }),
618
+ /* @__PURE__ */ jsxs("td", { title: draft2 ? `${t("sheet.raw")}: ${JSON.stringify(cell.raw)}${cell.format ? ` (${cell.format})` : ""}` : void 0, children: [
619
+ cell.display ? renderText(cell.display) : /* @__PURE__ */ jsx("span", { className: css$5.hint, children: t("sheet.empty") }),
620
+ cell.raw !== null && String(cell.raw) !== cell.display && /* @__PURE__ */ jsxs("small", { children: [
621
+ t("sheet.raw"),
622
+ ": ",
623
+ renderText(String(cell.raw))
624
+ ] })
625
+ ] }),
626
+ item.cells.some((value) => value.formula) && /* @__PURE__ */ jsx("td", { children: cell.formula && renderText(cell.formula) })
627
+ ] }, `${cell.row}:${cell.column}`)) })
628
+ ] }) }) : item.text && /* @__PURE__ */ jsx("blockquote", { className: css$5.quote, children: renderText(item.text) }),
629
+ item.version === 2 && item.image && renderImage?.(item)
630
+ ] }, `${item.id}:${index}`)),
631
+ openFailed && /* @__PURE__ */ jsx("p", { role: "alert", className: css$5.hint, children: t("file.openFailed") })
632
+ ] }) });
633
+ }
634
+ const overlay$1 = "_overlay_1y8gz_1";
635
+ const css$4 = {
636
+ overlay: overlay$1
637
+ };
638
+ const DefaultOverlay = forwardRef(function DefaultOverlay2({ children, className, style, ...props }, ref) {
639
+ if (typeof document === "undefined") return null;
640
+ return createPortal(/* @__PURE__ */ jsx("div", { ...props, ref, style, className: `${css$4.overlay}${className ? ` ${className}` : ""}`, "data-annotation-window-overlay": true, children }), document.body);
641
+ });
642
+ const defaultRegions = {
643
+ Overlay: DefaultOverlay,
644
+ interactiveWindowRegion: Object.freeze({ "data-annotation-window-interactive": "" })
645
+ };
646
+ const Regions = createContext(defaultRegions);
647
+ function AnnotationUiProvider({ value, children }) {
648
+ return /* @__PURE__ */ jsx(Regions.Provider, { value, children });
649
+ }
650
+ function useInteractiveWindowRegion() {
651
+ return useContext(Regions).interactiveWindowRegion;
652
+ }
653
+ const WindowOverlay = forwardRef(function WindowOverlay2(props, ref) {
654
+ const { Overlay } = useContext(Regions);
655
+ return /* @__PURE__ */ jsx(Overlay, { ...props, ref });
656
+ });
657
+ const control = "_control_1hlpi_1";
658
+ const toggle$1 = "_toggle_1hlpi_2";
659
+ const chrome = "_chrome_1hlpi_6";
660
+ const outline = "_outline_1hlpi_11";
661
+ const marker$1 = "_marker_1hlpi_12";
662
+ const ask$1 = "_ask_1hlpi_14";
663
+ const comment$1 = "_comment_1hlpi_16";
664
+ const row = "_row_1hlpi_17";
665
+ const confirm = "_confirm_1hlpi_20";
666
+ const expanded = "_expanded_1hlpi_21";
667
+ const actions = "_actions_1hlpi_23";
668
+ const save = "_save_1hlpi_26";
669
+ const batch = "_batch_1hlpi_27";
670
+ const count = "_count_1hlpi_28";
671
+ const grip = "_grip_1hlpi_29";
672
+ const send = "_send_1hlpi_34";
673
+ const error$1 = "_error_1hlpi_37";
674
+ const snapshotReview = "_snapshotReview_1hlpi_38";
675
+ const css$3 = {
676
+ control,
677
+ toggle: toggle$1,
678
+ chrome,
679
+ outline,
680
+ marker: marker$1,
681
+ ask: ask$1,
682
+ comment: comment$1,
683
+ row,
684
+ confirm,
685
+ expanded,
686
+ actions,
687
+ "delete": "_delete_1hlpi_25",
688
+ save,
689
+ batch,
690
+ count,
691
+ grip,
692
+ send,
693
+ error: error$1,
694
+ snapshotReview
695
+ };
696
+ function RequestIcon() {
697
+ return /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M3 16v3a2 2 0 0 0 2 2h3M14 10l7 5-4 1-1 5z" }) });
698
+ }
699
+ function CommentEditor({ t, style, input, comment: comment2, setComment, busy, saved, direct, save: save2, cancel: cancel2, remove: remove2, optional = false }) {
700
+ const interactiveWindowRegion = useInteractiveWindowRegion();
701
+ useLayoutEffect(() => {
702
+ input.current?.focus();
703
+ }, []);
704
+ useLayoutEffect(() => {
705
+ if (!input.current) return;
706
+ input.current.style.height = "24px";
707
+ input.current.style.height = `${Math.min(144, input.current.scrollHeight)}px`;
708
+ }, [comment2]);
709
+ return /* @__PURE__ */ jsxs(
710
+ "form",
711
+ {
712
+ ...interactiveWindowRegion,
713
+ className: `${css$3.comment} ${saved ? css$3.expanded : ""}`,
714
+ style,
715
+ "data-page-comment": true,
716
+ onSubmit: (e) => {
717
+ e.preventDefault();
718
+ save2();
719
+ },
720
+ children: [
721
+ /* @__PURE__ */ jsxs("div", { className: css$3.row, children: [
722
+ /* @__PURE__ */ jsx(
723
+ "textarea",
724
+ {
725
+ ref: input,
726
+ rows: 1,
727
+ maxLength: 8192,
728
+ value: comment2,
729
+ disabled: busy,
730
+ "aria-label": t(optional ? "response.placeholder" : direct ? "page.changes" : "page.placeholder"),
731
+ placeholder: t(optional ? "response.placeholder" : direct ? "page.changes" : "page.placeholder"),
732
+ onChange: (e) => setComment(e.target.value),
733
+ onKeyDown: (e) => {
734
+ if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing && e.keyCode !== 229) {
735
+ e.preventDefault();
736
+ save2();
737
+ }
738
+ }
739
+ }
740
+ ),
741
+ !saved && (!optional || comment2.length > 0) && /* @__PURE__ */ jsx("button", { type: "submit", className: css$3.confirm, "aria-label": t(optional ? "response.save" : direct ? "page.submit" : "page.comment"), disabled: busy || !comment2.trim(), children: /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.7", children: /* @__PURE__ */ jsx("path", { d: direct ? "M10 16V4m-5 5 5-5 5 5" : "m5 10 3 3 7-8" }) }) })
742
+ ] }),
743
+ saved && /* @__PURE__ */ jsxs("div", { className: css$3.actions, children: [
744
+ /* @__PURE__ */ jsx("button", { type: "button", className: css$3.delete, "aria-label": t("page.delete"), disabled: busy, onClick: remove2, children: /* @__PURE__ */ jsx(Trash, { width: 18, height: 18 }) }),
745
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: cancel2, children: t("page.cancel") }),
746
+ /* @__PURE__ */ jsx("button", { type: "submit", className: css$3.save, disabled: busy || !optional && !comment2.trim(), children: t("page.save") })
747
+ ] })
748
+ ]
749
+ }
750
+ );
751
+ }
752
+ function BatchControls({ t, bounds, count: count2, busy, sendDisabled, cancel: cancel2, send: send2, review }) {
753
+ const interactiveWindowRegion = useInteractiveWindowRegion();
754
+ const [offset, setOffset] = useState({ x: 0, y: 0 });
755
+ const bar = useRef(null);
756
+ const drag = useRef();
757
+ const room = Math.max(0, (bounds.width - (bar.current?.offsetWidth ?? 320)) / 2 - 12);
758
+ return /* @__PURE__ */ jsxs("div", { ...interactiveWindowRegion, ref: bar, className: css$3.batch, style: {
759
+ left: bounds.left + bounds.width / 2 + Math.max(-room, Math.min(room, offset.x)),
760
+ top: bounds.bottom - 52 + Math.max(100 - bounds.height, Math.min(0, offset.y)),
761
+ maxWidth: bounds.width - 24
762
+ }, "data-page-batch": true, children: [
763
+ /* @__PURE__ */ jsx("span", { className: css$3.grip, "aria-hidden": "true", onPointerDown: (e) => {
764
+ if (e.button !== 0) return;
765
+ e.preventDefault();
766
+ e.currentTarget.setPointerCapture(e.pointerId);
767
+ drag.current = { pointer: e.pointerId, x: e.clientX, y: e.clientY, offset };
768
+ }, onPointerMove: (e) => {
769
+ const start = drag.current;
770
+ if (!start || start.pointer !== e.pointerId) return;
771
+ setOffset({
772
+ x: Math.max(-room, Math.min(room, start.offset.x + e.clientX - start.x)),
773
+ y: Math.max(100 - bounds.height, Math.min(0, start.offset.y + e.clientY - start.y))
774
+ });
775
+ }, onPointerUp: () => {
776
+ drag.current = void 0;
777
+ }, onLostPointerCapture: () => {
778
+ drag.current = void 0;
779
+ }, children: "⠿" }),
780
+ review && count2 ? /* @__PURE__ */ jsxs("button", { type: "button", className: css$3.count, onClick: review, children: [
781
+ count2,
782
+ " ",
783
+ t(count2 === 1 ? "page.one" : "page.many")
784
+ ] }) : /* @__PURE__ */ jsx("span", { className: css$3.count, children: count2 ? `${count2} ${t(count2 === 1 ? "page.one" : "page.many")}` : t("page.pick") }),
785
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: cancel2, children: t("page.cancel") }),
786
+ !!count2 && /* @__PURE__ */ jsx("button", { type: "button", className: css$3.send, disabled: busy || sendDisabled, onClick: send2, children: t(busy ? "page.sending" : "page.send") })
787
+ ] });
788
+ }
789
+ const requestPick = (pick, presentation) => presentation ? { ...pick, intent: "presentation-request" } : { ...pick, intent: "batch" };
790
+ function pageActionComponent(bridge, batches = new PageBatches()) {
791
+ const workflow = new DocumentWorkflow(bridge, batches);
792
+ return function PageSelectionAction({ absolutePath, t }) {
793
+ const interactiveWindowRegion = useInteractiveWindowRegion();
794
+ const mount = useRef(null), chrome2 = useRef(null), input = useRef(null);
795
+ const pending = useRef(), operationId = useRef(0);
796
+ const [preview, setPreview] = useState(), [bounds, setBounds] = useState();
797
+ const [session, setSession] = useState(bridge.targetKey()), [mode, setMode] = useState(false);
798
+ const [interaction, dispatch] = useReducer(pageInteraction, { phase: "idle", error: "" });
799
+ const current = useRef(interaction);
800
+ current.current = interaction;
801
+ const menu = interaction.phase === "selected" ? interaction.selection : void 0;
802
+ const editor = "draft" in interaction ? interaction.draft : void 0;
803
+ const comment2 = "comment" in interaction ? interaction.comment ?? "" : "";
804
+ const busy = interaction.phase === "submitting", error2 = interaction.error;
805
+ const liveEditor = () => "draft" in current.current ? current.current.draft : void 0;
806
+ const liveMenu = () => current.current.phase === "selected" ? current.current.selection : void 0;
807
+ const setComment = (value) => dispatch({ type: "comment", value });
808
+ const [outline2, setOutline] = useState();
809
+ const [, geometry] = useState(0);
810
+ useSyncExternalStore(batches.subscribe, batches.getSnapshot);
811
+ const supported = /\.(pdf|docx?|pptx?)$/iu.test(absolutePath);
812
+ const kind = /\.pdf$/iu.test(absolutePath) ? "pdf" : "office", presentation = /\.pptx?$/iu.test(absolutePath);
813
+ const address = preview?.dataset.textpreviewUrl ?? "";
814
+ const scope = { session: session ?? "", address };
815
+ let items = [];
816
+ let restoreError = "";
817
+ try {
818
+ if (session && address) items = batches.read(session, address).filter((item) => item.version === 2);
819
+ } catch (e) {
820
+ restoreError = annotationFailure(e);
821
+ }
822
+ const select = (selection) => dispatch({ type: "select", selection });
823
+ const cancelEditor = () => {
824
+ pending.current?.abort();
825
+ dispatch({ type: "cancel" });
826
+ setOutline(void 0);
827
+ };
828
+ const begin = (draft2) => {
829
+ if (draft2.intent === "presentation-request" && !workflow.canSend) return;
830
+ dispatch({ type: "edit", draft: draft2, comment: draft2.saved?.comment ?? "" });
831
+ window.getSelection()?.removeAllRanges();
832
+ };
833
+ useLayoutEffect(() => {
834
+ setPreview(mount.current?.closest("[data-textpreview-url]") ?? void 0);
835
+ }, [absolutePath]);
836
+ useEffect(() => bridge.subscribe(() => setSession(bridge.targetKey())), []);
837
+ useEffect(() => {
838
+ cancelEditor();
839
+ setMode(false);
840
+ return () => {
841
+ pending.current?.abort();
842
+ };
843
+ }, [session, address]);
844
+ useLayoutEffect(() => {
845
+ if (!preview) return;
846
+ const measure = () => {
847
+ setBounds(preview.getBoundingClientRect());
848
+ geometry((n) => n + 1);
849
+ };
850
+ measure();
851
+ const observer = new ResizeObserver(measure);
852
+ observer.observe(preview);
853
+ const pages = new MutationObserver(measure);
854
+ pages.observe(preview, { childList: true, subtree: true, attributes: true, attributeFilter: ["width", "height"] });
855
+ window.addEventListener("resize", measure);
856
+ window.addEventListener("scroll", measure, true);
857
+ return () => {
858
+ observer.disconnect();
859
+ pages.disconnect();
860
+ window.removeEventListener("resize", measure);
861
+ window.removeEventListener("scroll", measure, true);
862
+ };
863
+ }, [preview]);
864
+ useLayoutEffect(() => {
865
+ if (editor) input.current?.focus();
866
+ }, [editor]);
867
+ useLayoutEffect(() => {
868
+ if (!input.current) return;
869
+ input.current.style.height = "24px";
870
+ input.current.style.height = `${Math.min(144, input.current.scrollHeight)}px`;
871
+ }, [comment2, editor]);
872
+ useEffect(() => {
873
+ if (!preview || !supported) return;
874
+ let drag;
875
+ const captureText = () => {
876
+ if (mode || liveEditor()) return;
877
+ const selection = window.getSelection(), range = selection?.rangeCount ? selection.getRangeAt(0) : void 0;
878
+ const value = range && readPageText(range, preview);
879
+ if (!range || !value) {
880
+ select();
881
+ return;
882
+ }
883
+ select({ value, source: range.startContainer, anchors: rangeAnchors(range, preview), bounds: range.getBoundingClientRect() });
884
+ };
885
+ const start = (event) => {
886
+ if (!mode || busy || event.button !== 0 || !(event.target instanceof Element)) return;
887
+ const page = event.target.closest("[data-pdf-page]"), canvas = page?.querySelector("canvas");
888
+ if (!canvas || !preview.contains(canvas) || !canvas.width || !canvas.height) return;
889
+ const box = canvas.getBoundingClientRect();
890
+ if (event.clientX < box.left || event.clientX > box.right || event.clientY < box.top || event.clientY > box.bottom) return;
891
+ event.preventDefault();
892
+ event.stopPropagation();
893
+ cancelEditor();
894
+ window.getSelection()?.removeAllRanges();
895
+ drag = { pointer: event.pointerId, start: { x: event.clientX, y: event.clientY }, canvas, page: Number(page.dataset.pdfPage), element: event.target };
896
+ preview.setPointerCapture(event.pointerId);
897
+ };
898
+ const move = (event) => {
899
+ if (!drag || event.pointerId !== drag.pointer) return;
900
+ const rect = pageRegion(drag.start, { x: event.clientX, y: event.clientY }, drag.canvas.getBoundingClientRect());
901
+ setOutline(rect ? projectAnchors([{ page: drag.page, rect }], preview)[0] : void 0);
902
+ };
903
+ const end = (event) => {
904
+ if (!drag || event.pointerId !== drag.pointer) return;
905
+ const active = drag;
906
+ drag = void 0;
907
+ if (preview.hasPointerCapture(event.pointerId)) preview.releasePointerCapture(event.pointerId);
908
+ const region = pageRegion(active.start, { x: event.clientX, y: event.clientY }, active.canvas.getBoundingClientRect());
909
+ setOutline(void 0);
910
+ if (region) {
911
+ const anchors = [{ page: active.page, rect: region }], box = projectAnchors(anchors, preview)[0];
912
+ if (box) begin({ intent: "batch", source: active.canvas, anchors, bounds: box, value: { canvas: active.canvas, region, page: active.page } });
913
+ } else {
914
+ const span = active.element.closest("[data-pdf-text] span");
915
+ if (!span) return;
916
+ const range = document.createRange();
917
+ range.selectNodeContents(span);
918
+ const value = readPageText(range, preview);
919
+ if (value) begin({ intent: "batch", source: span, anchors: rangeAnchors(range, preview), bounds: span.getBoundingClientRect(), value });
920
+ }
921
+ };
922
+ const keyboard = (event) => {
923
+ if (event.isComposing) return;
924
+ if (event.key === "Escape") {
925
+ if (liveEditor() || liveMenu()) {
926
+ event.preventDefault();
927
+ cancelEditor();
928
+ } else setMode(false);
929
+ }
930
+ const selected = liveMenu();
931
+ if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key.toLowerCase() === "x" && selected) {
932
+ event.preventDefault();
933
+ begin(requestPick(selected, presentation));
934
+ }
935
+ };
936
+ const outside = (event) => {
937
+ if (!(event.target instanceof Node) || chrome2.current?.contains(event.target) || mount.current?.contains(event.target)) return;
938
+ if (liveEditor() && !busy) cancelEditor();
939
+ if (!preview.contains(event.target)) select();
940
+ };
941
+ const mutations = new MutationObserver((records) => {
942
+ const source2 = liveEditor()?.source ?? liveMenu()?.source;
943
+ const page = (source2 instanceof Element ? source2 : source2?.parentElement)?.closest("[data-pdf-page]");
944
+ if (source2 && (!source2.isConnected || records.some((r) => page?.contains(r.target) && (r.type !== "childList" || [...r.addedNodes, ...r.removedNodes].some((node) => !(node instanceof Element && node.classList.contains("endOfContent"))))))) cancelEditor();
945
+ if (drag && !drag.canvas.isConnected) {
946
+ drag = void 0;
947
+ setOutline(void 0);
948
+ }
949
+ });
950
+ mutations.observe(preview, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["width", "height"] });
951
+ if (mode) preview.dataset.pageRegionSelecting = "true";
952
+ const cancelDrag = () => {
953
+ drag = void 0;
954
+ setOutline(void 0);
955
+ };
956
+ document.addEventListener("selectionchange", captureText);
957
+ preview.addEventListener("pointerdown", start, true);
958
+ window.addEventListener("pointermove", move);
959
+ window.addEventListener("pointerup", end);
960
+ window.addEventListener("pointercancel", cancelDrag);
961
+ window.addEventListener("scroll", cancelDrag, true);
962
+ window.addEventListener("keydown", keyboard);
963
+ window.addEventListener("pointerdown", outside);
964
+ return () => {
965
+ mutations.disconnect();
966
+ delete preview.dataset.pageRegionSelecting;
967
+ document.removeEventListener("selectionchange", captureText);
968
+ preview.removeEventListener("pointerdown", start, true);
969
+ window.removeEventListener("pointermove", move);
970
+ window.removeEventListener("pointerup", end);
971
+ window.removeEventListener("pointercancel", cancelDrag);
972
+ window.removeEventListener("scroll", cancelDrag, true);
973
+ window.removeEventListener("keydown", keyboard);
974
+ window.removeEventListener("pointerdown", outside);
975
+ };
976
+ }, [preview, supported, mode, busy]);
977
+ if (!supported) return null;
978
+ const save2 = async () => {
979
+ if (!editor || !session || !comment2.trim() || busy || pending.current && !pending.current.signal.aborted || editor.source && !editor.source.isConnected) return;
980
+ const controller = new AbortController();
981
+ pending.current?.abort();
982
+ pending.current = controller;
983
+ const token = ++operationId.current;
984
+ dispatch({ type: "start", operation: "commit", token });
985
+ try {
986
+ let snapshot = editor.saved, image2;
987
+ if (!snapshot) {
988
+ const id = crypto.randomUUID();
989
+ if ("canvas" in editor.value) {
990
+ const capture = await capturePageRegion(editor.value.canvas, editor.value.region, id, controller.signal);
991
+ image2 = capture.file;
992
+ snapshot = {
993
+ version: 2,
994
+ id,
995
+ address,
996
+ kind,
997
+ text: "",
998
+ startPage: editor.value.page,
999
+ endPage: editor.value.page,
1000
+ anchors: editor.anchors,
1001
+ region: editor.value.region,
1002
+ image: capture.image
1003
+ };
1004
+ } else snapshot = { version: 2, id, address, kind, ...editor.value, anchors: editor.anchors };
1005
+ }
1006
+ snapshot = { ...snapshot, comment: comment2.trim() };
1007
+ if (editor.intent === "presentation-request") {
1008
+ await workflow.commit(scope, { kind: "presentation-request", snapshot }, controller.signal);
1009
+ } else await workflow.commit(scope, { kind: "batch", snapshot, image: image2 }, controller.signal);
1010
+ if (!controller.signal.aborted) {
1011
+ dispatch({ type: "complete", token });
1012
+ setOutline(void 0);
1013
+ }
1014
+ } catch (e) {
1015
+ if (!controller.signal.aborted) dispatch({ type: "fail", token, error: annotationFailure(e) });
1016
+ } finally {
1017
+ if (pending.current === controller) pending.current = void 0;
1018
+ }
1019
+ };
1020
+ const remove2 = async (ids) => {
1021
+ if (!session || busy || pending.current && !pending.current.signal.aborted) return;
1022
+ const controller = new AbortController(), token = ++operationId.current;
1023
+ pending.current = controller;
1024
+ dispatch({ type: "start", operation: "batch-remove", token });
1025
+ try {
1026
+ await workflow.remove(scope, ids, controller.signal);
1027
+ if (!controller.signal.aborted) dispatch({ type: "complete", token });
1028
+ } catch (e) {
1029
+ if (!controller.signal.aborted) dispatch({ type: "fail", token, error: annotationFailure(e) });
1030
+ } finally {
1031
+ if (pending.current === controller) pending.current = void 0;
1032
+ }
1033
+ };
1034
+ const send2 = async () => {
1035
+ if (!session || !items.length || busy || pending.current && !pending.current.signal.aborted || editor || !workflow.canSend) return;
1036
+ const controller = new AbortController(), token = ++operationId.current;
1037
+ pending.current = controller;
1038
+ dispatch({ type: "start", operation: "batch-send", token });
1039
+ try {
1040
+ await workflow.send(scope, controller.signal);
1041
+ if (!controller.signal.aborted) {
1042
+ setMode(false);
1043
+ dispatch({ type: "complete", token });
1044
+ }
1045
+ } catch (e) {
1046
+ if (!controller.signal.aborted) dispatch({ type: "fail", token, error: annotationFailure(e) });
1047
+ } finally {
1048
+ if (pending.current === controller) pending.current = void 0;
1049
+ }
1050
+ };
1051
+ const openSaved = (item) => {
1052
+ if (busy || !preview) return;
1053
+ const anchors = item.anchors ?? (item.region ? [{ page: item.startPage, rect: item.region }] : []);
1054
+ const box = enclosing(projectAnchors(anchors, preview)) ?? pageBounds(preview, item.startPage);
1055
+ if (box) begin({ intent: "batch", saved: item, anchors, bounds: box, value: item });
1056
+ };
1057
+ const editorBoxes = editor && preview ? projectAnchors(editor.anchors, preview) : [];
1058
+ const editorBox = enclosing(editorBoxes) ?? editor?.bounds;
1059
+ const width = Math.min(296, (bounds?.width ?? 320) - 24);
1060
+ const beside = editorBox && bounds && editorBox.right + width + 24 < bounds.right;
1061
+ const left = editorBox && bounds ? Math.max(bounds.left + 12, Math.min(beside ? editorBox.right + 18 : editorBox.right - width, bounds.right - width - 12)) : 0;
1062
+ const top = editorBox && bounds ? Math.max(bounds.top + 48, Math.min(
1063
+ beside ? editorBox.top - 24 : editorBox.bottom + 8,
1064
+ bounds.bottom - (editor?.saved ? 160 : Math.max(64, (input.current?.scrollHeight ?? 24) + 40))
1065
+ )) : 0;
1066
+ const visible = (box) => bounds && box.bottom > bounds.top + 40 && box.top < bounds.bottom && box.right > bounds.left && box.left < bounds.right;
1067
+ return /* @__PURE__ */ jsxs("span", { ...interactiveWindowRegion, ref: mount, className: css$3.control, children: [
1068
+ /* @__PURE__ */ jsxs(
1069
+ "button",
1070
+ {
1071
+ type: "button",
1072
+ className: css$3.toggle,
1073
+ "data-page-region-toggle": true,
1074
+ "aria-pressed": mode,
1075
+ disabled: !session || busy,
1076
+ onClick: () => {
1077
+ cancelEditor();
1078
+ setMode(!mode);
1079
+ },
1080
+ children: [
1081
+ /* @__PURE__ */ jsx(RequestIcon, {}),
1082
+ t("page.request")
1083
+ ]
1084
+ }
1085
+ ),
1086
+ preview && bounds && /* @__PURE__ */ jsxs(
1087
+ WindowOverlay,
1088
+ {
1089
+ ref: chrome2,
1090
+ className: css$3.chrome,
1091
+ "data-document-annotations": true,
1092
+ style: { clipPath: `inset(${bounds.top + 40}px ${Math.max(0, window.innerWidth - bounds.right)}px ${Math.max(0, window.innerHeight - bounds.bottom)}px ${bounds.left}px)` },
1093
+ children: [
1094
+ items.map((item) => {
1095
+ if (item.id === editor?.saved?.id) return null;
1096
+ const boxes2 = projectAnchors(item.anchors ?? [], preview), box = enclosing(boxes2);
1097
+ return box && visible(box) ? /* @__PURE__ */ jsxs("div", { children: [
1098
+ boxes2.filter(visible).map((b, i) => /* @__PURE__ */ jsx("div", { className: css$3.outline, style: b }, i)),
1099
+ /* @__PURE__ */ jsx(
1100
+ "button",
1101
+ {
1102
+ ...interactiveWindowRegion,
1103
+ type: "button",
1104
+ className: css$3.marker,
1105
+ "aria-label": `${t("page.edit")} ${items.indexOf(item) + 1}`,
1106
+ style: { left: Math.min(box.right - 13, bounds.right - 30), top: Math.max(bounds.top + 44, box.top - 13) },
1107
+ onClick: () => openSaved(item)
1108
+ }
1109
+ )
1110
+ ] }, item.id) : null;
1111
+ }),
1112
+ outline2 && /* @__PURE__ */ jsx("div", { className: css$3.outline, style: outline2 }),
1113
+ menu && !editor && visible(menu.bounds) && /* @__PURE__ */ jsx(
1114
+ "button",
1115
+ {
1116
+ ...interactiveWindowRegion,
1117
+ type: "button",
1118
+ className: css$3.ask,
1119
+ "data-page-ask": true,
1120
+ disabled: presentation && !workflow.canSend,
1121
+ style: { left: Math.max(bounds.left + 8, Math.min(menu.bounds.right, bounds.right - 230)), top: Math.min(menu.bounds.bottom + 8, bounds.bottom - 44) },
1122
+ onPointerDown: (e) => e.preventDefault(),
1123
+ onClick: () => begin(requestPick(menu, presentation)),
1124
+ children: presentation ? /* @__PURE__ */ jsxs(Fragment, { children: [
1125
+ /* @__PURE__ */ jsx(RequestIcon, {}),
1126
+ t("page.request")
1127
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1128
+ t("page.ask"),
1129
+ /* @__PURE__ */ jsx("kbd", { children: "Ctrl Shift X" })
1130
+ ] })
1131
+ }
1132
+ ),
1133
+ editor && editorBox && visible(editorBox) && /* @__PURE__ */ jsxs(Fragment, { children: [
1134
+ (editorBoxes.length ? editorBoxes : [editorBox]).filter(visible).map((box, i) => /* @__PURE__ */ jsx("div", { className: css$3.outline, style: { ...box, borderStyle: editor.intent === "presentation-request" ? "solid" : void 0 } }, i)),
1135
+ editor.intent === "batch" && /* @__PURE__ */ jsx("span", { className: css$3.marker, style: { left: Math.min(editorBox.right - 13, bounds.right - 30), top: Math.max(bounds.top + 44, editorBox.top - 13) } }),
1136
+ /* @__PURE__ */ jsx(
1137
+ CommentEditor,
1138
+ {
1139
+ t,
1140
+ style: { left, top, width },
1141
+ input,
1142
+ comment: comment2,
1143
+ setComment,
1144
+ busy,
1145
+ saved: !!editor.saved,
1146
+ direct: editor.intent === "presentation-request",
1147
+ save: () => void save2(),
1148
+ cancel: cancelEditor,
1149
+ remove: () => void remove2([editor.saved.id])
1150
+ },
1151
+ editor.saved?.id ?? "new"
1152
+ )
1153
+ ] }),
1154
+ (mode || items.length > 0) && /* @__PURE__ */ jsx(
1155
+ BatchControls,
1156
+ {
1157
+ t,
1158
+ bounds,
1159
+ count: items.length,
1160
+ busy,
1161
+ sendDisabled: !!editor || !workflow.canSend,
1162
+ cancel: () => {
1163
+ void remove2(items.map((item) => item.id)).then(() => setMode(false));
1164
+ },
1165
+ send: () => void send2()
1166
+ },
1167
+ `${session}:${address}`
1168
+ ),
1169
+ (error2 || restoreError) && /* @__PURE__ */ jsx("div", { ...interactiveWindowRegion, className: css$3.error, role: "alert", style: { left: bounds.left + 12, bottom: window.innerHeight - bounds.bottom + 60, maxWidth: bounds.width - 24 }, children: annotationErrorText(error2 || restoreError, t) }),
1170
+ !workflow.canSend && (mode || items.length > 0 || menu) && /* @__PURE__ */ jsx(
1171
+ "div",
1172
+ {
1173
+ role: "status",
1174
+ className: css$3.error,
1175
+ style: { left: bounds.left + 12, bottom: window.innerHeight - bounds.bottom + 60, maxWidth: bounds.width - 24 },
1176
+ children: t("page.senderUnavailable")
1177
+ }
1178
+ )
1179
+ ]
1180
+ }
1181
+ )
1182
+ ] });
1183
+ };
1184
+ }
1185
+ function failedSheetPreview(root) {
1186
+ for (const host2 of root.querySelectorAll("[data-textpreview-url]")) {
1187
+ const address = host2.dataset.textpreviewUrl;
1188
+ if (!/\.(xlsx|csv)$/iu.test(parseFileAddress(address)?.path ?? "") || host2.querySelector("[data-annotation-sheet]")) continue;
1189
+ if (host2.querySelector("[data-textpreview-failed], [data-sheet-load-error]")) return { host: host2, address };
1190
+ }
1191
+ }
1192
+ function sheetRecoveryComponent(bridge, batches) {
1193
+ const workflow = new DocumentWorkflow(bridge, batches);
1194
+ return function SheetRecovery({ sessionId, t }) {
1195
+ const interactiveWindowRegion = useInteractiveWindowRegion();
1196
+ const [target, setTarget] = useState();
1197
+ const [bounds, setBounds] = useState(), [busy, setBusy] = useState(false), [error2, setError] = useState("");
1198
+ const pending = useRef();
1199
+ useSyncExternalStore(batches.subscribe, batches.getSnapshot);
1200
+ useEffect(() => {
1201
+ let frame;
1202
+ const scan = () => {
1203
+ frame = void 0;
1204
+ const next = bridge.targetKey() === sessionId ? failedSheetPreview(document) : void 0;
1205
+ setTarget((previous) => previous?.host === next?.host && previous?.address === next?.address ? previous : next);
1206
+ };
1207
+ const schedule = () => {
1208
+ if (frame === void 0) frame = requestAnimationFrame(scan);
1209
+ };
1210
+ const observer = new MutationObserver(schedule);
1211
+ observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["data-textpreview-url", "data-textpreview-failed"] });
1212
+ const unsubscribe = bridge.subscribe(schedule);
1213
+ scan();
1214
+ return () => {
1215
+ observer.disconnect();
1216
+ unsubscribe();
1217
+ if (frame !== void 0) cancelAnimationFrame(frame);
1218
+ };
1219
+ }, [sessionId]);
1220
+ useEffect(() => {
1221
+ setError("");
1222
+ setBusy(false);
1223
+ if (!target) return;
1224
+ const measure = () => setBounds(target.host.getBoundingClientRect());
1225
+ const observer = new ResizeObserver(measure);
1226
+ observer.observe(target.host);
1227
+ measure();
1228
+ window.addEventListener("resize", measure);
1229
+ return () => {
1230
+ observer.disconnect();
1231
+ window.removeEventListener("resize", measure);
1232
+ pending.current?.abort();
1233
+ };
1234
+ }, [target, sessionId]);
1235
+ if (!target || !bounds || bounds.width === 0) return null;
1236
+ let items;
1237
+ try {
1238
+ items = batches.read(sessionId, target.address).filter((s) => s.version === 3);
1239
+ } catch {
1240
+ return null;
1241
+ }
1242
+ if (!items.length) return null;
1243
+ const run = async (send2, ids = items.map((item) => item.id)) => {
1244
+ if (busy || pending.current && !pending.current.signal.aborted) return;
1245
+ const controller = new AbortController();
1246
+ pending.current = controller;
1247
+ setBusy(true);
1248
+ setError("");
1249
+ try {
1250
+ if (send2) {
1251
+ await workflow.send({ session: sessionId, address: target.address }, controller.signal);
1252
+ } else await workflow.remove({ session: sessionId, address: target.address }, ids, controller.signal);
1253
+ } catch (e) {
1254
+ if (!controller.signal.aborted) setError(annotationFailure(e));
1255
+ } finally {
1256
+ if (pending.current === controller) {
1257
+ pending.current = void 0;
1258
+ if (!controller.signal.aborted) setBusy(false);
1259
+ }
1260
+ }
1261
+ };
1262
+ return /* @__PURE__ */ jsxs(WindowOverlay, { className: css$3.chrome, "data-sheet-recovery": true, children: [
1263
+ /* @__PURE__ */ jsxs("div", { ...interactiveWindowRegion, className: css$3.snapshotReview, style: { left: bounds.left + 12, top: bounds.top + 58, width: bounds.width - 24, maxHeight: bounds.height - 125 }, children: [
1264
+ /* @__PURE__ */ jsx("p", { children: t("sheet.unavailable") }),
1265
+ !workflow.canSend && /* @__PURE__ */ jsx("p", { role: "status", children: t("page.senderUnavailable") }),
1266
+ /* @__PURE__ */ jsx(SelectionCard, { t, items, initiallyOpen: true, onRemove: busy ? void 0 : (index) => void run(false, [items[index].id]) }),
1267
+ error2 && /* @__PURE__ */ jsx("p", { role: "alert", children: annotationErrorText(error2, t) })
1268
+ ] }),
1269
+ /* @__PURE__ */ jsx(
1270
+ BatchControls,
1271
+ {
1272
+ t,
1273
+ bounds,
1274
+ count: items.length,
1275
+ busy,
1276
+ sendDisabled: !workflow.canSend,
1277
+ cancel: () => void run(false),
1278
+ send: () => void run(true)
1279
+ }
1280
+ )
1281
+ ] });
1282
+ };
1283
+ }
1284
+ function ResponseCard({ items, draft: draft2 = false, t, onOpen, onRemove, onClear, renderText = (text2) => text2 }) {
1285
+ const [failure, setFailure] = useState();
1286
+ const run = async (action2) => {
1287
+ try {
1288
+ await action2();
1289
+ setFailure(void 0);
1290
+ } catch (error2) {
1291
+ setFailure(annotationFailure(error2));
1292
+ }
1293
+ };
1294
+ if (!items.length) return null;
1295
+ return /* @__PURE__ */ jsxs(
1296
+ ReferenceCard,
1297
+ {
1298
+ kind: "response",
1299
+ draft: draft2,
1300
+ label: `${items.length} ${t(items.length === 1 ? "packet.one" : "packet.many")}`,
1301
+ clearLabel: t("packet.removeAll"),
1302
+ onClear: onClear ? () => {
1303
+ void run(onClear);
1304
+ } : void 0,
1305
+ children: [
1306
+ items.map((snapshot, index) => /* @__PURE__ */ jsxs("article", { className: css$5.entry, children: [
1307
+ /* @__PURE__ */ jsxs("div", { className: css$5.heading, children: [
1308
+ onOpen ? /* @__PURE__ */ jsxs(
1309
+ "button",
1310
+ {
1311
+ type: "button",
1312
+ className: css$5.source,
1313
+ title: t("response.source"),
1314
+ onClick: (event) => {
1315
+ const trigger = event.currentTarget;
1316
+ void run(() => onOpen(snapshot, false, trigger));
1317
+ },
1318
+ children: [
1319
+ index + 1,
1320
+ ". ",
1321
+ t("response.location")
1322
+ ]
1323
+ }
1324
+ ) : /* @__PURE__ */ jsxs("span", { className: css$5.source, children: [
1325
+ index + 1,
1326
+ ". ",
1327
+ t("response.location")
1328
+ ] }),
1329
+ draft2 && onOpen && /* @__PURE__ */ jsx(
1330
+ "button",
1331
+ {
1332
+ type: "button",
1333
+ className: css$5.remove,
1334
+ "aria-label": `${t("response.edit")} ${index + 1}`,
1335
+ onClick: (event) => {
1336
+ const trigger = event.currentTarget;
1337
+ void run(() => onOpen(snapshot, true, trigger));
1338
+ },
1339
+ children: /* @__PURE__ */ jsx(Pencil, { width: 14, height: 14 })
1340
+ }
1341
+ ),
1342
+ onRemove && /* @__PURE__ */ jsx(
1343
+ "button",
1344
+ {
1345
+ type: "button",
1346
+ className: css$5.remove,
1347
+ "aria-label": `${t("page.delete")} ${index + 1}`,
1348
+ onClick: () => {
1349
+ void run(() => onRemove(snapshot));
1350
+ },
1351
+ children: /* @__PURE__ */ jsx(Trash, { width: 14, height: 14 })
1352
+ }
1353
+ )
1354
+ ] }),
1355
+ /* @__PURE__ */ jsx("blockquote", { className: css$5.quote, "aria-label": t("response.selected"), children: renderText(snapshot.text) }),
1356
+ snapshot.comment && /* @__PURE__ */ jsx("p", { className: css$5.quote, children: renderText(snapshot.comment) })
1357
+ ] }, `${snapshot.id}:${index}`)),
1358
+ failure && /* @__PURE__ */ jsx("p", { role: "alert", className: css$5.hint, children: annotationErrorText(failure, t) })
1359
+ ]
1360
+ }
1361
+ );
1362
+ }
1363
+ const overlay = "_overlay_1izny_1";
1364
+ const markers = "_markers_1izny_3";
1365
+ const highlight = "_highlight_1izny_4";
1366
+ const marker = "_marker_1izny_3";
1367
+ const ask = "_ask_1izny_6";
1368
+ const css$2 = {
1369
+ overlay,
1370
+ markers,
1371
+ highlight,
1372
+ marker,
1373
+ ask
1374
+ };
1375
+ function viewport(root) {
1376
+ const frame = root.closest("[data-conversation-scroll]");
1377
+ if (frame) return frame.getBoundingClientRect();
1378
+ for (let parent = root.parentElement; parent; parent = parent.parentElement) {
1379
+ if (/(auto|scroll)/u.test(getComputedStyle(parent).overflowY)) return parent.getBoundingClientRect();
1380
+ }
1381
+ return (root.closest("[data-chat-flow]") ?? root).getBoundingClientRect();
1382
+ }
1383
+ const boxes = (ranges) => ranges.flatMap((range) => Array.from(range.getClientRects())).filter((rect) => rect.width > 0 && rect.height > 0);
1384
+ function ResponseOverlay({ controller, sessionId, t }) {
1385
+ const interactiveWindowRegion = useInteractiveWindowRegion();
1386
+ useSyncExternalStore(controller.subscribe, controller.getSnapshot);
1387
+ const [layout, setLayout] = useState(0);
1388
+ const [comment2, setComment] = useState("");
1389
+ const input = useRef(null);
1390
+ const addButton = useRef(null);
1391
+ const [addSize, setAddSize] = useState({ width: 0, height: 0 });
1392
+ const overlay2 = useRef(null);
1393
+ const observedEditors = useRef(/* @__PURE__ */ new Set());
1394
+ useEffect(() => controller.composer.subscribe(() => {
1395
+ setLayout((value) => value + 1);
1396
+ }), [controller]);
1397
+ const active = controller.composer.targetKey() === sessionId;
1398
+ const editor = active && controller.editor?.snapshot.sessionId === sessionId ? controller.editor : void 0;
1399
+ const candidate = active ? controller.candidate : void 0;
1400
+ const items = active ? controller.composer.responses?.read(sessionId) ?? [] : [];
1401
+ const source2 = [...controller.surfaces].find((surface) => surface.source.sessionId === sessionId)?.root;
1402
+ const frameRoot = source2?.closest("[data-chat-flow]") ?? source2;
1403
+ useLayoutEffect(() => {
1404
+ setComment(editor?.snapshot.comment ?? "");
1405
+ }, [editor]);
1406
+ useLayoutEffect(() => {
1407
+ const button2 = addButton.current;
1408
+ if (!button2) return;
1409
+ const measure = () => {
1410
+ const { width, height } = button2.getBoundingClientRect();
1411
+ setAddSize((previous) => previous.width === width && previous.height === height ? previous : { width, height });
1412
+ };
1413
+ measure();
1414
+ const observer = new ResizeObserver(measure);
1415
+ observer.observe(button2);
1416
+ return () => observer.disconnect();
1417
+ }, [candidate, editor, t]);
1418
+ useLayoutEffect(() => {
1419
+ if (!active) return;
1420
+ const observer = new ResizeObserver(() => setLayout((value) => value + 1));
1421
+ if (frameRoot) observer.observe(frameRoot);
1422
+ if (input.current?.form) observer.observe(input.current.form);
1423
+ return () => observer.disconnect();
1424
+ }, [active, frameRoot, editor]);
1425
+ useEffect(() => {
1426
+ if (!active) return;
1427
+ let frame = 0;
1428
+ const measure = () => {
1429
+ cancelAnimationFrame(frame);
1430
+ frame = requestAnimationFrame(() => setLayout((value) => value + 1));
1431
+ };
1432
+ const select = () => {
1433
+ if (overlay2.current?.contains(document.activeElement)) return;
1434
+ controller.select(sessionId);
1435
+ };
1436
+ const keyup = (event) => {
1437
+ if (!event.isComposing && event.key.startsWith("Arrow")) select();
1438
+ };
1439
+ const outside = (event) => {
1440
+ if (event.target instanceof Node && !overlay2.current?.contains(event.target)) {
1441
+ if (controller.editor) controller.dismiss();
1442
+ else if (controller.selectedId) controller.dismiss();
1443
+ }
1444
+ };
1445
+ const keydown = (event) => {
1446
+ if (event.key === "Tab" && !event.shiftKey && controller.candidate && !controller.editor && !overlay2.current?.contains(document.activeElement)) {
1447
+ const add = overlay2.current?.querySelector("[data-response-add]");
1448
+ if (add) {
1449
+ event.preventDefault();
1450
+ add.focus();
1451
+ return;
1452
+ }
1453
+ }
1454
+ if (event.key !== "Escape" || event.isComposing || !controller.editor && !controller.candidate && !controller.selectedId) return;
1455
+ event.preventDefault();
1456
+ controller.dismiss(true);
1457
+ };
1458
+ document.addEventListener("selectionchange", select);
1459
+ document.addEventListener("pointerup", select);
1460
+ document.addEventListener("keyup", keyup);
1461
+ document.addEventListener("keydown", keydown);
1462
+ document.addEventListener("pointerdown", outside);
1463
+ window.addEventListener("scroll", measure, true);
1464
+ window.addEventListener("resize", measure);
1465
+ return () => {
1466
+ cancelAnimationFrame(frame);
1467
+ document.removeEventListener("selectionchange", select);
1468
+ document.removeEventListener("pointerup", select);
1469
+ document.removeEventListener("keyup", keyup);
1470
+ document.removeEventListener("keydown", keydown);
1471
+ document.removeEventListener("pointerdown", outside);
1472
+ window.removeEventListener("scroll", measure, true);
1473
+ window.removeEventListener("resize", measure);
1474
+ controller.dismiss();
1475
+ };
1476
+ }, [active, controller, sessionId]);
1477
+ useEffect(() => {
1478
+ if (!editor) return;
1479
+ if (items.some((item) => item.id === editor.snapshot.id)) observedEditors.current.add(editor.snapshot.id);
1480
+ else if (observedEditors.current.has(editor.snapshot.id) && !controller.busy) controller.dismiss();
1481
+ }, [controller, editor, items]);
1482
+ if (!active) return null;
1483
+ const fallback = document.querySelector("[data-chat-flow]");
1484
+ if (!source2 && !fallback) return null;
1485
+ const bounds = viewport(source2 ?? fallback);
1486
+ if (bounds.width < 1 || bounds.height < 1) return null;
1487
+ const visible = (box) => box.bottom > bounds.top && box.top < bounds.bottom && box.right > bounds.left && box.left < bounds.right;
1488
+ const entries = items.map((snapshot) => {
1489
+ const ranges = controller.ranges(snapshot);
1490
+ const root = ranges[0]?.startContainer.parentElement?.closest("[data-response-source]");
1491
+ return { snapshot, rects: boxes(ranges), root };
1492
+ });
1493
+ const selectedRanges = controller.selected ? controller.ranges(controller.selected) : [];
1494
+ const selectedRoot = selectedRanges[0]?.startContainer.parentElement?.closest("[data-response-source]");
1495
+ const readonlySelection = controller.selected && !items.some((item) => item.id === controller.selectedId) && !editor ? boxes(selectedRanges).filter(visible) : [];
1496
+ const pick = candidate && boxes(candidate.ranges).filter(visible).at(-1);
1497
+ const editing = editor && (entries.find((entry2) => entry2.snapshot.id === editor.snapshot.id)?.rects.filter(visible).at(-1) ?? new DOMRect(bounds.left + bounds.width / 2, bounds.bottom - 30, 0, 0));
1498
+ const panelWidth = Math.max(100, Math.min(296, bounds.width - 16));
1499
+ const panelHeight = input.current?.form?.getBoundingClientRect().height || (editor?.initial ? 46 : 128);
1500
+ const position = (box, width, height) => ({
1501
+ width,
1502
+ left: Math.max(bounds.left + 8, Math.min(box.right - width, bounds.right - width - 8)),
1503
+ top: Math.max(bounds.top + 8, Math.min(box.top - height - 12 >= bounds.top ? box.top - height - 12 : box.bottom + 10, bounds.bottom - height - 8))
1504
+ });
1505
+ return /* @__PURE__ */ jsxs(
1506
+ WindowOverlay,
1507
+ {
1508
+ ref: overlay2,
1509
+ className: `${css$3.chrome} ${css$2.overlay}`,
1510
+ "data-response-annotation-ui": true,
1511
+ "data-layout-revision": layout,
1512
+ style: { clipPath: `inset(${Math.max(0, bounds.top)}px ${Math.max(0, window.innerWidth - bounds.right)}px ${Math.max(0, window.innerHeight - bounds.bottom)}px ${Math.max(0, bounds.left)}px)` },
1513
+ children: [
1514
+ selectedRoot && createPortal(/* @__PURE__ */ jsx("div", { "data-response-annotation-ui": true, className: css$2.markers, children: readonlySelection.map((rect, index) => /* @__PURE__ */ jsx(
1515
+ "span",
1516
+ {
1517
+ className: css$2.highlight,
1518
+ style: { left: rect.left - selectedRoot.getBoundingClientRect().left, top: rect.top - selectedRoot.getBoundingClientRect().top, width: rect.width, height: rect.height }
1519
+ },
1520
+ `source:${index}`
1521
+ )) }), selectedRoot),
1522
+ entries.map(({ snapshot, rects, root }, index) => {
1523
+ const visibleRects = rects.filter(visible);
1524
+ const end = visibleRects.at(-1);
1525
+ if (!end || !root) return null;
1526
+ const origin = root.getBoundingClientRect();
1527
+ return createPortal(/* @__PURE__ */ jsxs("div", { "data-response-annotation-ui": true, className: css$2.markers, children: [
1528
+ controller.selectedId === snapshot.id && visibleRects.map((rect, boxIndex) => /* @__PURE__ */ jsx(
1529
+ "span",
1530
+ {
1531
+ className: css$2.highlight,
1532
+ style: { left: rect.left - origin.left, top: rect.top - origin.top, width: rect.width, height: rect.height }
1533
+ },
1534
+ boxIndex
1535
+ )),
1536
+ /* @__PURE__ */ jsx(
1537
+ "button",
1538
+ {
1539
+ ...interactiveWindowRegion,
1540
+ type: "button",
1541
+ className: `${css$3.marker} ${css$2.marker}`,
1542
+ "aria-label": `${t("response.edit")} ${index + 1}`,
1543
+ style: { left: Math.max(bounds.left + 2, Math.min(end.right + 4, bounds.right - 22)) - origin.left, top: Math.max(bounds.top + 2, end.top + end.height / 2 - 10) - origin.top },
1544
+ onClick: (event) => {
1545
+ void controller.open(snapshot, true, event.currentTarget).catch((error2) => {
1546
+ controller.failure = annotationFailure(error2);
1547
+ controller.changed();
1548
+ });
1549
+ },
1550
+ children: index + 1
1551
+ }
1552
+ )
1553
+ ] }), root, snapshot.id);
1554
+ }),
1555
+ pick && !editor && /* @__PURE__ */ jsx(
1556
+ "button",
1557
+ {
1558
+ ...interactiveWindowRegion,
1559
+ ref: addButton,
1560
+ type: "button",
1561
+ className: `${css$3.ask} ${css$2.ask}`,
1562
+ "data-response-add": true,
1563
+ style: { ...position(pick, addSize.width || 80, addSize.height || 28), width: "max-content", maxWidth: bounds.width - 16 },
1564
+ disabled: controller.busy,
1565
+ onPointerDown: (event) => event.preventDefault(),
1566
+ onClick: () => {
1567
+ void controller.add();
1568
+ },
1569
+ children: t("response.add")
1570
+ }
1571
+ ),
1572
+ editor && editing && /* @__PURE__ */ jsx(
1573
+ CommentEditor,
1574
+ {
1575
+ optional: true,
1576
+ t,
1577
+ input,
1578
+ style: position(editing, panelWidth, panelHeight),
1579
+ comment: comment2,
1580
+ setComment,
1581
+ saved: !editor.initial,
1582
+ busy: controller.busy,
1583
+ save: () => controller.save(comment2),
1584
+ cancel: () => controller.dismiss(true),
1585
+ remove: () => controller.remove()
1586
+ },
1587
+ `${editor.snapshot.id}:${editor.initial}`
1588
+ ),
1589
+ controller.failure && /* @__PURE__ */ jsx(
1590
+ "div",
1591
+ {
1592
+ ...interactiveWindowRegion,
1593
+ role: "alert",
1594
+ className: css$3.error,
1595
+ style: { left: bounds.left + 12, top: bounds.bottom - 70, maxWidth: bounds.width - 24 },
1596
+ children: annotationErrorText(controller.failure, t)
1597
+ }
1598
+ )
1599
+ ]
1600
+ }
1601
+ );
1602
+ }
1603
+ const action = "_action_j93o2_1";
1604
+ const button = "_button_j93o2_2";
1605
+ const error = "_error_j93o2_6";
1606
+ const css$1 = {
1607
+ action,
1608
+ button,
1609
+ error
1610
+ };
1611
+ function sourceRoot(preview, kind) {
1612
+ const selector = kind === "code" ? "[data-code-preview]" : kind === "text" ? "[data-textpreview-plain]" : "[data-annotation-markdown-source] [data-document-markdown]";
1613
+ return preview.querySelector(selector);
1614
+ }
1615
+ function actionComponent(kind, bridge) {
1616
+ return function FileSelectionAction({ content, useTabInfo, t }) {
1617
+ const { tab } = useTabInfo();
1618
+ const mount = useRef(null);
1619
+ const popup = useRef(null);
1620
+ const pending = useRef(null);
1621
+ const mounted = useRef(false);
1622
+ const [selected, setSelected] = useState();
1623
+ const [target, setTarget] = useState(bridge.hasTarget());
1624
+ const [busy, setBusy] = useState(false);
1625
+ const [error2, setError] = useState("");
1626
+ const source2 = content.kind === "text" ? content.text : void 0;
1627
+ const eof = content.kind === "text" ? content.eof : false;
1628
+ useEffect(() => {
1629
+ let previous = bridge.targetKey();
1630
+ return bridge.subscribe(() => {
1631
+ setTarget(bridge.hasTarget());
1632
+ if (previous !== bridge.targetKey()) {
1633
+ previous = bridge.targetKey();
1634
+ pending.current?.abort();
1635
+ setSelected(void 0);
1636
+ }
1637
+ });
1638
+ }, []);
1639
+ useEffect(() => {
1640
+ mounted.current = true;
1641
+ return () => {
1642
+ mounted.current = false;
1643
+ pending.current?.abort();
1644
+ pending.current = null;
1645
+ };
1646
+ }, []);
1647
+ useEffect(() => {
1648
+ pending.current?.abort();
1649
+ pending.current = null;
1650
+ setBusy(false);
1651
+ setSelected(void 0);
1652
+ setError("");
1653
+ const preview = mount.current?.closest("[data-textpreview-url]");
1654
+ if (preview === void 0 || preview === null || source2 === void 0) return;
1655
+ const capture = () => {
1656
+ const selection = window.getSelection();
1657
+ const body = sourceRoot(preview, kind);
1658
+ if (body === null || selection === null || selection.rangeCount === 0) {
1659
+ setSelected(void 0);
1660
+ return;
1661
+ }
1662
+ const range = selection.getRangeAt(0);
1663
+ const value = readFileSelection(range, body, kind);
1664
+ if (!value) {
1665
+ setSelected(void 0);
1666
+ return;
1667
+ }
1668
+ const bounds = preview.getBoundingClientRect();
1669
+ const rect = range.getBoundingClientRect();
1670
+ if (rect.bottom < bounds.top || rect.top > bounds.bottom || rect.right < bounds.left || rect.left > bounds.right) {
1671
+ setSelected(void 0);
1672
+ return;
1673
+ }
1674
+ const width = popup.current?.offsetWidth || 140;
1675
+ const above = kind === "markdown" ? rect.top - bounds.top > 48 : rect.bottom + 48 > bounds.bottom;
1676
+ setSelected({
1677
+ selection: value,
1678
+ target: bridge.targetKey(),
1679
+ above,
1680
+ left: Math.max(bounds.left + 8, Math.min(rect.left, Math.min(bounds.right, window.innerWidth) - width - 8)),
1681
+ top: above ? Math.max(bounds.top + 48, rect.top - 8) : Math.min(bounds.bottom - 48, rect.bottom + 8)
1682
+ });
1683
+ };
1684
+ const escape = (event) => {
1685
+ if (event.key === "Escape") {
1686
+ pending.current?.abort();
1687
+ setSelected(void 0);
1688
+ const native = window.getSelection();
1689
+ if (native?.anchorNode && preview.contains(native.anchorNode)) native.removeAllRanges();
1690
+ }
1691
+ };
1692
+ const outside = (event) => {
1693
+ if (event.target instanceof Node && !preview.contains(event.target) && !popup.current?.contains(event.target)) setSelected(void 0);
1694
+ };
1695
+ document.addEventListener("selectionchange", capture);
1696
+ preview.addEventListener("pointerup", capture);
1697
+ window.addEventListener("scroll", capture, true);
1698
+ window.addEventListener("resize", capture);
1699
+ window.addEventListener("keydown", escape);
1700
+ window.addEventListener("pointerdown", outside);
1701
+ return () => {
1702
+ document.removeEventListener("selectionchange", capture);
1703
+ preview.removeEventListener("pointerup", capture);
1704
+ window.removeEventListener("scroll", capture, true);
1705
+ window.removeEventListener("resize", capture);
1706
+ window.removeEventListener("keydown", escape);
1707
+ window.removeEventListener("pointerdown", outside);
1708
+ pending.current?.abort();
1709
+ pending.current = null;
1710
+ };
1711
+ }, [tab.id, tab.contentId, source2, eof, kind]);
1712
+ if (content.kind !== "text") return null;
1713
+ const add = async () => {
1714
+ if (selected === void 0 || busy) return;
1715
+ const selection = { ...selected.selection, version: 1, id: crypto.randomUUID(), address: tab.contentId, kind };
1716
+ const controller = new AbortController();
1717
+ pending.current?.abort();
1718
+ pending.current = controller;
1719
+ setBusy(true);
1720
+ setError("");
1721
+ try {
1722
+ await bridge.add({ text: "", fileSelection: selection, signal: controller.signal, expectedTarget: selected.target });
1723
+ if (mounted.current && !controller.signal.aborted) {
1724
+ setSelected(void 0);
1725
+ const native = window.getSelection();
1726
+ const preview = mount.current?.closest("[data-textpreview-url]");
1727
+ if (native?.anchorNode && preview?.contains(native.anchorNode)) native.removeAllRanges();
1728
+ }
1729
+ } catch (failure) {
1730
+ if (mounted.current && !controller.signal.aborted) {
1731
+ setError(annotationFailure(failure));
1732
+ }
1733
+ } finally {
1734
+ if (pending.current === controller) {
1735
+ pending.current = null;
1736
+ if (mounted.current) setBusy(false);
1737
+ }
1738
+ }
1739
+ };
1740
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1741
+ /* @__PURE__ */ jsx("span", { ref: mount, hidden: true }),
1742
+ selected && createPortal(/* @__PURE__ */ jsxs(
1743
+ "div",
1744
+ {
1745
+ ref: popup,
1746
+ className: css$1.action,
1747
+ style: { left: selected.left, top: selected.top, transform: selected.above ? "translateY(-100%)" : void 0 },
1748
+ "data-file-selection-popover": true,
1749
+ children: [
1750
+ /* @__PURE__ */ jsxs(
1751
+ "button",
1752
+ {
1753
+ type: "button",
1754
+ className: css$1.button,
1755
+ "data-annotation-file-add": true,
1756
+ disabled: !target || busy,
1757
+ title: target ? t("file.addTitle") : t("file.openSession"),
1758
+ onPointerDown: (event) => event.preventDefault(),
1759
+ onClick: () => {
1760
+ void add();
1761
+ },
1762
+ children: [
1763
+ /* @__PURE__ */ jsx(Chat, { width: 16, height: 16 }),
1764
+ busy ? t("file.adding") : t("file.add")
1765
+ ]
1766
+ }
1767
+ ),
1768
+ error2 !== "" && /* @__PURE__ */ jsx("span", { className: css$1.error, role: "alert", children: annotationErrorText(error2, t) })
1769
+ ]
1770
+ }
1771
+ ), document.body)
1772
+ ] });
1773
+ };
1774
+ }
1775
+ const copyMenu = ["copy"];
1776
+ const emptyMenu = [];
1777
+ function SheetSurface({ resourceAddress: address, format, value, bridge, at: t, batches, t: excelT, Workbook, bodyCss, workbookCss = "" }) {
1778
+ const interactiveWindowRegion = useInteractiveWindowRegion();
1779
+ const workflow = useMemo(() => new DocumentWorkflow(bridge, batches), [bridge, batches]);
1780
+ const mount = useRef(null), workbookHost = useRef(null), api = useRef(null);
1781
+ const input = useRef(null), chrome2 = useRef(null);
1782
+ const [toolbar2, setToolbar] = useState(null), [bounds, setBounds] = useState();
1783
+ const [session, setSession] = useState(bridge.targetKey()), [mode, setMode] = useState(false);
1784
+ const [activeSheet, setActiveSheet] = useState(String(value.sheets.find((s) => s.status === 1)?.id ?? value.sheets[0].id));
1785
+ const [selection, setSelection] = useState(), [menu, setMenu] = useState(false);
1786
+ const [review, setReview] = useState(false);
1787
+ const [editor, setEditor] = useState(), [comment2, setComment] = useState("");
1788
+ const [editorHeight, setEditorHeight] = useState(0);
1789
+ const [error2, setError] = useState(""), [busy, setBusy] = useState(false);
1790
+ const [capture, setCapture] = useState();
1791
+ const captureRevision = useRef(0);
1792
+ const pending = useRef(), gesture = useRef(false), frame = useRef();
1793
+ const selectionFrame = useRef();
1794
+ const painted = useRef(new PaintedCells()), [, repaint] = useState(0);
1795
+ const current = useRef({ mode, busy, editor, session, activeSheet });
1796
+ current.current = { mode, busy, editor, session, activeSheet };
1797
+ const selectionRef = useRef();
1798
+ useSyncExternalStore(batches.subscribe, batches.getSnapshot);
1799
+ let items = [];
1800
+ let restoreError = "";
1801
+ try {
1802
+ if (session) items = batches.read(session, address).filter((s) => s.version === 3);
1803
+ } catch (e) {
1804
+ restoreError = annotationFailure(e);
1805
+ }
1806
+ const close = () => {
1807
+ captureRevision.current++;
1808
+ gesture.current = false;
1809
+ setEditor(void 0);
1810
+ setMenu(false);
1811
+ setReview(false);
1812
+ setComment("");
1813
+ setError("");
1814
+ workbookHost.current?.focus({ preventScroll: true });
1815
+ };
1816
+ const requestCapture = (action2) => {
1817
+ const request = { revision: ++captureRevision.current, action: action2, session: current.current.session, address };
1818
+ startTransition(() => setCapture(request));
1819
+ };
1820
+ const requestCaptureRef = useRef(requestCapture);
1821
+ requestCaptureRef.current = requestCapture;
1822
+ const begin = (saved) => {
1823
+ captureRevision.current++;
1824
+ if (!api.current || current.current.busy) return;
1825
+ try {
1826
+ const selections = api.current.getSelection();
1827
+ const selected = selections?.[0] ?? selectionRef.current;
1828
+ if (!saved && (selections?.length !== 1 || !selected)) throw new AnnotationError("error.separateRanges");
1829
+ const range = selected && readRange(selected);
1830
+ if (!saved && (!range || (range.row[1] - range.row[0] + 1) * (range.column[1] - range.column[0] + 1) > 4096)) throw new AnnotationError("error.selectionTooLarge");
1831
+ const snapshot = saved ?? spreadsheetSnapshot(address, format, api.current.getSheet(), selected, api.current.getCellsByRange(selected));
1832
+ fileReference(snapshot);
1833
+ setEditor({ snapshot, saved: !!saved });
1834
+ setComment(snapshot.comment ?? "");
1835
+ setMenu(false);
1836
+ setError("");
1837
+ } catch (e) {
1838
+ setError(annotationFailure(e));
1839
+ }
1840
+ };
1841
+ const beginRef = useRef(begin);
1842
+ beginRef.current = begin;
1843
+ useEffect(() => {
1844
+ if (!capture || capture.revision !== captureRevision.current || capture.session !== session || capture.address !== address) return;
1845
+ captureRevision.current++;
1846
+ if (current.current.busy) return;
1847
+ if (capture.action === "begin") beginRef.current();
1848
+ else setMenu(true);
1849
+ }, [capture, session, address]);
1850
+ const hooks = useMemo(() => ({
1851
+ beforeRenderCellArea: () => {
1852
+ if (frame.current === void 0) {
1853
+ painted.current.clear();
1854
+ frame.current = requestAnimationFrame(() => {
1855
+ frame.current = void 0;
1856
+ repaint((n) => n + 1);
1857
+ });
1858
+ }
1859
+ return true;
1860
+ },
1861
+ afterRenderCell: (_cell, info, ctx) => painted.current.record(info, ctx),
1862
+ afterRenderColumnHeaderCell: (_label, index, left2, width2, height2, ctx) => painted.current.column(index, left2, width2, ctx, height2),
1863
+ afterRenderRowHeaderCell: (_label, index, top2, width2, height2, ctx) => painted.current.row(index, top2, height2, ctx, width2),
1864
+ afterSelectionChange: (sheetId, selected) => {
1865
+ selectionRef.current = selected;
1866
+ const next = readRange(selected);
1867
+ if (selectionFrame.current !== void 0) cancelAnimationFrame(selectionFrame.current);
1868
+ selectionFrame.current = requestAnimationFrame(() => {
1869
+ selectionFrame.current = void 0;
1870
+ setSelection((previous) => JSON.stringify(previous) === JSON.stringify(next) ? previous : next);
1871
+ setActiveSheet(sheetId);
1872
+ });
1873
+ },
1874
+ afterActivateSheet: () => {
1875
+ captureRevision.current++;
1876
+ setMenu(false);
1877
+ setEditor(void 0);
1878
+ }
1879
+ }), []);
1880
+ useEffect(() => bridge.subscribe(() => setSession(bridge.targetKey())), []);
1881
+ useEffect(() => {
1882
+ close();
1883
+ setBusy(false);
1884
+ setMode(false);
1885
+ return () => pending.current?.abort();
1886
+ }, [session, address]);
1887
+ useLayoutEffect(() => {
1888
+ const form = input.current?.closest("form");
1889
+ if (!editor || !form) {
1890
+ setEditorHeight(0);
1891
+ return;
1892
+ }
1893
+ const measure = () => setEditorHeight(form.getBoundingClientRect().height);
1894
+ const observer = new ResizeObserver(measure);
1895
+ observer.observe(form);
1896
+ measure();
1897
+ return () => observer.disconnect();
1898
+ }, [editor?.snapshot.id, editor?.saved]);
1899
+ useLayoutEffect(() => {
1900
+ const preview = mount.current?.closest("[data-textpreview-url]");
1901
+ if (!preview || !workbookHost.current) return;
1902
+ setToolbar(preview.querySelector("[data-sheet-annotation-toolbar]"));
1903
+ const measure = () => {
1904
+ setBounds(mount.current.getBoundingClientRect());
1905
+ window.dispatchEvent(new Event("resize"));
1906
+ };
1907
+ const observer = new ResizeObserver(measure);
1908
+ observer.observe(workbookHost.current);
1909
+ measure();
1910
+ const scroll = () => {
1911
+ setBounds(mount.current.getBoundingClientRect());
1912
+ };
1913
+ window.addEventListener("scroll", scroll, true);
1914
+ return () => {
1915
+ observer.disconnect();
1916
+ window.removeEventListener("scroll", scroll, true);
1917
+ };
1918
+ }, []);
1919
+ useEffect(() => {
1920
+ const down = (e) => {
1921
+ if (chrome2.current?.contains(e.target)) return;
1922
+ captureRevision.current++;
1923
+ gesture.current = false;
1924
+ if (!mount.current?.contains(e.target)) {
1925
+ setMenu(false);
1926
+ if (!current.current.busy) setEditor(void 0);
1927
+ return;
1928
+ }
1929
+ gesture.current = e.button === 0 && e.target.closest(".fortune-cell-area") !== null;
1930
+ if (gesture.current && !current.current.busy) {
1931
+ setEditor(void 0);
1932
+ setMenu(false);
1933
+ }
1934
+ };
1935
+ const up = () => {
1936
+ if (!gesture.current) return;
1937
+ gesture.current = false;
1938
+ requestCaptureRef.current(current.current.mode ? "begin" : "menu");
1939
+ };
1940
+ const cancel2 = () => {
1941
+ gesture.current = false;
1942
+ captureRevision.current++;
1943
+ };
1944
+ const key = (e) => {
1945
+ const within = mount.current?.closest("[data-textpreview-url]")?.contains(document.activeElement) || chrome2.current?.contains(document.activeElement);
1946
+ if (!within) return;
1947
+ if (e.key === "Escape") {
1948
+ e.preventDefault();
1949
+ if (!current.current.busy) {
1950
+ if (!current.current.editor) setMode(false);
1951
+ close();
1952
+ }
1953
+ }
1954
+ if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key.toLowerCase() === "x" && !current.current.editor) {
1955
+ e.preventDefault();
1956
+ e.stopPropagation();
1957
+ requestCaptureRef.current("begin");
1958
+ }
1959
+ };
1960
+ document.addEventListener("pointerdown", down, true);
1961
+ document.addEventListener("pointerup", up);
1962
+ document.addEventListener("pointercancel", cancel2);
1963
+ window.addEventListener("keydown", key, true);
1964
+ return () => {
1965
+ document.removeEventListener("pointerdown", down, true);
1966
+ document.removeEventListener("pointerup", up);
1967
+ window.removeEventListener("keydown", key, true);
1968
+ document.removeEventListener("pointercancel", cancel2);
1969
+ cancel2();
1970
+ if (frame.current !== void 0) cancelAnimationFrame(frame.current);
1971
+ if (selectionFrame.current !== void 0) cancelAnimationFrame(selectionFrame.current);
1972
+ pending.current?.abort();
1973
+ };
1974
+ }, []);
1975
+ useEffect(() => registerSpreadsheet(address, (snapshot) => {
1976
+ const workbook = api.current;
1977
+ if (!workbook) throw new AnnotationError("error.sheetNotReady");
1978
+ const sheet = workbook.getAllSheets().find((s) => String(s.id) === snapshot.sheet.id && s.name === snapshot.sheet.name) ?? workbook.getAllSheets().find((s) => s.name === snapshot.sheet.name);
1979
+ if (!sheet || sheet.hide) throw new AnnotationError("error.sheetUnavailable");
1980
+ workbook.activateSheet({ id: sheet.id });
1981
+ requestAnimationFrame(() => {
1982
+ workbook.setSelection([snapshot.range], { id: sheet.id });
1983
+ workbook.scroll({ targetRow: snapshot.range.row[0], targetColumn: snapshot.range.column[0] });
1984
+ });
1985
+ }), [address]);
1986
+ const run = async (operation) => {
1987
+ if (busy || pending.current && !pending.current.signal.aborted || !session) return;
1988
+ const controller = new AbortController();
1989
+ pending.current = controller;
1990
+ setBusy(true);
1991
+ setError("");
1992
+ try {
1993
+ await operation(controller.signal);
1994
+ if (!controller.signal.aborted) close();
1995
+ } catch (e) {
1996
+ if (!controller.signal.aborted) setError(annotationFailure(e));
1997
+ } finally {
1998
+ if (pending.current === controller) {
1999
+ pending.current = void 0;
2000
+ if (!controller.signal.aborted) setBusy(false);
2001
+ }
2002
+ }
2003
+ };
2004
+ const scope = { session: session ?? "", address };
2005
+ const save2 = () => {
2006
+ if (editor && comment2.trim()) void run((signal) => workflow.commit(scope, { kind: "batch", snapshot: { ...editor.snapshot, comment: comment2.trim() } }, signal));
2007
+ };
2008
+ const remove2 = (ids) => run((signal) => workflow.remove(scope, ids, signal));
2009
+ const send2 = () => run(async (signal) => {
2010
+ await workflow.send(scope, signal);
2011
+ setMode(false);
2012
+ });
2013
+ const frozen = value.sheets.find((s) => String(s.id) === activeSheet)?.frozen;
2014
+ const editorBox = bounds && editor && editor.snapshot.sheet.id === activeSheet ? painted.current.range(editor.snapshot.range, bounds, frozen) : void 0;
2015
+ const selectedBox = bounds && selection && painted.current.range(selection, bounds, frozen);
2016
+ const width = Math.min(296, (bounds?.width ?? 320) - 24);
2017
+ const left = editorBox && bounds ? Math.max(bounds.left + 12, Math.min(editorBox.right - width, bounds.right - width - 12)) : 0;
2018
+ const height = editorHeight || (editor?.saved ? 148 : 56);
2019
+ const preferredTop = editorBox ? editor?.saved ? editorBox.top - 28 : editorBox.bottom - 4 : 0;
2020
+ const top = editorBox && bounds ? Math.max(bounds.top + 12, Math.min(
2021
+ preferredTop + height <= bounds.bottom - 12 ? preferredTop : editorBox.top - height - 8,
2022
+ bounds.bottom - height - 12
2023
+ )) : 0;
2024
+ const hasFormulas = value.sheets.some((s) => s.celldata?.some((c) => c.v?.f));
2025
+ return /* @__PURE__ */ jsxs("section", { ref: mount, className: bodyCss.body, "data-excel-preview": true, "data-annotation-sheet": address, "aria-label": excelT("title"), children: [
2026
+ /* @__PURE__ */ jsx("style", { children: workbookCss }),
2027
+ value.unsupportedFeatures.length > 0 && /* @__PURE__ */ jsx("div", { className: bodyCss.notice, role: "note", children: excelT("unsupportedNotice", { features: value.unsupportedFeatures.map((f) => excelT(f)).join(excelT("featureSeparator")) }) }),
2028
+ /* @__PURE__ */ jsxs("div", { ref: workbookHost, className: `${bodyCss.workbook} ${hasFormulas ? bodyCss.withFormulaWarning : ""}`, tabIndex: -1, style: { outline: "none" }, children: [
2029
+ hasFormulas && /* @__PURE__ */ jsx("button", { className: bodyCss.formulaWarning, type: "button", title: excelT("formulaWarning"), "aria-label": excelT("formulaWarning"), children: "ⓘ" }),
2030
+ /* @__PURE__ */ jsx(
2031
+ Workbook,
2032
+ {
2033
+ ref: api,
2034
+ data: value.sheets,
2035
+ hooks,
2036
+ lang: excelT("language"),
2037
+ rowHeaderWidth: 46,
2038
+ columnHeaderHeight: 20,
2039
+ allowEdit: false,
2040
+ showToolbar: false,
2041
+ showFormulaBar: true,
2042
+ showSheetTabs: true,
2043
+ forceCalculation: false,
2044
+ cellContextMenu: copyMenu,
2045
+ headerContextMenu: emptyMenu,
2046
+ sheetTabContextMenu: emptyMenu,
2047
+ filterContextMenu: emptyMenu
2048
+ }
2049
+ )
2050
+ ] }),
2051
+ toolbar2 && createPortal(/* @__PURE__ */ jsxs(
2052
+ "button",
2053
+ {
2054
+ ...interactiveWindowRegion,
2055
+ type: "button",
2056
+ className: css$3.toggle,
2057
+ "aria-pressed": mode,
2058
+ disabled: !session || busy,
2059
+ "data-sheet-request-edits": true,
2060
+ onClick: () => {
2061
+ close();
2062
+ setMode(!mode);
2063
+ },
2064
+ children: [
2065
+ /* @__PURE__ */ jsx(RequestIcon, {}),
2066
+ t("page.request")
2067
+ ]
2068
+ }
2069
+ ), toolbar2),
2070
+ bounds && /* @__PURE__ */ jsxs(
2071
+ WindowOverlay,
2072
+ {
2073
+ ref: chrome2,
2074
+ className: css$3.chrome,
2075
+ "data-document-annotations": true,
2076
+ "data-sheet-annotations": true,
2077
+ style: { clipPath: `inset(${Math.max(0, bounds.top)}px ${Math.max(0, window.innerWidth - bounds.right)}px ${Math.max(0, window.innerHeight - bounds.bottom)}px ${Math.max(0, bounds.left)}px)` },
2078
+ children: [
2079
+ items.filter((s) => s.sheet.id === activeSheet && s.id !== editor?.snapshot.id).map((item, i) => {
2080
+ const box = painted.current.range(item.range, bounds, frozen);
2081
+ return box && /* @__PURE__ */ jsxs("div", { children: [
2082
+ /* @__PURE__ */ jsx("div", { className: css$3.outline, style: box, "data-sheet-range": item.a1 }),
2083
+ /* @__PURE__ */ jsx(
2084
+ "button",
2085
+ {
2086
+ ...interactiveWindowRegion,
2087
+ type: "button",
2088
+ className: css$3.marker,
2089
+ "aria-label": `${t("page.edit")} ${i + 1}`,
2090
+ style: { left: Math.min(box.right - 13, bounds.right - 30), top: Math.max(bounds.top + 4, box.top - 13) },
2091
+ onClick: () => begin(item),
2092
+ disabled: busy
2093
+ }
2094
+ )
2095
+ ] }, item.id);
2096
+ }),
2097
+ menu && selectedBox && !editor && /* @__PURE__ */ jsxs(
2098
+ "button",
2099
+ {
2100
+ ...interactiveWindowRegion,
2101
+ type: "button",
2102
+ className: css$3.ask,
2103
+ "data-sheet-ask": true,
2104
+ style: { left: Math.max(bounds.left + 8, Math.min(selectedBox.left, bounds.right - 235)), top: Math.min(selectedBox.bottom + 8, bounds.bottom - 50) },
2105
+ onPointerDown: (e) => e.preventDefault(),
2106
+ onClick: () => requestCapture("begin"),
2107
+ children: [
2108
+ t("page.ask"),
2109
+ /* @__PURE__ */ jsx("kbd", { children: "Ctrl Shift X" })
2110
+ ]
2111
+ }
2112
+ ),
2113
+ editor && editorBox && /* @__PURE__ */ jsxs(Fragment, { children: [
2114
+ /* @__PURE__ */ jsx("div", { className: css$3.outline, style: editorBox, "data-sheet-range": editor.snapshot.a1 }),
2115
+ /* @__PURE__ */ jsx("span", { className: css$3.marker, style: { left: Math.min(editorBox.right - 13, bounds.right - 30), top: Math.max(bounds.top + 4, editorBox.top - 13) } }),
2116
+ /* @__PURE__ */ jsx(
2117
+ CommentEditor,
2118
+ {
2119
+ t,
2120
+ style: { left, top, width },
2121
+ input,
2122
+ comment: comment2,
2123
+ setComment,
2124
+ busy,
2125
+ saved: editor.saved,
2126
+ save: save2,
2127
+ cancel: close,
2128
+ remove: () => void remove2([editor.snapshot.id])
2129
+ },
2130
+ editor.snapshot.id
2131
+ )
2132
+ ] }),
2133
+ (mode || items.length > 0) && /* @__PURE__ */ jsx(
2134
+ BatchControls,
2135
+ {
2136
+ t,
2137
+ bounds,
2138
+ count: items.length,
2139
+ busy,
2140
+ sendDisabled: !!editor || !workflow.canSend,
2141
+ review: () => {
2142
+ setEditor(void 0);
2143
+ setReview(!review);
2144
+ },
2145
+ cancel: () => {
2146
+ void remove2(items.map((s) => s.id)).then(() => setMode(false));
2147
+ },
2148
+ send: () => void send2()
2149
+ },
2150
+ session
2151
+ ),
2152
+ review && /* @__PURE__ */ jsx("div", { ...interactiveWindowRegion, className: css$3.snapshotReview, style: { left: bounds.left + 12, top: bounds.top + 12, width: bounds.width - 24, maxHeight: bounds.height - 90 }, children: /* @__PURE__ */ jsx(SelectionCard, { t, items, initiallyOpen: true, onRemove: busy ? void 0 : (index) => void remove2([items[index].id]) }) }),
2153
+ (error2 || restoreError) && /* @__PURE__ */ jsx("div", { ...interactiveWindowRegion, className: css$3.error, role: "alert", style: { left: bounds.left + 12, top: bounds.top + 40, maxWidth: bounds.width - 24 }, children: annotationErrorText(error2 || restoreError, t) }),
2154
+ !workflow.canSend && (mode || items.length > 0) && /* @__PURE__ */ jsx(
2155
+ "div",
2156
+ {
2157
+ role: "status",
2158
+ className: css$3.error,
2159
+ style: { left: bounds.left + 12, top: bounds.top + 40, maxWidth: bounds.width - 24 },
2160
+ children: t("page.senderUnavailable")
2161
+ }
2162
+ )
2163
+ ]
2164
+ }
2165
+ )
2166
+ ] });
2167
+ }
2168
+ const card = "_card_y1yyl_1";
2169
+ const summary = "_summary_y1yyl_3";
2170
+ const toggle = "_toggle_y1yyl_9";
2171
+ const entry = "_entry_y1yyl_11";
2172
+ const context = "_context_y1yyl_13";
2173
+ const number = "_number_y1yyl_17";
2174
+ const comment = "_comment_y1yyl_19";
2175
+ const detail = "_detail_y1yyl_23";
2176
+ const url = "_url_y1yyl_25";
2177
+ const evidence = "_evidence_y1yyl_29";
2178
+ const css = {
2179
+ card,
2180
+ summary,
2181
+ toggle,
2182
+ entry,
2183
+ context,
2184
+ number,
2185
+ comment,
2186
+ detail,
2187
+ url,
2188
+ evidence
2189
+ };
2190
+ function SentAnnotations({ content, attachments, fallback, renderDefault, renderImage, renderText, imageName, t, openFile, openResponse }) {
2191
+ const [expanded2, setExpanded] = useState(false);
2192
+ const packets = content.flatMap((block) => {
2193
+ const packet2 = readAnnotation(block);
2194
+ return packet2 ? [packet2] : [];
2195
+ });
2196
+ const files = content.flatMap((block) => {
2197
+ const snapshot = readSentFile(block);
2198
+ return snapshot ? [snapshot] : [];
2199
+ });
2200
+ const responses = content.flatMap((block) => {
2201
+ const snapshot = readSentResponse(block);
2202
+ return snapshot ? [snapshot] : [];
2203
+ });
2204
+ if (packets.length === 0 && files.length === 0 && responses.length === 0) return fallback;
2205
+ const entries = packets.flatMap((packet2) => packet2.presentation.data.entries);
2206
+ const owned = /* @__PURE__ */ new Set();
2207
+ const rows = entries.map((entry2) => {
2208
+ const matching = attachments.filter((attachment) => imageName(attachment) === entry2.screenshot);
2209
+ const image2 = matching.length === 1 ? matching[0] : void 0;
2210
+ if (image2) owned.add(image2);
2211
+ return { entry: entry2, image: image2 };
2212
+ });
2213
+ const fileImages = /* @__PURE__ */ new Map();
2214
+ for (const snapshot of files) {
2215
+ if (snapshot.version !== 2 || !snapshot.image) continue;
2216
+ const matching = attachments.filter((attachment) => imageName(attachment) === snapshot.image.name);
2217
+ const image2 = matching.length === 1 ? matching[0] : void 0;
2218
+ if (image2) {
2219
+ owned.add(image2);
2220
+ fileImages.set(snapshot.id, image2);
2221
+ }
2222
+ }
2223
+ const remaining = content.filter((block) => !readAnnotation(block) && !readSentFile(block) && !readSentResponse(block)).filter((block) => !(typeof block === "object" && block !== null && "type" in block && block.type === "text" && "text" in block && typeof block.text === "string" && !block.text.trim()));
2224
+ const ordinaryAttachments = attachments.filter((attachment) => !owned.has(attachment));
2225
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2226
+ renderDefault(remaining, ordinaryAttachments),
2227
+ /* @__PURE__ */ jsx(ResponseCard, { items: responses, t, onOpen: openResponse, renderText }),
2228
+ /* @__PURE__ */ jsx(SelectionCard, { items: files, t, onOpen: openFile, renderText, renderImage: (snapshot) => {
2229
+ const image2 = fileImages.get(snapshot.id);
2230
+ return image2 ? renderImage(image2) : /* @__PURE__ */ jsx("span", { children: t("page.imageMissing") });
2231
+ } }),
2232
+ packets.length > 0 && /* @__PURE__ */ jsxs("section", { className: css.card, "data-annotation-message": true, children: [
2233
+ /* @__PURE__ */ jsxs("button", { type: "button", className: css.summary, "aria-expanded": expanded2, onClick: () => setExpanded(!expanded2), children: [
2234
+ /* @__PURE__ */ jsx(Chat, { width: 18, height: 18 }),
2235
+ /* @__PURE__ */ jsxs("span", { children: [
2236
+ entries.length,
2237
+ " ",
2238
+ entries.length === 1 ? t("packet.one") : t("packet.many")
2239
+ ] }),
2240
+ /* @__PURE__ */ jsx("span", { className: css.toggle, children: expanded2 ? t("message.collapse") : t("message.expand") })
2241
+ ] }),
2242
+ rows.map(({ entry: entry2, image: image2 }, index) => /* @__PURE__ */ jsxs("div", { className: css.entry, children: [
2243
+ /* @__PURE__ */ jsxs("div", { className: css.context, children: [
2244
+ /* @__PURE__ */ jsx("span", { className: css.number, children: index + 1 }),
2245
+ /* @__PURE__ */ jsx("span", { children: renderText(entry2.title || entry2.url) })
2246
+ ] }),
2247
+ /* @__PURE__ */ jsx("p", { className: css.comment, "data-expanded": expanded2 || void 0, children: renderText(entry2.comment || t("packet.title")) }),
2248
+ expanded2 && /* @__PURE__ */ jsxs("div", { className: css.detail, children: [
2249
+ /* @__PURE__ */ jsx("span", { className: css.url, children: renderText(entry2.url) }),
2250
+ /* @__PURE__ */ jsx("code", { children: renderText(entry2.target) }),
2251
+ image2 && renderImage(image2)
2252
+ ] })
2253
+ ] }, index)),
2254
+ expanded2 && /* @__PURE__ */ jsxs("details", { className: css.evidence, children: [
2255
+ /* @__PURE__ */ jsx("summary", { children: t("message.evidence") }),
2256
+ /* @__PURE__ */ jsx("pre", { children: renderText(packets.map((packet2) => packet2.text).join("\n\n")) })
2257
+ ] })
2258
+ ] })
2259
+ ] });
2260
+ }
2261
+ export {
2262
+ AnnotationButton,
2263
+ AnnotationOverlay,
2264
+ AnnotationUiProvider,
2265
+ BatchControls,
2266
+ CommentEditor,
2267
+ PacketPreview,
2268
+ ReferenceCard,
2269
+ RequestIcon,
2270
+ ResponseCard,
2271
+ ResponseOverlay,
2272
+ SelectionCard,
2273
+ SentAnnotations,
2274
+ SheetSurface,
2275
+ WindowOverlay,
2276
+ actionComponent,
2277
+ failedSheetPreview,
2278
+ pageActionComponent,
2279
+ sheetRecoveryComponent,
2280
+ useInteractiveWindowRegion
2281
+ };
2282
+ //# sourceMappingURL=react.js.map