superdoc 2.12.0-next.2 → 2.12.0-next.20

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 (50) hide show
  1. package/dist/chunks/{FindReplaceSurface-40AnrqWk.es.js → FindReplaceSurface-CVKgivXi.es.js} +8 -2
  2. package/dist/chunks/{FindReplaceSurface-0p7EtMZh.cjs → FindReplaceSurface-qi9OhbwN.cjs} +8 -2
  3. package/dist/chunks/{create-super-doc-ui-CBFGggZ2.cjs → create-super-doc-ui-BWWWNPP8.cjs} +180 -84
  4. package/dist/chunks/{create-super-doc-ui-C-AuEM9F.es.js → create-super-doc-ui-FnwgTkUv.es.js} +180 -84
  5. package/dist/collaboration-upgrade-engine.cjs +1 -1
  6. package/dist/collaboration-upgrade-engine.es.js +1 -1
  7. package/dist/layout-engine/contracts/src/column-layout.d.ts +58 -1
  8. package/dist/layout-engine/contracts/src/graphic-placement.d.ts +3 -0
  9. package/dist/layout-engine/contracts/src/index.d.ts +17 -3
  10. package/dist/layout-engine/contracts/src/run-helpers.d.ts +8 -0
  11. package/dist/layout-engine/layout-bridge/src/cache.d.ts +5 -4
  12. package/dist/layout-engine/layout-bridge/src/incrementalLayout.d.ts +4 -1
  13. package/dist/layout-engine/layout-bridge/src/note-marker-measure-key.d.ts +3 -0
  14. package/dist/layout-engine/layout-bridge/src/remeasure.d.ts +2 -1
  15. package/dist/layout-engine/layout-engine/src/column-balancing.d.ts +11 -7
  16. package/dist/layout-engine/layout-engine/src/layout-paragraph.d.ts +2 -6
  17. package/dist/layout-engine/painters/dom/src/derived-run-text-plane.d.ts +9 -0
  18. package/dist/layout-engine/painters/dom/src/index.d.ts +1 -0
  19. package/dist/layout-engine/painters/dom/src/page-content.d.ts +3 -1
  20. package/dist/layout-engine/painters/dom/src/persistent-page-surface.d.ts +2 -0
  21. package/dist/layout-engine/painters/dom/src/renderer.d.ts +9 -16
  22. package/dist/public/ui-react.cjs +56 -10
  23. package/dist/public/ui-react.es.js +56 -11
  24. package/dist/public/ui-vue.cjs +18 -1
  25. package/dist/public/ui-vue.es.js +18 -2
  26. package/dist/public/ui.cjs +1 -1
  27. package/dist/public/ui.es.js +1 -1
  28. package/dist/shared/font-system/src/index.d.ts +3 -1
  29. package/dist/shared/font-system/src/natural-line-metrics.d.ts +11 -0
  30. package/dist/shared/font-system/src/resolver.d.ts +14 -0
  31. package/dist/style.css +65 -65
  32. package/dist/style.layered.css +65 -65
  33. package/dist/superdoc/src/core/types/index.d.ts +35 -12
  34. package/dist/superdoc/src/public/index.d.cts +2 -0
  35. package/dist/superdoc/src/public/index.d.ts +1 -0
  36. package/dist/superdoc/src/public/ui/react.d.ts +12 -3
  37. package/dist/superdoc/src/public/ui/types.d.ts +81 -21
  38. package/dist/superdoc/src/public/ui/vue.d.ts +4 -2
  39. package/dist/superdoc/src/public/ui-react.d.cts +3 -0
  40. package/dist/superdoc/src/public/ui-react.d.ts +2 -2
  41. package/dist/superdoc/src/public/ui-vue.d.cts +1 -0
  42. package/dist/superdoc/src/public/ui-vue.d.ts +1 -1
  43. package/dist/superdoc/src/public/ui.d.cts +2 -0
  44. package/dist/superdoc/src/public/ui.d.ts +1 -1
  45. package/dist/superdoc.cjs +166 -14
  46. package/dist/superdoc.es.js +166 -14
  47. package/dist-cdn/style.layered.css +1 -1
  48. package/dist-cdn/superdoc.min.css +1 -1
  49. package/dist-cdn/superdoc.min.js +37 -37
  50. package/package.json +2 -2
package/dist/superdoc.cjs CHANGED
@@ -4,7 +4,7 @@ const require_blank_docx = require("./chunks/blank-docx-BuFAbRjs.cjs");
4
4
  const require_eventemitter3 = require("./chunks/eventemitter3-C_TAnXOl.cjs");
5
5
  const require_uuid = require("./chunks/uuid-BhG0ngwk.cjs");
6
6
  const require_jszip = require("./chunks/jszip-D8mAFF-r.cjs");
7
- const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-CBFGggZ2.cjs");
7
+ const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-BWWWNPP8.cjs");
8
8
  const require__plugin_vue_export_helper = require("./chunks/_plugin-vue_export-helper-SDR04tiH.cjs");
9
9
  const require_constants = require("./chunks/constants-DpXuDx_g.cjs");
10
10
  let vue = require("vue");
@@ -316,7 +316,7 @@ var shuffleArray = (array) => {
316
316
  var DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
317
317
  function getSuperdocVersion() {
318
318
  try {
319
- return "2.12.0-next.2";
319
+ return "2.12.0-next.20";
320
320
  } catch {
321
321
  return "unknown";
322
322
  }
@@ -10188,6 +10188,115 @@ var useCommentsStore = defineStore("comments", () => {
10188
10188
  const type = normalizeTrackedChangeDecisionType(comment?.trackedChangeDisplayType) ?? normalizeTrackedChangeDecisionType(comment?.trackedChangeType);
10189
10189
  return normalizedDecision === "accept" && type === "insertion" || normalizedDecision === "reject" && type === "deletion";
10190
10190
  };
10191
+ const getBodyTrackedChangeIdentityIds = (comment) => {
10192
+ const commentId = normalizeCommentId(comment.commentId);
10193
+ const canonicalId = normalizeCommentId(comment.trackedChangeCanonicalId);
10194
+ return [commentId, ...!(canonicalId && canonicalId !== commentId) ? [
10195
+ comment.importedId,
10196
+ canonicalId,
10197
+ comment.trackedChangeAnchorKey,
10198
+ ...normalizeTrackedChangePositionAliases(comment.trackedChangePositionAliases)
10199
+ ] : []].map((id) => normalizeCommentId(id)).filter(Boolean);
10200
+ };
10201
+ const groupBodyTrackedChangeCommentsByIdentity = (comments) => {
10202
+ const parents = comments.map((_, index) => index);
10203
+ const findRoot = (index) => {
10204
+ let root = index;
10205
+ while (parents[root] !== root) root = parents[root];
10206
+ while (parents[index] !== index) {
10207
+ const next = parents[index];
10208
+ parents[index] = root;
10209
+ index = next;
10210
+ }
10211
+ return root;
10212
+ };
10213
+ const join = (left, right) => {
10214
+ const leftRoot = findRoot(left);
10215
+ const rightRoot = findRoot(right);
10216
+ if (leftRoot === rightRoot) return;
10217
+ parents[Math.max(leftRoot, rightRoot)] = Math.min(leftRoot, rightRoot);
10218
+ };
10219
+ const identityOwner = /* @__PURE__ */ new Map();
10220
+ comments.forEach((comment, index) => {
10221
+ getBodyTrackedChangeIdentityIds(comment).forEach((identityId) => {
10222
+ const owner = identityOwner.get(identityId);
10223
+ if (owner === void 0) identityOwner.set(identityId, index);
10224
+ else join(index, owner);
10225
+ });
10226
+ });
10227
+ const groups = /* @__PURE__ */ new Map();
10228
+ comments.forEach((comment, index) => {
10229
+ const root = findRoot(index);
10230
+ const group = groups.get(root) ?? [];
10231
+ group.push(comment);
10232
+ groups.set(root, group);
10233
+ });
10234
+ return groups;
10235
+ };
10236
+ const mergeDuplicateBodyTrackedChangeComments = (groups) => {
10237
+ const duplicates = /* @__PURE__ */ new Map();
10238
+ groups.forEach(([survivor, ...duplicateRows]) => {
10239
+ if (!survivor || duplicateRows.length === 0) return;
10240
+ const resolvedDuplicate = duplicateRows.find((comment) => comment.resolvedTime != null);
10241
+ if (survivor.resolvedTime == null && resolvedDuplicate) {
10242
+ survivor.resolvedTime = resolvedDuplicate.resolvedTime;
10243
+ survivor.resolvedById = resolvedDuplicate.resolvedById;
10244
+ survivor.resolvedByEmail = resolvedDuplicate.resolvedByEmail;
10245
+ survivor.resolvedByName = resolvedDuplicate.resolvedByName;
10246
+ survivor.trackedChangeDecision = resolvedDuplicate.trackedChangeDecision;
10247
+ }
10248
+ survivor.trackedChangePositionAliases = normalizeTrackedChangePositionAliases([...normalizeTrackedChangePositionAliases(survivor.trackedChangePositionAliases), ...duplicateRows.flatMap((comment) => [
10249
+ comment.commentId,
10250
+ comment.importedId,
10251
+ comment.trackedChangeCanonicalId,
10252
+ ...normalizeTrackedChangePositionAliases(comment.trackedChangePositionAliases)
10253
+ ])]);
10254
+ duplicateRows.forEach((comment) => duplicates.set(comment, survivor));
10255
+ });
10256
+ return duplicates;
10257
+ };
10258
+ const createDroppedCommentIdMap = (duplicates) => {
10259
+ const droppedCommentIds = /* @__PURE__ */ new Map();
10260
+ duplicates.forEach((survivor, duplicate) => {
10261
+ const fromId = normalizeCommentId(duplicate.commentId);
10262
+ const toId = normalizeCommentId(survivor.commentId);
10263
+ if (fromId && toId) droppedCommentIds.set(fromId, toId);
10264
+ });
10265
+ return droppedCommentIds;
10266
+ };
10267
+ const remapTrackedChangeReferences = (droppedCommentIds, activeDocumentId) => {
10268
+ const remapSelection = (selection) => {
10269
+ const selectedId = normalizeCommentId(selection.value);
10270
+ if (selectedId && droppedCommentIds.has(selectedId)) selection.value = droppedCommentIds.get(selectedId);
10271
+ };
10272
+ remapSelection(activeComment);
10273
+ remapSelection(activeFloatingCommentInstanceId);
10274
+ commentsList.value.forEach((row) => {
10275
+ if (!belongsToTrackedChangeSyncDocument(row, activeDocumentId)) return;
10276
+ const rowIdentityUpdates = {};
10277
+ for (const field of [
10278
+ "trackedChangeThreadParentId",
10279
+ "trackedChangeParentId",
10280
+ "threadingParentCommentId",
10281
+ "parentCommentId"
10282
+ ]) {
10283
+ const replacementId = droppedCommentIds.get(normalizeCommentId(row?.[field]));
10284
+ if (!replacementId) continue;
10285
+ row[field] = replacementId;
10286
+ rowIdentityUpdates[field] = replacementId;
10287
+ }
10288
+ if (Object.keys(rowIdentityUpdates).length && typeof row.updateIdentityValues === "function") row.updateIdentityValues(rowIdentityUpdates);
10289
+ });
10290
+ };
10291
+ const compactDuplicateBodyTrackedChangeComments = (activeDocumentId) => {
10292
+ const candidates = commentsList.value.filter((comment) => isBodyTrackedChangeComment(comment) && belongsToTrackedChangeSyncDocument(comment, activeDocumentId));
10293
+ const groups = groupBodyTrackedChangeCommentsByIdentity(candidates);
10294
+ const duplicates = mergeDuplicateBodyTrackedChangeComments(groups);
10295
+ if (!duplicates.size) return 0;
10296
+ commentsList.value = commentsList.value.filter((comment) => !duplicates.has(comment));
10297
+ remapTrackedChangeReferences(createDroppedCommentIdMap(duplicates), activeDocumentId);
10298
+ return duplicates.size;
10299
+ };
10191
10300
  /**
10192
10301
  * Remove tracked-change comments that no longer have a corresponding mark in the editor.
10193
10302
  * Also removes any replies linked to those removed tracked-change threads.
@@ -10233,7 +10342,9 @@ var useCommentsStore = defineStore("comments", () => {
10233
10342
  if (importedId) removedIds.add(importedId);
10234
10343
  return false;
10235
10344
  });
10345
+ compactDuplicateBodyTrackedChangeComments(activeDocumentId);
10236
10346
  restoredComments.forEach((comment) => {
10347
+ if (!commentsList.value.includes(comment)) return;
10237
10348
  const payload = getCommentEventPayload(comment);
10238
10349
  const event = {
10239
10350
  type: COMMENT_EVENTS.UPDATE,
@@ -10823,11 +10934,14 @@ var useCommentsStore = defineStore("comments", () => {
10823
10934
  } finally {
10824
10935
  endInteractionSpan(identitySpan, trackedChangeIdentityIndex.work());
10825
10936
  }
10826
- if (!effectiveDocumentId || !pruneStale) return {
10827
- liveIds,
10828
- liveAnchorKeys,
10829
- appliedCount
10830
- };
10937
+ if (!effectiveDocumentId || !pruneStale) {
10938
+ if (effectiveDocumentId) compactDuplicateBodyTrackedChangeComments(String(effectiveDocumentId));
10939
+ return {
10940
+ liveIds,
10941
+ liveAnchorKeys,
10942
+ appliedCount
10943
+ };
10944
+ }
10831
10945
  pruneStaleTrackedChangeComments(liveIds, liveAnchorKeys, String(effectiveDocumentId), superdoc, { broadcastChanges: false });
10832
10946
  return {
10833
10947
  liveIds,
@@ -16137,6 +16251,8 @@ function useCommentSmallScreen({ commentsModuleConfig, superdocRoot, layers }) {
16137
16251
  const isCompactCommentsMode = (0, vue.ref)(false);
16138
16252
  let commentsContainerResizeObserver = null;
16139
16253
  let compactMeasurementTarget = null;
16254
+ let documentWidthResizeObserver = null;
16255
+ let documentWidthTargets = [];
16140
16256
  const isValidMeasurementTarget = (element) => {
16141
16257
  if (!(element instanceof HTMLElement)) return false;
16142
16258
  if ((typeof window !== "undefined" ? window.getComputedStyle(element) : null)?.display === "contents") return false;
@@ -16190,7 +16306,25 @@ function useCommentSmallScreen({ commentsModuleConfig, superdocRoot, layers }) {
16190
16306
  if (Number.isFinite(rectWidth) && rectWidth > 0) return rectWidth;
16191
16307
  return 0;
16192
16308
  };
16309
+ const ensureDocumentWidthObserver = () => {
16310
+ const ResizeObserverClass = typeof window !== "undefined" ? window.ResizeObserver : void 0;
16311
+ if (typeof ResizeObserverClass === "undefined") return;
16312
+ const documentElement = superdocRoot.value?.querySelector?.(SUPERDOC_DOCUMENT_SELECTOR) ?? null;
16313
+ const nextTargets = [layers.value, documentElement].filter((el, index, all) => el instanceof HTMLElement && all.indexOf(el) === index);
16314
+ if (nextTargets.length === documentWidthTargets.length && nextTargets.every((el, index) => el === documentWidthTargets[index])) return;
16315
+ if (documentWidthResizeObserver) {
16316
+ documentWidthResizeObserver.disconnect();
16317
+ documentWidthResizeObserver = null;
16318
+ }
16319
+ documentWidthTargets = nextTargets;
16320
+ if (!nextTargets.length) return;
16321
+ documentWidthResizeObserver = new ResizeObserverClass(() => {
16322
+ recalculateCompactCommentsMode();
16323
+ });
16324
+ nextTargets.forEach((el) => documentWidthResizeObserver.observe(el));
16325
+ };
16193
16326
  const getMeasuredDocumentWidth = () => {
16327
+ ensureDocumentWidthObserver();
16194
16328
  const documentElement = superdocRoot.value?.querySelector?.(SUPERDOC_DOCUMENT_SELECTOR);
16195
16329
  const layersElement = layers.value;
16196
16330
  const measuredFromDocument = Number(documentElement?.clientWidth ?? documentElement?.getBoundingClientRect?.().width ?? 0);
@@ -16229,6 +16363,11 @@ function useCommentSmallScreen({ commentsModuleConfig, superdocRoot, layers }) {
16229
16363
  commentsContainerResizeObserver = null;
16230
16364
  }
16231
16365
  compactMeasurementTarget = null;
16366
+ if (documentWidthResizeObserver) {
16367
+ documentWidthResizeObserver.disconnect();
16368
+ documentWidthResizeObserver = null;
16369
+ }
16370
+ documentWidthTargets = [];
16232
16371
  });
16233
16372
  return {
16234
16373
  superdocContainerWidth,
@@ -17238,6 +17377,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17238
17377
  const matchCount = (0, vue.ref)(0);
17239
17378
  const activeMatchIndex = (0, vue.ref)(-1);
17240
17379
  const canReplaceState = (0, vue.ref)(true);
17380
+ const canReplaceAllState = (0, vue.ref)(true);
17241
17381
  const replacePending = (0, vue.ref)(false);
17242
17382
  const ignoreDiacriticsSupported = (0, vue.ref)(true);
17243
17383
  const regexSupported = (0, vue.ref)(false);
@@ -17252,6 +17392,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17252
17392
  });
17253
17393
  const hasMatches = (0, vue.computed)(() => matchCount.value > 0);
17254
17394
  const canReplaceComputed = (0, vue.computed)(() => hasMatches.value && !replacePending.value && canReplaceState.value);
17395
+ const canReplaceAllComputed = (0, vue.computed)(() => canReplaceComputed.value && canReplaceAllState.value);
17255
17396
  /** @type {ReturnType<typeof setTimeout> | null} */
17256
17397
  let debounceTimer = null;
17257
17398
  /** @type {ReturnType<typeof setInterval> | null} */
@@ -17286,6 +17427,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17286
17427
  matchCount.value = typeof slice.total === "number" ? slice.total : 0;
17287
17428
  activeMatchIndex.value = typeof slice.activeIndex === "number" ? slice.activeIndex : -1;
17288
17429
  canReplaceState.value = slice.canReplace === true;
17430
+ canReplaceAllState.value = typeof slice.canReplaceAll === "boolean" ? slice.canReplaceAll : slice.canReplace === true;
17289
17431
  searchError.value = slice.reason === "search-invalid-pattern" ? currentTexts.invalidPatternLabel : null;
17290
17432
  }
17291
17433
  function runSearch() {
@@ -17422,7 +17564,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17422
17564
  if (replacePending.value) return;
17423
17565
  if (!hasMatches.value) return;
17424
17566
  if (currentV2Search) {
17425
- if (!canReplaceState.value) return;
17567
+ if (!canReplaceState.value || !canReplaceAllState.value) return;
17426
17568
  const search = currentV2Search;
17427
17569
  replacePending.value = true;
17428
17570
  const finish = createReplaceContinuation(search, {
@@ -17483,6 +17625,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17483
17625
  matchCount.value = 0;
17484
17626
  activeMatchIndex.value = -1;
17485
17627
  canReplaceState.value = true;
17628
+ canReplaceAllState.value = true;
17486
17629
  replacePending.value = false;
17487
17630
  }
17488
17631
  /**
@@ -17587,6 +17730,11 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17587
17730
  hasMatches,
17588
17731
  canReplace: canReplaceComputed,
17589
17732
  /**
17733
+ * `canReplace` plus the session enumerating every match. A truncated V2
17734
+ * session keeps Replace enabled and disables Replace all.
17735
+ */
17736
+ canReplaceAll: canReplaceAllComputed,
17737
+ /**
17590
17738
  * Runtime mutability of the active session (viewing/read-only mode sets
17591
17739
  * this false). Distinct from the static `replaceEnabled` config and from
17592
17740
  * `canReplace`, which also requires matches: surfaces hide replace
@@ -17676,6 +17824,9 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17676
17824
  get canReplace() {
17677
17825
  return handle.canReplace.value;
17678
17826
  },
17827
+ get canReplaceAll() {
17828
+ return handle.canReplaceAll ? handle.canReplaceAll.value : handle.canReplace.value;
17829
+ },
17679
17830
  get replacePending() {
17680
17831
  return handle.replacePending.value;
17681
17832
  },
@@ -17826,7 +17977,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17826
17977
  /** @type {object | undefined} */
17827
17978
  let FindReplaceSurface;
17828
17979
  try {
17829
- FindReplaceSurface = (await Promise.resolve().then(() => require("./chunks/FindReplaceSurface-0p7EtMZh.cjs"))).default;
17980
+ FindReplaceSurface = (await Promise.resolve().then(() => require("./chunks/FindReplaceSurface-qi9OhbwN.cjs"))).default;
17830
17981
  } catch {
17831
17982
  opening = false;
17832
17983
  return;
@@ -22789,7 +22940,8 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
22789
22940
  error: new Error(message),
22790
22941
  code: reason,
22791
22942
  ...documentId ? { documentId } : {},
22792
- editor: null
22943
+ editor: null,
22944
+ ...workerFailure ? { workerFailure } : {}
22793
22945
  });
22794
22946
  const bootDiagnostic = translateBootFailureReason(reason, detail, {
22795
22947
  documentId,
@@ -23992,7 +24144,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
23992
24144
  ], 38);
23993
24145
  };
23994
24146
  }
23995
- }, [["__scopeId", "data-v-01062725"]]);
24147
+ }, [["__scopeId", "data-v-770204b4"]]);
23996
24148
  //#endregion
23997
24149
  //#region src/core/create-app.js
23998
24150
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
@@ -37416,7 +37568,7 @@ var TOOLBAR_ITEM_ALIASES = Object.freeze({
37416
37568
  * - `ui: false` renders no built-in chrome. It is an explicit
37417
37569
  * no-built-in-UI profile, not the inverse of a hypothetical `ui: true`,
37418
37570
  * and it never disables document content, the Document API, or
37419
- * `editor.ui`.
37571
+ * `superdoc.ui`.
37420
37572
  * - `ui: { ... }` opts in per surface. An omitted key keeps that surface's
37421
37573
  * historical default rather than inheriting from its siblings.
37422
37574
  *
@@ -45188,7 +45340,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
45188
45340
  this.config.colors = shuffleArray(this.config.colors);
45189
45341
  this.userColorMap = /* @__PURE__ */ new Map();
45190
45342
  this.colorIndex = 0;
45191
- this.version = "2.12.0-next.2";
45343
+ this.version = "2.12.0-next.20";
45192
45344
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
45193
45345
  this.superdocId = config.superdocId || require_uuid.v4();
45194
45346
  this.colors = this.config.colors ?? [];
@@ -45357,7 +45509,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
45357
45509
  this.#pendingContentControlInputSource = null;
45358
45510
  this.#pendingContentControlInputExpiresAt = null;
45359
45511
  });
45360
- contentControlsUnsub = this.#ui.contentControls.observe((snapshot) => {
45512
+ contentControlsUnsub = this.#ui.contentControls.observeActivePath((snapshot) => {
45361
45513
  const editor = this.activeEditor;
45362
45514
  const activePath = snapshot.activeIds.map((id) => toContentControlRef(snapshot.items.find((item) => item.id === id)));
45363
45515
  if (activePath.some((item) => item === null)) return;
@@ -3,7 +3,7 @@ import { t as blank_default } from "./chunks/blank-docx-DzQccOlW.es.js";
3
3
  import { t as import_eventemitter3 } from "./chunks/eventemitter3-DEIiXiH2.es.js";
4
4
  import { t as v4 } from "./chunks/uuid-H0Xcmmhy.es.js";
5
5
  import { a as init_dist$1, i as global, n as init_dist$2, o as Buffer, r as process$1, s as init_dist, t as require_jszip_min } from "./chunks/jszip-BzJ3CyxR.es.js";
6
- import { a as DOM_CLASS_NAMES, i as getV2TrackedChangeMutationImpact, t as createSuperDocUI } from "./chunks/create-super-doc-ui-C-AuEM9F.es.js";
6
+ import { a as DOM_CLASS_NAMES, i as getV2TrackedChangeMutationImpact, t as createSuperDocUI } from "./chunks/create-super-doc-ui-FnwgTkUv.es.js";
7
7
  import { t as _plugin_vue_export_helper_default } from "./chunks/_plugin-vue_export-helper-BOaGB7Aw.es.js";
8
8
  import { t as PDF_TO_CSS_UNITS } from "./chunks/constants-B6VBlmKp.es.js";
9
9
  import * as Vue from "vue";
@@ -315,7 +315,7 @@ var shuffleArray = (array) => {
315
315
  var DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
316
316
  function getSuperdocVersion() {
317
317
  try {
318
- return "2.12.0-next.2";
318
+ return "2.12.0-next.20";
319
319
  } catch {
320
320
  return "unknown";
321
321
  }
@@ -10187,6 +10187,115 @@ var useCommentsStore = defineStore("comments", () => {
10187
10187
  const type = normalizeTrackedChangeDecisionType(comment?.trackedChangeDisplayType) ?? normalizeTrackedChangeDecisionType(comment?.trackedChangeType);
10188
10188
  return normalizedDecision === "accept" && type === "insertion" || normalizedDecision === "reject" && type === "deletion";
10189
10189
  };
10190
+ const getBodyTrackedChangeIdentityIds = (comment) => {
10191
+ const commentId = normalizeCommentId(comment.commentId);
10192
+ const canonicalId = normalizeCommentId(comment.trackedChangeCanonicalId);
10193
+ return [commentId, ...!(canonicalId && canonicalId !== commentId) ? [
10194
+ comment.importedId,
10195
+ canonicalId,
10196
+ comment.trackedChangeAnchorKey,
10197
+ ...normalizeTrackedChangePositionAliases(comment.trackedChangePositionAliases)
10198
+ ] : []].map((id) => normalizeCommentId(id)).filter(Boolean);
10199
+ };
10200
+ const groupBodyTrackedChangeCommentsByIdentity = (comments) => {
10201
+ const parents = comments.map((_, index) => index);
10202
+ const findRoot = (index) => {
10203
+ let root = index;
10204
+ while (parents[root] !== root) root = parents[root];
10205
+ while (parents[index] !== index) {
10206
+ const next = parents[index];
10207
+ parents[index] = root;
10208
+ index = next;
10209
+ }
10210
+ return root;
10211
+ };
10212
+ const join = (left, right) => {
10213
+ const leftRoot = findRoot(left);
10214
+ const rightRoot = findRoot(right);
10215
+ if (leftRoot === rightRoot) return;
10216
+ parents[Math.max(leftRoot, rightRoot)] = Math.min(leftRoot, rightRoot);
10217
+ };
10218
+ const identityOwner = /* @__PURE__ */ new Map();
10219
+ comments.forEach((comment, index) => {
10220
+ getBodyTrackedChangeIdentityIds(comment).forEach((identityId) => {
10221
+ const owner = identityOwner.get(identityId);
10222
+ if (owner === void 0) identityOwner.set(identityId, index);
10223
+ else join(index, owner);
10224
+ });
10225
+ });
10226
+ const groups = /* @__PURE__ */ new Map();
10227
+ comments.forEach((comment, index) => {
10228
+ const root = findRoot(index);
10229
+ const group = groups.get(root) ?? [];
10230
+ group.push(comment);
10231
+ groups.set(root, group);
10232
+ });
10233
+ return groups;
10234
+ };
10235
+ const mergeDuplicateBodyTrackedChangeComments = (groups) => {
10236
+ const duplicates = /* @__PURE__ */ new Map();
10237
+ groups.forEach(([survivor, ...duplicateRows]) => {
10238
+ if (!survivor || duplicateRows.length === 0) return;
10239
+ const resolvedDuplicate = duplicateRows.find((comment) => comment.resolvedTime != null);
10240
+ if (survivor.resolvedTime == null && resolvedDuplicate) {
10241
+ survivor.resolvedTime = resolvedDuplicate.resolvedTime;
10242
+ survivor.resolvedById = resolvedDuplicate.resolvedById;
10243
+ survivor.resolvedByEmail = resolvedDuplicate.resolvedByEmail;
10244
+ survivor.resolvedByName = resolvedDuplicate.resolvedByName;
10245
+ survivor.trackedChangeDecision = resolvedDuplicate.trackedChangeDecision;
10246
+ }
10247
+ survivor.trackedChangePositionAliases = normalizeTrackedChangePositionAliases([...normalizeTrackedChangePositionAliases(survivor.trackedChangePositionAliases), ...duplicateRows.flatMap((comment) => [
10248
+ comment.commentId,
10249
+ comment.importedId,
10250
+ comment.trackedChangeCanonicalId,
10251
+ ...normalizeTrackedChangePositionAliases(comment.trackedChangePositionAliases)
10252
+ ])]);
10253
+ duplicateRows.forEach((comment) => duplicates.set(comment, survivor));
10254
+ });
10255
+ return duplicates;
10256
+ };
10257
+ const createDroppedCommentIdMap = (duplicates) => {
10258
+ const droppedCommentIds = /* @__PURE__ */ new Map();
10259
+ duplicates.forEach((survivor, duplicate) => {
10260
+ const fromId = normalizeCommentId(duplicate.commentId);
10261
+ const toId = normalizeCommentId(survivor.commentId);
10262
+ if (fromId && toId) droppedCommentIds.set(fromId, toId);
10263
+ });
10264
+ return droppedCommentIds;
10265
+ };
10266
+ const remapTrackedChangeReferences = (droppedCommentIds, activeDocumentId) => {
10267
+ const remapSelection = (selection) => {
10268
+ const selectedId = normalizeCommentId(selection.value);
10269
+ if (selectedId && droppedCommentIds.has(selectedId)) selection.value = droppedCommentIds.get(selectedId);
10270
+ };
10271
+ remapSelection(activeComment);
10272
+ remapSelection(activeFloatingCommentInstanceId);
10273
+ commentsList.value.forEach((row) => {
10274
+ if (!belongsToTrackedChangeSyncDocument(row, activeDocumentId)) return;
10275
+ const rowIdentityUpdates = {};
10276
+ for (const field of [
10277
+ "trackedChangeThreadParentId",
10278
+ "trackedChangeParentId",
10279
+ "threadingParentCommentId",
10280
+ "parentCommentId"
10281
+ ]) {
10282
+ const replacementId = droppedCommentIds.get(normalizeCommentId(row?.[field]));
10283
+ if (!replacementId) continue;
10284
+ row[field] = replacementId;
10285
+ rowIdentityUpdates[field] = replacementId;
10286
+ }
10287
+ if (Object.keys(rowIdentityUpdates).length && typeof row.updateIdentityValues === "function") row.updateIdentityValues(rowIdentityUpdates);
10288
+ });
10289
+ };
10290
+ const compactDuplicateBodyTrackedChangeComments = (activeDocumentId) => {
10291
+ const candidates = commentsList.value.filter((comment) => isBodyTrackedChangeComment(comment) && belongsToTrackedChangeSyncDocument(comment, activeDocumentId));
10292
+ const groups = groupBodyTrackedChangeCommentsByIdentity(candidates);
10293
+ const duplicates = mergeDuplicateBodyTrackedChangeComments(groups);
10294
+ if (!duplicates.size) return 0;
10295
+ commentsList.value = commentsList.value.filter((comment) => !duplicates.has(comment));
10296
+ remapTrackedChangeReferences(createDroppedCommentIdMap(duplicates), activeDocumentId);
10297
+ return duplicates.size;
10298
+ };
10190
10299
  /**
10191
10300
  * Remove tracked-change comments that no longer have a corresponding mark in the editor.
10192
10301
  * Also removes any replies linked to those removed tracked-change threads.
@@ -10232,7 +10341,9 @@ var useCommentsStore = defineStore("comments", () => {
10232
10341
  if (importedId) removedIds.add(importedId);
10233
10342
  return false;
10234
10343
  });
10344
+ compactDuplicateBodyTrackedChangeComments(activeDocumentId);
10235
10345
  restoredComments.forEach((comment) => {
10346
+ if (!commentsList.value.includes(comment)) return;
10236
10347
  const payload = getCommentEventPayload(comment);
10237
10348
  const event = {
10238
10349
  type: COMMENT_EVENTS.UPDATE,
@@ -10822,11 +10933,14 @@ var useCommentsStore = defineStore("comments", () => {
10822
10933
  } finally {
10823
10934
  endInteractionSpan(identitySpan, trackedChangeIdentityIndex.work());
10824
10935
  }
10825
- if (!effectiveDocumentId || !pruneStale) return {
10826
- liveIds,
10827
- liveAnchorKeys,
10828
- appliedCount
10829
- };
10936
+ if (!effectiveDocumentId || !pruneStale) {
10937
+ if (effectiveDocumentId) compactDuplicateBodyTrackedChangeComments(String(effectiveDocumentId));
10938
+ return {
10939
+ liveIds,
10940
+ liveAnchorKeys,
10941
+ appliedCount
10942
+ };
10943
+ }
10830
10944
  pruneStaleTrackedChangeComments(liveIds, liveAnchorKeys, String(effectiveDocumentId), superdoc, { broadcastChanges: false });
10831
10945
  return {
10832
10946
  liveIds,
@@ -16122,6 +16236,8 @@ function useCommentSmallScreen({ commentsModuleConfig, superdocRoot, layers }) {
16122
16236
  const isCompactCommentsMode = ref(false);
16123
16237
  let commentsContainerResizeObserver = null;
16124
16238
  let compactMeasurementTarget = null;
16239
+ let documentWidthResizeObserver = null;
16240
+ let documentWidthTargets = [];
16125
16241
  const isValidMeasurementTarget = (element) => {
16126
16242
  if (!(element instanceof HTMLElement)) return false;
16127
16243
  if ((typeof window !== "undefined" ? window.getComputedStyle(element) : null)?.display === "contents") return false;
@@ -16175,7 +16291,25 @@ function useCommentSmallScreen({ commentsModuleConfig, superdocRoot, layers }) {
16175
16291
  if (Number.isFinite(rectWidth) && rectWidth > 0) return rectWidth;
16176
16292
  return 0;
16177
16293
  };
16294
+ const ensureDocumentWidthObserver = () => {
16295
+ const ResizeObserverClass = typeof window !== "undefined" ? window.ResizeObserver : void 0;
16296
+ if (typeof ResizeObserverClass === "undefined") return;
16297
+ const documentElement = superdocRoot.value?.querySelector?.(SUPERDOC_DOCUMENT_SELECTOR) ?? null;
16298
+ const nextTargets = [layers.value, documentElement].filter((el, index, all) => el instanceof HTMLElement && all.indexOf(el) === index);
16299
+ if (nextTargets.length === documentWidthTargets.length && nextTargets.every((el, index) => el === documentWidthTargets[index])) return;
16300
+ if (documentWidthResizeObserver) {
16301
+ documentWidthResizeObserver.disconnect();
16302
+ documentWidthResizeObserver = null;
16303
+ }
16304
+ documentWidthTargets = nextTargets;
16305
+ if (!nextTargets.length) return;
16306
+ documentWidthResizeObserver = new ResizeObserverClass(() => {
16307
+ recalculateCompactCommentsMode();
16308
+ });
16309
+ nextTargets.forEach((el) => documentWidthResizeObserver.observe(el));
16310
+ };
16178
16311
  const getMeasuredDocumentWidth = () => {
16312
+ ensureDocumentWidthObserver();
16179
16313
  const documentElement = superdocRoot.value?.querySelector?.(SUPERDOC_DOCUMENT_SELECTOR);
16180
16314
  const layersElement = layers.value;
16181
16315
  const measuredFromDocument = Number(documentElement?.clientWidth ?? documentElement?.getBoundingClientRect?.().width ?? 0);
@@ -16214,6 +16348,11 @@ function useCommentSmallScreen({ commentsModuleConfig, superdocRoot, layers }) {
16214
16348
  commentsContainerResizeObserver = null;
16215
16349
  }
16216
16350
  compactMeasurementTarget = null;
16351
+ if (documentWidthResizeObserver) {
16352
+ documentWidthResizeObserver.disconnect();
16353
+ documentWidthResizeObserver = null;
16354
+ }
16355
+ documentWidthTargets = [];
16217
16356
  });
16218
16357
  return {
16219
16358
  superdocContainerWidth,
@@ -17223,6 +17362,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17223
17362
  const matchCount = ref(0);
17224
17363
  const activeMatchIndex = ref(-1);
17225
17364
  const canReplaceState = ref(true);
17365
+ const canReplaceAllState = ref(true);
17226
17366
  const replacePending = ref(false);
17227
17367
  const ignoreDiacriticsSupported = ref(true);
17228
17368
  const regexSupported = ref(false);
@@ -17237,6 +17377,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17237
17377
  });
17238
17378
  const hasMatches = computed(() => matchCount.value > 0);
17239
17379
  const canReplaceComputed = computed(() => hasMatches.value && !replacePending.value && canReplaceState.value);
17380
+ const canReplaceAllComputed = computed(() => canReplaceComputed.value && canReplaceAllState.value);
17240
17381
  /** @type {ReturnType<typeof setTimeout> | null} */
17241
17382
  let debounceTimer = null;
17242
17383
  /** @type {ReturnType<typeof setInterval> | null} */
@@ -17271,6 +17412,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17271
17412
  matchCount.value = typeof slice.total === "number" ? slice.total : 0;
17272
17413
  activeMatchIndex.value = typeof slice.activeIndex === "number" ? slice.activeIndex : -1;
17273
17414
  canReplaceState.value = slice.canReplace === true;
17415
+ canReplaceAllState.value = typeof slice.canReplaceAll === "boolean" ? slice.canReplaceAll : slice.canReplace === true;
17274
17416
  searchError.value = slice.reason === "search-invalid-pattern" ? currentTexts.invalidPatternLabel : null;
17275
17417
  }
17276
17418
  function runSearch() {
@@ -17407,7 +17549,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17407
17549
  if (replacePending.value) return;
17408
17550
  if (!hasMatches.value) return;
17409
17551
  if (currentV2Search) {
17410
- if (!canReplaceState.value) return;
17552
+ if (!canReplaceState.value || !canReplaceAllState.value) return;
17411
17553
  const search = currentV2Search;
17412
17554
  replacePending.value = true;
17413
17555
  const finish = createReplaceContinuation(search, {
@@ -17468,6 +17610,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17468
17610
  matchCount.value = 0;
17469
17611
  activeMatchIndex.value = -1;
17470
17612
  canReplaceState.value = true;
17613
+ canReplaceAllState.value = true;
17471
17614
  replacePending.value = false;
17472
17615
  }
17473
17616
  /**
@@ -17572,6 +17715,11 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17572
17715
  hasMatches,
17573
17716
  canReplace: canReplaceComputed,
17574
17717
  /**
17718
+ * `canReplace` plus the session enumerating every match. A truncated V2
17719
+ * session keeps Replace enabled and disables Replace all.
17720
+ */
17721
+ canReplaceAll: canReplaceAllComputed,
17722
+ /**
17575
17723
  * Runtime mutability of the active session (viewing/read-only mode sets
17576
17724
  * this false). Distinct from the static `replaceEnabled` config and from
17577
17725
  * `canReplace`, which also requires matches: surfaces hide replace
@@ -17661,6 +17809,9 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17661
17809
  get canReplace() {
17662
17810
  return handle.canReplace.value;
17663
17811
  },
17812
+ get canReplaceAll() {
17813
+ return handle.canReplaceAll ? handle.canReplaceAll.value : handle.canReplace.value;
17814
+ },
17664
17815
  get replacePending() {
17665
17816
  return handle.replacePending.value;
17666
17817
  },
@@ -17811,7 +17962,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17811
17962
  /** @type {object | undefined} */
17812
17963
  let FindReplaceSurface;
17813
17964
  try {
17814
- FindReplaceSurface = (await import("./chunks/FindReplaceSurface-40AnrqWk.es.js")).default;
17965
+ FindReplaceSurface = (await import("./chunks/FindReplaceSurface-CVKgivXi.es.js")).default;
17815
17966
  } catch {
17816
17967
  opening = false;
17817
17968
  return;
@@ -22749,7 +22900,8 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
22749
22900
  error: new Error(message),
22750
22901
  code: reason,
22751
22902
  ...documentId ? { documentId } : {},
22752
- editor: null
22903
+ editor: null,
22904
+ ...workerFailure ? { workerFailure } : {}
22753
22905
  });
22754
22906
  const bootDiagnostic = translateBootFailureReason(reason, detail, {
22755
22907
  documentId,
@@ -23952,7 +24104,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
23952
24104
  ], 38);
23953
24105
  };
23954
24106
  }
23955
- }, [["__scopeId", "data-v-01062725"]]);
24107
+ }, [["__scopeId", "data-v-770204b4"]]);
23956
24108
  //#endregion
23957
24109
  //#region src/core/create-app.js
23958
24110
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
@@ -37376,7 +37528,7 @@ var TOOLBAR_ITEM_ALIASES = Object.freeze({
37376
37528
  * - `ui: false` renders no built-in chrome. It is an explicit
37377
37529
  * no-built-in-UI profile, not the inverse of a hypothetical `ui: true`,
37378
37530
  * and it never disables document content, the Document API, or
37379
- * `editor.ui`.
37531
+ * `superdoc.ui`.
37380
37532
  * - `ui: { ... }` opts in per surface. An omitted key keeps that surface's
37381
37533
  * historical default rather than inheriting from its siblings.
37382
37534
  *
@@ -45119,7 +45271,7 @@ var SuperDoc = class extends import_eventemitter3.default {
45119
45271
  this.config.colors = shuffleArray(this.config.colors);
45120
45272
  this.userColorMap = /* @__PURE__ */ new Map();
45121
45273
  this.colorIndex = 0;
45122
- this.version = "2.12.0-next.2";
45274
+ this.version = "2.12.0-next.20";
45123
45275
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
45124
45276
  this.superdocId = config.superdocId || v4();
45125
45277
  this.colors = this.config.colors ?? [];
@@ -45288,7 +45440,7 @@ var SuperDoc = class extends import_eventemitter3.default {
45288
45440
  this.#pendingContentControlInputSource = null;
45289
45441
  this.#pendingContentControlInputExpiresAt = null;
45290
45442
  });
45291
- contentControlsUnsub = this.#ui.contentControls.observe((snapshot) => {
45443
+ contentControlsUnsub = this.#ui.contentControls.observeActivePath((snapshot) => {
45292
45444
  const editor = this.activeEditor;
45293
45445
  const activePath = snapshot.activeIds.map((id) => toContentControlRef(snapshot.items.find((item) => item.id === id)));
45294
45446
  if (activePath.some((item) => item === null)) return;