@arbidocs/blocks 0.4.95 → 0.4.97

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 (106) hide show
  1. package/dist/agents/index.cjs +283 -283
  2. package/dist/agents/index.js +7 -7
  3. package/dist/chat/index.cjs +127 -127
  4. package/dist/chat/index.js +4 -4
  5. package/dist/{chunk-DCZGYCN2.cjs → chunk-3US5OIDQ.cjs} +5 -5
  6. package/dist/{chunk-DCZGYCN2.cjs.map → chunk-3US5OIDQ.cjs.map} +1 -1
  7. package/dist/{chunk-QQH2NI5U.js → chunk-5NQIBJPP.js} +8 -8
  8. package/dist/{chunk-QQH2NI5U.js.map → chunk-5NQIBJPP.js.map} +1 -1
  9. package/dist/{chunk-KBPHLW2R.cjs → chunk-B4SRYJLM.cjs} +3 -3
  10. package/dist/{chunk-KBPHLW2R.cjs.map → chunk-B4SRYJLM.cjs.map} +1 -1
  11. package/dist/chunk-D54JXTPG.cjs +164 -0
  12. package/dist/chunk-D54JXTPG.cjs.map +1 -0
  13. package/dist/{chunk-VQ65UHTV.cjs → chunk-DDYXFALG.cjs} +47 -47
  14. package/dist/{chunk-VQ65UHTV.cjs.map → chunk-DDYXFALG.cjs.map} +1 -1
  15. package/dist/{chunk-RZGVMV6B.cjs → chunk-EHB7J6SR.cjs} +5 -5
  16. package/dist/{chunk-RZGVMV6B.cjs.map → chunk-EHB7J6SR.cjs.map} +1 -1
  17. package/dist/{chunk-EJAYB7PP.js → chunk-EWSEQEPL.js} +4 -4
  18. package/dist/{chunk-EJAYB7PP.js.map → chunk-EWSEQEPL.js.map} +1 -1
  19. package/dist/{chunk-3OXWJ3CZ.cjs → chunk-GVOTXXZG.cjs} +5 -5
  20. package/dist/{chunk-3OXWJ3CZ.cjs.map → chunk-GVOTXXZG.cjs.map} +1 -1
  21. package/dist/{chunk-CKYA2IG5.cjs → chunk-HBF4BDRU.cjs} +3 -3
  22. package/dist/{chunk-CKYA2IG5.cjs.map → chunk-HBF4BDRU.cjs.map} +1 -1
  23. package/dist/{chunk-E2AAIMXO.cjs → chunk-HGJGALLN.cjs} +832 -219
  24. package/dist/chunk-HGJGALLN.cjs.map +1 -0
  25. package/dist/{chunk-24CS7JWU.js → chunk-HOXKR26S.js} +3 -3
  26. package/dist/{chunk-24CS7JWU.js.map → chunk-HOXKR26S.js.map} +1 -1
  27. package/dist/{chunk-PDEHIGJ5.js → chunk-ICBAFX5B.js} +3 -3
  28. package/dist/{chunk-PDEHIGJ5.js.map → chunk-ICBAFX5B.js.map} +1 -1
  29. package/dist/{chunk-OYMYNYFV.cjs → chunk-IF6VAYIA.cjs} +5 -5
  30. package/dist/{chunk-OYMYNYFV.cjs.map → chunk-IF6VAYIA.cjs.map} +1 -1
  31. package/dist/{chunk-TOVSTC2M.cjs → chunk-LENXDIMY.cjs} +5 -5
  32. package/dist/{chunk-TOVSTC2M.cjs.map → chunk-LENXDIMY.cjs.map} +1 -1
  33. package/dist/{chunk-VYKMBLDX.cjs → chunk-MP6FK43E.cjs} +8 -8
  34. package/dist/{chunk-VYKMBLDX.cjs.map → chunk-MP6FK43E.cjs.map} +1 -1
  35. package/dist/{chunk-HUFLBZWI.js → chunk-PGDL26TQ.js} +4 -4
  36. package/dist/{chunk-HUFLBZWI.js.map → chunk-PGDL26TQ.js.map} +1 -1
  37. package/dist/{chunk-WXLKMEMB.js → chunk-QIEEFBZ5.js} +834 -221
  38. package/dist/chunk-QIEEFBZ5.js.map +1 -0
  39. package/dist/{chunk-YPKSLTL5.js → chunk-RSUOOMPT.js} +3 -3
  40. package/dist/{chunk-YPKSLTL5.js.map → chunk-RSUOOMPT.js.map} +1 -1
  41. package/dist/{chunk-UMY6Y6BM.js → chunk-VLOPZHFH.js} +3 -3
  42. package/dist/{chunk-UMY6Y6BM.js.map → chunk-VLOPZHFH.js.map} +1 -1
  43. package/dist/{chunk-GNHOB2ZB.cjs → chunk-XMBYDLZQ.cjs} +4 -4
  44. package/dist/{chunk-GNHOB2ZB.cjs.map → chunk-XMBYDLZQ.cjs.map} +1 -1
  45. package/dist/{chunk-YG7DGGCA.js → chunk-XQDM6T6C.js} +3 -3
  46. package/dist/{chunk-YG7DGGCA.js.map → chunk-XQDM6T6C.js.map} +1 -1
  47. package/dist/{chunk-ZNDAXSDP.js → chunk-YH6SSJDW.js} +3 -3
  48. package/dist/{chunk-ZNDAXSDP.js.map → chunk-YH6SSJDW.js.map} +1 -1
  49. package/dist/{chunk-EOEDGJ75.js → chunk-Z3G65XHZ.js} +3 -3
  50. package/dist/{chunk-EOEDGJ75.js.map → chunk-Z3G65XHZ.js.map} +1 -1
  51. package/dist/chunk-ZCOVDK2E.js +153 -0
  52. package/dist/chunk-ZCOVDK2E.js.map +1 -0
  53. package/dist/components/DetailPanel.d.ts +106 -0
  54. package/dist/connect/docx.cjs +6 -6
  55. package/dist/connect/docx.js +4 -4
  56. package/dist/connect/index.cjs +47 -47
  57. package/dist/connect/index.js +17 -17
  58. package/dist/databases/DatabaseRowDetail.d.ts +32 -0
  59. package/dist/databases/DatabaseTable.d.ts +3 -1
  60. package/dist/databases/DatabaseToolbar.d.ts +9 -1
  61. package/dist/databases/PickCellEditor.d.ts +8 -1
  62. package/dist/databases/SelectCellEditor.d.ts +6 -1
  63. package/dist/databases/column-types.d.ts +5 -0
  64. package/dist/databases/databaseColumns.d.ts +7 -0
  65. package/dist/databases/index.cjs +381 -11
  66. package/dist/databases/index.cjs.map +1 -1
  67. package/dist/databases/index.d.ts +7 -5
  68. package/dist/databases/index.js +379 -14
  69. package/dist/databases/index.js.map +1 -1
  70. package/dist/databases/linkCells.d.ts +4 -2
  71. package/dist/docx/NativeDocxEditor.d.ts +3 -1
  72. package/dist/docx/RibbonMenu.d.ts +15 -0
  73. package/dist/docx/StyleGallery.d.ts +9 -0
  74. package/dist/docx/WordReferences.d.ts +21 -0
  75. package/dist/docx/index.cjs +24 -24
  76. package/dist/docx/index.js +3 -3
  77. package/dist/docx/review/DocxEditorReview.d.ts +4 -2
  78. package/dist/docx/review/commentText.d.ts +7 -0
  79. package/dist/docx/wordStyles.d.ts +21 -0
  80. package/dist/facts/index.cjs +5 -5
  81. package/dist/facts/index.js +2 -2
  82. package/dist/files/index.cjs +308 -0
  83. package/dist/files/index.cjs.map +1 -1
  84. package/dist/files/index.js +298 -2
  85. package/dist/files/index.js.map +1 -1
  86. package/dist/files/views/gridViews.d.ts +80 -0
  87. package/dist/files/views/index.d.ts +2 -0
  88. package/dist/files/views/useGridViews.d.ts +38 -0
  89. package/dist/index.cjs +1237 -1237
  90. package/dist/index.cjs.map +1 -1
  91. package/dist/index.d.ts +1 -0
  92. package/dist/index.js +100 -139
  93. package/dist/index.js.map +1 -1
  94. package/dist/settings/index.cjs +1 -1
  95. package/dist/settings/index.js +1 -1
  96. package/dist/shell/index.cjs +67 -67
  97. package/dist/shell/index.js +3 -3
  98. package/dist/studio/index.cjs +7 -7
  99. package/dist/studio/index.js +6 -6
  100. package/dist/tags/TagDetail.d.ts +1 -0
  101. package/dist/workflow-canvas/index.cjs +51 -51
  102. package/dist/workflow-canvas/index.js +8 -8
  103. package/package.json +4 -4
  104. package/src/docx/styles.css +564 -91
  105. package/dist/chunk-E2AAIMXO.cjs.map +0 -1
  106. package/dist/chunk-WXLKMEMB.js.map +0 -1
@@ -7,14 +7,14 @@ var fflate = require('fflate');
7
7
  var store = require('@docx-editor.dev/core/store');
8
8
  var React3 = require('react');
9
9
  var react = require('@docx-editor.dev/react');
10
+ var ui = require('@arbidocs/react/ui');
11
+ var lucideReact = require('lucide-react');
10
12
  var jsxRuntime = require('react/jsx-runtime');
11
13
  var reactDom = require('react-dom');
12
- var ui = require('@arbidocs/react/ui');
13
14
  var server = require('react-dom/server');
14
15
  var Y = require('yjs');
15
16
  var coedit = require('@arbidocs/coedit');
16
17
  var hocuspocus = require('@arbidocs/coedit/hocuspocus');
17
- var lucideReact = require('lucide-react');
18
18
 
19
19
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
20
20
 
@@ -161,7 +161,25 @@ var ReviewItemContext = React3.createContext(null);
161
161
  function useReviewItem() {
162
162
  return React3.useContext(ReviewItemContext);
163
163
  }
164
+
165
+ // src/docx/review/commentText.ts
166
+ var SKIP = /* @__PURE__ */ new Set(["delText", "instrText", "delInstrText", "pPr", "rPr"]);
167
+ function inline(node) {
168
+ if (node.kind === "textValue") return node.value ?? "";
169
+ if (node.localName && SKIP.has(node.localName)) return "";
170
+ if (node.localName === "br" || node.localName === "cr") return "\n";
171
+ if (node.localName === "tab") return " ";
172
+ return (node.children ?? []).map(inline).join("");
173
+ }
174
+ function commentText(entry) {
175
+ if (entry.kind !== "comment") return entry.text;
176
+ const blocks = entry.item?.comment?.blocks ?? [];
177
+ const text = blocks.map(inline).join("\n").trim();
178
+ return text || entry.text;
179
+ }
164
180
  var CARD_GAP = 8;
181
+ var CLUSTER_GAP = 24;
182
+ var REVIEW_MARGIN_MIN_WIDTH = 900;
165
183
  var REVISION_LABELS = {
166
184
  insert: "Inserted",
167
185
  delete: "Deleted",
@@ -177,33 +195,102 @@ function isTopLevel(entry) {
177
195
  if (entry.kind === "custom") return entry.item.carded;
178
196
  return true;
179
197
  }
180
- function formatDate(value) {
181
- if (!value) return "";
198
+ function kindLabel(entry) {
199
+ if (entry.kind === "revision") return REVISION_LABELS[entry.revisionKind] ?? "Change";
200
+ if (entry.kind === "comment") return entry.resolved ? "Resolved" : "Comment";
201
+ return "Note";
202
+ }
203
+ function parseDate(value) {
204
+ if (!value) return null;
182
205
  const date = new Date(value);
183
- return Number.isNaN(date.getTime()) ? "" : date.toLocaleString();
206
+ return Number.isNaN(date.getTime()) ? null : date;
207
+ }
208
+ function relativeDate(date) {
209
+ const seconds = (Date.now() - date.getTime()) / 1e3;
210
+ if (seconds < 45) return "just now";
211
+ if (seconds < 45 * 60) return `${Math.round(seconds / 60)}m ago`;
212
+ if (seconds < 22 * 3600) return `${Math.round(seconds / 3600)}h ago`;
213
+ if (seconds < 7 * 86400) return `${Math.round(seconds / 86400)}d ago`;
214
+ const sameYear = date.getFullYear() === (/* @__PURE__ */ new Date()).getFullYear();
215
+ return date.toLocaleDateString(void 0, {
216
+ day: "numeric",
217
+ month: "short",
218
+ year: sameYear ? void 0 : "numeric"
219
+ });
220
+ }
221
+ function authorHue(author) {
222
+ let hash = 0;
223
+ for (const char of author) hash = hash * 31 + char.charCodeAt(0) | 0;
224
+ return Math.abs(hash) % 360;
184
225
  }
185
226
  function Avatar({ entry }) {
186
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "native-docx-review-avatar", "aria-hidden": "true", children: entry.initials });
227
+ return /* @__PURE__ */ jsxRuntime.jsx(
228
+ "span",
229
+ {
230
+ className: "native-docx-review-avatar",
231
+ "aria-hidden": "true",
232
+ style: { "--review-author-hue": authorHue(entry.author || "") },
233
+ children: entry.initials
234
+ }
235
+ );
187
236
  }
188
- function Message({ entry }) {
189
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-message", children: [
190
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-byline", children: [
191
- /* @__PURE__ */ jsxRuntime.jsx(Avatar, { entry }),
237
+ function Byline({ entry, children }) {
238
+ const date = parseDate(entry.date);
239
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-byline", children: [
240
+ /* @__PURE__ */ jsxRuntime.jsx(Avatar, { entry }),
241
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-who", children: [
192
242
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "native-docx-review-author", children: entry.author || "Reviewer" }),
193
- /* @__PURE__ */ jsxRuntime.jsx("time", { className: "native-docx-review-date", children: formatDate(entry.date) })
243
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "native-docx-review-meta", children: [
244
+ kindLabel(entry),
245
+ date && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
246
+ " \xB7 ",
247
+ /* @__PURE__ */ jsxRuntime.jsx(
248
+ "time",
249
+ {
250
+ className: "native-docx-review-date",
251
+ dateTime: date.toISOString(),
252
+ title: date.toLocaleString(),
253
+ children: relativeDate(date)
254
+ }
255
+ )
256
+ ] })
257
+ ] })
258
+ ] }),
259
+ children
260
+ ] });
261
+ }
262
+ function ChangeText({ entry }) {
263
+ const removed = entry.revisionKind === "replace" ? entry.replacedText : void 0;
264
+ const struck = entry.revisionKind === "delete" || entry.revisionKind === "moveFrom";
265
+ const added = entry.revisionKind === "insert" || entry.revisionKind === "moveTo" || removed !== void 0;
266
+ return /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "native-docx-review-change", "data-testid": "review-text", children: [
267
+ removed && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
268
+ /* @__PURE__ */ jsxRuntime.jsx("del", { children: removed }),
269
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "native-docx-review-arrow", "aria-hidden": "true", children: "\u2192" })
194
270
  ] }),
195
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "native-docx-review-text", "data-testid": "review-text", children: entry.text })
271
+ struck ? /* @__PURE__ */ jsxRuntime.jsx("del", { children: entry.text }) : added ? /* @__PURE__ */ jsxRuntime.jsx("ins", { children: entry.text }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: entry.text })
272
+ ] });
273
+ }
274
+ function CommentText({ text, testId }) {
275
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "native-docx-review-text", "data-testid": testId, children: /* @__PURE__ */ jsxRuntime.jsx(ui.AiMarkdown, { className: "native-docx-review-markdown", allowHtml: false, children: text }) });
276
+ }
277
+ function Reply({ entry }) {
278
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-message", children: [
279
+ /* @__PURE__ */ jsxRuntime.jsx(Byline, { entry }),
280
+ /* @__PURE__ */ jsxRuntime.jsx(CommentText, { text: commentText(entry) })
196
281
  ] });
197
282
  }
198
283
  function ReplyForm({
199
284
  entry,
200
- review
285
+ review,
286
+ onDismiss
201
287
  }) {
202
288
  const [text, setText] = React3.useState("");
203
289
  return /* @__PURE__ */ jsxRuntime.jsxs(
204
290
  "form",
205
291
  {
206
292
  className: "native-docx-review-reply",
293
+ onClick: (event) => event.stopPropagation(),
207
294
  onSubmit: (event) => {
208
295
  event.preventDefault();
209
296
  const value = text.trim();
@@ -215,25 +302,39 @@ function ReplyForm({
215
302
  {
216
303
  "data-testid": "review-reply-input",
217
304
  "aria-label": "Reply",
218
- placeholder: "Reply",
305
+ placeholder: "Reply\u2026",
219
306
  value: text,
220
307
  onChange: (event) => setText(event.target.value),
221
308
  onKeyDown: (event) => {
222
309
  if (event.key === "Escape") {
310
+ event.preventDefault();
223
311
  event.stopPropagation();
224
- review.setActive(null);
312
+ onDismiss();
225
313
  }
226
314
  }
227
315
  }
228
316
  ),
229
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", disabled: !text.trim(), children: "Reply" }),
317
+ /* @__PURE__ */ jsxRuntime.jsx(
318
+ "button",
319
+ {
320
+ type: "submit",
321
+ className: "native-docx-review-icon native-docx-review-send",
322
+ "aria-label": "Reply",
323
+ title: "Reply",
324
+ disabled: !text.trim(),
325
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SendHorizontal, { size: 14 })
326
+ }
327
+ ),
230
328
  /* @__PURE__ */ jsxRuntime.jsx(
231
329
  "button",
232
330
  {
233
331
  type: "button",
332
+ className: "native-docx-review-icon",
234
333
  "data-testid": "review-reply-cancel",
235
- onClick: () => review.setActive(null),
236
- children: "Cancel"
334
+ "aria-label": "Cancel",
335
+ title: "Cancel",
336
+ onClick: onDismiss,
337
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 14 })
237
338
  }
238
339
  )
239
340
  ]
@@ -246,7 +347,8 @@ function Card({
246
347
  review,
247
348
  extra,
248
349
  cardRef,
249
- top
350
+ top,
351
+ onDismiss
250
352
  }) {
251
353
  const resolved = entry.kind === "comment" && entry.resolved;
252
354
  return /* @__PURE__ */ jsxRuntime.jsx(ReviewItemContext.Provider, { value: entry, children: /* @__PURE__ */ jsxRuntime.jsxs(
@@ -265,60 +367,85 @@ function Card({
265
367
  },
266
368
  "data-testid": "review-card",
267
369
  "data-kind": entry.kind,
370
+ "data-change": entry.kind === "revision" ? entry.revisionKind : void 0,
268
371
  "data-active": entry.isActive || void 0,
269
372
  "data-resolved": resolved || void 0,
270
- style: { top },
373
+ "data-floating": top === void 0 || void 0,
374
+ style: top === void 0 ? void 0 : { top },
271
375
  onClick: () => {
272
376
  if (entry.activatable && !entry.isActive) review.setActive(entry.key);
273
377
  },
274
378
  children: [
275
- entry.kind === "revision" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "native-docx-review-kind", children: REVISION_LABELS[entry.revisionKind] ?? "Change" }),
276
- /* @__PURE__ */ jsxRuntime.jsx(Message, { entry }),
277
- replies.map((reply) => /* @__PURE__ */ jsxRuntime.jsx(Message, { entry: reply }, reply.key)),
379
+ /* @__PURE__ */ jsxRuntime.jsx(Byline, { entry, children: entry.kind === "comment" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-tools", children: [
380
+ /* @__PURE__ */ jsxRuntime.jsx(
381
+ "button",
382
+ {
383
+ type: "button",
384
+ className: "native-docx-review-icon",
385
+ "data-testid": "review-resolve",
386
+ "aria-label": entry.resolved ? "Reopen" : "Resolve",
387
+ title: entry.resolved ? "Reopen" : "Resolve",
388
+ onClick: (event) => {
389
+ event.stopPropagation();
390
+ review.resolve(entry, !entry.resolved);
391
+ },
392
+ children: entry.resolved ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleCheck, { size: 14 })
393
+ }
394
+ ),
395
+ /* @__PURE__ */ jsxRuntime.jsx(
396
+ "button",
397
+ {
398
+ type: "button",
399
+ className: "native-docx-review-icon",
400
+ "data-testid": "review-delete",
401
+ "aria-label": "Delete",
402
+ title: "Delete",
403
+ onClick: (event) => {
404
+ event.stopPropagation();
405
+ review.remove(entry);
406
+ },
407
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 14 })
408
+ }
409
+ )
410
+ ] }) }),
411
+ entry.kind === "revision" ? /* @__PURE__ */ jsxRuntime.jsx(ChangeText, { entry }) : /* @__PURE__ */ jsxRuntime.jsx(CommentText, { text: commentText(entry), testId: "review-text" }),
412
+ replies.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "native-docx-review-thread", children: replies.map((reply) => /* @__PURE__ */ jsxRuntime.jsx(Reply, { entry: reply }, reply.key)) }),
278
413
  extra,
279
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-actions", children: [
280
- entry.kind === "revision" && !entry.readOnly && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
281
- /* @__PURE__ */ jsxRuntime.jsx(
282
- "button",
283
- {
284
- type: "button",
285
- "data-testid": "review-accept",
286
- onClick: () => review.accept(entry),
287
- children: "Accept"
288
- }
289
- ),
290
- /* @__PURE__ */ jsxRuntime.jsx(
291
- "button",
292
- {
293
- type: "button",
294
- "data-testid": "review-reject",
295
- onClick: () => review.reject(entry),
296
- children: "Reject"
297
- }
298
- )
299
- ] }),
300
- entry.kind === "comment" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
301
- /* @__PURE__ */ jsxRuntime.jsx(
302
- "button",
303
- {
304
- type: "button",
305
- "data-testid": "review-resolve",
306
- onClick: () => review.resolve(entry, !entry.resolved),
307
- children: entry.resolved ? "Reopen" : "Resolve"
308
- }
309
- ),
310
- /* @__PURE__ */ jsxRuntime.jsx(
311
- "button",
312
- {
313
- type: "button",
314
- "data-testid": "review-delete",
315
- onClick: () => review.remove(entry),
316
- children: "Delete"
317
- }
318
- )
319
- ] })
414
+ entry.kind === "revision" && !entry.readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-decide", children: [
415
+ /* @__PURE__ */ jsxRuntime.jsxs(
416
+ "button",
417
+ {
418
+ type: "button",
419
+ className: "native-docx-review-accept",
420
+ "data-testid": "review-accept",
421
+ onClick: (event) => {
422
+ event.stopPropagation();
423
+ review.accept(entry);
424
+ },
425
+ children: [
426
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 14 }),
427
+ "Accept"
428
+ ]
429
+ }
430
+ ),
431
+ /* @__PURE__ */ jsxRuntime.jsxs(
432
+ "button",
433
+ {
434
+ type: "button",
435
+ className: "native-docx-review-reject",
436
+ "data-testid": "review-reject",
437
+ onClick: (event) => {
438
+ event.stopPropagation();
439
+ review.reject(entry);
440
+ },
441
+ children: [
442
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 14 }),
443
+ "Reject"
444
+ ]
445
+ }
446
+ )
320
447
  ] }),
321
- entry.isActive && entry.kind !== "custom" && /* @__PURE__ */ jsxRuntime.jsx(ReplyForm, { entry, review })
448
+ entry.isActive && entry.kind !== "custom" && /* @__PURE__ */ jsxRuntime.jsx(ReplyForm, { entry, review, onDismiss })
322
449
  ]
323
450
  }
324
451
  ) });
@@ -338,7 +465,8 @@ function Draft({
338
465
  ref: formRef,
339
466
  className: "native-docx-review-card native-docx-review-draft",
340
467
  "data-testid": "review-draft",
341
- style: { top },
468
+ "data-floating": top === void 0 || void 0,
469
+ style: top === void 0 ? void 0 : { top },
342
470
  onSubmit: (event) => {
343
471
  event.preventDefault();
344
472
  if (text.trim()) onSubmit(text.trim());
@@ -350,22 +478,53 @@ function Draft({
350
478
  ref: input,
351
479
  "data-testid": "review-draft-input",
352
480
  "aria-label": "Comment",
353
- placeholder: "Add a comment",
481
+ placeholder: "Add a comment\u2026",
354
482
  value: text,
355
483
  onChange: (event) => setText(event.target.value),
356
484
  onKeyDown: (event) => {
357
- if (event.key === "Escape") onCancel();
485
+ if (event.key === "Escape") {
486
+ event.preventDefault();
487
+ event.stopPropagation();
488
+ onCancel();
489
+ }
358
490
  }
359
491
  }
360
492
  ),
361
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-actions", children: [
362
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", disabled: !text.trim(), children: "Comment" }),
363
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "data-testid": "review-draft-cancel", onClick: onCancel, children: "Cancel" })
493
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-decide", children: [
494
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "native-docx-review-accept", disabled: !text.trim(), children: "Comment" }),
495
+ /* @__PURE__ */ jsxRuntime.jsx(
496
+ "button",
497
+ {
498
+ type: "button",
499
+ className: "native-docx-review-plain",
500
+ "data-testid": "review-draft-cancel",
501
+ onClick: onCancel,
502
+ children: "Cancel"
503
+ }
504
+ )
364
505
  ] })
365
506
  ]
366
507
  }
367
508
  );
368
509
  }
510
+ function useScrollArea(anchor, ready) {
511
+ const [area, setArea] = React3.useState({
512
+ element: null,
513
+ width: 0
514
+ });
515
+ React3.useLayoutEffect(() => {
516
+ const element = anchor.current?.closest(".docx-editor__scroll-container") ?? null;
517
+ const measure = () => setArea(
518
+ (previous) => previous.element === element && previous.width === (element?.clientWidth ?? 0) ? previous : { element, width: element?.clientWidth ?? 0 }
519
+ );
520
+ measure();
521
+ if (!element || typeof ResizeObserver === "undefined") return;
522
+ const observer = new ResizeObserver(measure);
523
+ observer.observe(element);
524
+ return () => observer.disconnect();
525
+ }, [anchor, ready]);
526
+ return area;
527
+ }
369
528
  function DocxEditorReviewRail({
370
529
  children
371
530
  }) {
@@ -376,6 +535,10 @@ function DocxEditorReviewRail({
376
535
  const [tops, setTops] = React3.useState({});
377
536
  const [showResolved, setShowResolved] = React3.useState(true);
378
537
  const elements = React3.useRef(/* @__PURE__ */ new Map());
538
+ const anchor = React3.useRef(null);
539
+ const toolbar = React3.useRef(null);
540
+ const area = useScrollArea(anchor, review.ready);
541
+ const layout = area.width === 0 || area.width >= REVIEW_MARGIN_MIN_WIDTH ? "margin" : "compact";
379
542
  const openDraft = React3.useCallback(() => {
380
543
  const placement = editor?.getSelectionPlacement();
381
544
  if (!placement) return;
@@ -384,9 +547,22 @@ function DocxEditorReviewRail({
384
547
  }, [editor, review.setPaneOpen]);
385
548
  React3.useEffect(() => registry?.register(), [registry]);
386
549
  React3.useEffect(() => registry?.registerCommentDraft(openDraft), [registry, openDraft]);
550
+ const shownLayout = React3.useRef(null);
551
+ React3.useEffect(() => {
552
+ if (!review.ready || area.width === 0 || shownLayout.current === layout) return;
553
+ editor?.setReviewPaneOptions({ opening: layout === "margin" ? "auto" : "manual" });
554
+ const deepLinked = shownLayout.current === null && review.activeKey !== null;
555
+ shownLayout.current = layout;
556
+ if (layout === "margin") review.setPaneOpen(true);
557
+ else if (!deepLinked) {
558
+ setDraft(null);
559
+ review.setPaneOpen(false);
560
+ }
561
+ }, [editor, layout, area.width, review.ready, review.setPaneOpen]);
387
562
  const scale = editor?.getRenderScale() ?? 1;
388
563
  const top = review.items.filter(isTopLevel).filter((entry) => entry.anchorY !== null).filter((entry) => showResolved || entry.kind !== "comment" || !entry.resolved);
389
- const hasChanges = review.items.some((entry) => entry.kind === "revision");
564
+ const changes = review.items.filter((entry) => entry.kind === "revision").length;
565
+ const comments = top.filter((entry) => entry.kind === "comment").length;
390
566
  const hasResolved = review.items.some((entry) => entry.kind === "comment" && entry.resolved);
391
567
  const repliesOf = React3.useCallback(
392
568
  (entry) => review.items.filter(
@@ -394,10 +570,49 @@ function DocxEditorReviewRail({
394
570
  ),
395
571
  [review.items]
396
572
  );
573
+ const column = layout === "margin" && review.paneOpen && (top.length > 0 || draft !== null);
574
+ const bubbleOpen = layout === "compact" && (review.paneOpen || draft !== null);
575
+ React3.useEffect(() => {
576
+ const element = area.element;
577
+ if (!element) return;
578
+ if (column) element.setAttribute("data-review-margin", "");
579
+ else element.removeAttribute("data-review-margin");
580
+ return () => element.removeAttribute("data-review-margin");
581
+ }, [area.element, column]);
582
+ const dismiss = React3.useCallback(() => {
583
+ setDraft(null);
584
+ if (layout === "compact") review.setPaneOpen(false);
585
+ else review.setActive(null);
586
+ }, [layout, review.setPaneOpen, review.setActive]);
587
+ React3.useEffect(() => {
588
+ const element = area.element;
589
+ if (!element || !bubbleOpen) return;
590
+ const close = (event) => {
591
+ const target = event.target;
592
+ if (target instanceof Element && target.closest(
593
+ ".native-docx-review-bubble, .native-docx-review-marker, .native-docx-review-add"
594
+ ))
595
+ return;
596
+ dismiss();
597
+ };
598
+ const escape = (event) => {
599
+ if (event.key !== "Escape" || event.defaultPrevented) return;
600
+ if (!(event.target instanceof Node) || !element.contains(event.target)) return;
601
+ event.preventDefault();
602
+ dismiss();
603
+ };
604
+ const document2 = element.ownerDocument;
605
+ element.addEventListener("pointerdown", close);
606
+ document2.addEventListener("keydown", escape);
607
+ return () => {
608
+ element.removeEventListener("pointerdown", close);
609
+ document2.removeEventListener("keydown", escape);
610
+ };
611
+ }, [area.element, bubbleOpen, dismiss]);
397
612
  React3.useLayoutEffect(() => {
398
- if (!review.paneOpen) return;
613
+ if (!column) return;
399
614
  const next = {};
400
- let floor = 0;
615
+ let floor = (toolbar.current?.offsetHeight ?? 0) + CARD_GAP;
401
616
  const rows = top.map((entry) => ({
402
617
  key: entry.key,
403
618
  y: (entry.anchorY ?? 0) * scale
@@ -415,7 +630,7 @@ function DocxEditorReviewRail({
415
630
  return same ? previous : next;
416
631
  });
417
632
  });
418
- if (!review.ready) return null;
633
+ if (!review.ready) return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: anchor, className: "native-docx-review" });
419
634
  const selection = editor?.getSelectionPlacement();
420
635
  const addComment = selection && /* @__PURE__ */ jsxRuntime.jsx(
421
636
  "button",
@@ -424,108 +639,254 @@ function DocxEditorReviewRail({
424
639
  className: "native-docx-review-add",
425
640
  "data-testid": "review-add-comment",
426
641
  "aria-label": "Add comment",
642
+ title: "Add comment",
427
643
  style: { top: selection.anchorY * scale },
428
644
  onMouseDown: (event) => event.preventDefault(),
429
645
  onClick: openDraft,
430
- children: "+"
646
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquarePlus, { size: 14 })
431
647
  }
432
648
  );
433
- if (!review.paneOpen) {
434
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-markers", "data-testid": "review-markers", children: [
649
+ if (column) {
650
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: anchor, className: "native-docx-review", children: /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "native-docx-review-pane", "data-testid": "review-pane", "aria-label": "Review", children: [
435
651
  addComment,
652
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: toolbar, className: "native-docx-review-toolbar", "data-testid": "review-toolbar", children: [
653
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "native-docx-review-summary", children: [
654
+ changes && `${changes} ${changes === 1 ? "change" : "changes"}`,
655
+ comments && `${comments} ${comments === 1 ? "comment" : "comments"}`
656
+ ].filter(Boolean).join(" \xB7 ") || "Review" }),
657
+ hasResolved && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "native-docx-review-switch", children: [
658
+ /* @__PURE__ */ jsxRuntime.jsx(
659
+ "input",
660
+ {
661
+ type: "checkbox",
662
+ "data-testid": "review-show-resolved",
663
+ checked: showResolved,
664
+ onChange: (event) => setShowResolved(event.target.checked)
665
+ }
666
+ ),
667
+ "Resolved"
668
+ ] }),
669
+ changes > 0 && /* @__PURE__ */ jsxRuntime.jsx(BulkActions, { review, navigate: true })
670
+ ] }),
671
+ draft && /* @__PURE__ */ jsxRuntime.jsx(
672
+ Draft,
673
+ {
674
+ top: tops.__draft ?? draft.anchorY * scale,
675
+ formRef: (element) => {
676
+ if (element) elements.current.set("__draft", element);
677
+ },
678
+ onCancel: () => setDraft(null),
679
+ onSubmit: (text) => {
680
+ if (review.addComment(text)) setDraft(null);
681
+ }
682
+ }
683
+ ),
436
684
  top.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
685
+ Card,
686
+ {
687
+ entry,
688
+ replies: repliesOf(entry),
689
+ review,
690
+ extra: children?.(entry),
691
+ top: tops[entry.key] ?? (entry.anchorY ?? 0) * scale,
692
+ onDismiss: dismiss,
693
+ cardRef: (element) => {
694
+ if (element) elements.current.set(entry.key, element);
695
+ else elements.current.delete(entry.key);
696
+ }
697
+ },
698
+ entry.key
699
+ ))
700
+ ] }) });
701
+ }
702
+ const clusters = [];
703
+ for (const entry of [...top].sort((a, b) => (a.anchorY ?? 0) - (b.anchorY ?? 0))) {
704
+ const y = (entry.anchorY ?? 0) * scale;
705
+ const last = clusters[clusters.length - 1];
706
+ if (last && y - last.y < CLUSTER_GAP) last.entries.push(entry);
707
+ else clusters.push({ y, entries: [entry] });
708
+ }
709
+ const shown = bubbleOpen ? draft ? null : clusters.find((cluster) => cluster.entries.some((entry) => entry.isActive)) ?? clusters[0] : void 0;
710
+ const focus = shown?.entries.find((entry) => entry.isActive) ?? shown?.entries[0];
711
+ const position = focus ? top.indexOf(focus) : -1;
712
+ const step = (offset) => {
713
+ const next = top[position + offset];
714
+ if (next) review.setActive(next.key);
715
+ };
716
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: anchor, className: "native-docx-review", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-markers", "data-testid": "review-markers", children: [
717
+ addComment,
718
+ clusters.map((cluster) => {
719
+ const [first] = cluster.entries;
720
+ const comment = cluster.entries.some((entry) => entry.kind === "comment");
721
+ return /* @__PURE__ */ jsxRuntime.jsxs(
437
722
  "button",
438
723
  {
439
724
  type: "button",
440
725
  className: "native-docx-review-marker",
441
726
  "data-testid": "review-marker",
442
- "data-kind": entry.kind,
443
- "aria-label": `${entry.author}: ${entry.text.slice(0, 60)}`,
444
- style: { top: (entry.anchorY ?? 0) * scale },
727
+ "data-kind": comment ? "comment" : first.kind,
728
+ "data-active": cluster === shown || void 0,
729
+ "aria-label": cluster.entries.length === 1 ? `${first.author}: ${first.text.slice(0, 60)}` : `${cluster.entries.length} comments and changes`,
730
+ "aria-expanded": layout === "compact" ? cluster === shown : void 0,
731
+ style: { top: cluster.y },
445
732
  onClick: () => {
733
+ if (layout === "compact" && cluster === shown) return dismiss();
734
+ setDraft(null);
446
735
  review.setPaneOpen(true);
447
- review.setActive(entry.key);
448
- }
736
+ review.setActive(first.key);
737
+ },
738
+ children: [
739
+ comment ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { size: 13 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PencilLine, { size: 13 }),
740
+ cluster.entries.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "native-docx-review-count", children: cluster.entries.length })
741
+ ]
449
742
  },
450
- entry.key
451
- ))
452
- ] });
453
- }
454
- return /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "native-docx-review-pane", "data-testid": "review-pane", "aria-label": "Review", children: [
455
- addComment,
456
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-toolbar", "data-testid": "review-toolbar", children: [
457
- hasChanges && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
458
- /* @__PURE__ */ jsxRuntime.jsx(
459
- "button",
460
- {
461
- type: "button",
462
- "data-testid": "review-previous",
463
- onClick: () => review.navigate("previous"),
464
- children: "Previous change"
465
- }
466
- ),
467
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "data-testid": "review-next", onClick: () => review.navigate("next"), children: "Next change" }),
468
- /* @__PURE__ */ jsxRuntime.jsx(
469
- "button",
470
- {
471
- type: "button",
472
- "data-testid": "review-accept-all",
473
- onClick: () => review.resolveAll("accept"),
474
- children: "Accept all"
475
- }
476
- ),
477
- /* @__PURE__ */ jsxRuntime.jsx(
478
- "button",
479
- {
480
- type: "button",
481
- "data-testid": "review-reject-all",
482
- onClick: () => review.resolveAll("reject"),
483
- children: "Reject all"
743
+ first.key
744
+ );
745
+ }),
746
+ bubbleOpen && (draft || shown || top.length === 0) && /* @__PURE__ */ jsxRuntime.jsx(
747
+ "div",
748
+ {
749
+ className: "native-docx-review-bubble",
750
+ "data-testid": "review-bubble",
751
+ role: "dialog",
752
+ "data-state": "open",
753
+ "aria-label": "Review",
754
+ style: {
755
+ top: draft ? draft.anchorY * scale : shown?.y ?? 0,
756
+ width: Math.max(220, Math.min(340, area.width - 56))
757
+ },
758
+ onKeyDown: (event) => {
759
+ if (event.key === "Escape") {
760
+ event.preventDefault();
761
+ event.stopPropagation();
762
+ dismiss();
484
763
  }
485
- )
486
- ] }),
487
- hasResolved && /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
488
- /* @__PURE__ */ jsxRuntime.jsx(
489
- "input",
764
+ },
765
+ children: draft ? /* @__PURE__ */ jsxRuntime.jsx(
766
+ Draft,
490
767
  {
491
- type: "checkbox",
492
- "data-testid": "review-show-resolved",
493
- checked: showResolved,
494
- onChange: (event) => setShowResolved(event.target.checked)
768
+ onCancel: dismiss,
769
+ onSubmit: (text) => {
770
+ if (review.addComment(text)) setDraft(null);
771
+ }
495
772
  }
496
- ),
497
- "Show resolved"
498
- ] })
773
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
774
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-bubble-head", children: [
775
+ top.length > 1 && position >= 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
776
+ /* @__PURE__ */ jsxRuntime.jsx(
777
+ "button",
778
+ {
779
+ type: "button",
780
+ className: "native-docx-review-icon",
781
+ "data-testid": "review-bubble-previous",
782
+ "aria-label": "Previous",
783
+ disabled: position === 0,
784
+ onClick: () => step(-1),
785
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 14 })
786
+ }
787
+ ),
788
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "native-docx-review-summary", children: [
789
+ position + 1,
790
+ " of ",
791
+ top.length
792
+ ] }),
793
+ /* @__PURE__ */ jsxRuntime.jsx(
794
+ "button",
795
+ {
796
+ type: "button",
797
+ className: "native-docx-review-icon",
798
+ "data-testid": "review-bubble-next",
799
+ "aria-label": "Next",
800
+ disabled: position === top.length - 1,
801
+ onClick: () => step(1),
802
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 14 })
803
+ }
804
+ )
805
+ ] }),
806
+ /* @__PURE__ */ jsxRuntime.jsx(
807
+ "button",
808
+ {
809
+ type: "button",
810
+ className: "native-docx-review-icon native-docx-review-close",
811
+ "data-testid": "review-bubble-close",
812
+ "aria-label": "Close",
813
+ onClick: dismiss,
814
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 14 })
815
+ }
816
+ )
817
+ ] }),
818
+ shown?.entries.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
819
+ Card,
820
+ {
821
+ entry,
822
+ replies: repliesOf(entry),
823
+ review,
824
+ extra: children?.(entry),
825
+ onDismiss: dismiss
826
+ },
827
+ entry.key
828
+ )),
829
+ !shown && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "native-docx-review-empty", "data-testid": "review-empty", children: "No comments or changes." }),
830
+ changes > 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "native-docx-review-bubble-foot", children: /* @__PURE__ */ jsxRuntime.jsx(BulkActions, { review }) })
831
+ ] })
832
+ }
833
+ )
834
+ ] }) });
835
+ }
836
+ function BulkActions({ review, navigate }) {
837
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-review-bulk", children: [
838
+ navigate && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "native-docx-review-segment", children: [
839
+ /* @__PURE__ */ jsxRuntime.jsx(
840
+ "button",
841
+ {
842
+ type: "button",
843
+ className: "native-docx-review-icon",
844
+ "data-testid": "review-previous",
845
+ "aria-label": "Previous change",
846
+ title: "Previous change",
847
+ onClick: () => review.navigate("previous"),
848
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { size: 14 })
849
+ }
850
+ ),
851
+ /* @__PURE__ */ jsxRuntime.jsx(
852
+ "button",
853
+ {
854
+ type: "button",
855
+ className: "native-docx-review-icon",
856
+ "data-testid": "review-next",
857
+ "aria-label": "Next change",
858
+ title: "Next change",
859
+ onClick: () => review.navigate("next"),
860
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 14 })
861
+ }
862
+ )
499
863
  ] }),
500
- draft && /* @__PURE__ */ jsxRuntime.jsx(
501
- Draft,
864
+ /* @__PURE__ */ jsxRuntime.jsxs(
865
+ "button",
502
866
  {
503
- top: tops.__draft ?? draft.anchorY * scale,
504
- formRef: (element) => {
505
- if (element) elements.current.set("__draft", element);
506
- },
507
- onCancel: () => setDraft(null),
508
- onSubmit: (text) => {
509
- if (review.addComment(text)) setDraft(null);
510
- }
867
+ type: "button",
868
+ className: "native-docx-review-accept",
869
+ "data-testid": "review-accept-all",
870
+ onClick: () => review.resolveAll("accept"),
871
+ children: [
872
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCheck, { size: 14 }),
873
+ "Accept all"
874
+ ]
511
875
  }
512
876
  ),
513
- top.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
514
- Card,
877
+ /* @__PURE__ */ jsxRuntime.jsxs(
878
+ "button",
515
879
  {
516
- entry,
517
- replies: repliesOf(entry),
518
- review,
519
- extra: children?.(entry),
520
- top: tops[entry.key] ?? (entry.anchorY ?? 0) * scale,
521
- cardRef: (element) => {
522
- if (element) elements.current.set(entry.key, element);
523
- else elements.current.delete(entry.key);
524
- }
525
- },
526
- entry.key
527
- )),
528
- top.length === 0 && !draft && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "native-docx-review-empty", "data-testid": "review-empty", children: "No comments or changes." })
880
+ type: "button",
881
+ className: "native-docx-review-reject",
882
+ "data-testid": "review-reject-all",
883
+ onClick: () => review.resolveAll("reject"),
884
+ children: [
885
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 14 }),
886
+ "Reject all"
887
+ ]
888
+ }
889
+ )
529
890
  ] });
530
891
  }
531
892
  function ReviewCardSlot({ children }) {
@@ -607,14 +968,7 @@ function DocxCommentActivity() {
607
968
  /* @__PURE__ */ jsxRuntime.jsx(ui.DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.DialogTitle, { children: "Document comment" }) }),
608
969
  thread.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 border-b pb-4 last:border-0", children: [
609
970
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: entry.author || "Reviewer" }),
610
- /* @__PURE__ */ jsxRuntime.jsx(
611
- "p",
612
- {
613
- className: "whitespace-pre-wrap break-words text-sm",
614
- "data-testid": "docx-full-comment-text",
615
- children: entry.text
616
- }
617
- )
971
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": "docx-full-comment-text", children: /* @__PURE__ */ jsxRuntime.jsx(ui.AiMarkdown, { allowHtml: false, children: commentText(entry) }) })
618
972
  ] }, entry.key)),
619
973
  statuses.map(([id, run]) => /* @__PURE__ */ jsxRuntime.jsxs("p", { role: "status", className: "text-sm", children: [
620
974
  run.label,
@@ -1880,57 +2234,12 @@ var WordDraftStream = class {
1880
2234
  }
1881
2235
  }
1882
2236
  };
1883
- function contentPath(documentId) {
1884
- const match = /^(doc-[A-Za-z0-9]+)@v([1-9][0-9]*)$/.exec(documentId);
1885
- return match ? `/${encodeURIComponent(match[1])}/content?version=${match[2]}` : `/${encodeURIComponent(documentId)}/content`;
1886
- }
1887
- var drafts = /* @__PURE__ */ new Map();
1888
- var stateBytes = (room) => Y__namespace.encodeStateAsUpdate(room.ydoc);
1889
- var encodeState = (room) => {
1890
- let binary = "";
1891
- for (const byte of stateBytes(room)) binary += String.fromCharCode(byte);
1892
- return btoa(binary);
1893
- };
1894
- function covered(room, vector, receipt) {
1895
- const decode = (value) => Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
1896
- const server = Y__namespace.decodeStateVector(decode(vector)), local = Y__namespace.decodeStateVector(Y__namespace.encodeStateVector(room.ydoc));
1897
- for (const [client, clock] of local) if ((server.get(client) || 0) < clock) return false;
1898
- const deleted = Y__namespace.decodeUpdate(stateBytes(room)).ds.clients;
1899
- const saved = Y__namespace.decodeUpdate(decode(receipt)).ds.clients;
1900
- for (const [client, ranges] of deleted)
1901
- for (const range of ranges) {
1902
- if (!(saved.get(client) || []).some(
1903
- (s) => s.clock <= range.clock && s.clock + s.len >= range.clock + range.len
1904
- ))
1905
- return false;
1906
- }
1907
- return true;
1908
- }
1909
- var EditorBoundary = class extends React3__default.default.Component {
1910
- state = {};
1911
- static getDerivedStateFromError(error) {
1912
- return { error };
1913
- }
1914
- render() {
1915
- return this.state.error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", className: "native-docx-error", children: [
1916
- "Could not render this Word document: ",
1917
- this.state.error.message
1918
- ] }) : this.props.children;
1919
- }
1920
- };
1921
- function DocxParticipants({ session }) {
1922
- const [people, setPeople] = React3.useState([]);
1923
- React3.useEffect(() => {
1924
- if (!session) return;
1925
- setPeople(session.participants());
1926
- return session.subscribeParticipants(setPeople);
1927
- }, [session]);
1928
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "native-docx-presence", "aria-label": "People viewing this document", children: people.map((p) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "native-docx-avatar", title: p.name, children: p.name.split(" ").map((s) => s[0]).slice(0, 2).join("") }, p.actorId)) });
1929
- }
1930
2237
  function RibbonMenu({
1931
2238
  label,
1932
2239
  icon,
1933
- children
2240
+ children,
2241
+ className,
2242
+ testId
1934
2243
  }) {
1935
2244
  const [open, setOpen] = React3.useState(false);
1936
2245
  const element = React3.useRef(null);
@@ -1954,10 +2263,11 @@ function RibbonMenu({
1954
2263
  "button",
1955
2264
  {
1956
2265
  type: "button",
1957
- className: "native-docx-ribbon-trigger",
2266
+ className: ["native-docx-ribbon-trigger", className].filter(Boolean).join(" "),
1958
2267
  "aria-label": label,
1959
2268
  title: label,
1960
2269
  "aria-expanded": open,
2270
+ "data-testid": testId,
1961
2271
  onMouseDown: (event) => event.preventDefault(),
1962
2272
  onClick: () => setOpen((value) => !value),
1963
2273
  children: icon
@@ -1973,11 +2283,295 @@ function RibbonMenu({
1973
2283
  if (!(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement))
1974
2284
  event.preventDefault();
1975
2285
  },
1976
- children
2286
+ children: typeof children === "function" ? children(() => setOpen(false)) : children
2287
+ }
2288
+ )
2289
+ ] });
2290
+ }
2291
+
2292
+ // src/docx/wordStyles.ts
2293
+ var QUICK_STYLES = ["Normal", "Title", "heading 1", "heading 2", "heading 3"];
2294
+ function findStyle(options, name) {
2295
+ const wanted = name.toLowerCase();
2296
+ const id = name.replace(/(^|\s)(\w)/g, (_, _s, c) => c.toUpperCase());
2297
+ return options.find((o) => o.name.toLowerCase() === wanted) ?? options.find((o) => o.styleId.toLowerCase() === id.toLowerCase());
2298
+ }
2299
+ function styleLabel(name) {
2300
+ return name === name.toLowerCase() ? name.replace(/(^|\s)(\w)/g, (m) => m.toUpperCase()) : name;
2301
+ }
2302
+ function wordShortcut(event) {
2303
+ const mod = event.ctrlKey || event.metaKey;
2304
+ if (!mod) return null;
2305
+ if (event.altKey && !event.shiftKey) {
2306
+ switch (event.code) {
2307
+ case "KeyF":
2308
+ return "footnote";
2309
+ case "KeyD":
2310
+ return "endnote";
2311
+ case "Digit1":
2312
+ case "Numpad1":
2313
+ return "heading 1";
2314
+ case "Digit2":
2315
+ case "Numpad2":
2316
+ return "heading 2";
2317
+ case "Digit3":
2318
+ case "Numpad3":
2319
+ return "heading 3";
2320
+ }
2321
+ }
2322
+ if (event.shiftKey && !event.altKey && event.code === "KeyN") return "Normal";
2323
+ return null;
2324
+ }
2325
+ var FILTER_FROM = 8;
2326
+ function previewStyle(option) {
2327
+ const { fontFamily, fontSizePt, bold, italic, color } = option.preview;
2328
+ return {
2329
+ fontFamily: fontFamily ? `"${fontFamily}", serif` : void 0,
2330
+ // In the face of the style, but within a row: 9-18pt.
2331
+ fontSize: fontSizePt ? `${Math.min(18, Math.max(9, fontSizePt))}pt` : void 0,
2332
+ fontWeight: bold ? 700 : void 0,
2333
+ fontStyle: italic ? "italic" : void 0,
2334
+ color: color ? `#${color}` : void 0
2335
+ };
2336
+ }
2337
+ function Gallery({ close }) {
2338
+ const { value, setValue, options, isEnabled } = react.useParagraphStyle();
2339
+ const editor = react.useDocxEditor();
2340
+ const [query, setQuery] = React3.useState("");
2341
+ const quick = React3.useMemo(
2342
+ () => QUICK_STYLES.map((name) => findStyle(options, name)).filter(
2343
+ (o) => !!o
2344
+ ),
2345
+ [options]
2346
+ );
2347
+ const shown = React3.useMemo(() => {
2348
+ const q = query.trim().toLowerCase();
2349
+ return q ? options.filter(
2350
+ (o) => o.name.toLowerCase().includes(q) || o.styleId.toLowerCase().includes(q)
2351
+ ) : options;
2352
+ }, [options, query]);
2353
+ const apply = (option) => {
2354
+ setValue(option.styleId);
2355
+ close();
2356
+ editor?.surface?.focus();
2357
+ };
2358
+ const nav = chunk7UTB4PHM_cjs.useListKeyNav({
2359
+ open: true,
2360
+ items: shown,
2361
+ onPick: apply,
2362
+ onDismiss: close,
2363
+ resetKey: query
2364
+ });
2365
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-style-gallery", "data-testid": "docx-style-gallery", children: [
2366
+ quick.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "native-docx-style-quick", role: "group", "aria-label": "Quick styles", children: quick.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
2367
+ "button",
2368
+ {
2369
+ type: "button",
2370
+ "aria-pressed": value === option.styleId,
2371
+ disabled: !isEnabled,
2372
+ "data-style-id": option.styleId,
2373
+ title: styleLabel(option.name),
2374
+ onClick: () => apply(option),
2375
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: previewStyle(option), children: styleLabel(option.name) })
2376
+ },
2377
+ option.styleId
2378
+ )) }),
2379
+ options.length > FILTER_FROM && /* @__PURE__ */ jsxRuntime.jsx(
2380
+ "input",
2381
+ {
2382
+ className: "native-docx-style-filter",
2383
+ placeholder: "Find a style",
2384
+ "aria-label": "Find a style",
2385
+ value: query,
2386
+ autoFocus: true,
2387
+ onChange: (event) => setQuery(event.target.value),
2388
+ onKeyDown: (event) => {
2389
+ nav.handleKeyDown(event);
2390
+ },
2391
+ ...nav.comboboxProps
2392
+ }
2393
+ ),
2394
+ /* @__PURE__ */ jsxRuntime.jsxs(
2395
+ "ul",
2396
+ {
2397
+ className: "native-docx-style-list",
2398
+ role: "listbox",
2399
+ id: nav.listboxId,
2400
+ "aria-label": "Document styles",
2401
+ children: [
2402
+ shown.map((option, index) => /* @__PURE__ */ jsxRuntime.jsx(
2403
+ "li",
2404
+ {
2405
+ id: chunk7UTB4PHM_cjs.listOptionId(nav.listboxId, index),
2406
+ role: "option",
2407
+ "aria-selected": value === option.styleId,
2408
+ "aria-disabled": !isEnabled,
2409
+ "data-active": index === nav.selectedIndex || void 0,
2410
+ "data-style-id": option.styleId,
2411
+ onMouseEnter: () => nav.setSelectedIndex(index),
2412
+ onClick: () => isEnabled && apply(option),
2413
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: previewStyle(option), children: styleLabel(option.name) })
2414
+ },
2415
+ option.styleId
2416
+ )),
2417
+ shown.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("li", { className: "native-docx-style-empty", children: "No matching style" })
2418
+ ]
2419
+ }
2420
+ )
2421
+ ] });
2422
+ }
2423
+ function StyleGallery() {
2424
+ const { value, options } = react.useParagraphStyle();
2425
+ const current = options.find((o) => o.styleId === value);
2426
+ const name = current ? styleLabel(current.name) : "Styles";
2427
+ return /* @__PURE__ */ jsxRuntime.jsx(
2428
+ RibbonMenu,
2429
+ {
2430
+ label: "Styles",
2431
+ className: "native-docx-style-trigger",
2432
+ testId: "docx-style-trigger",
2433
+ icon: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2434
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "native-docx-style-current", "data-testid": "docx-current-style", children: name }),
2435
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 12, "aria-hidden": true })
2436
+ ] }),
2437
+ children: (close) => /* @__PURE__ */ jsxRuntime.jsx(Gallery, { close })
2438
+ }
2439
+ );
2440
+ }
2441
+ function useInsertNote(kind) {
2442
+ const command = react.useEditorCommand(kind === "footnote" ? "insert.footnote" : "insert.endnote");
2443
+ const { options } = react.useParagraphStyle();
2444
+ const editor = react.useDocxEditor();
2445
+ const insert = () => {
2446
+ if (!command.execute()) return false;
2447
+ const text = findStyle(options, `${kind} text`);
2448
+ if (text && editor)
2449
+ withDirectEditing(
2450
+ editor,
2451
+ () => editor.exec({ type: "setParagraphStyle", styleId: text.styleId })
2452
+ );
2453
+ return true;
2454
+ };
2455
+ return { insert, isEnabled: command.isEnabled, disabledReason: command.disabledReason };
2456
+ }
2457
+ function NoteAction({
2458
+ kind,
2459
+ label,
2460
+ shortcut,
2461
+ icon
2462
+ }) {
2463
+ const note = useInsertNote(kind);
2464
+ return /* @__PURE__ */ jsxRuntime.jsx(
2465
+ react.DocxEditor.Toolbar.Action,
2466
+ {
2467
+ label: `${label} (${shortcut})`,
2468
+ icon,
2469
+ disabled: !note.isEnabled,
2470
+ disabledReason: note.disabledReason ?? void 0,
2471
+ onSelect: () => void note.insert(),
2472
+ className: `native-docx-insert-${kind}`
2473
+ }
2474
+ );
2475
+ }
2476
+ function NoteButtons() {
2477
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2478
+ /* @__PURE__ */ jsxRuntime.jsx(
2479
+ NoteAction,
2480
+ {
2481
+ kind: "footnote",
2482
+ label: "Insert footnote",
2483
+ shortcut: "Ctrl+Alt+F",
2484
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Superscript, { size: 16 })
2485
+ }
2486
+ ),
2487
+ /* @__PURE__ */ jsxRuntime.jsx(
2488
+ NoteAction,
2489
+ {
2490
+ kind: "endnote",
2491
+ label: "Insert endnote",
2492
+ shortcut: "Ctrl+Alt+D",
2493
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.NotebookText, { size: 16 })
1977
2494
  }
1978
2495
  )
1979
2496
  ] });
1980
2497
  }
2498
+ function WordShortcuts({ scope }) {
2499
+ const footnote = useInsertNote("footnote");
2500
+ const endnote = useInsertNote("endnote");
2501
+ const styles = react.useParagraphStyle();
2502
+ const editor = react.useDocxEditor();
2503
+ const latest = React3.useRef({ footnote, endnote, styles, editor });
2504
+ latest.current = { footnote, endnote, styles, editor };
2505
+ React3.useEffect(() => {
2506
+ const element = scope.current;
2507
+ if (!element) return;
2508
+ const onKeyDown = (event) => {
2509
+ const action = wordShortcut(event);
2510
+ if (!action) return;
2511
+ const { footnote: footnote2, endnote: endnote2, styles: styles2, editor: editor2 } = latest.current;
2512
+ if (action === "footnote" || action === "endnote") {
2513
+ if (!(action === "footnote" ? footnote2 : endnote2).insert()) return;
2514
+ } else {
2515
+ const style = findStyle(styles2.options, action);
2516
+ if (!style) return;
2517
+ styles2.setValue(style.styleId);
2518
+ }
2519
+ event.preventDefault();
2520
+ event.stopPropagation();
2521
+ editor2?.surface?.focus();
2522
+ };
2523
+ element.addEventListener("keydown", onKeyDown, true);
2524
+ return () => element.removeEventListener("keydown", onKeyDown, true);
2525
+ }, [scope]);
2526
+ return null;
2527
+ }
2528
+ function contentPath(documentId) {
2529
+ const match = /^(doc-[A-Za-z0-9]+)@v([1-9][0-9]*)$/.exec(documentId);
2530
+ return match ? `/${encodeURIComponent(match[1])}/content?version=${match[2]}` : `/${encodeURIComponent(documentId)}/content`;
2531
+ }
2532
+ var drafts = /* @__PURE__ */ new Map();
2533
+ var stateBytes = (room) => Y__namespace.encodeStateAsUpdate(room.ydoc);
2534
+ var encodeState = (room) => {
2535
+ let binary = "";
2536
+ for (const byte of stateBytes(room)) binary += String.fromCharCode(byte);
2537
+ return btoa(binary);
2538
+ };
2539
+ function covered(room, vector, receipt) {
2540
+ const decode = (value) => Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
2541
+ const server = Y__namespace.decodeStateVector(decode(vector)), local = Y__namespace.decodeStateVector(Y__namespace.encodeStateVector(room.ydoc));
2542
+ for (const [client, clock] of local) if ((server.get(client) || 0) < clock) return false;
2543
+ const deleted = Y__namespace.decodeUpdate(stateBytes(room)).ds.clients;
2544
+ const saved = Y__namespace.decodeUpdate(decode(receipt)).ds.clients;
2545
+ for (const [client, ranges] of deleted)
2546
+ for (const range of ranges) {
2547
+ if (!(saved.get(client) || []).some(
2548
+ (s) => s.clock <= range.clock && s.clock + s.len >= range.clock + range.len
2549
+ ))
2550
+ return false;
2551
+ }
2552
+ return true;
2553
+ }
2554
+ var EditorBoundary = class extends React3__default.default.Component {
2555
+ state = {};
2556
+ static getDerivedStateFromError(error) {
2557
+ return { error };
2558
+ }
2559
+ render() {
2560
+ return this.state.error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", className: "native-docx-error", children: [
2561
+ "Could not render this Word document: ",
2562
+ this.state.error.message
2563
+ ] }) : this.props.children;
2564
+ }
2565
+ };
2566
+ function DocxParticipants({ session }) {
2567
+ const [people, setPeople] = React3.useState([]);
2568
+ React3.useEffect(() => {
2569
+ if (!session) return;
2570
+ setPeople(session.participants());
2571
+ return session.subscribeParticipants(setPeople);
2572
+ }, [session]);
2573
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "native-docx-presence", "aria-label": "People viewing this document", children: people.map((p) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "native-docx-avatar", title: p.name, children: p.name.split(" ").map((s) => s[0]).slice(0, 2).join("") }, p.actorId)) });
2574
+ }
1981
2575
  function DefinedTermButtons() {
1982
2576
  const terms = useDefinedTerms();
1983
2577
  if (!terms) return null;
@@ -2010,13 +2604,17 @@ function DefinedTermButtons() {
2010
2604
  )
2011
2605
  ] });
2012
2606
  }
2013
- function DocxRibbon({ onSave }) {
2607
+ function DocxRibbon({
2608
+ onSave,
2609
+ badge
2610
+ }) {
2014
2611
  const [tab, setTab] = React3.useState("Home");
2015
2612
  const t = react.useChromeTranslate();
2016
2613
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-ribbon", children: [
2017
2614
  /* @__PURE__ */ jsxRuntime.jsxs("select", { "aria-label": "Ribbon tab", value: tab, onChange: (event) => setTab(event.target.value), children: [
2018
2615
  /* @__PURE__ */ jsxRuntime.jsx("option", { children: "Home" }),
2019
2616
  /* @__PURE__ */ jsxRuntime.jsx("option", { children: "Insert" }),
2617
+ /* @__PURE__ */ jsxRuntime.jsx("option", { children: "References" }),
2020
2618
  /* @__PURE__ */ jsxRuntime.jsx("option", { children: "Review" })
2021
2619
  ] }),
2022
2620
  /* @__PURE__ */ jsxRuntime.jsxs(react.DocxEditor.Toolbar, { t, preset: false, onSave, children: [
@@ -2024,6 +2622,7 @@ function DocxRibbon({ onSave }) {
2024
2622
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Redo, {}),
2025
2623
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Save, {}),
2026
2624
  tab === "Home" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2625
+ /* @__PURE__ */ jsxRuntime.jsx(StyleGallery, {}),
2027
2626
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Bold, {}),
2028
2627
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Italic, {}),
2029
2628
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Underline, {}),
@@ -2034,7 +2633,6 @@ function DocxRibbon({ onSave }) {
2034
2633
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Highlight, {})
2035
2634
  ] }),
2036
2635
  /* @__PURE__ */ jsxRuntime.jsxs(RibbonMenu, { label: "More formatting", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Ellipsis, { size: 16 }), children: [
2037
- /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.StylePicker, {}),
2038
2636
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Alignment, {}),
2039
2637
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.BulletList, {}),
2040
2638
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.NumberedList, {}),
@@ -2051,12 +2649,14 @@ function DocxRibbon({ onSave }) {
2051
2649
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Link, {}),
2052
2650
  /* @__PURE__ */ jsxRuntime.jsx(DefinedTermButtons, {})
2053
2651
  ] }),
2652
+ tab === "References" && /* @__PURE__ */ jsxRuntime.jsx(NoteButtons, {}),
2054
2653
  tab === "Review" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2055
2654
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.EditingMode, {}),
2056
2655
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Comments, {}),
2057
2656
  /* @__PURE__ */ jsxRuntime.jsx(RibbonMenu, { label: "Review options", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Ellipsis, { size: 16 }), children: /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Reviewers, {}) })
2058
2657
  ] })
2059
- ] })
2658
+ ] }),
2659
+ badge && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "native-docx-ribbon-badge", "data-testid": "docx-ribbon-badge", children: badge })
2060
2660
  ] });
2061
2661
  }
2062
2662
  var fixedZoomMode = { type: "fixed" };
@@ -2321,7 +2921,10 @@ function NativeDocxEditor({
2321
2921
  const resize = new ResizeObserver(fit);
2322
2922
  const chrome = new MutationObserver(fit);
2323
2923
  resize.observe(viewport);
2324
- chrome.observe(viewport, { attributes: true, attributeFilter: ["style", "data-review-pane"] });
2924
+ chrome.observe(viewport, {
2925
+ attributes: true,
2926
+ attributeFilter: ["style", "data-review-pane", "data-review-margin"]
2927
+ });
2325
2928
  fit();
2326
2929
  fitCleanup.current = () => {
2327
2930
  resize.disconnect();
@@ -2403,7 +3006,17 @@ function NativeDocxEditor({
2403
3006
  trackMoves: true
2404
3007
  }
2405
3008
  ),
2406
- !stream && /* @__PURE__ */ jsxRuntime.jsx(DocxRibbon, { onSave: () => void saveVersion() }),
3009
+ !stream && /* @__PURE__ */ jsxRuntime.jsx(
3010
+ DocxRibbon,
3011
+ {
3012
+ onSave: () => void saveVersion(),
3013
+ badge: (
3014
+ // Says the document is shared live, in place of a title row above the ribbon.
3015
+ ready.room && ready.canEdit && !snapshot ? /* @__PURE__ */ jsxRuntime.jsx("span", { title: "Edits sync live with everyone who has this document open", children: "Co-edit mode" }) : void 0
3016
+ )
3017
+ }
3018
+ ),
3019
+ !stream && /* @__PURE__ */ jsxRuntime.jsx(WordShortcuts, { scope: stageRef }),
2407
3020
  /* @__PURE__ */ jsxRuntime.jsxs(
2408
3021
  "div",
2409
3022
  {
@@ -2554,5 +3167,5 @@ exports.useDefinedTerms = useDefinedTerms;
2554
3167
  exports.useReview = useReview;
2555
3168
  exports.useReviewItem = useReviewItem;
2556
3169
  exports.withDirectEditing = withDirectEditing;
2557
- //# sourceMappingURL=chunk-E2AAIMXO.cjs.map
2558
- //# sourceMappingURL=chunk-E2AAIMXO.cjs.map
3170
+ //# sourceMappingURL=chunk-HGJGALLN.cjs.map
3171
+ //# sourceMappingURL=chunk-HGJGALLN.cjs.map