superdoc 2.29.0 → 2.30.0-next.4

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 (112) hide show
  1. package/README.md +127 -25
  2. package/dist/chunks/{FindReplaceSurface-B_5ggdWg.es.js → FindReplaceSurface-BEnRLpD9.es.js} +1 -1
  3. package/dist/chunks/{FindReplaceSurface-fVHxPXtn.cjs → FindReplaceSurface-DWXgWgg7.cjs} +1 -1
  4. package/dist/chunks/{PasswordPromptSurface-CZqhDQQ1.es.js → PasswordPromptSurface-DjV1JgjD.es.js} +1 -1
  5. package/dist/chunks/{PasswordPromptSurface-x8GNmMJT.cjs → PasswordPromptSurface-VFPnQOML.cjs} +1 -1
  6. package/dist/chunks/{PdfViewer-CW09MNrB.es.js → PdfViewer-C2r-K8zC.es.js} +1 -1
  7. package/dist/chunks/{PdfViewer-CkrAHpC-.cjs → PdfViewer-DYTbPVUr.cjs} +1 -1
  8. package/dist/chunks/{_plugin-vue_export-helper-BkbCxNeT.cjs → _plugin-vue_export-helper-BzM2_tVU.cjs} +9 -0
  9. package/dist/chunks/{_plugin-vue_export-helper-DRPRMkeA.es.js → _plugin-vue_export-helper-CmkaJDnr.es.js} +4 -1
  10. package/dist/chunks/{create-super-doc-ui-L60GhVAX.cjs → create-super-doc-ui--kQBNTyt.cjs} +2733 -2574
  11. package/dist/chunks/{create-super-doc-ui-Dc8dQAMc.es.js → create-super-doc-ui-CbEDXtpm.es.js} +2734 -2581
  12. package/dist/collaboration-upgrade-engine.cjs +1 -1
  13. package/dist/collaboration-upgrade-engine.es.js +1 -1
  14. package/dist/document-api/src/changes/changes.d.ts +26 -0
  15. package/dist/document-api/src/changes/changes.types.d.ts +156 -0
  16. package/dist/document-api/src/contract/command-catalog.d.ts +1 -1
  17. package/dist/document-api/src/contract/operation-definitions.d.ts +51 -1
  18. package/dist/document-api/src/create/create.d.ts +0 -4
  19. package/dist/document-api/src/diff/diff.types.d.ts +6 -6
  20. package/dist/document-api/src/header-footers/header-footers.d.ts +1 -3
  21. package/dist/document-api/src/index.d.ts +6 -0
  22. package/dist/document-api/src/inline-semantics/token-sets.d.ts +0 -2
  23. package/dist/document-api/src/insert/insert.d.ts +0 -1
  24. package/dist/layout-engine/contracts/src/index.d.ts +3 -1
  25. package/dist/layout-engine/dom-contract/src/data-attrs.d.ts +4 -0
  26. package/dist/layout-engine/layout-bridge/src/coupled-footnote-pagination.d.ts +1 -0
  27. package/dist/layout-engine/layout-bridge/src/footnote-page-certificate.d.ts +1 -0
  28. package/dist/layout-engine/layout-bridge/src/footnote-page-planner.d.ts +5 -0
  29. package/dist/layout-engine/layout-bridge/src/incrementalLayout.d.ts +1 -0
  30. package/dist/layout-engine/layout-engine/src/footnote-page-flow.d.ts +3 -0
  31. package/dist/layout-engine/layout-engine/src/index.d.ts +7 -0
  32. package/dist/layout-engine/layout-engine/src/layout-paragraph.d.ts +5 -0
  33. package/dist/layout-engine/layout-engine/src/layout-table.d.ts +3 -1
  34. package/dist/layout-engine/layout-engine/src/layout-utils.d.ts +0 -4
  35. package/dist/layout-engine/layout-engine/src/paginator.d.ts +4 -0
  36. package/dist/layout-engine/painters/dom/src/paragraph/list-marker.d.ts +1 -1
  37. package/dist/layout-engine/painters/dom/src/paragraph/renderParagraphContent.d.ts +1 -1
  38. package/dist/layout-engine/painters/dom/src/styles.d.ts +0 -10
  39. package/dist/layout-engine/painters/dom/src/svg-utils.d.ts +1 -13
  40. package/dist/public/ui-react.cjs +1 -1
  41. package/dist/public/ui-react.es.js +1 -1
  42. package/dist/public/ui-vue.cjs +1 -1
  43. package/dist/public/ui-vue.es.js +1 -1
  44. package/dist/public/ui.cjs +1 -1
  45. package/dist/public/ui.es.js +1 -1
  46. package/dist/shared/common/Telemetry.d.ts +50 -0
  47. package/dist/shared/common/event-types.d.ts +13 -0
  48. package/dist/shared/font-system/src/bundled-face-names.d.ts +1 -0
  49. package/dist/shared/font-system/src/resolver.d.ts +1 -0
  50. package/dist/style.css +1 -1
  51. package/dist/style.layered.css +1 -1
  52. package/dist/superdoc/src/components/CommentsLayer/CommentDialog.vue.d.ts +17 -33
  53. package/dist/superdoc/src/components/CommentsLayer/collect-tracked-change-thread.d.ts +6 -4
  54. package/dist/superdoc/src/components/CommentsLayer/comment-params.d.ts +84 -0
  55. package/dist/superdoc/src/components/CommentsLayer/use-comment.d.ts +15 -95
  56. package/dist/superdoc/src/components/Whiteboard/use-whiteboard.d.ts +30 -9
  57. package/dist/superdoc/src/composables/use-document.d.ts +51 -99
  58. package/dist/superdoc/src/core/SuperDoc.d.ts +12 -3
  59. package/dist/superdoc/src/core/collaboration/cdn-collaboration-comment-map.d.ts +1 -0
  60. package/dist/superdoc/src/core/collaboration/collaboration-comment-map.d.ts +2 -0
  61. package/dist/superdoc/src/core/collaboration/helpers.d.ts +1 -1
  62. package/dist/superdoc/src/core/document-open-telemetry.d.ts +1 -1
  63. package/dist/superdoc/src/core/editor-runtime/v2/v2-editor-runtime-adapter.d.ts +1 -0
  64. package/dist/superdoc/src/core/types/index.d.ts +71 -39
  65. package/dist/superdoc/src/helpers/deprecation.d.ts +12 -3
  66. package/dist/superdoc/src/helpers/object-guards.d.ts +1 -0
  67. package/dist/superdoc/src/helpers/v2-review-geometry-delivery.d.ts +22 -0
  68. package/dist/superdoc/src/internal/toolbar/built-in/ToolbarButton.vue.d.ts +1 -0
  69. package/dist/superdoc/src/public/content-control-ref.d.ts +12 -0
  70. package/dist/superdoc/src/public/index.d.cts +16 -0
  71. package/dist/superdoc/src/public/index.d.ts +3 -0
  72. package/dist/superdoc/src/public/ui/async-read-cache.d.ts +32 -0
  73. package/dist/superdoc/src/public/ui/block-commands.d.ts +39 -0
  74. package/dist/superdoc/src/public/ui/command-results.d.ts +39 -0
  75. package/dist/superdoc/src/public/ui/create-super-doc-ui.d.ts +1 -17
  76. package/dist/superdoc/src/public/ui/document-change-records.d.ts +7 -0
  77. package/dist/superdoc/src/public/ui/entity-rows.d.ts +14 -0
  78. package/dist/superdoc/src/public/ui/font-options.d.ts +7 -0
  79. package/dist/superdoc/src/public/ui/format-painter-state.d.ts +33 -0
  80. package/dist/superdoc/src/public/ui/heavy-read-policy.d.ts +11 -0
  81. package/dist/superdoc/src/public/ui/host-events.d.ts +3 -0
  82. package/dist/superdoc/src/public/ui/inline-values.d.ts +79 -0
  83. package/dist/superdoc/src/public/ui/loose-record.d.ts +4 -0
  84. package/dist/superdoc/src/public/ui/selection-targets.d.ts +45 -0
  85. package/dist/superdoc/src/public/ui/shared-tracked-changes-catalog.d.ts +16 -0
  86. package/dist/superdoc/src/public/ui/tracked-change-ids.d.ts +51 -0
  87. package/dist/superdoc/src/public/ui/types.d.ts +12 -2
  88. package/dist/superdoc/src/public/ui/ui-bench-timing.d.ts +9 -0
  89. package/dist/superdoc/src/public/ui.d.cts +14 -0
  90. package/dist/superdoc/src/public/ui.d.ts +1 -1
  91. package/dist/superdoc/src/stores/comments-store.d.ts +23000 -536
  92. package/dist/superdoc/src/stores/helpers/comment-grouping.d.ts +1 -1
  93. package/dist/superdoc/src/stores/helpers/comment-identity.d.ts +10 -10
  94. package/dist/superdoc/src/stores/helpers/review-presentation.d.ts +90 -0
  95. package/dist/superdoc/src/stores/helpers/tracked-change-identity.d.ts +4 -2
  96. package/dist/superdoc/src/stores/helpers/tracked-change-row-values.d.ts +1 -8
  97. package/dist/superdoc/src/stores/helpers/tracked-change-thread-index.d.ts +1 -1
  98. package/dist/superdoc/src/stores/helpers/v2-review-adapters.d.ts +147 -0
  99. package/dist/superdoc/src/stores/superdoc-store.d.ts +12 -2895
  100. package/dist/superdoc.cjs +604 -388
  101. package/dist/superdoc.es.js +604 -388
  102. package/dist/word-layout/src/marker-utils.d.ts +0 -5
  103. package/dist/word-layout/src/unit-conversions.d.ts +0 -1
  104. package/dist-cdn/style.layered.css +1 -1
  105. package/dist-cdn/superdoc.min.css +1 -1
  106. package/dist-cdn/superdoc.min.js +38 -42
  107. package/package.json +12 -3
  108. package/dist/document-api/src/diff/index.d.ts +0 -3
  109. package/dist/document-api/src/write/locator.d.ts +0 -24
  110. package/dist/layout-engine/painters/dom/src/features/feature-registry.d.ts +0 -41
  111. package/dist/superdoc/src/internal/toolbar/built-in/format-negation.d.ts +0 -11
  112. package/dist/word-layout/src/tab-layout.d.ts +0 -193
@@ -1,7 +1,7 @@
1
1
  import { i as __toESM$2 } from "./chunks/rolldown-runtime-0pSA04fp.es.js";
2
- import { $ as vModelSelect, A as onActivated, B as renderList, C as isRef$1, D as normalizeClass, E as nextTick, F as openBlock, G as toDisplayString, H as resolveDirective, I as provide$1, J as toRefs, K as toRaw$2, L as reactive, M as onDeactivated, N as onMounted, O as normalizeProps, P as onScopeDispose$1, Q as vModelCheckbox, R as readonly, S as isReactive$1, T as mergeProps, U as resolveDynamicComponent, V as renderSlot, W as shallowRef$1, X as useAttrs, Y as unref, Z as useSlots, _ as getCurrentInstance, a as computed$1, at as withModifiers, b as hasInjectionContext, c as createCommentVNode, d as createStaticVNode, et as vModelText, f as createTextVNode, g as effectScope, h as defineComponent, i as Transition, it as withKeys, j as onBeforeUnmount, k as normalizeStyle, l as createElementBlock, m as defineAsyncComponent, n as Fragment, nt as withCtx, o as createApp, ot as isPlainObject$1, p as createVNode, q as toRef, r as Teleport, rt as withDirectives, s as createBlock, st as isThenable$1, t as _plugin_vue_export_helper_default, tt as watch$1, u as createElementVNode, v as getCurrentScope, w as markRaw, x as inject$1, y as h, z as ref } from "./chunks/_plugin-vue_export-helper-DRPRMkeA.es.js";
2
+ import { $ as vModelSelect, A as onActivated, B as renderList, C as isRef$1, D as normalizeClass, E as nextTick, F as openBlock, G as toDisplayString, H as resolveDirective, I as provide$1, J as toRefs, K as toRaw$2, L as reactive, M as onDeactivated, N as onMounted, O as normalizeProps, P as onScopeDispose$1, Q as vModelCheckbox, R as readonly, S as isReactive$1, T as mergeProps, U as resolveDynamicComponent, V as renderSlot, W as shallowRef$1, X as useAttrs, Y as unref, Z as useSlots, _ as getCurrentInstance, a as computed$1, at as withModifiers, b as hasInjectionContext, c as createCommentVNode, ct as isThenable$1, d as createStaticVNode, et as vModelText, f as createTextVNode, g as effectScope, h as defineComponent, i as Transition, it as withKeys, j as onBeforeUnmount, k as normalizeStyle, l as createElementBlock, m as defineAsyncComponent, n as Fragment, nt as withCtx, o as createApp, p as createVNode, q as toRef, r as Teleport, rt as withDirectives, s as createBlock, st as isPlainObject$1, t as _plugin_vue_export_helper_default, tt as watch$1, u as createElementVNode, v as getCurrentScope, w as markRaw, x as inject$1, y as h, z as ref } from "./chunks/_plugin-vue_export-helper-CmkaJDnr.es.js";
3
3
  import { t as blank_default } from "./chunks/blank-docx-DzQccOlW.es.js";
4
- import { C as getWorkflowRecording, S as createInteractionHistory, T as recordInteraction, _ as createTrackedChangePermissionResolver, a as registerFirstPartyCommandMutation, b as DOM_CLASS_NAMES, c as getCommandDescriptor, d as mutationCommittedEventChangedDocument, f as createImageFilePicker, g as PERMISSIONS, h as BUILT_IN_PERMISSION_METHOD, i as readFirstPartyPageNumbers, l as applyV2CommentInvalidationFromMutation, n as executeFirstPartyCommandAsync, o as isRecord, p as resolveImageSrc, r as executeFirstPartyShortcutAsync, t as createSuperDocUI, u as getV2TrackedChangeMutationImpact, v as isAllowed, w as isWorkflowRecordingEnabled, x as closeInteractionHistory, y as isSyntheticTrackedChangeCommentLaneItem } from "./chunks/create-super-doc-ui-Dc8dQAMc.es.js";
4
+ import { C as createInteractionHistory, E as recordInteraction, S as closeInteractionHistory, T as isWorkflowRecordingEnabled, _ as isAllowed, a as registerFirstPartyCommandMutation, b as isRecord, c as applyV2CommentInvalidationFromMutation, d as createImageFilePicker, f as resolveImageSrc, g as createTrackedChangePermissionResolver, h as PERMISSIONS, i as readFirstPartyPageNumbers, l as getV2TrackedChangeMutationImpact, m as BUILT_IN_PERMISSION_METHOD, n as executeFirstPartyCommandAsync, r as executeFirstPartyShortcutAsync, s as getCommandDescriptor, t as createSuperDocUI, u as mutationCommittedEventChangedDocument, v as isSyntheticTrackedChangeCommentLaneItem, w as getWorkflowRecording, x as DOM_CLASS_NAMES, y as isStoryLocator } from "./chunks/create-super-doc-ui-CbEDXtpm.es.js";
5
5
  import { t as import_eventemitter3 } from "./chunks/eventemitter3-DEIiXiH2.es.js";
6
6
  import { t as v4 } from "./chunks/uuid-H0Xcmmhy.es.js";
7
7
  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";
@@ -258,7 +258,7 @@ var v_click_outside_default = {
258
258
  var DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
259
259
  function getSuperdocVersion() {
260
260
  try {
261
- return "2.29.0";
261
+ return "2.30.0-next.4";
262
262
  } catch {
263
263
  return "unknown";
264
264
  }
@@ -5838,6 +5838,11 @@ function useCheckboxField(field) {
5838
5838
  }) : itemoptions) };
5839
5839
  }
5840
5840
  //#endregion
5841
+ //#region src/core/collaboration/collaboration-comment-map.ts
5842
+ function createCollaborationCommentMap(entries) {
5843
+ return new Map$1(entries);
5844
+ }
5845
+ //#endregion
5841
5846
  //#region src/core/collaboration/collaboration-comments.ts
5842
5847
  var readComment = (event) => {
5843
5848
  const { comment } = event;
@@ -5846,14 +5851,14 @@ var readComment = (event) => {
5846
5851
  };
5847
5852
  var addYComment = (yArray, ydoc, event, user) => {
5848
5853
  const comment = readComment(event);
5849
- const yComment = new Map$1(Object.entries(comment));
5854
+ const yComment = createCollaborationCommentMap(Object.entries(comment));
5850
5855
  ydoc.transact(() => {
5851
5856
  yArray.push([yComment]);
5852
5857
  }, { user });
5853
5858
  };
5854
5859
  var updateYComment = (yArray, ydoc, event, user) => {
5855
5860
  const comment = readComment(event);
5856
- const yComment = new Map$1(Object.entries(comment));
5861
+ const yComment = createCollaborationCommentMap(Object.entries(comment));
5857
5862
  const commentIndex = getCommentIndex(yArray, comment);
5858
5863
  if (commentIndex === -1) return;
5859
5864
  ydoc.transact(() => {
@@ -6750,6 +6755,124 @@ var useSuperdocStore = defineStore("superdoc", () => {
6750
6755
  };
6751
6756
  });
6752
6757
  //#endregion
6758
+ //#region ../document-api/src/errors.ts
6759
+ /**
6760
+ * Structured validation error thrown by document-api execute* functions.
6761
+ *
6762
+ * Consumers should prefer checking `error.code` over `instanceof` for resilience
6763
+ * across package boundaries and bundling scenarios.
6764
+ */
6765
+ var DocumentApiValidationError = class DocumentApiValidationError extends Error {
6766
+ constructor(code, message, details) {
6767
+ super(message);
6768
+ this.name = "DocumentApiValidationError";
6769
+ this.code = code;
6770
+ this.details = details;
6771
+ Object.setPrototypeOf(this, DocumentApiValidationError.prototype);
6772
+ }
6773
+ };
6774
+ //#endregion
6775
+ //#region ../document-api/src/export/export.types.ts
6776
+ /** Canonical tracked-change export modes accepted by every public export surface. */
6777
+ var SD_EXPORT_MODES = [
6778
+ "review-preserving",
6779
+ "final",
6780
+ "original"
6781
+ ];
6782
+ /** Default mode for modeful DOCX export. */
6783
+ var DEFAULT_SD_EXPORT_MODE = "review-preserving";
6784
+ var SD_COMMENT_EXPORT_POLICIES = [
6785
+ "preserve",
6786
+ "resolve",
6787
+ "remove"
6788
+ ];
6789
+ var SD_CONTENT_CONTROL_EXPORT_POLICIES = ["preserve", "flatten"];
6790
+ //#endregion
6791
+ //#region ../document-api/src/export/export.ts
6792
+ /**
6793
+ * Export namespace: engine-agnostic public API and adapter contract.
6794
+ *
6795
+ * Validates the requested mode at the public boundary so an invalid mode fails
6796
+ * closed with `INVALID_INPUT` before any document work happens, then delegates
6797
+ * byte production and degradation reporting to the engine adapter.
6798
+ */
6799
+ function resolveMode$1(input) {
6800
+ if (input != null && !isRecord(input)) throw new DocumentApiValidationError("INVALID_INPUT", "export.toDocx input must be an object.");
6801
+ const raw = input?.mode;
6802
+ if (raw === void 0) return DEFAULT_SD_EXPORT_MODE;
6803
+ if (typeof raw !== "string" || !SD_EXPORT_MODES.includes(raw)) throw new DocumentApiValidationError("INVALID_INPUT", `export.toDocx mode must be one of: ${SD_EXPORT_MODES.join(", ")}.`, {
6804
+ mode: raw,
6805
+ allowedModes: [...SD_EXPORT_MODES]
6806
+ });
6807
+ return raw;
6808
+ }
6809
+ function resolveDocxExportPolicies(input = {}) {
6810
+ if (!isRecord(input)) throw new DocumentApiValidationError("INVALID_INPUT", "DOCX export input must be an object.");
6811
+ const mode = resolveMode$1(input);
6812
+ const policies = input.policies;
6813
+ if (policies !== void 0 && !isRecord(policies)) throw new DocumentApiValidationError("INVALID_INPUT", "DOCX export policies must be an object.");
6814
+ const allowed = {
6815
+ trackedChanges: SD_EXPORT_MODES,
6816
+ comments: SD_COMMENT_EXPORT_POLICIES,
6817
+ contentControls: SD_CONTENT_CONTROL_EXPORT_POLICIES
6818
+ };
6819
+ for (const [key, value] of Object.entries(policies ?? {})) if (!Object.prototype.hasOwnProperty.call(allowed, key) || value !== void 0 && !allowed[key].includes(value)) throw new DocumentApiValidationError("INVALID_INPUT", `Unsupported DOCX export policy: ${key}.`, {
6820
+ policy: key,
6821
+ value
6822
+ });
6823
+ const requested = policies;
6824
+ if (input.mode !== void 0 && requested?.trackedChanges !== void 0 && mode !== requested.trackedChanges) throw new DocumentApiValidationError("INVALID_INPUT", "mode conflicts with policies.trackedChanges.");
6825
+ return {
6826
+ trackedChanges: requested?.trackedChanges ?? mode,
6827
+ comments: requested?.comments ?? "preserve",
6828
+ contentControls: requested?.contentControls ?? "preserve"
6829
+ };
6830
+ }
6831
+ //#endregion
6832
+ //#region ../document-api/src/permission-ranges/permission-ranges.ts
6833
+ function editorKey(editor) {
6834
+ return editor.id?.trim() ? editor.id : editor.email?.trim() || void 0;
6835
+ }
6836
+ function editorMatches(editor, id) {
6837
+ return Boolean(id.trim()) && (editor.id === id || Boolean(editor.email?.trim() && editor.email.trim().toLowerCase() === id.toLowerCase()));
6838
+ }
6839
+ /**
6840
+ * Resolve a host ID or imported Word email against an explicitly supplied roster.
6841
+ * Word receives the email when available; opaque IDs remain host-specific.
6842
+ * Duplicate IDs/emails are ambiguous. This does not authenticate a user.
6843
+ */
6844
+ function resolvePermissionRangeEditor(editors, id) {
6845
+ const matches = editors.filter((editor) => editorMatches(editor, id));
6846
+ if (matches.length === 0) return {
6847
+ status: "unknown",
6848
+ id
6849
+ };
6850
+ if (matches.length !== 1) return {
6851
+ status: "ambiguous",
6852
+ id
6853
+ };
6854
+ const editor = matches[0];
6855
+ const wordId = editor.email?.trim() || editor.id;
6856
+ if (editors.filter((candidate) => editorMatches(candidate, wordId)).length !== 1) return {
6857
+ status: "ambiguous",
6858
+ id
6859
+ };
6860
+ return {
6861
+ status: "resolved",
6862
+ id,
6863
+ editor: { ...editor },
6864
+ principal: {
6865
+ kind: "editor",
6866
+ id: wordId
6867
+ }
6868
+ };
6869
+ }
6870
+ /** Enumerate the host roster for an editor picker, including ambiguous entries. */
6871
+ function listPermissionRangeEditors(editors) {
6872
+ const ids = new Set(editors.map(editorKey).filter((id) => id !== void 0));
6873
+ return Array.from(ids, (id) => resolvePermissionRangeEditor(editors, id));
6874
+ }
6875
+ //#endregion
6753
6876
  //#region src/stores/helpers/floating-comment-instances.ts
6754
6877
  var HEADER_FOOTER_TRACKED_CHANGE_STORY_TYPE = "headerFooterPart";
6755
6878
  var toFinitePageIndex = (value) => {
@@ -7111,6 +7234,7 @@ var groupCommittedComments = (comments, presentations, isVisible) => {
7111
7234
  });
7112
7235
  const grouped = groupComments(comments.filter((row) => !presentations[String(row.fileId)]), isVisible);
7113
7236
  for (const [documentId, presentation] of Object.entries(presentations)) {
7237
+ if (!presentation) continue;
7114
7238
  grouped.parentComments.push(...visibleRowsInOrder(documentId, presentation.groupOrder.open));
7115
7239
  grouped.resolvedComments.push(...visibleRowsInOrder(documentId, presentation.groupOrder.resolved));
7116
7240
  }
@@ -7806,9 +7930,55 @@ var createDroppedCommentIdMap = (duplicates) => {
7806
7930
  return droppedCommentIds;
7807
7931
  };
7808
7932
  //#endregion
7809
- //#region src/stores/comments-store.js
7933
+ //#region src/stores/helpers/v2-review-adapters.ts
7934
+ var COMMENTS_ADAPTER_METHODS = [
7935
+ "setActiveComment",
7936
+ "commitPendingComment",
7937
+ "reply",
7938
+ "edit",
7939
+ "resolve",
7940
+ "reopen",
7941
+ "delete",
7942
+ "mapV2CommentToUseCommentInput"
7943
+ ];
7944
+ var COMMENTS_ADAPTER_OPTIONAL_METHODS = [
7945
+ "getCapabilityState",
7946
+ "captureCurrentSelection",
7947
+ "isPendingCommentCreation",
7948
+ "seedReviewCatalog",
7949
+ "selectVisibleReviewComments"
7950
+ ];
7951
+ var TRACKED_CHANGES_ADAPTER_METHODS = [
7952
+ "accept",
7953
+ "reject",
7954
+ "mapV2TrackedChangeToCommentParams"
7955
+ ];
7956
+ var TRACKED_CHANGES_ADAPTER_OPTIONAL_METHODS = ["clearActiveTrackedChangeTargetIfMatches"];
7957
+ var isAdapterDocumentId = (value) => value === void 0 || value === null || typeof value === "string" || typeof value === "number";
7958
+ var hasAdapterShape = (value, required, optional) => isPlainObject$1(value) && isAdapterDocumentId(value.documentId) && required.every((member) => typeof value[member] === "function") && optional.every((member) => value[member] === void 0 || typeof value[member] === "function");
7959
+ function isV2CommentsAdapter(value) {
7960
+ return hasAdapterShape(value, COMMENTS_ADAPTER_METHODS, COMMENTS_ADAPTER_OPTIONAL_METHODS);
7961
+ }
7962
+ function isV2TrackedChangesAdapter(value) {
7963
+ return hasAdapterShape(value, TRACKED_CHANGES_ADAPTER_METHODS, TRACKED_CHANGES_ADAPTER_OPTIONAL_METHODS);
7964
+ }
7965
+ //#endregion
7966
+ //#region src/stores/comments-store.ts
7967
+ var requireHost = (superdoc, action) => {
7968
+ if (!superdoc) throw new TypeError(`${action} requires superdoc`);
7969
+ return superdoc;
7970
+ };
7971
+ var PARENT_LINK_FIELDS = [
7972
+ "trackedChangeThreadParentId",
7973
+ "trackedChangeParentId",
7974
+ "threadingParentCommentId",
7975
+ "parentCommentId"
7976
+ ];
7977
+ var isPresent = (value) => Boolean(value);
7978
+ var errorDetail = (error) => (error == null ? void 0 : Reflect.get(Object(error), "message")) ?? String(error);
7810
7979
  var useCommentsStore = defineStore("comments", () => {
7811
7980
  const superdocStore = useSuperdocStore();
7981
+ const findDocument = (documentId) => typeof documentId === "string" ? superdocStore.getDocument(documentId) : void 0;
7812
7982
  const commentsConfig = reactive({
7813
7983
  name: "comments",
7814
7984
  readOnly: false,
@@ -7875,7 +8045,7 @@ var useCommentsStore = defineStore("comments", () => {
7875
8045
  });
7876
8046
  const getTrackedChangeThread = (parentComment) => {
7877
8047
  const id = parentComment?.commentId ?? null;
7878
- return (id != null ? trackedChangeThreadIndex.value.get(id) : null) ?? [parentComment].filter(Boolean);
8048
+ return (id != null ? trackedChangeThreadIndex.value.get(id) : null) ?? (parentComment ? [parentComment] : []);
7879
8049
  };
7880
8050
  let priorTrackedChangeDecisionLinkIndex = /* @__PURE__ */ new Map();
7881
8051
  const trackedChangeDecisionLinkIndex = computed$1(() => {
@@ -7907,29 +8077,27 @@ var useCommentsStore = defineStore("comments", () => {
7907
8077
  });
7908
8078
  const v2CommentsAdapter = shallowRef$1(null);
7909
8079
  const setV2CommentsAdapter = (adapter) => {
7910
- if (v2CommentsAdapter.value !== (adapter ?? null)) pendingCommentRequest += 1;
7911
- v2CommentsAdapter.value = adapter ?? null;
8080
+ const next = isV2CommentsAdapter(adapter) ? adapter : null;
8081
+ if (v2CommentsAdapter.value !== next) pendingCommentRequest += 1;
8082
+ v2CommentsAdapter.value = next;
7912
8083
  };
7913
8084
  const getV2CommentsAdapter = (superdoc) => {
7914
- const fromFacade = superdoc?.activeEditor?.v2Comments ?? null;
7915
- if (fromFacade) return fromFacade;
8085
+ const fromFacade = superdoc?.activeEditor?.v2Comments;
8086
+ if (isV2CommentsAdapter(fromFacade)) return fromFacade;
7916
8087
  return v2CommentsAdapter.value;
7917
8088
  };
7918
8089
  const isV2EditorActive = (superdoc) => superdoc?.activeEditor?.editorVersion === 2 || v2CommentsAdapter.value !== null;
7919
8090
  const v2TrackedChangesAdapter = shallowRef$1(null);
7920
8091
  const setV2TrackedChangesAdapter = (adapter) => {
7921
- v2TrackedChangesAdapter.value = adapter ?? null;
8092
+ v2TrackedChangesAdapter.value = isV2TrackedChangesAdapter(adapter) ? adapter : null;
7922
8093
  };
7923
8094
  const getV2TrackedChangesAdapter = (superdoc) => {
7924
- const fromFacade = superdoc?.activeEditor?.v2TrackedChanges ?? null;
7925
- if (fromFacade) return fromFacade;
8095
+ const fromFacade = superdoc?.activeEditor?.v2TrackedChanges;
8096
+ if (isV2TrackedChangesAdapter(fromFacade)) return fromFacade;
7926
8097
  return v2TrackedChangesAdapter.value;
7927
8098
  };
7928
8099
  /**
7929
8100
  * Initialize the store
7930
- *
7931
- * @param {Object} config The comments module config from SuperDoc
7932
- * @returns {void}
7933
8101
  */
7934
8102
  const init = (config = {}) => {
7935
8103
  const updatedConfig = {
@@ -7961,12 +8129,17 @@ var useCommentsStore = defineStore("comments", () => {
7961
8129
  comment.trackedChangeDecision = null;
7962
8130
  };
7963
8131
  const getCommentEventPayload = (comment) => typeof comment?.getValues === "function" ? comment.getValues() : { ...comment };
8132
+ const getDeletedCommentChanges = (sources, fileId) => {
8133
+ const commentId = [...sources.map((source) => source?.commentId), ...sources.map((source) => source?.importedId)].map(normalizeCommentId).find(Boolean);
8134
+ return commentId ? [{
8135
+ key: "deleted",
8136
+ commentId,
8137
+ fileId
8138
+ }] : [];
8139
+ };
7964
8140
  /**
7965
8141
  * Check if a comment originated from the document editor (or has no explicit source).
7966
8142
  * Comments without a source are assumed to be editor-backed for backward compatibility.
7967
- *
7968
- * @param {Object} comment - The comment to check
7969
- * @returns {boolean} True if the comment is editor-backed
7970
8143
  */
7971
8144
  const isEditorBackedComment = (comment) => {
7972
8145
  const source = comment?.selection?.source;
@@ -8000,9 +8173,6 @@ var useCommentsStore = defineStore("comments", () => {
8000
8173
  }, { deep: false });
8001
8174
  /**
8002
8175
  * Get the editor position data for a comment.
8003
- *
8004
- * @param {Object | string} commentOrId The comment object or comment ID
8005
- * @returns {Object | null} The position data from editorCommentPositions
8006
8176
  */
8007
8177
  const getCommentPosition = (commentOrId) => {
8008
8178
  return resolveCommentPositionEntry(commentOrId).entry ?? null;
@@ -8015,10 +8185,6 @@ var useCommentsStore = defineStore("comments", () => {
8015
8185
  };
8016
8186
  /**
8017
8187
  * Set the active comment or clear all active comments
8018
- *
8019
- * @param {Object | undefined | null} superdoc The SuperDoc instance holding the active editor
8020
- * @param {string | undefined | null} id The comment ID
8021
- * @returns {void}
8022
8188
  */
8023
8189
  const setActiveComment = (superdoc, id) => {
8024
8190
  const activeEditor = superdoc?.activeEditor;
@@ -8029,7 +8195,7 @@ var useCommentsStore = defineStore("comments", () => {
8029
8195
  v2Adapter.setActiveComment(null);
8030
8196
  return;
8031
8197
  }
8032
- activeEditor?.commands?.setActiveComment({ commentId: null });
8198
+ activeEditor?.commands?.setActiveComment?.({ commentId: null });
8033
8199
  return;
8034
8200
  }
8035
8201
  const comment = getComment(id);
@@ -8040,16 +8206,11 @@ var useCommentsStore = defineStore("comments", () => {
8040
8206
  v2Adapter.setActiveComment(comment.commentId);
8041
8207
  return;
8042
8208
  }
8043
- activeEditor?.commands?.setActiveComment({ commentId: activeComment.value });
8209
+ activeEditor?.commands?.setActiveComment?.({ commentId: activeComment.value });
8044
8210
  };
8045
8211
  /**
8046
8212
  * Called when a tracked change is updated. Creates a new comment if necessary,
8047
8213
  * or updates an existing tracked-change comment.
8048
- *
8049
- * @param {Object} param0
8050
- * @param {Object} param0.superdoc The SuperDoc instance
8051
- * @param {Object} param0.params The tracked change params
8052
- * @returns {void}
8053
8214
  */
8054
8215
  const handleTrackedChangeUpdate = ({ superdoc, params, broadcastChanges = true, trackedChangeIdentityIndex = null }) => {
8055
8216
  const span = startInteractionSpan("store.trackedChanges.handleUpdate", "store-reconciliation", {
@@ -8078,7 +8239,7 @@ var useCommentsStore = defineStore("comments", () => {
8078
8239
  const normalizedTrackedChangeImagePreview = normalizeTrackedChangeImagePreview(trackedChangeImagePreview);
8079
8240
  const comment = getPendingComment({
8080
8241
  documentId,
8081
- commentId: changeId,
8242
+ commentId: normalizedChangeId ?? void 0,
8082
8243
  trackedChange: true,
8083
8244
  trackedChangeText,
8084
8245
  trackedChangeType,
@@ -8130,7 +8291,7 @@ var useCommentsStore = defineStore("comments", () => {
8130
8291
  commentCanonicalId,
8131
8292
  commentAnchorKey,
8132
8293
  ...commentAliasIds
8133
- ].filter(Boolean).some((id) => incomingAliasIds.has(id))) return true;
8294
+ ].some((id) => id != null && incomingAliasIds.has(id))) return true;
8134
8295
  const hasSyntheticSideIdentity = normalizedTrackedChangeCanonicalId && normalizedTrackedChangeCanonicalId !== normalizedChangeId || commentCanonicalId && commentCanonicalId !== commentId;
8135
8296
  if (hasCompatibleStory && commentId === normalizedChangeId) return true;
8136
8297
  if (hasCompatibleStory && !hasSyntheticSideIdentity && (importedId === normalizedChangeId || normalizedImportedId !== void 0 && normalizedImportedId != null && importedId === normalizedImportedId)) return true;
@@ -8154,7 +8315,7 @@ var useCommentsStore = defineStore("comments", () => {
8154
8315
  };
8155
8316
  const emitTrackedChangeEvent = (event) => {
8156
8317
  if (!broadcastChanges) return;
8157
- syncCommentsToClients(superdoc, event);
8318
+ syncCommentsToClients(requireHost(superdoc, "handleTrackedChangeUpdate"), event);
8158
8319
  debounceEmit(changeId, event, superdoc);
8159
8320
  };
8160
8321
  const setIfChanged = (target, key, value) => {
@@ -8197,8 +8358,9 @@ var useCommentsStore = defineStore("comments", () => {
8197
8358
  target?.trackedChangeCanonicalId,
8198
8359
  target?.trackedChangeAnchorKey,
8199
8360
  ...getCommentAliasIds(target)
8200
- ].map((id) => normalizeCommentId(id)).filter(Boolean));
8201
- fields.trackedChangePositionAliases = normalizedTrackedChangePositionAliases.some((alias) => targetIdentityIds.has(alias)) ? normalizeTrackedChangePositionAliases([...Array.isArray(target?.trackedChangePositionAliases) ? target.trackedChangePositionAliases : [], ...normalizedTrackedChangePositionAliases]) : normalizedTrackedChangePositionAliases;
8361
+ ].map((id) => normalizeCommentId(id)).filter(isPresent));
8362
+ const incomingAliases = normalizedTrackedChangePositionAliases ?? [];
8363
+ fields.trackedChangePositionAliases = incomingAliases.some((alias) => targetIdentityIds.has(alias)) ? normalizeTrackedChangePositionAliases([...Array.isArray(target?.trackedChangePositionAliases) ? target.trackedChangePositionAliases : [], ...incomingAliases]) : incomingAliases;
8202
8364
  }
8203
8365
  let didChange = false;
8204
8366
  for (const [key, value] of Object.entries(fields)) didChange = setIfChanged(target, key, value) || didChange;
@@ -8207,7 +8369,7 @@ var useCommentsStore = defineStore("comments", () => {
8207
8369
  didChange = true;
8208
8370
  }
8209
8371
  if (hasCustomAttributes && target && !trackedChangeCustomAttributesEqual(target.customAttributes, normalizedCustomAttributes)) {
8210
- target.customAttributes = normalizedCustomAttributes;
8372
+ target.customAttributes = normalizedCustomAttributes ?? null;
8211
8373
  didChange = true;
8212
8374
  }
8213
8375
  return applyStoryMetadata(target) || didChange;
@@ -8229,7 +8391,7 @@ var useCommentsStore = defineStore("comments", () => {
8229
8391
  return;
8230
8392
  }
8231
8393
  addHydratedComment({
8232
- superdoc,
8394
+ superdoc: requireHost(superdoc, "handleTrackedChangeUpdate"),
8233
8395
  comment,
8234
8396
  broadcastChanges
8235
8397
  });
@@ -8249,11 +8411,12 @@ var useCommentsStore = defineStore("comments", () => {
8249
8411
  id: params.resolvedById ?? superdoc?.user?.id ?? null,
8250
8412
  email: params.resolvedByEmail ?? superdoc?.user?.email ?? null,
8251
8413
  name: params.resolvedByName ?? superdoc?.user?.name ?? null,
8252
- decision: params.decision ?? null,
8414
+ decision: params.decision ?? void 0,
8253
8415
  superdoc
8254
8416
  };
8255
8417
  if (existingTrackedChange && !existingTrackedChange.resolvedTime) existingTrackedChange.resolveComment({
8256
8418
  ...resolveArgs,
8419
+ superdoc: requireHost(superdoc, "handleTrackedChangeUpdate"),
8257
8420
  reconciliationToken: COMMENT_RECONCILIATION_TOKEN
8258
8421
  });
8259
8422
  }
@@ -8278,10 +8441,11 @@ var useCommentsStore = defineStore("comments", () => {
8278
8441
  instantSidebarAlignmentInstanceId.value = null;
8279
8442
  };
8280
8443
  const debounceEmit = (commentId, event, superdoc, delay = 1e3) => {
8281
- if (debounceTimers[commentId]) clearTimeout(debounceTimers[commentId]);
8282
- debounceTimers[commentId] = setTimeout(() => {
8444
+ const timerKey = String(commentId);
8445
+ if (debounceTimers[timerKey]) clearTimeout(debounceTimers[timerKey]);
8446
+ debounceTimers[timerKey] = setTimeout(() => {
8283
8447
  if (superdoc) superdoc.emit("comments-update", event);
8284
- delete debounceTimers[commentId];
8448
+ delete debounceTimers[timerKey];
8285
8449
  }, delay);
8286
8450
  };
8287
8451
  const showAddComment = (superdoc, targetClientY = null) => {
@@ -8293,7 +8457,7 @@ var useCommentsStore = defineStore("comments", () => {
8293
8457
  if (!ownsRequest()) return;
8294
8458
  superdoc.emit("comments-update", {
8295
8459
  type: COMMENT_EVENTS.PENDING,
8296
- pendingSelection: superdoc?.activeEditor === editor ? snapshot : null
8460
+ pendingSelection: superdoc?.activeEditor === editor && isPlainObject$1(snapshot) ? snapshot : null
8297
8461
  });
8298
8462
  };
8299
8463
  if (v2Adapter) {
@@ -8338,12 +8502,11 @@ var useCommentsStore = defineStore("comments", () => {
8338
8502
  }).catch((err) => ({
8339
8503
  ok: false,
8340
8504
  reason: "selection-capture-failed",
8341
- detail: err?.message ?? String(err)
8505
+ detail: errorDetail(err)
8342
8506
  }));
8343
8507
  }
8344
8508
  const pendingSelection = getPendingSelectionSnapshot(superdoc, superdocStore.activeSelection);
8345
- const asyncSnapshot = typeof pendingSelection?.then === "function";
8346
- if (!asyncSnapshot) emitPending(pendingSelection);
8509
+ if (!isThenable$1(pendingSelection)) emitPending(pendingSelection);
8347
8510
  const showLegacyPending = (selection) => {
8348
8511
  selection.selectionBounds = { ...selection.selectionBounds };
8349
8512
  if (superdocStore.selectionPosition?.source && superdocStore.selectionPosition.source !== "pdf") superdocStore.selectionPosition.source = null;
@@ -8353,7 +8516,7 @@ var useCommentsStore = defineStore("comments", () => {
8353
8516
  parentCommentId: null
8354
8517
  });
8355
8518
  if (!superdoc.config.isInternal) pendingComment.value.isInternal = false;
8356
- if (superdoc.activeEditor?.commands) superdoc.activeEditor.commands.insertComment({
8519
+ if (superdoc.activeEditor?.commands) superdoc.activeEditor.commands.insertComment?.({
8357
8520
  ...pendingComment.value.getValues(),
8358
8521
  commentId: "pending",
8359
8522
  skipEmit: true
@@ -8364,10 +8527,10 @@ var useCommentsStore = defineStore("comments", () => {
8364
8527
  activeComment.value = pendingComment.value.commentId;
8365
8528
  return { ok: true };
8366
8529
  };
8367
- if (asyncSnapshot) {
8530
+ if (isThenable$1(pendingSelection)) {
8368
8531
  const selection = { ...superdocStore.activeSelection };
8369
8532
  selection.selectionBounds = { ...selection.selectionBounds };
8370
- return pendingSelection.then((snapshot) => {
8533
+ return Promise.resolve(pendingSelection).then((snapshot) => {
8371
8534
  if (!ownsRequest()) return {
8372
8535
  ok: false,
8373
8536
  reason: "pending-comment-superseded"
@@ -8399,12 +8562,6 @@ var useCommentsStore = defineStore("comments", () => {
8399
8562
  });
8400
8563
  /**
8401
8564
  * Get a new pending comment
8402
- *
8403
- * @param {Object} param0
8404
- * @param {Object} [param0.selection] The selection object
8405
- * @param {string | null} [param0.documentId] The document ID
8406
- * @param {string | null} [param0.parentCommentId] The parent comment
8407
- * @returns {ReturnType<typeof useComment>} The new comment object
8408
8565
  */
8409
8566
  const getPendingComment = ({ selection, documentId, parentCommentId, ...options }) => {
8410
8567
  return _getNewcomment({
@@ -8416,17 +8573,11 @@ var useCommentsStore = defineStore("comments", () => {
8416
8573
  };
8417
8574
  /**
8418
8575
  * Get the new comment object
8419
- *
8420
- * @param {Object} param0
8421
- * @param {Object} param0.selection The selection object
8422
- * @param {String} param0.documentId The document ID
8423
- * @param {String} param0.parentCommentId The parent comment ID
8424
- * @returns {Object} The new comment object
8425
8576
  */
8426
8577
  const _getNewcomment = ({ selection, documentId, parentCommentId, ...options }) => {
8427
8578
  let activeDocument;
8428
- if (documentId) activeDocument = superdocStore.getDocument(documentId);
8429
- else if (selection) activeDocument = superdocStore.getDocument(selection.documentId);
8579
+ if (documentId) activeDocument = findDocument(documentId);
8580
+ else if (selection) activeDocument = findDocument(selection.documentId);
8430
8581
  if (!activeDocument) activeDocument = superdocStore.documents[0];
8431
8582
  return useComment({
8432
8583
  fileId: activeDocument.id,
@@ -8443,8 +8594,6 @@ var useCommentsStore = defineStore("comments", () => {
8443
8594
  };
8444
8595
  /**
8445
8596
  * Remove the pending comment
8446
- *
8447
- * @returns {void}
8448
8597
  */
8449
8598
  const resetPendingCommentState = (superdoc, clearViewingSelection = true) => {
8450
8599
  const hadPending = !!pendingComment.value;
@@ -8460,7 +8609,7 @@ var useCommentsStore = defineStore("comments", () => {
8460
8609
  } catch {}
8461
8610
  return;
8462
8611
  }
8463
- superdoc?.activeEditor?.commands?.removeComment({ commentId: "pending" });
8612
+ superdoc?.activeEditor?.commands?.removeComment?.({ commentId: "pending" });
8464
8613
  };
8465
8614
  const removePendingComment = (superdoc) => {
8466
8615
  pendingCommentRequest += 1;
@@ -8473,14 +8622,6 @@ var useCommentsStore = defineStore("comments", () => {
8473
8622
  };
8474
8623
  /**
8475
8624
  * Add a new comment to the document
8476
- *
8477
- * @param {Object} param0
8478
- * @param {Object} param0.superdoc The SuperDoc instance
8479
- * @param {ReturnType<typeof useComment>} param0.comment The comment to add
8480
- * @param {boolean} [param0.skipEditorUpdate] Skip writing the comment into the editor
8481
- * @param {boolean} [param0.broadcastChanges] Emit and sync the change
8482
- * @param {boolean} [param0.isHydration] The comment comes from import or collaboration
8483
- * @returns {{ ok: boolean, reason?: string, detail?: string, code?: string } | Promise<{ ok: boolean, reason?: string, detail?: string, code?: string }>} The mutation outcome
8484
8625
  */
8485
8626
  const addCommentInternal = ({ superdoc, comment, skipEditorUpdate = false, broadcastChanges = true, isHydration = false }) => {
8486
8627
  if (commentsAreReadOnly() && !isHydration) return readOnlyMutationOutcome();
@@ -8510,7 +8651,7 @@ var useCommentsStore = defineStore("comments", () => {
8510
8651
  return {
8511
8652
  ok: false,
8512
8653
  reason: "adapter-threw",
8513
- detail: err?.message ?? String(err)
8654
+ detail: errorDetail(err)
8514
8655
  };
8515
8656
  }
8516
8657
  })().then((outcome) => {
@@ -8582,7 +8723,7 @@ var useCommentsStore = defineStore("comments", () => {
8582
8723
  activeComment.value = newComment.commentId;
8583
8724
  setActiveFloatingCommentInstance(newComment.commentId);
8584
8725
  }
8585
- if (!skipEditorUpdate && !comment.trackedChange && superdoc.activeEditor?.commands && !comment.parentCommentId) superdoc.activeEditor.commands.insertComment({
8726
+ if (!skipEditorUpdate && !comment.trackedChange && superdoc.activeEditor?.commands && !comment.parentCommentId) superdoc.activeEditor.commands.insertComment?.({
8586
8727
  ...newComment.getValues(),
8587
8728
  skipEmit: true
8588
8729
  });
@@ -8630,7 +8771,7 @@ var useCommentsStore = defineStore("comments", () => {
8630
8771
  return {
8631
8772
  ok: false,
8632
8773
  reason: "adapter-threw",
8633
- detail: err?.message ?? String(err)
8774
+ detail: errorDetail(err)
8634
8775
  };
8635
8776
  }
8636
8777
  })().then((outcome) => {
@@ -8650,16 +8791,16 @@ var useCommentsStore = defineStore("comments", () => {
8650
8791
  });
8651
8792
  return outcome;
8652
8793
  }
8653
- const removedIds = new Set((Array.isArray(outcome.removedCommentIds) ? outcome.removedCommentIds : []).map(normalizeCommentId).filter(Boolean));
8654
- if (removedIds.has(normalizeCommentId(commentId))) {
8655
- if (importedId != null) removedIds.add(normalizeCommentId(importedId));
8794
+ const removedIds = new Set((Array.isArray(outcome.removedCommentIds) ? outcome.removedCommentIds : []).map(normalizeCommentId).filter(isPresent));
8795
+ if (removedIds.has(normalizeCommentId(commentId) ?? "")) {
8796
+ if (importedId != null) removedIds.add(String(importedId));
8656
8797
  let addedChild = true;
8657
8798
  while (addedChild) {
8658
8799
  addedChild = false;
8659
8800
  for (const row of commentsList.value) {
8660
8801
  const parentId = normalizeCommentId(row?.parentCommentId);
8661
8802
  if (!parentId || !removedIds.has(parentId)) continue;
8662
- for (const alias of [row?.commentId, row?.importedId].map(normalizeCommentId).filter(Boolean)) {
8803
+ for (const alias of [row?.commentId, row?.importedId].map(normalizeCommentId).filter(isPresent)) {
8663
8804
  if (removedIds.has(alias)) continue;
8664
8805
  removedIds.add(alias);
8665
8806
  addedChild = true;
@@ -8667,9 +8808,9 @@ var useCommentsStore = defineStore("comments", () => {
8667
8808
  }
8668
8809
  }
8669
8810
  commentsList.value = commentsList.value.filter((row) => {
8670
- return ![row?.commentId, row?.importedId].map(normalizeCommentId).filter(Boolean).some((alias) => removedIds.has(alias));
8811
+ return ![row?.commentId, row?.importedId].map(normalizeCommentId).filter(isPresent).some((alias) => removedIds.has(alias));
8671
8812
  });
8672
- if (removedIds.has(normalizeCommentId(activeComment.value))) clearActiveCommentSelection();
8813
+ if (removedIds.has(normalizeCommentId(activeComment.value) ?? "")) clearActiveCommentSelection();
8673
8814
  } else reconcileCommentsFromV2({
8674
8815
  superdoc,
8675
8816
  adapter: v2Adapter,
@@ -8677,18 +8818,18 @@ var useCommentsStore = defineStore("comments", () => {
8677
8818
  items: outcome.items ?? [],
8678
8819
  pruneStale: outcome.complete !== false
8679
8820
  });
8821
+ const payload = comment.getValues();
8680
8822
  superdoc.emit("comments-update", {
8681
8823
  type: COMMENT_EVENTS.DELETED,
8682
- comment: comment.getValues(),
8683
- changes: [{
8684
- key: "deleted",
8824
+ comment: payload,
8825
+ changes: getDeletedCommentChanges([{
8685
8826
  commentId,
8686
- fileId
8687
- }]
8827
+ importedId
8828
+ }, payload], fileId)
8688
8829
  });
8689
8830
  return { ok: true };
8690
8831
  });
8691
- superdoc.activeEditor?.commands?.removeComment({
8832
+ superdoc.activeEditor?.commands?.removeComment?.({
8692
8833
  commentId,
8693
8834
  importedId
8694
8835
  });
@@ -8696,14 +8837,14 @@ var useCommentsStore = defineStore("comments", () => {
8696
8837
  const childCommentIds = commentsList.value.filter((c) => c.parentCommentId === commentId).map((c) => c.commentId || c.importedId);
8697
8838
  commentsList.value = commentsList.value.filter((c) => !childCommentIds.includes(c.commentId));
8698
8839
  if (activeComment.value === commentId || childCommentIds.includes(activeComment.value)) clearActiveCommentSelection();
8840
+ const payload = comment.getValues();
8699
8841
  const event = {
8700
8842
  type: COMMENT_EVENTS.DELETED,
8701
- comment: comment.getValues(),
8702
- changes: [{
8703
- key: "deleted",
8843
+ comment: payload,
8844
+ changes: getDeletedCommentChanges([{
8704
8845
  commentId,
8705
- fileId
8706
- }]
8846
+ importedId
8847
+ }, payload], fileId)
8707
8848
  };
8708
8849
  superdoc.emit("comments-update", event);
8709
8850
  syncCommentsToClients(superdoc, event);
@@ -8715,7 +8856,7 @@ var useCommentsStore = defineStore("comments", () => {
8715
8856
  * otherwise keep the source bubble after the document comment is gone.
8716
8857
  */
8717
8858
  const dropCommentsFromMutationImpact = ({ superdoc, documentId, removedIds } = {}) => {
8718
- const seedIds = new Set([...removedIds instanceof Set ? removedIds : removedIds ?? []].map((id) => normalizeCommentId(id)).filter(Boolean));
8859
+ const seedIds = new Set([...removedIds instanceof Set ? removedIds : removedIds ?? []].map((id) => normalizeCommentId(id)).filter(isPresent));
8719
8860
  if (!seedIds.size) return {
8720
8861
  ok: true,
8721
8862
  removedIds: []
@@ -8739,7 +8880,7 @@ var useCommentsStore = defineStore("comments", () => {
8739
8880
  if (!isDroppableCommentRow(row)) continue;
8740
8881
  const aliases = getCommentAliasIds(row);
8741
8882
  const alreadyDropped = aliases.some((alias) => droppedIds.has(alias));
8742
- const parentDropped = droppedIds.has(normalizeCommentId(row?.parentCommentId)) || droppedIds.has(normalizeCommentId(row?.threadingParentCommentId));
8883
+ const parentDropped = droppedIds.has(normalizeCommentId(row?.parentCommentId) ?? "") || droppedIds.has(normalizeCommentId(row?.threadingParentCommentId) ?? "");
8743
8884
  if (!alreadyDropped && !parentDropped) continue;
8744
8885
  for (const alias of aliases) {
8745
8886
  if (droppedIds.has(alias)) continue;
@@ -8767,11 +8908,7 @@ var useCommentsStore = defineStore("comments", () => {
8767
8908
  const event = {
8768
8909
  type: COMMENT_EVENTS.DELETED,
8769
8910
  comment: payload,
8770
- changes: [{
8771
- key: "deleted",
8772
- commentId: payload?.commentId,
8773
- fileId: payload?.fileId
8774
- }]
8911
+ changes: getDeletedCommentChanges([payload, comment], payload?.fileId)
8775
8912
  };
8776
8913
  superdoc?.emit?.("comments-update", event);
8777
8914
  });
@@ -8793,7 +8930,7 @@ var useCommentsStore = defineStore("comments", () => {
8793
8930
  const byAlias = /* @__PURE__ */ new Map();
8794
8931
  const childrenByParentAlias = /* @__PURE__ */ new Map();
8795
8932
  for (const row of rows) {
8796
- for (const alias of [row.commentId, row.importedId].map(normalizeCommentId).filter(Boolean)) if (!byAlias.has(alias)) byAlias.set(alias, row);
8933
+ for (const alias of [row.commentId, row.importedId].map(normalizeCommentId).filter(isPresent)) if (!byAlias.has(alias)) byAlias.set(alias, row);
8797
8934
  const parentId = normalizeCommentId(row.parentCommentId);
8798
8935
  if (parentId) {
8799
8936
  const children = childrenByParentAlias.get(parentId) ?? [];
@@ -8820,7 +8957,7 @@ var useCommentsStore = defineStore("comments", () => {
8820
8957
  if (!row || visited.has(row)) continue;
8821
8958
  visited.add(row);
8822
8959
  family.push(row);
8823
- for (const alias of [row.commentId, row.importedId].map(normalizeCommentId).filter(Boolean)) queue.push(...childrenByParentAlias.get(alias) ?? []);
8960
+ for (const alias of [row.commentId, row.importedId].map(normalizeCommentId).filter(isPresent)) queue.push(...childrenByParentAlias.get(alias) ?? []);
8824
8961
  }
8825
8962
  const isResolved = status === "resolved";
8826
8963
  const resolvedAt = Date.now();
@@ -8843,7 +8980,7 @@ var useCommentsStore = defineStore("comments", () => {
8843
8980
  outcome = {
8844
8981
  ok: false,
8845
8982
  reason: "adapter-threw",
8846
- detail: err?.message ?? String(err)
8983
+ detail: errorDetail(err)
8847
8984
  };
8848
8985
  }
8849
8986
  if (!isCurrentV2CommentsAdapter(adapter)) return {
@@ -8915,14 +9052,14 @@ var useCommentsStore = defineStore("comments", () => {
8915
9052
  return (outcome.items ?? []).map((item) => adapter.mapV2CommentToUseCommentInput(item, {
8916
9053
  fileId,
8917
9054
  fileType: null
8918
- })).filter(Boolean);
9055
+ })).filter(isPresent);
8919
9056
  };
8920
9057
  const isBlankDirectoryCommentText = (value) => typeof value !== "string" || value.replace(/<[^>]*>/g, "").replace(/&nbsp;/gi, " ").trim().length === 0;
8921
9058
  const trackedChangeDirectoryComment = (params) => {
8922
9059
  if (!params || params.event === "omit" || params.changeId == null) return null;
8923
9060
  return getPendingComment({
8924
9061
  documentId: params.documentId,
8925
- commentId: params.changeId,
9062
+ commentId: String(params.changeId),
8926
9063
  trackedChange: true,
8927
9064
  trackedChangeText: params.trackedChangeText,
8928
9065
  trackedChangeType: params.trackedChangeType,
@@ -8969,7 +9106,7 @@ var useCommentsStore = defineStore("comments", () => {
8969
9106
  const mappedCommentInputs = commentItems.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
8970
9107
  fileId: documentId,
8971
9108
  fileType: document?.type ?? null
8972
- })).filter(Boolean);
9109
+ })).filter(isPresent);
8973
9110
  const trackedParentBySidecarId = /* @__PURE__ */ new Map();
8974
9111
  for (const input of mappedCommentInputs) if (input.trackedChange !== true && input.trackedChangeThreadParentId && !input.parentCommentId && isBlankDirectoryCommentText(input.commentText)) trackedParentBySidecarId.set(String(input.commentId), String(input.trackedChangeThreadParentId));
8975
9112
  const commentRows = mappedCommentInputs.filter((input) => !trackedParentBySidecarId.has(String(input.commentId))).map((input) => {
@@ -8980,7 +9117,7 @@ var useCommentsStore = defineStore("comments", () => {
8980
9117
  }
8981
9118
  return useComment(input);
8982
9119
  });
8983
- const trackedRows = trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).map((params) => trackedChangeDirectoryComment(params)).filter(Boolean);
9120
+ const trackedRows = trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).map((params) => trackedChangeDirectoryComment(params)).filter(isPresent);
8984
9121
  reviewDirectoryList.value = [...commentRows, ...trackedRows];
8985
9122
  return {
8986
9123
  ok: true,
@@ -9061,13 +9198,6 @@ var useCommentsStore = defineStore("comments", () => {
9061
9198
  * - successful mutation refreshes from v2 list and reconciles
9062
9199
  * - rejection preserves rows and emits a rejected `comments-update` event
9063
9200
  * - committed-but-refresh-failed surfaces honestly (no reconcile with `[]`)
9064
- *
9065
- * @param {Object} [param0]
9066
- * @param {Object} [param0.superdoc] The SuperDoc instance.
9067
- * @param {string | null} [param0.parentCommentId] The comment or tracked-change row being replied to.
9068
- * @param {string} [param0.text] The reply text.
9069
- * @param {Array<{ name?: string | null, email?: string | null }>} [param0.mentions] Users mentioned in the reply.
9070
- * @param {{ kind: 'trackedChange', trackedChangeId: string, story?: unknown, side?: import('./helpers/tracked-change-thread-index.js').TrackedChangeSideGroup }} [param0.trackedChangeTarget] The tracked change a first reply anchors to.
9071
9201
  */
9072
9202
  const replyCommentV2 = async ({ superdoc, parentCommentId, text, mentions = [], trackedChangeTarget } = {}) => {
9073
9203
  if (commentsAreReadOnly()) return readOnlyMutationOutcome();
@@ -9342,7 +9472,7 @@ var useCommentsStore = defineStore("comments", () => {
9342
9472
  row?.threadingParentCommentId,
9343
9473
  row?.trackedChangeParentId,
9344
9474
  row?.trackedChangeThreadParentId
9345
- ].map((value) => normalizeCommentId(value)).filter(Boolean));
9475
+ ].map((value) => normalizeCommentId(value)).filter(isPresent));
9346
9476
  /** Keep one mounted review window plus rows whose interaction is still live. */
9347
9477
  const boundReviewPresentationRows = ({ documentId, visibleIds }) => {
9348
9478
  const currentRows = commentsList.value;
@@ -9363,7 +9493,7 @@ var useCommentsStore = defineStore("comments", () => {
9363
9493
  pendingComment.value?.commentId,
9364
9494
  instantSidebarAlignmentThreadId.value,
9365
9495
  instantSidebarAlignmentInstanceId.value
9366
- ].map((value) => normalizeCommentId(value)).filter(Boolean));
9496
+ ].map((value) => normalizeCommentId(value)).filter(isPresent));
9367
9497
  let changed = true;
9368
9498
  while (changed) {
9369
9499
  changed = false;
@@ -9431,7 +9561,8 @@ var useCommentsStore = defineStore("comments", () => {
9431
9561
  if (preparedReview?.reuseCatalog === true && committedReviewPresentations.value[normalizedDocumentId]) {
9432
9562
  const previous = committedReviewPresentations.value[normalizedDocumentId];
9433
9563
  const next = preparedReview.presentation;
9434
- if (!next || !Number.isSafeInteger(preparedReview.catalogVersion) || preparedReview.catalogVersion < 1 || previous.catalogVersion > preparedReview.catalogVersion || next.catalogVersion !== preparedReview.catalogVersion || previous.coordinate?.documentEpoch !== next.coordinate?.documentEpoch || previous.coordinate?.mountId !== next.coordinate?.mountId || next.coordinate?.reviewVersion <= previous.coordinate?.reviewVersion || next.coordinate?.commitSequence < previous.coordinate?.commitSequence) return {
9564
+ const catalogVersion = preparedReview.catalogVersion ?? NaN;
9565
+ if (!next || !Number.isSafeInteger(catalogVersion) || catalogVersion < 1 || (previous.catalogVersion ?? 0) > catalogVersion || next.catalogVersion !== preparedReview.catalogVersion || previous.coordinate?.documentEpoch !== next.coordinate?.documentEpoch || previous.coordinate?.mountId !== next.coordinate?.mountId || next.coordinate?.reviewVersion <= previous.coordinate?.reviewVersion || next.coordinate?.commitSequence < previous.coordinate?.commitSequence) return {
9435
9566
  ok: false,
9436
9567
  reason: "review-catalog-stale"
9437
9568
  };
@@ -9477,8 +9608,8 @@ var useCommentsStore = defineStore("comments", () => {
9477
9608
  previousPresentation.updateContext = presentation.updateContext;
9478
9609
  return {
9479
9610
  ok: true,
9480
- commentItems: preparedReview.comments.length,
9481
- trackedItems: preparedReview.trackedChanges.length
9611
+ commentItems: preparedReview?.comments.length,
9612
+ trackedItems: preparedReview?.trackedChanges.length
9482
9613
  };
9483
9614
  }
9484
9615
  let effectiveCommentItems = commentItems;
@@ -9492,7 +9623,7 @@ var useCommentsStore = defineStore("comments", () => {
9492
9623
  reason: seeded?.reason ?? "comment-catalog-seed-failed"
9493
9624
  };
9494
9625
  const selected = commentsAdapter.selectVisibleReviewComments?.();
9495
- if (selected?.ok === true && Array.isArray(selected.items) && (!Array.isArray(selected.unresolvedIds) || selected.unresolvedIds.length === 0)) effectiveCommentItems = selected.items;
9626
+ if (!isThenable$1(selected) && selected?.ok === true && Array.isArray(selected.items) && (!Array.isArray(selected.unresolvedIds) || selected.unresolvedIds.length === 0)) effectiveCommentItems = selected.items;
9496
9627
  }
9497
9628
  const fileType = (normalizedDocumentId ? superdocStore.getDocument(normalizedDocumentId) : null)?.type ?? null;
9498
9629
  let preparedInputs;
@@ -9509,16 +9640,16 @@ var useCommentsStore = defineStore("comments", () => {
9509
9640
  })) : effectiveCommentItems.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
9510
9641
  fileId: normalizedDocumentId,
9511
9642
  fileType
9512
- })).filter(Boolean);
9643
+ })).filter(isPresent);
9513
9644
  preparedParams = preparedReview ? preparedReview.trackedChanges.map((params) => ({
9514
9645
  ...params,
9515
9646
  documentId: normalizedDocumentId
9516
- })) : trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).filter(Boolean);
9647
+ })) : trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).filter(isPresent);
9517
9648
  } catch (error) {
9518
9649
  return {
9519
9650
  ok: false,
9520
9651
  reason: "mapper-failed",
9521
- detail: error?.message ?? String(error)
9652
+ detail: errorDetail(error)
9522
9653
  };
9523
9654
  }
9524
9655
  if (typeof patch !== "function") return {
@@ -9546,7 +9677,7 @@ var useCommentsStore = defineStore("comments", () => {
9546
9677
  params?.trackedChangeCanonicalId,
9547
9678
  params?.trackedChangeAnchorKey
9548
9679
  ])
9549
- ].map((value) => normalizeCommentId(value)).filter(Boolean));
9680
+ ].map((value) => normalizeCommentId(value)).filter(isPresent));
9550
9681
  patch(() => {
9551
9682
  const previousPreparedPresentation = committedReviewPresentations.value[normalizedDocumentId] ?? null;
9552
9683
  if (preparedReview?.presentation) committedReviewPresentations.value = {
@@ -9594,7 +9725,7 @@ var useCommentsStore = defineStore("comments", () => {
9594
9725
  };
9595
9726
  const effectiveDocumentId = documentId ?? adapter.documentId ?? null;
9596
9727
  const normalizedEffectiveDocumentId = effectiveDocumentId != null ? String(effectiveDocumentId) : null;
9597
- const fileType = (effectiveDocumentId ? superdocStore.getDocument(effectiveDocumentId) : null)?.type ?? null;
9728
+ const fileType = (effectiveDocumentId ? findDocument(effectiveDocumentId) : null)?.type ?? null;
9598
9729
  const isBlankV2CommentText = (value) => {
9599
9730
  if (typeof value !== "string") return true;
9600
9731
  return value.replace(/<[^>]*>/g, "").replace(/&nbsp;/gi, " ").trim().length === 0;
@@ -9614,7 +9745,7 @@ var useCommentsStore = defineStore("comments", () => {
9614
9745
  const candidateInputs = Array.isArray(preparedInputs) ? preparedInputs : items.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => adapter.mapV2CommentToUseCommentInput(item, {
9615
9746
  fileId: effectiveDocumentId,
9616
9747
  fileType
9617
- })).filter(Boolean);
9748
+ })).filter(isPresent);
9618
9749
  for (const input of candidateInputs) {
9619
9750
  if (isV2TrackedChangeSidecarCommentInput(input)) {
9620
9751
  trackedChangeSidecarParentByCommentId.set(String(input.commentId), String(input.trackedChangeThreadParentId));
@@ -9667,7 +9798,7 @@ var useCommentsStore = defineStore("comments", () => {
9667
9798
  existing.mentions = Array.isArray(input.mentions) ? input.mentions : [];
9668
9799
  existing.isInternal = typeof input.isInternal === "boolean" ? input.isInternal : existing.isInternal;
9669
9800
  if (typeof input.resolvedTime === "number") {
9670
- existing.resolvedTime = input.resolvedTimeWasSynthesized === true && typeof existing.resolvedTime === "number" ? existing.resolvedTime : input.resolvedTime;
9801
+ existing.resolvedTime = input.resolvedTimeWasSynthesized === true && typeof existing.resolvedTime === "number" ? existing.resolvedTime : input.resolvedTime ?? null;
9671
9802
  existing.resolvedByEmail = input.resolvedByEmail ?? null;
9672
9803
  existing.resolvedByName = input.resolvedByName ?? null;
9673
9804
  } else {
@@ -9741,7 +9872,7 @@ var useCommentsStore = defineStore("comments", () => {
9741
9872
  return {
9742
9873
  ok: false,
9743
9874
  reason: "comment-read-failed",
9744
- detail: err?.message ?? String(err)
9875
+ detail: errorDetail(err)
9745
9876
  };
9746
9877
  }
9747
9878
  if (!isCurrentV2CommentsAdapter(adapter)) return {
@@ -9767,8 +9898,6 @@ var useCommentsStore = defineStore("comments", () => {
9767
9898
  };
9768
9899
  /**
9769
9900
  * Cancel the pending comment
9770
- *
9771
- * @returns {void}
9772
9901
  */
9773
9902
  const cancelComment = (superdoc) => {
9774
9903
  removePendingComment(superdoc);
@@ -9778,9 +9907,6 @@ var useCommentsStore = defineStore("comments", () => {
9778
9907
  *
9779
9908
  * Replace-file swaps call this before hydrating the replacement so stale
9780
9909
  * comments never survive long enough to render beside the new content.
9781
- *
9782
- * @param {string | null | undefined} documentId
9783
- * @returns {void}
9784
9910
  */
9785
9911
  function removeCommentsForDocument(documentId) {
9786
9912
  const activeDocumentId = documentId != null ? String(documentId) : null;
@@ -9811,20 +9937,16 @@ var useCommentsStore = defineStore("comments", () => {
9811
9937
  const remapTrackedChangeReferences = (droppedCommentIds, activeDocumentId) => {
9812
9938
  const remapSelection = (selection) => {
9813
9939
  const selectedId = normalizeCommentId(selection.value);
9814
- if (selectedId && droppedCommentIds.has(selectedId)) selection.value = droppedCommentIds.get(selectedId);
9940
+ const replacementId = selectedId ? droppedCommentIds.get(selectedId) : void 0;
9941
+ if (replacementId) selection.value = replacementId;
9815
9942
  };
9816
9943
  remapSelection(activeComment);
9817
9944
  remapSelection(activeFloatingCommentInstanceId);
9818
9945
  commentsList.value.forEach((row) => {
9819
9946
  if (!belongsToTrackedChangeSyncDocument(row, activeDocumentId)) return;
9820
9947
  const rowIdentityUpdates = {};
9821
- for (const field of [
9822
- "trackedChangeThreadParentId",
9823
- "trackedChangeParentId",
9824
- "threadingParentCommentId",
9825
- "parentCommentId"
9826
- ]) {
9827
- const replacementId = droppedCommentIds.get(normalizeCommentId(row?.[field]));
9948
+ for (const field of PARENT_LINK_FIELDS) {
9949
+ const replacementId = droppedCommentIds.get(normalizeCommentId(row?.[field]) ?? "");
9828
9950
  if (!replacementId) continue;
9829
9951
  row[field] = replacementId;
9830
9952
  rowIdentityUpdates[field] = replacementId;
@@ -9845,10 +9967,6 @@ var useCommentsStore = defineStore("comments", () => {
9845
9967
  *
9846
9968
  * Pruning is scoped to the active editor document so replay in one document does not
9847
9969
  * delete tracked-change comments from other open documents.
9848
- *
9849
- * @param {Set<string>} liveTrackedChangeIds IDs currently present in editor marks.
9850
- * @param {string | null} activeDocumentId Document currently being synced.
9851
- * @returns {void}
9852
9970
  */
9853
9971
  const pruneStaleTrackedChangeComments = (liveTrackedChangeIds, liveTrackedChangeAnchorKeys, activeDocumentId, superdoc = null, { broadcastChanges = true } = {}) => withInteractionSpan("store.trackedChanges.pruneStaleComments", "store-reconciliation", {
9854
9972
  activeDocumentId: activeDocumentId ?? null,
@@ -9893,7 +10011,7 @@ var useCommentsStore = defineStore("comments", () => {
9893
10011
  comment: payload
9894
10012
  };
9895
10013
  if (broadcastChanges) {
9896
- syncCommentsToClients(superdoc, event);
10014
+ syncCommentsToClients(requireHost(superdoc, "pruneStaleTrackedChangeComments"), event);
9897
10015
  superdoc?.emit?.("comments-update", event);
9898
10016
  }
9899
10017
  });
@@ -9925,14 +10043,10 @@ var useCommentsStore = defineStore("comments", () => {
9925
10043
  const event = {
9926
10044
  type: COMMENT_EVENTS.DELETED,
9927
10045
  comment: payload,
9928
- changes: [{
9929
- key: "deleted",
9930
- commentId: payload.commentId,
9931
- fileId: payload.fileId
9932
- }]
10046
+ changes: getDeletedCommentChanges([payload, comment], payload.fileId)
9933
10047
  };
9934
10048
  if (broadcastChanges) {
9935
- syncCommentsToClients(superdoc, event);
10049
+ syncCommentsToClients(requireHost(superdoc, "pruneStaleTrackedChangeComments"), event);
9936
10050
  superdoc?.emit?.("comments-update", event);
9937
10051
  }
9938
10052
  });
@@ -9955,7 +10069,6 @@ var useCommentsStore = defineStore("comments", () => {
9955
10069
  const resolveArgs = {
9956
10070
  email: superdoc?.user?.email ?? null,
9957
10071
  name: superdoc?.user?.name ?? null,
9958
- superdoc,
9959
10072
  reconciliationToken: COMMENT_RECONCILIATION_TOKEN
9960
10073
  };
9961
10074
  let resolvedCount = 0;
@@ -9966,9 +10079,12 @@ var useCommentsStore = defineStore("comments", () => {
9966
10079
  trackedChangeThreadParentIdForComment(linkedComment),
9967
10080
  linkedComment.threadingParentCommentId,
9968
10081
  linkedComment.parentCommentId
9969
- ].map((id) => normalizeCommentId(id)).filter(Boolean).some((id) => trackedChangeIds.has(id))) return;
10082
+ ].map((id) => normalizeCommentId(id)).filter(isPresent).some((id) => trackedChangeIds.has(id))) return;
9970
10083
  if (typeof linkedComment.resolveComment === "function") {
9971
- linkedComment.resolveComment(resolveArgs);
10084
+ linkedComment.resolveComment({
10085
+ ...resolveArgs,
10086
+ superdoc: requireHost(superdoc, "resolveLinkedCommentsForTrackedChangeDecision")
10087
+ });
9972
10088
  resolvedCount += 1;
9973
10089
  }
9974
10090
  });
@@ -10065,7 +10181,7 @@ var useCommentsStore = defineStore("comments", () => {
10065
10181
  if (previousCommentId) anchorSourceIds.add(previousCommentId);
10066
10182
  if (previousCanonicalId) anchorSourceIds.add(previousCanonicalId);
10067
10183
  }
10068
- if (comment.trackedChangeAnchorKey == null || anchorSourceIds.has(currentAnchorParts?.rawId)) comment.trackedChangeAnchorKey = toAnchor;
10184
+ if (comment.trackedChangeAnchorKey == null || anchorSourceIds.has(currentAnchorParts?.rawId ?? "")) comment.trackedChangeAnchorKey = toAnchor;
10069
10185
  comment.trackedChangePositionAliases = normalizeTrackedChangePositionAliases([...Array.isArray(comment.trackedChangePositionAliases) ? comment.trackedChangePositionAliases : [], fromId]);
10070
10186
  const identitySourceIds = /* @__PURE__ */ new Set([fromId]);
10071
10187
  if (advancesRemapLineage) {
@@ -10076,33 +10192,28 @@ var useCommentsStore = defineStore("comments", () => {
10076
10192
  if (row === comment) return;
10077
10193
  if (!belongsToTrackedChangeSyncDocument(row, normalizedDocumentId)) return;
10078
10194
  const rowIdentityUpdates = {};
10079
- for (const field of [
10080
- "trackedChangeThreadParentId",
10081
- "trackedChangeParentId",
10082
- "threadingParentCommentId",
10083
- "parentCommentId"
10084
- ]) {
10085
- if (!identitySourceIds.has(normalizeCommentId(row?.[field]))) continue;
10195
+ for (const field of PARENT_LINK_FIELDS) {
10196
+ if (!identitySourceIds.has(normalizeCommentId(row?.[field]) ?? "")) continue;
10086
10197
  row[field] = toId;
10087
10198
  rowIdentityUpdates[field] = toId;
10088
10199
  }
10089
10200
  if (Object.keys(rowIdentityUpdates).length && typeof row.updateIdentityValues === "function") row.updateIdentityValues(rowIdentityUpdates);
10090
10201
  });
10091
10202
  if (Object.keys(commentIdentityUpdates).length && typeof comment.updateIdentityValues === "function") comment.updateIdentityValues(commentIdentityUpdates);
10092
- const previousEntry = previousPositionEntry ?? [positions[fromId], positions[buildBodyTrackedChangeAnchorKey(fromId)]].find((entry) => entry && positionEntryMatchesTrackedChangeStory(entry, comment));
10203
+ const previousEntry = previousPositionEntry ?? [positions[fromId], positions[String(buildBodyTrackedChangeAnchorKey(fromId))]].find((entry) => entry && positionEntryMatchesTrackedChangeStory(entry, comment));
10093
10204
  if (previousEntry) {
10094
- for (const key of [toId, toAnchor]) if (positions[key] === void 0) {
10205
+ for (const key of [toId, String(toAnchor)]) if (positions[key] === void 0) {
10095
10206
  positions[key] = previousEntry;
10096
10207
  positionsChanged = true;
10097
10208
  }
10098
10209
  }
10099
- if (identitySourceIds.has(normalizeCommentId(activeComment.value))) activeComment.value = toId;
10100
- if (identitySourceIds.has(normalizeCommentId(activeFloatingCommentInstanceId.value))) activeFloatingCommentInstanceId.value = toId;
10210
+ if (identitySourceIds.has(normalizeCommentId(activeComment.value) ?? "")) activeComment.value = toId;
10211
+ if (identitySourceIds.has(normalizeCommentId(activeFloatingCommentInstanceId.value) ?? "")) activeFloatingCommentInstanceId.value = toId;
10101
10212
  }
10102
10213
  if (positionsChanged) editorCommentPositions.value = { ...positions };
10103
10214
  };
10104
10215
  const pruneDecidedTrackedChangeRow = ({ superdoc, comment: targetComment, decidedId, decidedIds, documentId } = {}) => {
10105
- const normalizedDecidedIds = new Set((Array.isArray(decidedIds) || decidedIds instanceof Set ? Array.from(decidedIds) : [decidedId]).map((id) => normalizeCommentId(id)).filter(Boolean));
10216
+ const normalizedDecidedIds = new Set((Array.isArray(decidedIds) || decidedIds instanceof Set ? Array.from(decidedIds) : [decidedId]).map((id) => normalizeCommentId(id)).filter(isPresent));
10106
10217
  const normalizedDocumentId = normalizeCommentId(documentId);
10107
10218
  if (!normalizedDecidedIds.size || !normalizedDocumentId) return {
10108
10219
  removed: false,
@@ -10162,12 +10273,6 @@ var useCommentsStore = defineStore("comments", () => {
10162
10273
  };
10163
10274
  /**
10164
10275
  * Accept or reject the tracked change behind a sidebar row.
10165
- *
10166
- * @param {Object} param0
10167
- * @param {Object} param0.superdoc The SuperDoc instance.
10168
- * @param {Object} param0.comment The tracked-change row.
10169
- * @param {string} param0.decision `'accept'` or `'reject'`.
10170
- * @returns {{ ok: boolean, success?: boolean, reason?: string } | Promise<{ ok: boolean, success?: boolean, reason?: string }>} The decision outcome.
10171
10276
  */
10172
10277
  const decideTrackedChangeFromSidebar = ({ superdoc, comment, decision }) => {
10173
10278
  const disabledReason = trackedChangeDecisionDisabledReason(superdoc);
@@ -10195,7 +10300,7 @@ var useCommentsStore = defineStore("comments", () => {
10195
10300
  return {
10196
10301
  ok: false,
10197
10302
  reason: "adapter-threw",
10198
- detail: err?.message ?? String(err)
10303
+ detail: errorDetail(err)
10199
10304
  };
10200
10305
  }
10201
10306
  })().then(async (outcome) => {
@@ -10248,9 +10353,11 @@ var useCommentsStore = defineStore("comments", () => {
10248
10353
  if (!activeEditor) return { ok: false };
10249
10354
  const id = comment.commentId ?? comment.importedId;
10250
10355
  if (!id) return { ok: false };
10251
- const story = comment.trackedChangeStory ?? void 0;
10356
+ const rawStory = comment.trackedChangeStory ?? void 0;
10357
+ const story = isStoryLocator(rawStory) ? rawStory : void 0;
10252
10358
  const documentApi = typeof activeEditor.doc === "object" ? activeEditor.doc : null;
10253
10359
  if (documentApi?.trackChanges?.decide) try {
10360
+ if (rawStory && !story) throw new TypeError("trackedChangeStory must be a valid StoryLocator");
10254
10361
  const target = story ? {
10255
10362
  id,
10256
10363
  story
@@ -10263,7 +10370,7 @@ var useCommentsStore = defineStore("comments", () => {
10263
10370
  success: Boolean(receipt?.success)
10264
10371
  }));
10265
10372
  } catch (error) {
10266
- if (story) return {
10373
+ if (rawStory) return {
10267
10374
  ok: false,
10268
10375
  error
10269
10376
  };
@@ -10310,9 +10417,9 @@ var useCommentsStore = defineStore("comments", () => {
10310
10417
  documentId: effectiveDocumentId
10311
10418
  })).removed
10312
10419
  };
10313
- const normalizedRemovedIds = new Set(Array.from(removedIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean));
10314
- const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean))];
10315
- const remappedSourceIds = new Set((Array.isArray(remappedPairs) ? remappedPairs : []).map((pair) => normalizeCommentId(pair?.from)).filter(Boolean));
10420
+ const normalizedRemovedIds = new Set(Array.from(removedIds ?? []).map((id) => normalizeCommentId(id)).filter(isPresent));
10421
+ const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(isPresent))];
10422
+ const remappedSourceIds = new Set((Array.isArray(remappedPairs) ? remappedPairs : []).map((pair) => normalizeCommentId(pair?.from)).filter(isPresent));
10316
10423
  const locallyRemovedIds = new Set([...normalizedRemovedIds].filter((id) => !remappedSourceIds.has(id)));
10317
10424
  if (locallyRemovedIds.size > 0 && effectiveDocumentId != null) pruneDecidedTrackedChangeRow({
10318
10425
  superdoc,
@@ -10422,23 +10529,24 @@ var useCommentsStore = defineStore("comments", () => {
10422
10529
  for (const params of paramsList) {
10423
10530
  if (!params) continue;
10424
10531
  let retainUnchangedRow = false;
10532
+ const anchorParts = parseTrackedChangeAnchorKey(params.trackedChangeAnchorKey);
10533
+ const anchorStoryKey = anchorParts ? normalizeTrackedChangeStoryKey(anchorParts.storyKey) : void 0;
10425
10534
  if (preparedPresentation) {
10426
10535
  if (preparedPresentation !== committedReviewPresentations.value[String(effectiveDocumentId)]) continue;
10427
10536
  if (params.documentId != null && String(params.documentId) !== String(effectiveDocumentId)) continue;
10428
10537
  const id = normalizeCommentId(params.changeId);
10429
- const version = preparedPresentation.rowVersions?.[id];
10538
+ const version = preparedPresentation.rowVersions?.[String(id)] ?? NaN;
10430
10539
  if (!id || !Number.isSafeInteger(version) || version < 1) continue;
10431
10540
  const existing = [...trackedChangeIdentityIndex.candidates({ changeId: id })].filter((row) => String(row.fileId) === String(effectiveDocumentId) && String(row.commentId) === id);
10432
10541
  if (existing.length > 1) continue;
10433
10542
  const row = existing[0];
10434
10543
  if (row && row.trackedChangeCanonicalId !== params.trackedChangeCanonicalId) {
10435
- const previousVersion = previousPreparedPresentation?.rowVersions?.[id];
10436
- if (!row.trackedChange || params.event !== "add" || typeof params.trackedChangeCanonicalId !== "string" || !params.trackedChangeCanonicalId || !Number.isSafeInteger(previousVersion) || previousVersion < 1 || version <= previousVersion || normalizeTrackedChangeStoryKey(getTrackedChangeStoryKey(row)) !== normalizeTrackedChangeStoryKey(parseTrackedChangeAnchorKey(params.trackedChangeAnchorKey)?.storyKey) || row.trackedChangeType !== params.trackedChangeType || row.trackedChangeDisplayType !== params.trackedChangeDisplayType) continue;
10544
+ const previousVersion = previousPreparedPresentation?.rowVersions?.[id] ?? NaN;
10545
+ if (!row.trackedChange || params.event !== "add" || typeof params.trackedChangeCanonicalId !== "string" || !params.trackedChangeCanonicalId || !Number.isSafeInteger(previousVersion) || previousVersion < 1 || version <= previousVersion || normalizeTrackedChangeStoryKey(getTrackedChangeStoryKey(row)) !== anchorStoryKey || row.trackedChangeType !== params.trackedChangeType || row.trackedChangeDisplayType !== params.trackedChangeDisplayType) continue;
10437
10546
  row.trackedChangeCanonicalId = params.trackedChangeCanonicalId;
10438
- row.updateIdentityValues?.({ trackedChangeCanonicalId: params.trackedChangeCanonicalId });
10439
10547
  trackedChangeIdentityIndex.add(row);
10440
10548
  }
10441
- retainUnchangedRow = retainUnchangedRows && row?.trackedChange === true && previousPreparedPresentation.rowVersions?.[id] === version && row.trackedChangeCanonicalId === params.trackedChangeCanonicalId && row.trackedChangeType === params.trackedChangeType && row.trackedChangeDisplayType === params.trackedChangeDisplayType && normalizeTrackedChangeStoryKey(getTrackedChangeStoryKey(row)) === normalizeTrackedChangeStoryKey(parseTrackedChangeAnchorKey(params.trackedChangeAnchorKey)?.storyKey) && !row.resolvedTime && !row.resolvedById && !row.resolvedByEmail && !row.resolvedByName;
10549
+ retainUnchangedRow = retainUnchangedRows === true && row?.trackedChange === true && previousPreparedPresentation.rowVersions?.[id] === version && row.trackedChangeCanonicalId === params.trackedChangeCanonicalId && row.trackedChangeType === params.trackedChangeType && row.trackedChangeDisplayType === params.trackedChangeDisplayType && normalizeTrackedChangeStoryKey(getTrackedChangeStoryKey(row)) === anchorStoryKey && !row.resolvedTime && !row.resolvedById && !row.resolvedByEmail && !row.resolvedByName;
10442
10550
  }
10443
10551
  if (params.event === "omit") {
10444
10552
  if (params.changeId != null) liveIds.add(String(params.changeId));
@@ -10479,22 +10587,18 @@ var useCommentsStore = defineStore("comments", () => {
10479
10587
  return commentsList.value.filter((comment) => {
10480
10588
  if (comment?.trackedChange !== true) return false;
10481
10589
  if (normalizedDocumentId == null) return true;
10482
- const rowDocumentId = comment?.documentId ?? comment?.fileId ?? null;
10590
+ const rowDocumentId = Reflect.get(comment, "documentId") ?? comment?.fileId ?? null;
10483
10591
  return rowDocumentId != null && String(rowDocumentId) === normalizedDocumentId;
10484
10592
  }).length;
10485
10593
  };
10486
10594
  /**
10487
10595
  * Triggered when the editor locations are updated
10488
10596
  * Updates floating comment locations from the editor
10489
- *
10490
- * @param {Object} allCommentPositions The collected geometry keyed by comment / tracked-change id
10491
- * @param {{ retainMissingTrackedChangeGeometry?: boolean, retainedTrackedChangeIds?: Iterable<string> }} options Geometry reconciliation options
10492
- * @returns {void}
10493
10597
  */
10494
10598
  const handleEditorLocationsUpdate = (allCommentPositions, options = {}) => {
10495
10599
  if (allCommentPositions == null) return;
10496
10600
  const retainMissingTrackedChangeGeometry = options?.retainMissingTrackedChangeGeometry === true;
10497
- const retainedTrackedChangeIds = retainMissingTrackedChangeGeometry && options?.retainedTrackedChangeIds != null ? new Set(Array.from(options.retainedTrackedChangeIds, (id) => normalizeCommentId(id)).filter(Boolean)) : null;
10601
+ const retainedTrackedChangeIds = retainMissingTrackedChangeGeometry && options?.retainedTrackedChangeIds != null ? new Set(Array.from(options.retainedTrackedChangeIds, (id) => normalizeCommentId(id)).filter(isPresent)) : null;
10498
10602
  const previousPositions = editorCommentPositions.value || {};
10499
10603
  const normalizedPositions = {};
10500
10604
  Object.entries(allCommentPositions).forEach(([key, entry]) => {
@@ -10521,7 +10625,7 @@ var useCommentsStore = defineStore("comments", () => {
10521
10625
  return null;
10522
10626
  };
10523
10627
  const writeTrackedChangeAliases = (entry, comment, extraKeys = []) => {
10524
- const keys = [...aliasesFor(comment), ...extraKeys.map((id) => normalizeCommentId(id)).filter(Boolean)];
10628
+ const keys = [...aliasesFor(comment), ...extraKeys.map((id) => normalizeCommentId(id)).filter(isPresent)];
10525
10629
  for (const key of new Set(keys)) if (normalizedPositions[key] === void 0) normalizedPositions[key] = entry;
10526
10630
  };
10527
10631
  commentsList.value.forEach((comment) => {
@@ -11827,8 +11931,8 @@ var _hoisted_11$3 = {
11827
11931
  key: 0,
11828
11932
  class: "card-section comment-body"
11829
11933
  };
11830
- var _hoisted_12$2 = ["data-track-change-semantic-color-key"];
11831
- var _hoisted_13$2 = { key: 0 };
11934
+ var _hoisted_12$3 = ["data-track-change-semantic-color-key"];
11935
+ var _hoisted_13$3 = { key: 0 };
11832
11936
  var _hoisted_14$2 = { class: "change-type tracked-change-label" };
11833
11937
  var _hoisted_15$2 = {
11834
11938
  key: 0,
@@ -13002,7 +13106,7 @@ var CommentDialog_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/*
13002
13106
  "data-track-change-semantic-color-key": comment.semanticColorKey ?? void 0,
13003
13107
  ref_for: true,
13004
13108
  ref: index === 0 ? setParentBodyRef : void 0
13005
- }, [comment.trackedChangeLabel ? (openBlock(), createElementBlock("div", _hoisted_13$2, [createElementVNode("span", _hoisted_14$2, toDisplayString(comment.trackedChangeLabel), 1), comment.trackedChangeDetailLines && comment.trackedChangeDetailLines.length ? (openBlock(), createElementBlock("div", _hoisted_15$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(comment.trackedChangeDetailLines, (line, lineIndex) => {
13109
+ }, [comment.trackedChangeLabel ? (openBlock(), createElementBlock("div", _hoisted_13$3, [createElementVNode("span", _hoisted_14$2, toDisplayString(comment.trackedChangeLabel), 1), comment.trackedChangeDetailLines && comment.trackedChangeDetailLines.length ? (openBlock(), createElementBlock("div", _hoisted_15$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(comment.trackedChangeDetailLines, (line, lineIndex) => {
13006
13110
  return openBlock(), createElementBlock("div", {
13007
13111
  key: lineIndex,
13008
13112
  class: "tracked-change-detail-line"
@@ -13027,7 +13131,7 @@ var CommentDialog_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/*
13027
13131
  style: normalizeStyle(trackedChangeImagePreviewStyle(comment.trackedChangeImagePreview)),
13028
13132
  loading: "lazy",
13029
13133
  decoding: "async"
13030
- }, null, 12, _hoisted_38)], 8, _hoisted_37)) : createCommentVNode("", true)], 10, _hoisted_12$2),
13134
+ }, null, 12, _hoisted_38)], 8, _hoisted_37)) : createCommentVNode("", true)], 10, _hoisted_12$3),
13031
13135
  shouldTruncate.value && isTextOverflowing.value && index === 0 ? (openBlock(), createElementBlock("div", {
13032
13136
  key: 0,
13033
13137
  class: "show-more-toggle",
@@ -13121,7 +13225,7 @@ var CommentDialog_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/*
13121
13225
  ], 64))], 14, _hoisted_1$26)), [[_directive_click_outside, handleClickOutside]]);
13122
13226
  };
13123
13227
  }
13124
- }), [["__scopeId", "data-v-4220ce17"]]);
13228
+ }), [["__scopeId", "data-v-c0cc448b"]]);
13125
13229
  //#endregion
13126
13230
  //#region src/components/CommentsLayer/commentsList/ReviewDirectoryListItem.vue?vue&type=script&setup=true&lang.ts
13127
13231
  var _hoisted_1$25 = [
@@ -13888,124 +13992,6 @@ var composeAuthorColorResolver = (config) => {
13888
13992
  };
13889
13993
  };
13890
13994
  //#endregion
13891
- //#region ../document-api/src/errors.ts
13892
- /**
13893
- * Structured validation error thrown by document-api execute* functions.
13894
- *
13895
- * Consumers should prefer checking `error.code` over `instanceof` for resilience
13896
- * across package boundaries and bundling scenarios.
13897
- */
13898
- var DocumentApiValidationError = class DocumentApiValidationError extends Error {
13899
- constructor(code, message, details) {
13900
- super(message);
13901
- this.name = "DocumentApiValidationError";
13902
- this.code = code;
13903
- this.details = details;
13904
- Object.setPrototypeOf(this, DocumentApiValidationError.prototype);
13905
- }
13906
- };
13907
- //#endregion
13908
- //#region ../document-api/src/export/export.types.ts
13909
- /** Canonical tracked-change export modes accepted by every public export surface. */
13910
- var SD_EXPORT_MODES = [
13911
- "review-preserving",
13912
- "final",
13913
- "original"
13914
- ];
13915
- /** Default mode for modeful DOCX export. */
13916
- var DEFAULT_SD_EXPORT_MODE = "review-preserving";
13917
- var SD_COMMENT_EXPORT_POLICIES = [
13918
- "preserve",
13919
- "resolve",
13920
- "remove"
13921
- ];
13922
- var SD_CONTENT_CONTROL_EXPORT_POLICIES = ["preserve", "flatten"];
13923
- //#endregion
13924
- //#region ../document-api/src/export/export.ts
13925
- /**
13926
- * Export namespace: engine-agnostic public API and adapter contract.
13927
- *
13928
- * Validates the requested mode at the public boundary so an invalid mode fails
13929
- * closed with `INVALID_INPUT` before any document work happens, then delegates
13930
- * byte production and degradation reporting to the engine adapter.
13931
- */
13932
- function resolveMode$1(input) {
13933
- if (input != null && !isRecord(input)) throw new DocumentApiValidationError("INVALID_INPUT", "export.toDocx input must be an object.");
13934
- const raw = input?.mode;
13935
- if (raw === void 0) return DEFAULT_SD_EXPORT_MODE;
13936
- if (typeof raw !== "string" || !SD_EXPORT_MODES.includes(raw)) throw new DocumentApiValidationError("INVALID_INPUT", `export.toDocx mode must be one of: ${SD_EXPORT_MODES.join(", ")}.`, {
13937
- mode: raw,
13938
- allowedModes: [...SD_EXPORT_MODES]
13939
- });
13940
- return raw;
13941
- }
13942
- function resolveDocxExportPolicies(input = {}) {
13943
- if (!isRecord(input)) throw new DocumentApiValidationError("INVALID_INPUT", "DOCX export input must be an object.");
13944
- const mode = resolveMode$1(input);
13945
- const policies = input.policies;
13946
- if (policies !== void 0 && !isRecord(policies)) throw new DocumentApiValidationError("INVALID_INPUT", "DOCX export policies must be an object.");
13947
- const allowed = {
13948
- trackedChanges: SD_EXPORT_MODES,
13949
- comments: SD_COMMENT_EXPORT_POLICIES,
13950
- contentControls: SD_CONTENT_CONTROL_EXPORT_POLICIES
13951
- };
13952
- for (const [key, value] of Object.entries(policies ?? {})) if (!Object.prototype.hasOwnProperty.call(allowed, key) || value !== void 0 && !allowed[key].includes(value)) throw new DocumentApiValidationError("INVALID_INPUT", `Unsupported DOCX export policy: ${key}.`, {
13953
- policy: key,
13954
- value
13955
- });
13956
- const requested = policies;
13957
- if (input.mode !== void 0 && requested?.trackedChanges !== void 0 && mode !== requested.trackedChanges) throw new DocumentApiValidationError("INVALID_INPUT", "mode conflicts with policies.trackedChanges.");
13958
- return {
13959
- trackedChanges: requested?.trackedChanges ?? mode,
13960
- comments: requested?.comments ?? "preserve",
13961
- contentControls: requested?.contentControls ?? "preserve"
13962
- };
13963
- }
13964
- //#endregion
13965
- //#region ../document-api/src/permission-ranges/permission-ranges.ts
13966
- function editorKey(editor) {
13967
- return editor.id?.trim() ? editor.id : editor.email?.trim() || void 0;
13968
- }
13969
- function editorMatches(editor, id) {
13970
- return Boolean(id.trim()) && (editor.id === id || Boolean(editor.email?.trim() && editor.email.trim().toLowerCase() === id.toLowerCase()));
13971
- }
13972
- /**
13973
- * Resolve a host ID or imported Word email against an explicitly supplied roster.
13974
- * Word receives the email when available; opaque IDs remain host-specific.
13975
- * Duplicate IDs/emails are ambiguous. This does not authenticate a user.
13976
- */
13977
- function resolvePermissionRangeEditor(editors, id) {
13978
- const matches = editors.filter((editor) => editorMatches(editor, id));
13979
- if (matches.length === 0) return {
13980
- status: "unknown",
13981
- id
13982
- };
13983
- if (matches.length !== 1) return {
13984
- status: "ambiguous",
13985
- id
13986
- };
13987
- const editor = matches[0];
13988
- const wordId = editor.email?.trim() || editor.id;
13989
- if (editors.filter((candidate) => editorMatches(candidate, wordId)).length !== 1) return {
13990
- status: "ambiguous",
13991
- id
13992
- };
13993
- return {
13994
- status: "resolved",
13995
- id,
13996
- editor: { ...editor },
13997
- principal: {
13998
- kind: "editor",
13999
- id: wordId
14000
- }
14001
- };
14002
- }
14003
- /** Enumerate the host roster for an editor picker, including ambiguous entries. */
14004
- function listPermissionRangeEditors(editors) {
14005
- const ids = new Set(editors.map(editorKey).filter((id) => id !== void 0));
14006
- return Array.from(ids, (id) => resolvePermissionRangeEditor(editors, id));
14007
- }
14008
- //#endregion
14009
13995
  //#region src/core/superdoc-ui-owner.ts
14010
13996
  var ownerIds = /* @__PURE__ */ new WeakMap();
14011
13997
  function getSuperDocUiOwnerId(superdoc) {
@@ -15066,7 +15052,7 @@ var FloatingComments_default = /* @__PURE__ */ _plugin_vue_export_helper_default
15066
15052
  }), 128))], 4)], 36);
15067
15053
  };
15068
15054
  }
15069
- }), [["__scopeId", "data-v-aaf2c537"]]);
15055
+ }), [["__scopeId", "data-v-df897b8d"]]);
15070
15056
  //#endregion
15071
15057
  //#region src/components/CommentsLayer/PdfCommentsLayer.vue?vue&type=script&setup=true&lang.ts
15072
15058
  var _hoisted_1$22 = {
@@ -15199,7 +15185,7 @@ var PdfCommentsLayer_default = /* @__PURE__ */ _plugin_vue_export_helper_default
15199
15185
  }), 128))]);
15200
15186
  };
15201
15187
  }
15202
- }), [["__scopeId", "data-v-86166837"]]);
15188
+ }), [["__scopeId", "data-v-e3ae65fa"]]);
15203
15189
  //#endregion
15204
15190
  //#region src/components/Whiteboard/use-whiteboard.ts
15205
15191
  var useWhiteboard = ({ proxy, layers, documents, modules }) => {
@@ -16169,6 +16155,116 @@ function resolveV2ReviewTargetCommentId(target, getComment) {
16169
16155
  return commentId != null ? String(commentId) : null;
16170
16156
  }
16171
16157
  //#endregion
16158
+ //#region src/helpers/v2-review-geometry-delivery.ts
16159
+ function createReviewGeometryDelivery(deliver) {
16160
+ const packets = /* @__PURE__ */ new Map();
16161
+ let stageOwners = /* @__PURE__ */ new WeakMap();
16162
+ let active = null;
16163
+ const documentOf = (current) => current.documentId != null ? String(current.documentId) : current.mountContainer?.dataset.superdocV2DocumentId ?? null;
16164
+ const matches = (documentId, current) => current.mountContainer?.dataset.superdocV2DocumentId === documentId && (current.documentId == null || String(current.documentId) === documentId);
16165
+ const matchesPaint = (packet, current) => typeof packet.payload.paintEpoch !== "number" || packet.payload.paintEpoch === current.epoch;
16166
+ const observe = (current) => {
16167
+ const stage = current.mountContainer;
16168
+ const documentId = documentOf(current);
16169
+ if (!stage || !documentId || !matches(documentId, current)) return;
16170
+ const previousOwner = stageOwners.get(stage);
16171
+ if (previousOwner && previousOwner !== documentId) packets.delete(previousOwner);
16172
+ stageOwners.set(stage, documentId);
16173
+ const packet = packets.get(documentId);
16174
+ if (packet?.stage && packet.stage !== stage) packets.delete(documentId);
16175
+ else if (packet && (!active || active.stage !== stage || active.documentId !== documentId || active.epoch !== current.epoch)) packet.delivered = false;
16176
+ active = {
16177
+ documentId,
16178
+ stage,
16179
+ epoch: current.epoch
16180
+ };
16181
+ };
16182
+ const dispatch = (documentId, packet, current, replay = false) => {
16183
+ if (!current.mountContainer || !matches(documentId, current) || !matchesPaint(packet, current)) return false;
16184
+ if (packet.stage && packet.stage !== current.mountContainer || packet.delivering || packet.delivered && !replay) return false;
16185
+ const wasDelivered = packet.delivered;
16186
+ const previousStage = packet.stage;
16187
+ packet.stage = current.mountContainer;
16188
+ packet.delivered = true;
16189
+ packet.delivering = true;
16190
+ try {
16191
+ deliver(current, packet.payload);
16192
+ } catch (error) {
16193
+ packet.delivered = wasDelivered;
16194
+ packet.stage = previousStage;
16195
+ throw error;
16196
+ } finally {
16197
+ packet.delivering = false;
16198
+ }
16199
+ return true;
16200
+ };
16201
+ const notify = (documentId, payload, current, ready = true) => {
16202
+ if (!payload.reviewGeometry || !Array.isArray(payload.pageIndices)) return false;
16203
+ let key;
16204
+ try {
16205
+ key = JSON.stringify([
16206
+ documentId,
16207
+ payload.documentEpoch,
16208
+ payload.paintEpoch,
16209
+ payload.commitSequence,
16210
+ payload.pageIndices,
16211
+ payload.zoomScale,
16212
+ payload.reviewGeometry
16213
+ ]);
16214
+ } catch {
16215
+ return false;
16216
+ }
16217
+ const isCurrent = current != null && matches(documentId, current);
16218
+ if (isCurrent) observe(current);
16219
+ const previous = packets.get(documentId);
16220
+ if (previous && previous.payload.documentEpoch === payload.documentEpoch && typeof previous.payload.paintEpoch === "number" && typeof payload.paintEpoch === "number" && payload.paintEpoch < previous.payload.paintEpoch) return false;
16221
+ if (previous && previous.payload.documentEpoch === payload.documentEpoch && typeof previous.payload.commitSequence === "number" && typeof payload.commitSequence === "number" && payload.commitSequence < previous.payload.commitSequence) return false;
16222
+ if (previous && previous.payload.documentEpoch === payload.documentEpoch && typeof previous.payload.generation === "number" && typeof payload.generation === "number" && payload.generation < previous.payload.generation) return false;
16223
+ if (isCurrent && typeof payload.paintEpoch === "number" && typeof current.epoch === "number" && payload.paintEpoch < current.epoch) return false;
16224
+ if (previous?.key === key) {
16225
+ previous.payload = payload;
16226
+ return isCurrent && ready ? dispatch(documentId, previous, current) : false;
16227
+ }
16228
+ const packet = {
16229
+ payload,
16230
+ key,
16231
+ stage: isCurrent ? current.mountContainer : previous && previous.payload.documentEpoch === payload.documentEpoch ? previous.stage : null,
16232
+ delivered: false,
16233
+ delivering: false
16234
+ };
16235
+ packets.set(documentId, packet);
16236
+ if (!isCurrent || !ready) return false;
16237
+ try {
16238
+ return dispatch(documentId, packet, current);
16239
+ } catch (error) {
16240
+ if (previous) packets.set(documentId, previous);
16241
+ else packets.delete(documentId);
16242
+ throw error;
16243
+ }
16244
+ };
16245
+ return {
16246
+ notify,
16247
+ observe,
16248
+ flush(current) {
16249
+ observe(current);
16250
+ const documentId = documentOf(current);
16251
+ const packet = documentId == null ? null : packets.get(documentId);
16252
+ return documentId && packet ? dispatch(documentId, packet, current) : false;
16253
+ },
16254
+ replay(current) {
16255
+ observe(current);
16256
+ const documentId = documentOf(current);
16257
+ const packet = documentId == null ? null : packets.get(documentId);
16258
+ return documentId && packet ? dispatch(documentId, packet, current, true) : false;
16259
+ },
16260
+ reset() {
16261
+ packets.clear();
16262
+ stageOwners = /* @__PURE__ */ new WeakMap();
16263
+ active = null;
16264
+ }
16265
+ };
16266
+ }
16267
+ //#endregion
16172
16268
  //#region src/helpers/v2-mutation-exception.ts
16173
16269
  var FAILURE_SOURCES = /* @__PURE__ */ new Set(["shell", "receipt"]);
16174
16270
  var DIAGNOSTIC_CODE = /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/;
@@ -16538,7 +16634,7 @@ function usePasswordPrompt({ getSurfaceManager, getPasswordPromptConfig, onUnhan
16538
16634
  })
16539
16635
  });
16540
16636
  } else {
16541
- const mod = await import("./chunks/PasswordPromptSurface-CZqhDQQ1.es.js");
16637
+ const mod = await import("./chunks/PasswordPromptSurface-DjV1JgjD.es.js");
16542
16638
  const surfaceId = `password-prompt-${doc.id}`;
16543
16639
  handle = manager.open({
16544
16640
  id: surfaceId,
@@ -17198,7 +17294,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17198
17294
  } else {
17199
17295
  let FindReplaceSurface;
17200
17296
  try {
17201
- FindReplaceSurface = (await import("./chunks/FindReplaceSurface-B_5ggdWg.es.js")).default;
17297
+ FindReplaceSurface = (await import("./chunks/FindReplaceSurface-BEnRLpD9.es.js")).default;
17202
17298
  } catch {
17203
17299
  opening = false;
17204
17300
  return;
@@ -17572,11 +17668,11 @@ var _hoisted_8$3 = { class: "input-row url-input-row" };
17572
17668
  var _hoisted_9$2 = ["innerHTML"];
17573
17669
  var _hoisted_10$2 = ["readonly", "onKeydown"];
17574
17670
  var _hoisted_11$2 = ["innerHTML"];
17575
- var _hoisted_12$1 = {
17671
+ var _hoisted_12$2 = {
17576
17672
  key: 0,
17577
17673
  class: "input-row link-buttons"
17578
17674
  };
17579
- var _hoisted_13$1 = ["disabled"];
17675
+ var _hoisted_13$2 = ["disabled"];
17580
17676
  var _hoisted_14$1 = ["innerHTML"];
17581
17677
  var _hoisted_15$1 = ["disabled"];
17582
17678
  var _hoisted_16$1 = {
@@ -17835,7 +17931,7 @@ var LinkInput_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__
17835
17931
  "data-item": "btn-link-open"
17836
17932
  }, null, 10, _hoisted_11$2)
17837
17933
  ]),
17838
- !isViewingMode.value ? (openBlock(), createElementBlock("div", _hoisted_12$1, [isEditing.value ? (openBlock(), createElementBlock("button", {
17934
+ !isViewingMode.value ? (openBlock(), createElementBlock("div", _hoisted_12$2, [isEditing.value ? (openBlock(), createElementBlock("button", {
17839
17935
  key: 0,
17840
17936
  class: "remove-btn",
17841
17937
  onClick: handleRemove,
@@ -17844,7 +17940,7 @@ var LinkInput_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__
17844
17940
  }, [createElementVNode("div", {
17845
17941
  class: "remove-btn__icon",
17846
17942
  innerHTML: unref(toolbarIcons).removeLink
17847
- }, null, 8, _hoisted_14$1), _cache[6] || (_cache[6] = createTextVNode(" Remove ", -1))], 8, _hoisted_13$1)) : createCommentVNode("", true), createElementVNode("button", {
17943
+ }, null, 8, _hoisted_14$1), _cache[6] || (_cache[6] = createTextVNode(" Remove ", -1))], 8, _hoisted_13$2)) : createCommentVNode("", true), createElementVNode("button", {
17848
17944
  class: normalizeClass(["sd-submit-btn", { "disable-btn": isDisabled.value }]),
17849
17945
  onClick: handleSubmit,
17850
17946
  disabled: isSubmitting.value,
@@ -18754,6 +18850,7 @@ function createV2EditorRuntimeAdapter(options) {
18754
18850
  const { id, documentId, root, host, getLegacyEditorProjection, onUnregister } = options;
18755
18851
  let snapshot = host.getSnapshot();
18756
18852
  let didDispose = false;
18853
+ let detached = false;
18757
18854
  let unregistered = false;
18758
18855
  let mountedFocusHandle = null;
18759
18856
  let tokenRevision = 0;
@@ -19050,7 +19147,17 @@ function createV2EditorRuntimeAdapter(options) {
19050
19147
  };
19051
19148
  }
19052
19149
  }
19150
+ function releaseHostSubscriptions() {
19151
+ hostUnsubscribe?.();
19152
+ hostUnsubscribe = null;
19153
+ pageMetricsUnsubscribe?.();
19154
+ pageMetricsUnsubscribe = null;
19155
+ selectionUnsubscribe?.();
19156
+ selectionUnsubscribe = null;
19157
+ activeSelectionController = null;
19158
+ }
19053
19159
  function handleHostSnapshot(next) {
19160
+ if (detached || didDispose) return;
19054
19161
  snapshot = next;
19055
19162
  invalidatePositionTokens();
19056
19163
  syncSelectionSubscription();
@@ -19064,10 +19171,7 @@ function createV2EditorRuntimeAdapter(options) {
19064
19171
  });
19065
19172
  if (next.state === "disposed" && !didDispose) {
19066
19173
  didDispose = true;
19067
- hostUnsubscribe?.();
19068
- pageMetricsUnsubscribe?.();
19069
- selectionUnsubscribe?.();
19070
- selectionUnsubscribe = null;
19174
+ releaseHostSubscriptions();
19071
19175
  emit({ type: "disposed" });
19072
19176
  listeners.clear();
19073
19177
  if (!unregistered) {
@@ -19214,10 +19318,7 @@ function createV2EditorRuntimeAdapter(options) {
19214
19318
  await host.dispose();
19215
19319
  if (!didDispose) {
19216
19320
  didDispose = true;
19217
- hostUnsubscribe?.();
19218
- pageMetricsUnsubscribe?.();
19219
- selectionUnsubscribe?.();
19220
- selectionUnsubscribe = null;
19321
+ releaseHostSubscriptions();
19221
19322
  emit({ type: "disposed" });
19222
19323
  listeners.clear();
19223
19324
  if (!unregistered) {
@@ -19296,8 +19397,17 @@ function createV2EditorRuntimeAdapter(options) {
19296
19397
  }
19297
19398
  },
19298
19399
  attachMountHandle(handle) {
19400
+ if (detached || didDispose) return;
19299
19401
  mountedFocusHandle = handle;
19300
19402
  syncSelectionSubscription();
19403
+ },
19404
+ detach() {
19405
+ if (detached) return;
19406
+ detached = true;
19407
+ unregistered = true;
19408
+ releaseHostSubscriptions();
19409
+ mountedFocusHandle = null;
19410
+ listeners.clear();
19301
19411
  }
19302
19412
  };
19303
19413
  }
@@ -20460,7 +20570,7 @@ var SuperDoc_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__P
20460
20570
  __name: "SuperDoc",
20461
20571
  emits: ["selection-update"],
20462
20572
  setup(__props, { emit: __emit }) {
20463
- const PdfViewer = defineAsyncComponent(() => import("./chunks/PdfViewer-CW09MNrB.es.js"));
20573
+ const PdfViewer = defineAsyncComponent(() => import("./chunks/PdfViewer-C2r-K8zC.es.js"));
20464
20574
  const getDocumentLoadPassword = (doc) => doc.password ?? proxy.$superdoc.config.password;
20465
20575
  const resolveDocumentV2Collaboration = (doc) => {
20466
20576
  const rawConfig = doc?.v2Collaboration;
@@ -20574,6 +20684,8 @@ var SuperDoc_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__P
20574
20684
  const v2ReviewSidebarUnlocked = ref(false);
20575
20685
  const v2GeometryEpoch = ref(null);
20576
20686
  let v2GeometryRafHandle = 0;
20687
+ let v2GeometryReplayRafHandle = 0;
20688
+ let v2GeometryReplayGeneration = 0;
20577
20689
  let v2TrackedChangeRestampRetainPaints = 0;
20578
20690
  let v2TrackedChangeRestampRetentionReason = null;
20579
20691
  const activeEditorRef = computed$1(() => proxy.$superdoc.activeEditor);
@@ -20757,6 +20869,7 @@ var SuperDoc_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__P
20757
20869
  if (document && provider?.sendStateless) document.provider = null;
20758
20870
  const entry = v2Runtimes.get(documentId);
20759
20871
  if (!entry) return;
20872
+ entry.adapter.detach();
20760
20873
  proxy.$superdoc["unregisterEditorRuntime"](entry.runtimeId);
20761
20874
  v2Runtimes.delete(documentId);
20762
20875
  const hostRoot = subDocumentRoots.get(documentId);
@@ -21526,6 +21639,7 @@ var SuperDoc_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__P
21526
21639
  pending: entry
21527
21640
  });
21528
21641
  };
21642
+ let scheduleV2GeometryReplay = () => {};
21529
21643
  const v2GeometryPublisher = resolvedEditorIntegration.createGeometryPublisher({
21530
21644
  getLayersContainer: () => layers.value ?? null,
21531
21645
  isCommentsEnabled: () => Boolean(commentsModuleConfig.value) && shouldRenderCommentsInViewing.value,
@@ -21541,7 +21655,8 @@ var SuperDoc_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__P
21541
21655
  v2ReviewSidebarUnlocked.value = true;
21542
21656
  v2GeometryEpoch.value = v2GeometryPublisher.getLastEpoch();
21543
21657
  }
21544
- }
21658
+ },
21659
+ onCommittedPagePaint: ({ payload }) => scheduleV2GeometryReplay(payload)
21545
21660
  });
21546
21661
  const armV2TrackedChangeRestampGeometryRetention = (reason = "tracked-change-restamp") => {
21547
21662
  v2TrackedChangeRestampRetainPaints = Math.max(v2TrackedChangeRestampRetainPaints, 3);
@@ -21561,20 +21676,53 @@ var SuperDoc_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__P
21561
21676
  v2TrackedChangeRestampRetentionReason = null;
21562
21677
  };
21563
21678
  const resolveV2GeometryPublishOptions = (options) => typeof options === "function" ? options() : options;
21679
+ const isV2GeometryReady = (current) => {
21680
+ if (!current?.mountContainer || !commentsModuleConfig.value || !shouldRenderCommentsInViewing.value) return false;
21681
+ const mount = current.mount;
21682
+ let liveEpoch;
21683
+ try {
21684
+ liveEpoch = mount?.getRenderEpoch?.();
21685
+ } catch {
21686
+ return false;
21687
+ }
21688
+ return v2GeometryPublisher.getLastPayload() === current && v2GeometryPublisher.getLastEpoch() != null && v2GeometryPublisher.getLastEpoch() === current.epoch && (liveEpoch == null || liveEpoch === current.epoch);
21689
+ };
21564
21690
  const scheduleV2GeometryPublish = (payload, options = void 0) => {
21565
21691
  v2GeometryRender.value = payload;
21566
21692
  if (v2GeometryRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryRafHandle);
21693
+ const publish = () => {
21694
+ const current = v2GeometryRender.value;
21695
+ Promise.resolve(v2GeometryPublisher.publish(current, resolveV2GeometryPublishOptions(options))).then(() => {
21696
+ if (current === v2GeometryRender.value && isV2GeometryReady(current)) v2ReviewGeometryDelivery.flush(current);
21697
+ });
21698
+ };
21567
21699
  if (typeof requestAnimationFrame !== "function") {
21568
- v2GeometryPublisher.publish(v2GeometryRender.value, resolveV2GeometryPublishOptions(options));
21700
+ publish();
21569
21701
  return;
21570
21702
  }
21571
21703
  v2GeometryRafHandle = requestAnimationFrame(() => {
21572
21704
  v2GeometryRafHandle = 0;
21573
- v2GeometryPublisher.publish(v2GeometryRender.value, resolveV2GeometryPublishOptions(options));
21705
+ publish();
21574
21706
  });
21575
21707
  };
21708
+ const v2ReviewGeometryDelivery = createReviewGeometryDelivery((current, packet) => {
21709
+ v2GeometryPublisher.noteCommittedPageWindow?.(packet);
21710
+ scheduleV2GeometryPublish(current);
21711
+ });
21712
+ scheduleV2GeometryReplay = (payload) => {
21713
+ if (!payload || payload !== v2GeometryRender.value) return;
21714
+ const generation = ++v2GeometryReplayGeneration;
21715
+ if (v2GeometryReplayRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryReplayRafHandle);
21716
+ const replay = () => {
21717
+ v2GeometryReplayRafHandle = 0;
21718
+ if (generation === v2GeometryReplayGeneration && payload === v2GeometryRender.value && isV2GeometryReady(payload)) v2ReviewGeometryDelivery.replay(payload);
21719
+ };
21720
+ if (typeof requestAnimationFrame === "function") v2GeometryReplayRafHandle = requestAnimationFrame(replay);
21721
+ else queueMicrotask(replay);
21722
+ };
21576
21723
  const onV2Render = (payload) => {
21577
21724
  if (!payload) return;
21725
+ v2ReviewGeometryDelivery.observe(payload);
21578
21726
  scheduleV2GeometryPublish(payload, () => takeV2TrackedChangeRestampGeometryRetention());
21579
21727
  if (v2HasRangeSelection.value) scheduleV2SelectionToolbarStateSync();
21580
21728
  };
@@ -21607,7 +21755,11 @@ var SuperDoc_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__P
21607
21755
  cancelScheduledV2SelectionToolbarSync();
21608
21756
  if (v2GeometryRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryRafHandle);
21609
21757
  v2GeometryRafHandle = 0;
21758
+ if (v2GeometryReplayRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryReplayRafHandle);
21759
+ v2GeometryReplayRafHandle = 0;
21760
+ v2GeometryReplayGeneration++;
21610
21761
  v2GeometryRender.value = null;
21762
+ v2ReviewGeometryDelivery.reset();
21611
21763
  v2GeometryEpoch.value = null;
21612
21764
  v2GeometryAvailable.value = false;
21613
21765
  v2ReviewSidebarUnlocked.value = false;
@@ -21755,14 +21907,15 @@ var SuperDoc_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__P
21755
21907
  if (!isV2Mode.value) return;
21756
21908
  if (!v2GeometryPublisher.getLastPayload()) return;
21757
21909
  if (v2GeometryRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryRafHandle);
21758
- if (typeof requestAnimationFrame !== "function") {
21759
- v2GeometryPublisher.recollect(resolveV2GeometryPublishOptions(options));
21760
- return;
21761
- }
21762
- v2GeometryRafHandle = requestAnimationFrame(() => {
21910
+ const recollect = () => {
21763
21911
  v2GeometryRafHandle = 0;
21764
- v2GeometryPublisher.recollect(resolveV2GeometryPublishOptions(options));
21765
- });
21912
+ const current = v2GeometryRender.value;
21913
+ Promise.resolve(v2GeometryPublisher.recollect(resolveV2GeometryPublishOptions(options))).then(() => {
21914
+ if (current === v2GeometryRender.value && isV2GeometryReady(current)) v2ReviewGeometryDelivery.replay(current);
21915
+ });
21916
+ };
21917
+ if (typeof requestAnimationFrame !== "function") recollect();
21918
+ else v2GeometryRafHandle = requestAnimationFrame(recollect);
21766
21919
  };
21767
21920
  const handleV2DocumentModeChange = () => {
21768
21921
  if (!isV2Mode.value) return;
@@ -21990,13 +22143,17 @@ var SuperDoc_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__P
21990
22143
  ...payload,
21991
22144
  documentId: doc.id
21992
22145
  });
21993
- v2GeometryPublisher.noteCommittedPageWindow?.(payload);
22146
+ if (payload.reviewGeometry && Array.isArray(payload.pageIndices)) {
22147
+ const current = v2GeometryRender.value;
22148
+ v2ReviewGeometryDelivery.notify(String(doc.id), payload, current, isV2GeometryReady(current));
22149
+ } else v2GeometryPublisher.noteCommittedPageWindow?.(payload);
21994
22150
  },
21995
22151
  fontAssets: proxy.$superdoc.config.fonts,
21996
22152
  workerUrls: proxy.$superdoc.config.workerUrls,
21997
22153
  fieldContext: doc.fieldContext,
21998
22154
  fieldUpdatePolicy: proxy.$superdoc.config.fieldUpdatePolicy,
21999
22155
  workerStartupTimeoutMs: proxy.$superdoc.config.workerStartupTimeoutMs,
22156
+ ...proxy.$superdoc.config.changes ? { changes: proxy.$superdoc.config.changes } : {},
22000
22157
  proofing: resolvedProofingConfig.value,
22001
22158
  isNewFile,
22002
22159
  password: getDocumentLoadPassword(doc),
@@ -23079,7 +23236,7 @@ var SuperDoc_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__P
23079
23236
  ], 46, _hoisted_1$17);
23080
23237
  };
23081
23238
  }
23082
- }), [["__scopeId", "data-v-d2540ad3"]]);
23239
+ }), [["__scopeId", "data-v-7c932add"]]);
23083
23240
  //#endregion
23084
23241
  //#region src/core/create-app.ts
23085
23242
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
@@ -24560,6 +24717,16 @@ var SurfaceManager = class {
24560
24717
  };
24561
24718
  //#endregion
24562
24719
  //#region src/helpers/deprecation.ts
24720
+ /**
24721
+ * Deprecation utilities for steering consumers toward the Document API.
24722
+ *
24723
+ * ProseMirror will be removed in a future major version. These helpers emit
24724
+ * one-time console warnings when deprecated properties are accessed on an
24725
+ * editor instance handed to consumer callbacks (onEditorCreate, etc.).
24726
+ *
24727
+ * Internal code continues to use the raw Editor — only the consumer-facing
24728
+ * boundary wraps the editor with a Proxy.
24729
+ */
24563
24730
  var MIGRATION_URL = "https://docs.superdoc.dev/guides/migration/document-api";
24564
24731
  var _warned = /* @__PURE__ */ new Set();
24565
24732
  /**
@@ -25949,26 +26116,39 @@ var ToolbarButtonIcon_default = /* @__PURE__ */ _plugin_vue_export_helper_defaul
25949
26116
  var _hoisted_1$15 = [
25950
26117
  "role",
25951
26118
  "aria-label",
25952
- "aria-disabled"
26119
+ "aria-disabled",
26120
+ "aria-busy"
25953
26121
  ];
25954
26122
  var _hoisted_2$11 = ["data-item"];
25955
26123
  var _hoisted_3$10 = ["data-item"];
25956
26124
  var _hoisted_4$8 = {
25957
- key: 1,
26125
+ key: 0,
26126
+ role: "status",
26127
+ "aria-label": "Applying list",
26128
+ style: { "font-weight": "700" }
26129
+ };
26130
+ var _hoisted_5$6 = {
26131
+ key: 2,
25958
26132
  class: "sd-button-label"
25959
26133
  };
25960
- var _hoisted_5$6 = ["data-item", "aria-label"];
25961
- var _hoisted_6$4 = ["innerHTML"];
25962
- var _hoisted_7$2 = { key: 2 };
25963
- var _hoisted_8$2 = ["onKeydown", "id"];
25964
- var _hoisted_9$1 = [
26134
+ var _hoisted_6$4 = ["data-item", "aria-label"];
26135
+ var _hoisted_7$2 = ["innerHTML"];
26136
+ var _hoisted_8$2 = {
26137
+ key: 0,
26138
+ role: "status",
26139
+ "aria-label": "Applying list",
26140
+ style: { "font-weight": "700" }
26141
+ };
26142
+ var _hoisted_9$1 = { key: 3 };
26143
+ var _hoisted_10$1 = ["onKeydown", "id"];
26144
+ var _hoisted_11$1 = [
25965
26145
  "placeholder",
25966
26146
  "onKeydown",
25967
26147
  "id",
25968
26148
  "data-item"
25969
26149
  ];
25970
- var _hoisted_10$1 = ["data-item", "innerHTML"];
25971
- var _hoisted_11$1 = {
26150
+ var _hoisted_12$1 = ["data-item", "innerHTML"];
26151
+ var _hoisted_13$1 = {
25972
26152
  "aria-live": "polite",
25973
26153
  class: "sd-visually-hidden"
25974
26154
  };
@@ -26141,6 +26321,7 @@ var ToolbarButton_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/*
26141
26321
  role: __props.isOverflowItem ? "menuitem" : "button",
26142
26322
  "aria-label": unref(attributes).ariaLabel,
26143
26323
  "aria-disabled": unref(disabled) ? "true" : void 0,
26324
+ "aria-busy": unref(attributes).ariaBusy || void 0,
26144
26325
  "data-sd-part": "toolbar-item",
26145
26326
  onClick: handleOuterClick,
26146
26327
  onKeydown: _cache[9] || (_cache[9] = withKeys(($event) => onEnterKeydown($event), ["enter"])),
@@ -26162,17 +26343,21 @@ var ToolbarButton_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/*
26162
26343
  class: "sd-toolbar-button__main",
26163
26344
  "data-item": `btn-${unref(name) || ""}-main`,
26164
26345
  onClick: _cache[0] || (_cache[0] = ($event) => handleSplitMainClick($event))
26165
- }, [unref(icon) ? (openBlock(), createBlock(ToolbarButtonIcon_default, {
26166
- key: 0,
26167
- color: unref(iconColor),
26168
- class: "sd-toolbar-icon",
26169
- icon: unref(icon),
26170
- name: unref(name)
26171
- }, null, 8, [
26172
- "color",
26173
- "icon",
26174
- "name"
26175
- ])) : createCommentVNode("", true), unref(label) && !unref(hideLabel) && !unref(inlineTextInputVisible) ? (openBlock(), createElementBlock("div", _hoisted_4$8, toDisplayString(unref(label)), 1)) : createCommentVNode("", true)], 8, _hoisted_3$10)) : createCommentVNode("", true),
26346
+ }, [
26347
+ unref(attributes).ariaBusy && isSplit.value ? (openBlock(), createElementBlock("span", _hoisted_4$8, "…")) : createCommentVNode("", true),
26348
+ unref(icon) ? (openBlock(), createBlock(ToolbarButtonIcon_default, {
26349
+ key: 1,
26350
+ color: unref(iconColor),
26351
+ class: "sd-toolbar-icon",
26352
+ icon: unref(icon),
26353
+ name: unref(name)
26354
+ }, null, 8, [
26355
+ "color",
26356
+ "icon",
26357
+ "name"
26358
+ ])) : createCommentVNode("", true),
26359
+ unref(label) && !unref(hideLabel) && !unref(inlineTextInputVisible) ? (openBlock(), createElementBlock("div", _hoisted_5$6, toDisplayString(unref(label)), 1)) : createCommentVNode("", true)
26360
+ ], 8, _hoisted_3$10)) : createCommentVNode("", true),
26176
26361
  isSplit.value ? (openBlock(), createElementBlock("div", {
26177
26362
  key: 1,
26178
26363
  class: "sd-toolbar-button__caret",
@@ -26183,9 +26368,10 @@ var ToolbarButton_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/*
26183
26368
  class: "sd-dropdown-caret",
26184
26369
  innerHTML: caretIcon.value,
26185
26370
  style: normalizeStyle({ opacity: unref(disabled) ? .6 : 1 })
26186
- }, null, 12, _hoisted_6$4)], 8, _hoisted_5$6)) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [
26371
+ }, null, 12, _hoisted_7$2)], 8, _hoisted_6$4)) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [
26372
+ unref(attributes).ariaBusy ? (openBlock(), createElementBlock("span", _hoisted_8$2, "…")) : createCommentVNode("", true),
26187
26373
  unref(icon) ? (openBlock(), createBlock(ToolbarButtonIcon_default, {
26188
- key: 0,
26374
+ key: 1,
26189
26375
  color: unref(iconColor),
26190
26376
  class: "sd-toolbar-icon",
26191
26377
  icon: unref(icon),
@@ -26196,10 +26382,10 @@ var ToolbarButton_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/*
26196
26382
  "name"
26197
26383
  ])) : createCommentVNode("", true),
26198
26384
  unref(label) && !unref(hideLabel) && (!unref(inlineTextInputVisible) || itemName.value === "fontFamily") ? (openBlock(), createElementBlock("div", {
26199
- key: 1,
26385
+ key: 2,
26200
26386
  class: normalizeClass(["sd-button-label", { "sd-visually-hidden": unref(inlineTextInputVisible) && itemName.value === "fontFamily" }])
26201
26387
  }, toDisplayString(unref(label)), 3)) : createCommentVNode("", true),
26202
- unref(inlineTextInputVisible) ? (openBlock(), createElementBlock("span", _hoisted_7$2, [unref(name) === "fontSize" ? withDirectives((openBlock(), createElementBlock("input", {
26388
+ unref(inlineTextInputVisible) ? (openBlock(), createElementBlock("span", _hoisted_9$1, [unref(name) === "fontSize" ? withDirectives((openBlock(), createElementBlock("input", {
26203
26389
  key: 0,
26204
26390
  "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => inlineTextInput.value = $event),
26205
26391
  onKeydown: [withKeys(withModifiers(handleInlineInputEnter, ["stop", "prevent"]), ["enter"]), _cache[2] || (_cache[2] = withKeys(($event) => handleInlineInputTab($event), ["tab"]))],
@@ -26211,7 +26397,7 @@ var ToolbarButton_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/*
26211
26397
  autocomplete: "off",
26212
26398
  ref_key: "inlineInput",
26213
26399
  ref: inlineInput
26214
- }, null, 42, _hoisted_8$2)), [[vModelText, inlineTextInput.value]]) : withDirectives((openBlock(), createElementBlock("input", {
26400
+ }, null, 42, _hoisted_10$1)), [[vModelText, inlineTextInput.value]]) : withDirectives((openBlock(), createElementBlock("input", {
26215
26401
  key: 1,
26216
26402
  "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => inlineTextInput.value = $event),
26217
26403
  placeholder: unref(label),
@@ -26228,20 +26414,20 @@ var ToolbarButton_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/*
26228
26414
  autocomplete: "off",
26229
26415
  ref_key: "inlineInput",
26230
26416
  ref: inlineInput
26231
- }, null, 42, _hoisted_9$1)), [[vModelText, inlineTextInput.value]])])) : createCommentVNode("", true),
26417
+ }, null, 42, _hoisted_11$1)), [[vModelText, inlineTextInput.value]])])) : createCommentVNode("", true),
26232
26418
  unref(hasCaret) ? (openBlock(), createElementBlock("div", {
26233
- key: 3,
26419
+ key: 4,
26234
26420
  class: "sd-dropdown-caret",
26235
26421
  "data-item": `btn-${unref(name) || ""}-toggle`,
26236
26422
  innerHTML: caretIcon.value,
26237
26423
  style: normalizeStyle({ opacity: unref(disabled) ? .6 : 1 })
26238
- }, null, 12, _hoisted_10$1)) : createCommentVNode("", true)
26424
+ }, null, 12, _hoisted_12$1)) : createCommentVNode("", true)
26239
26425
  ], 64)),
26240
- createElementVNode("div", _hoisted_11$1, toDisplayString(`${unref(attributes).ariaLabel} ${unref(active) ? "selected" : "unset"}`), 1)
26426
+ createElementVNode("div", _hoisted_13$1, toDisplayString(`${unref(attributes).ariaLabel} ${unref(active) ? "selected" : "unset"}`), 1)
26241
26427
  ], 10, _hoisted_2$11)], 46, _hoisted_1$15);
26242
26428
  };
26243
26429
  }
26244
- }), [["__scopeId", "data-v-e599674c"]]);
26430
+ }), [["__scopeId", "data-v-b49ede12"]]);
26245
26431
  //#endregion
26246
26432
  //#region src/internal/toolbar/built-in/ToolbarSeparator.vue?vue&type=script&setup=true&lang.ts
26247
26433
  var _hoisted_1$14 = {
@@ -27533,7 +27719,7 @@ var SdTooltip_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__
27533
27719
  ]);
27534
27720
  const contentStyle = computed$1(() => ({
27535
27721
  ...props.contentStyle,
27536
- ...attrs.style || {},
27722
+ ...attrs.style,
27537
27723
  position: "fixed",
27538
27724
  top: position.value.top,
27539
27725
  left: position.value.left,
@@ -27702,7 +27888,7 @@ var SdTooltip_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__
27702
27888
  }, 8, ["css"])]))], 64);
27703
27889
  };
27704
27890
  }
27705
- }), [["__scopeId", "data-v-dfd6f1d1"]]);
27891
+ }), [["__scopeId", "data-v-87967ab1"]]);
27706
27892
  //#endregion
27707
27893
  //#region src/internal/toolbar/built-in/ButtonGroup.vue?vue&type=script&setup=true&lang.ts
27708
27894
  var _hoisted_1$11 = [
@@ -30526,6 +30712,8 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons: configuredIcons, toolbarTe
30526
30712
  attributes: { ariaLabel: "Bold" }
30527
30713
  });
30528
30714
  const fontOptions = (toolbarFonts ?? TOOLBAR_FONTS).map(normalizeFontOption);
30715
+ const isFontOption = (option) => typeof option.label === "string" && typeof option.key === "string";
30716
+ const currentFontOptions = () => fontButton.nestedOptions.value.filter(isFontOption);
30529
30717
  const fontButton = useToolbarItem({
30530
30718
  type: "dropdown",
30531
30719
  name: "fontFamily",
@@ -30552,8 +30740,9 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons: configuredIcons, toolbarTe
30552
30740
  if (typeof fontFamily !== "string" || !fontFamily) return;
30553
30741
  const family = fontFamily.split(",")[0];
30554
30742
  const optionValue = (option) => option.value ?? option.label;
30555
- const defaultFont = fontOptions.find((option) => optionValue(option) === fontButton.defaultLabel.value);
30556
- const foundFont = fontOptions.find((option) => optionValue(option) === family);
30743
+ const options = currentFontOptions();
30744
+ const defaultFont = options.find((option) => optionValue(option) === fontButton.defaultLabel.value);
30745
+ const foundFont = options.find((option) => optionValue(option) === family);
30557
30746
  if (foundFont) {
30558
30747
  fontButton.label.value = foundFont.label;
30559
30748
  fontButton.selectedValue.value = foundFont.key;
@@ -30566,7 +30755,7 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons: configuredIcons, toolbarTe
30566
30755
  }
30567
30756
  },
30568
30757
  onDeactivate: () => {
30569
- const defaultFont = fontOptions.find((option) => (option.value ?? option.label) === fontButton.defaultLabel.value);
30758
+ const defaultFont = currentFontOptions().find((option) => (option.value ?? option.label) === fontButton.defaultLabel.value);
30570
30759
  fontButton.label.value = defaultFont?.label ?? fontButton.defaultLabel.value;
30571
30760
  fontButton.selectedValue.value = defaultFont?.key ?? "";
30572
30761
  }
@@ -31778,7 +31967,7 @@ var V1_ENABLED_ON_SELECTION_REASON = /* @__PURE__ */ new Set([
31778
31967
  ]);
31779
31968
  var V1_ENABLED_ON_EMPTY_REASON = /* @__PURE__ */ new Set(["list", "numberedlist"]);
31780
31969
  function shouldKeepEnabledForV1ToolbarParity(name, commandState) {
31781
- if (name === "fontFamily" || name === "fontSize") return true;
31970
+ if (name === "fontFamily" || name === "fontSize") return commandState?.reason !== "document-readonly";
31782
31971
  if (V1_ENABLED_ON_EMPTY_REASON.has(name) && commandState?.disabled && commandState?.supported !== false && commandState?.reason == null) return true;
31783
31972
  if (!V1_ENABLED_ON_SELECTION_REASON.has(name)) return false;
31784
31973
  const reason = commandState?.reason;
@@ -31969,7 +32158,7 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
31969
32158
  const signature = this.#fontOptionsSignature();
31970
32159
  if (signature !== this._lastFontOptionsSignature) {
31971
32160
  this._lastFontOptionsSignature = signature;
31972
- this.#rebuildToolbarItems();
32161
+ if (!this.#refreshFontFamilyOptions()) this.#rebuildToolbarItems();
31973
32162
  }
31974
32163
  this.updateToolbarState();
31975
32164
  };
@@ -32117,6 +32306,13 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
32117
32306
  this.#makeToolbarItems();
32118
32307
  this.emit("toolbar-items-changed");
32119
32308
  }
32309
+ #refreshFontFamilyOptions() {
32310
+ const items = this.#getAllToolbarItems().filter((item) => item.name.value === "fontFamily");
32311
+ if (!items.length) return false;
32312
+ const options = (this.#resolveToolbarFonts(this.config.fonts) ?? TOOLBAR_FONTS).map(normalizeFontOption);
32313
+ for (const item of items) item.nestedOptions.value = options.map((option) => ({ ...option }));
32314
+ return true;
32315
+ }
32120
32316
  #fontOptionsSignature() {
32121
32317
  if (this.config.fonts) return "custom-fonts";
32122
32318
  const options = this.superdoc?.fonts?.getFontFamilyOptions?.() ?? [];
@@ -32296,6 +32492,10 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
32296
32492
  const commandId = resolveBuiltInItemCommandId(name);
32297
32493
  if (!commandId) return false;
32298
32494
  const commandState = this.snapshot?.commands?.[commandId];
32495
+ if ((name === "list" || name === "numberedlist") && item.attributes.value.ariaBusy !== Boolean(commandState?.pending)) item.attributes.value = {
32496
+ ...item.attributes.value,
32497
+ ariaBusy: Boolean(commandState?.pending)
32498
+ };
32299
32499
  if (shouldKeepEnabledForV1ToolbarParity(name, commandState)) item.setDisabled(false);
32300
32500
  else item.setDisabled(Boolean(commandState?.disabled));
32301
32501
  const handlers = {
@@ -32840,6 +33040,7 @@ var SuperDoc = class extends import_eventemitter3.default {
32840
33040
  #v2ConnectionUnsub;
32841
33041
  /** Latest `collaboration-connection-change` payload, for late readers. */
32842
33042
  #v2ConnectionState;
33043
+ #v2ConnectionFailureReported;
32843
33044
  /** Whether the current collaboration room has completed a sync at least once. */
32844
33045
  #v2ConnectionHasSynced;
32845
33046
  /** Lock state captured before a local → v2 collaboration promotion remount. */
@@ -32942,6 +33143,7 @@ var SuperDoc = class extends import_eventemitter3.default {
32942
33143
  this.#v2LockUnsub = null;
32943
33144
  this.#v2ConnectionUnsub = null;
32944
33145
  this.#v2ConnectionState = null;
33146
+ this.#v2ConnectionFailureReported = false;
32945
33147
  this.#v2ConnectionHasSynced = false;
32946
33148
  this.#pendingV2LockSeed = null;
32947
33149
  this.#mountWrapper = null;
@@ -33144,7 +33346,7 @@ var SuperDoc = class extends import_eventemitter3.default {
33144
33346
  }
33145
33347
  this.userColorMap = /* @__PURE__ */ new Map();
33146
33348
  this.colorIndex = 0;
33147
- this.version = "2.29.0";
33349
+ this.version = "2.30.0-next.4";
33148
33350
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
33149
33351
  this.superdocId = config.superdocId || v4();
33150
33352
  this.colors = this.config.colors ?? [];
@@ -33563,6 +33765,7 @@ var SuperDoc = class extends import_eventemitter3.default {
33563
33765
  if (this.#v2ConnectionState?.documentId !== documentId) {
33564
33766
  this.#v2ConnectionState = null;
33565
33767
  this.#v2ConnectionHasSynced = false;
33768
+ this.#v2ConnectionFailureReported = false;
33566
33769
  }
33567
33770
  const publish = (snapshot) => {
33568
33771
  if (this.#destroyed) return;
@@ -33577,8 +33780,21 @@ var SuperDoc = class extends import_eventemitter3.default {
33577
33780
  superdoc: this
33578
33781
  };
33579
33782
  this.#v2ConnectionState = payload;
33580
- if (snapshot.state === "synced") this.#v2ConnectionHasSynced = true;
33581
- this.emit("collaboration-connection-change", payload);
33783
+ if (snapshot.state === "synced") {
33784
+ this.#v2ConnectionHasSynced = true;
33785
+ this.#v2ConnectionFailureReported = false;
33786
+ }
33787
+ const firstFailure = snapshot.state === "failed" && !this.#v2ConnectionFailureReported;
33788
+ if (firstFailure) this.#v2ConnectionFailureReported = true;
33789
+ const reportFailure = firstFailure && snapshot.detail !== "worker-transport-failed";
33790
+ try {
33791
+ this.emit("collaboration-connection-change", payload);
33792
+ } finally {
33793
+ if (reportFailure) {
33794
+ const exception = createCollaborationException("collaboration-connection-failed", documentId);
33795
+ if (exception) this.emit("exception", exception);
33796
+ }
33797
+ }
33582
33798
  };
33583
33799
  try {
33584
33800
  const initialSnapshot = connection.getSnapshot?.();