superdoc 2.29.0 → 2.30.0-next.19

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 (138) 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-D7uBUZfs.cjs} +14347 -13625
  11. package/dist/chunks/{create-super-doc-ui-Dc8dQAMc.es.js → create-super-doc-ui-_t6kMzLs.es.js} +14076 -13360
  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/comments/comment-create-attribution.d.ts +4 -0
  17. package/dist/document-api/src/comments/comments.types.d.ts +4 -0
  18. package/dist/document-api/src/content-controls/content-controls.types.d.ts +1 -0
  19. package/dist/document-api/src/contract/command-catalog.d.ts +1 -1
  20. package/dist/document-api/src/contract/operation-definitions.d.ts +76 -8
  21. package/dist/document-api/src/contract/operation-registry.d.ts +2 -2
  22. package/dist/document-api/src/contract/schemas.d.ts +7 -1
  23. package/dist/document-api/src/create/create.d.ts +0 -4
  24. package/dist/document-api/src/diff/diff.types.d.ts +6 -6
  25. package/dist/document-api/src/header-footers/header-footers.d.ts +1 -3
  26. package/dist/document-api/src/index.d.ts +13 -1
  27. package/dist/document-api/src/inline-semantics/token-sets.d.ts +0 -2
  28. package/dist/document-api/src/insert/insert.d.ts +0 -1
  29. package/dist/document-api/src/paragraphs/paragraphs.d.ts +8 -3
  30. package/dist/document-api/src/paragraphs/paragraphs.types.d.ts +49 -5
  31. package/dist/document-api/src/tables/data/table-preset-appearance.generated.d.ts +2 -0
  32. package/dist/document-api/src/tables/table-style-presets.d.ts +73 -0
  33. package/dist/document-api/src/tables/tables.d.ts +4 -1
  34. package/dist/document-api/src/types/sd-props.d.ts +7 -0
  35. package/dist/document-api/src/types/table-operations.types.d.ts +12 -15
  36. package/dist/layout-engine/contracts/src/index.d.ts +4 -1
  37. package/dist/layout-engine/contracts/src/table-cell-vector-overlay.d.ts +2 -0
  38. package/dist/layout-engine/dom-contract/src/data-attrs.d.ts +4 -0
  39. package/dist/layout-engine/layout-bridge/src/coupled-footnote-pagination.d.ts +1 -0
  40. package/dist/layout-engine/layout-bridge/src/footnote-page-certificate.d.ts +1 -0
  41. package/dist/layout-engine/layout-bridge/src/footnote-page-planner.d.ts +5 -0
  42. package/dist/layout-engine/layout-bridge/src/incrementalLayout.d.ts +1 -0
  43. package/dist/layout-engine/layout-engine/src/footnote-page-flow.d.ts +3 -0
  44. package/dist/layout-engine/layout-engine/src/index.d.ts +7 -0
  45. package/dist/layout-engine/layout-engine/src/layout-paragraph.d.ts +5 -0
  46. package/dist/layout-engine/layout-engine/src/layout-table.d.ts +3 -1
  47. package/dist/layout-engine/layout-engine/src/layout-utils.d.ts +0 -4
  48. package/dist/layout-engine/layout-engine/src/paginator.d.ts +4 -0
  49. package/dist/layout-engine/painters/dom/src/index.d.ts +2 -0
  50. package/dist/layout-engine/painters/dom/src/paragraph/list-marker.d.ts +1 -1
  51. package/dist/layout-engine/painters/dom/src/paragraph/renderParagraphContent.d.ts +1 -1
  52. package/dist/layout-engine/painters/dom/src/styles.d.ts +0 -10
  53. package/dist/layout-engine/painters/dom/src/svg-utils.d.ts +1 -13
  54. package/dist/public/ui-react.cjs +1 -1
  55. package/dist/public/ui-react.es.js +1 -1
  56. package/dist/public/ui-vue.cjs +1 -1
  57. package/dist/public/ui-vue.es.js +1 -1
  58. package/dist/public/ui.cjs +1 -1
  59. package/dist/public/ui.es.js +1 -1
  60. package/dist/shared/common/Telemetry.d.ts +50 -0
  61. package/dist/shared/common/event-types.d.ts +13 -0
  62. package/dist/shared/font-system/src/bundled-face-names.d.ts +1 -0
  63. package/dist/shared/font-system/src/resolver.d.ts +1 -0
  64. package/dist/style.css +1 -1
  65. package/dist/style.layered.css +1 -1
  66. package/dist/superdoc/src/components/CommentsLayer/CommentDialog.vue.d.ts +17 -33
  67. package/dist/superdoc/src/components/CommentsLayer/collect-tracked-change-thread.d.ts +6 -4
  68. package/dist/superdoc/src/components/CommentsLayer/comment-params.d.ts +84 -0
  69. package/dist/superdoc/src/components/CommentsLayer/use-comment.d.ts +15 -95
  70. package/dist/superdoc/src/components/Whiteboard/use-whiteboard.d.ts +30 -9
  71. package/dist/superdoc/src/composables/use-comment-sidebar.d.ts +6 -0
  72. package/dist/superdoc/src/composables/use-document.d.ts +51 -99
  73. package/dist/superdoc/src/core/SuperDoc.d.ts +12 -3
  74. package/dist/superdoc/src/core/collaboration/cdn-collaboration-comment-map.d.ts +1 -0
  75. package/dist/superdoc/src/core/collaboration/collaboration-comment-map.d.ts +2 -0
  76. package/dist/superdoc/src/core/collaboration/helpers.d.ts +1 -1
  77. package/dist/superdoc/src/core/config/normalize-interaction-config.d.ts +10 -1
  78. package/dist/superdoc/src/core/document-open-telemetry.d.ts +1 -1
  79. package/dist/superdoc/src/core/editor-runtime/types.d.ts +4 -0
  80. package/dist/superdoc/src/core/editor-runtime/v2/v2-editor-runtime-adapter.d.ts +1 -0
  81. package/dist/superdoc/src/core/types/index.d.ts +110 -42
  82. package/dist/superdoc/src/core/v2-integration/v2-integration.d.ts +6 -0
  83. package/dist/superdoc/src/helpers/deprecation.d.ts +12 -3
  84. package/dist/superdoc/src/helpers/object-guards.d.ts +1 -0
  85. package/dist/superdoc/src/helpers/v2-review-geometry-delivery.d.ts +22 -0
  86. package/dist/superdoc/src/internal/toolbar/built-in/ToolbarButton.vue.d.ts +1 -0
  87. package/dist/superdoc/src/public/content-control-ref.d.ts +12 -0
  88. package/dist/superdoc/src/public/index.d.cts +20 -0
  89. package/dist/superdoc/src/public/index.d.ts +4 -1
  90. package/dist/superdoc/src/public/ui/async-read-cache.d.ts +32 -0
  91. package/dist/superdoc/src/public/ui/block-commands.d.ts +39 -0
  92. package/dist/superdoc/src/public/ui/command-results.d.ts +39 -0
  93. package/dist/superdoc/src/public/ui/commands.d.ts +5 -1
  94. package/dist/superdoc/src/public/ui/content-controls-handle.d.ts +31 -0
  95. package/dist/superdoc/src/public/ui/create-super-doc-ui.d.ts +1 -17
  96. package/dist/superdoc/src/public/ui/document-change-records.d.ts +7 -0
  97. package/dist/superdoc/src/public/ui/entity-rows.d.ts +14 -0
  98. package/dist/superdoc/src/public/ui/font-options.d.ts +7 -0
  99. package/dist/superdoc/src/public/ui/format-painter-paragraph.d.ts +7 -0
  100. package/dist/superdoc/src/public/ui/format-painter-state.d.ts +29 -0
  101. package/dist/superdoc/src/public/ui/heavy-read-gate.d.ts +41 -0
  102. package/dist/superdoc/src/public/ui/heavy-read-policy.d.ts +11 -0
  103. package/dist/superdoc/src/public/ui/host-events.d.ts +3 -0
  104. package/dist/superdoc/src/public/ui/inline-values.d.ts +80 -0
  105. package/dist/superdoc/src/public/ui/loose-record.d.ts +4 -0
  106. package/dist/superdoc/src/public/ui/reasons.d.ts +1 -0
  107. package/dist/superdoc/src/public/ui/search-handle.d.ts +24 -0
  108. package/dist/superdoc/src/public/ui/selection-targets.d.ts +53 -0
  109. package/dist/superdoc/src/public/ui/shared-tracked-changes-catalog.d.ts +16 -0
  110. package/dist/superdoc/src/public/ui/styles-handle.d.ts +54 -0
  111. package/dist/superdoc/src/public/ui/tracked-change-ids.d.ts +51 -0
  112. package/dist/superdoc/src/public/ui/types.d.ts +31 -2
  113. package/dist/superdoc/src/public/ui/ui-bench-timing.d.ts +9 -0
  114. package/dist/superdoc/src/public/ui.d.cts +18 -0
  115. package/dist/superdoc/src/public/ui.d.ts +1 -1
  116. package/dist/superdoc/src/stores/comments-store.d.ts +23052 -537
  117. package/dist/superdoc/src/stores/helpers/comment-grouping.d.ts +1 -1
  118. package/dist/superdoc/src/stores/helpers/comment-identity.d.ts +10 -10
  119. package/dist/superdoc/src/stores/helpers/pending-selection-snapshot.d.ts +3 -3
  120. package/dist/superdoc/src/stores/helpers/review-presentation.d.ts +91 -0
  121. package/dist/superdoc/src/stores/helpers/tracked-change-identity.d.ts +4 -2
  122. package/dist/superdoc/src/stores/helpers/tracked-change-row-values.d.ts +1 -8
  123. package/dist/superdoc/src/stores/helpers/tracked-change-thread-index.d.ts +1 -1
  124. package/dist/superdoc/src/stores/helpers/v2-review-adapters.d.ts +148 -0
  125. package/dist/superdoc/src/stores/superdoc-store.d.ts +12 -2895
  126. package/dist/superdoc.cjs +1000 -566
  127. package/dist/superdoc.es.js +1000 -566
  128. package/dist/word-layout/src/marker-utils.d.ts +0 -5
  129. package/dist/word-layout/src/unit-conversions.d.ts +0 -1
  130. package/dist-cdn/style.layered.css +1 -1
  131. package/dist-cdn/superdoc.min.css +1 -1
  132. package/dist-cdn/superdoc.min.js +38 -42
  133. package/package.json +12 -3
  134. package/dist/document-api/src/diff/index.d.ts +0 -3
  135. package/dist/document-api/src/write/locator.d.ts +0 -24
  136. package/dist/layout-engine/painters/dom/src/features/feature-registry.d.ts +0 -41
  137. package/dist/superdoc/src/internal/toolbar/built-in/format-negation.d.ts +0 -11
  138. package/dist/word-layout/src/tab-layout.d.ts +0 -193
package/dist/superdoc.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_rolldown_runtime = require("./chunks/rolldown-runtime-74VwNCz7.cjs");
3
- const require__plugin_vue_export_helper = require("./chunks/_plugin-vue_export-helper-BkbCxNeT.cjs");
3
+ const require__plugin_vue_export_helper = require("./chunks/_plugin-vue_export-helper-BzM2_tVU.cjs");
4
4
  const require_blank_docx = require("./chunks/blank-docx-BuFAbRjs.cjs");
5
- const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-L60GhVAX.cjs");
5
+ const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-D7uBUZfs.cjs");
6
6
  const require_eventemitter3 = require("./chunks/eventemitter3-C_TAnXOl.cjs");
7
7
  const require_uuid = require("./chunks/uuid-BhG0ngwk.cjs");
8
8
  const require_jszip = require("./chunks/jszip-D8mAFF-r.cjs");
@@ -259,7 +259,7 @@ var v_click_outside_default = {
259
259
  var DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
260
260
  function getSuperdocVersion() {
261
261
  try {
262
- return "2.29.0";
262
+ return "2.30.0-next.19";
263
263
  } catch {
264
264
  return "unknown";
265
265
  }
@@ -5839,6 +5839,11 @@ function useCheckboxField(field) {
5839
5839
  }) : itemoptions) };
5840
5840
  }
5841
5841
  //#endregion
5842
+ //#region src/core/collaboration/collaboration-comment-map.ts
5843
+ function createCollaborationCommentMap(entries) {
5844
+ return new yjs.Map(entries);
5845
+ }
5846
+ //#endregion
5842
5847
  //#region src/core/collaboration/collaboration-comments.ts
5843
5848
  var readComment = (event) => {
5844
5849
  const { comment } = event;
@@ -5847,14 +5852,14 @@ var readComment = (event) => {
5847
5852
  };
5848
5853
  var addYComment = (yArray, ydoc, event, user) => {
5849
5854
  const comment = readComment(event);
5850
- const yComment = new yjs.Map(Object.entries(comment));
5855
+ const yComment = createCollaborationCommentMap(Object.entries(comment));
5851
5856
  ydoc.transact(() => {
5852
5857
  yArray.push([yComment]);
5853
5858
  }, { user });
5854
5859
  };
5855
5860
  var updateYComment = (yArray, ydoc, event, user) => {
5856
5861
  const comment = readComment(event);
5857
- const yComment = new yjs.Map(Object.entries(comment));
5862
+ const yComment = createCollaborationCommentMap(Object.entries(comment));
5858
5863
  const commentIndex = getCommentIndex(yArray, comment);
5859
5864
  if (commentIndex === -1) return;
5860
5865
  ydoc.transact(() => {
@@ -6751,6 +6756,136 @@ var useSuperdocStore = defineStore("superdoc", () => {
6751
6756
  };
6752
6757
  });
6753
6758
  //#endregion
6759
+ //#region ../document-api/src/errors.ts
6760
+ /**
6761
+ * Structured validation error thrown by document-api execute* functions.
6762
+ *
6763
+ * Consumers should prefer checking `error.code` over `instanceof` for resilience
6764
+ * across package boundaries and bundling scenarios.
6765
+ */
6766
+ var DocumentApiValidationError = class DocumentApiValidationError extends Error {
6767
+ constructor(code, message, details) {
6768
+ super(message);
6769
+ this.name = "DocumentApiValidationError";
6770
+ this.code = code;
6771
+ this.details = details;
6772
+ Object.setPrototypeOf(this, DocumentApiValidationError.prototype);
6773
+ }
6774
+ };
6775
+ //#endregion
6776
+ //#region ../document-api/src/export/export.types.ts
6777
+ /** Canonical tracked-change export modes accepted by every public export surface. */
6778
+ var SD_EXPORT_MODES = [
6779
+ "review-preserving",
6780
+ "final",
6781
+ "original"
6782
+ ];
6783
+ /** Default mode for modeful DOCX export. */
6784
+ var DEFAULT_SD_EXPORT_MODE = "review-preserving";
6785
+ var SD_COMMENT_EXPORT_POLICIES = [
6786
+ "preserve",
6787
+ "resolve",
6788
+ "remove"
6789
+ ];
6790
+ var SD_CONTENT_CONTROL_EXPORT_POLICIES = ["preserve", "flatten"];
6791
+ //#endregion
6792
+ //#region ../document-api/src/export/export.ts
6793
+ /**
6794
+ * Export namespace: engine-agnostic public API and adapter contract.
6795
+ *
6796
+ * Validates the requested mode at the public boundary so an invalid mode fails
6797
+ * closed with `INVALID_INPUT` before any document work happens, then delegates
6798
+ * byte production and degradation reporting to the engine adapter.
6799
+ */
6800
+ function resolveMode$1(input) {
6801
+ if (input != null && !require_create_super_doc_ui.isRecord(input)) throw new DocumentApiValidationError("INVALID_INPUT", "export.toDocx input must be an object.");
6802
+ const raw = input?.mode;
6803
+ if (raw === void 0) return DEFAULT_SD_EXPORT_MODE;
6804
+ 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(", ")}.`, {
6805
+ mode: raw,
6806
+ allowedModes: [...SD_EXPORT_MODES]
6807
+ });
6808
+ return raw;
6809
+ }
6810
+ function resolveDocxExportPolicies(input = {}) {
6811
+ if (!require_create_super_doc_ui.isRecord(input)) throw new DocumentApiValidationError("INVALID_INPUT", "DOCX export input must be an object.");
6812
+ const mode = resolveMode$1(input);
6813
+ const policies = input.policies;
6814
+ if (policies !== void 0 && !require_create_super_doc_ui.isRecord(policies)) throw new DocumentApiValidationError("INVALID_INPUT", "DOCX export policies must be an object.");
6815
+ const allowed = {
6816
+ trackedChanges: SD_EXPORT_MODES,
6817
+ comments: SD_COMMENT_EXPORT_POLICIES,
6818
+ contentControls: SD_CONTENT_CONTROL_EXPORT_POLICIES
6819
+ };
6820
+ 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}.`, {
6821
+ policy: key,
6822
+ value
6823
+ });
6824
+ const requested = policies;
6825
+ if (input.mode !== void 0 && requested?.trackedChanges !== void 0 && mode !== requested.trackedChanges) throw new DocumentApiValidationError("INVALID_INPUT", "mode conflicts with policies.trackedChanges.");
6826
+ return {
6827
+ trackedChanges: requested?.trackedChanges ?? mode,
6828
+ comments: requested?.comments ?? "preserve",
6829
+ contentControls: requested?.contentControls ?? "preserve"
6830
+ };
6831
+ }
6832
+ new TextEncoder();
6833
+ function normalizeCommentIdentity(value) {
6834
+ if (value === null) return null;
6835
+ if (typeof value !== "object" || Array.isArray(value) || !("providerId" in value) || !("userId" in value) || !xmlIdentityString(value.providerId) || !xmlIdentityString(value.userId)) throw new TypeError("commentIdentity requires XML-safe non-empty providerId and userId strings.");
6836
+ return {
6837
+ providerId: value.providerId,
6838
+ userId: value.userId
6839
+ };
6840
+ }
6841
+ function xmlIdentityString(value) {
6842
+ return typeof value === "string" && value.trim().length > 0 && !/[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/u.test(value);
6843
+ }
6844
+ //#endregion
6845
+ //#region ../document-api/src/permission-ranges/permission-ranges.ts
6846
+ function editorKey(editor) {
6847
+ return editor.id?.trim() ? editor.id : editor.email?.trim() || void 0;
6848
+ }
6849
+ function editorMatches(editor, id) {
6850
+ return Boolean(id.trim()) && (editor.id === id || Boolean(editor.email?.trim() && editor.email.trim().toLowerCase() === id.toLowerCase()));
6851
+ }
6852
+ /**
6853
+ * Resolve a host ID or imported Word email against an explicitly supplied roster.
6854
+ * Word receives the email when available; opaque IDs remain host-specific.
6855
+ * Duplicate IDs/emails are ambiguous. This does not authenticate a user.
6856
+ */
6857
+ function resolvePermissionRangeEditor(editors, id) {
6858
+ const matches = editors.filter((editor) => editorMatches(editor, id));
6859
+ if (matches.length === 0) return {
6860
+ status: "unknown",
6861
+ id
6862
+ };
6863
+ if (matches.length !== 1) return {
6864
+ status: "ambiguous",
6865
+ id
6866
+ };
6867
+ const editor = matches[0];
6868
+ const wordId = editor.email?.trim() || editor.id;
6869
+ if (editors.filter((candidate) => editorMatches(candidate, wordId)).length !== 1) return {
6870
+ status: "ambiguous",
6871
+ id
6872
+ };
6873
+ return {
6874
+ status: "resolved",
6875
+ id,
6876
+ editor: { ...editor },
6877
+ principal: {
6878
+ kind: "editor",
6879
+ id: wordId
6880
+ }
6881
+ };
6882
+ }
6883
+ /** Enumerate the host roster for an editor picker, including ambiguous entries. */
6884
+ function listPermissionRangeEditors(editors) {
6885
+ const ids = new Set(editors.map(editorKey).filter((id) => id !== void 0));
6886
+ return Array.from(ids, (id) => resolvePermissionRangeEditor(editors, id));
6887
+ }
6888
+ //#endregion
6754
6889
  //#region src/stores/helpers/floating-comment-instances.ts
6755
6890
  var HEADER_FOOTER_TRACKED_CHANGE_STORY_TYPE = "headerFooterPart";
6756
6891
  var toFinitePageIndex = (value) => {
@@ -7112,6 +7247,7 @@ var groupCommittedComments = (comments, presentations, isVisible) => {
7112
7247
  });
7113
7248
  const grouped = groupComments(comments.filter((row) => !presentations[String(row.fileId)]), isVisible);
7114
7249
  for (const [documentId, presentation] of Object.entries(presentations)) {
7250
+ if (!presentation) continue;
7115
7251
  grouped.parentComments.push(...visibleRowsInOrder(documentId, presentation.groupOrder.open));
7116
7252
  grouped.resolvedComments.push(...visibleRowsInOrder(documentId, presentation.groupOrder.resolved));
7117
7253
  }
@@ -7807,9 +7943,55 @@ var createDroppedCommentIdMap = (duplicates) => {
7807
7943
  return droppedCommentIds;
7808
7944
  };
7809
7945
  //#endregion
7810
- //#region src/stores/comments-store.js
7946
+ //#region src/stores/helpers/v2-review-adapters.ts
7947
+ var COMMENTS_ADAPTER_METHODS = [
7948
+ "setActiveComment",
7949
+ "commitPendingComment",
7950
+ "reply",
7951
+ "edit",
7952
+ "resolve",
7953
+ "reopen",
7954
+ "delete",
7955
+ "mapV2CommentToUseCommentInput"
7956
+ ];
7957
+ var COMMENTS_ADAPTER_OPTIONAL_METHODS = [
7958
+ "getCapabilityState",
7959
+ "captureCurrentSelection",
7960
+ "isPendingCommentCreation",
7961
+ "seedReviewCatalog",
7962
+ "selectVisibleReviewComments"
7963
+ ];
7964
+ var TRACKED_CHANGES_ADAPTER_METHODS = [
7965
+ "accept",
7966
+ "reject",
7967
+ "mapV2TrackedChangeToCommentParams"
7968
+ ];
7969
+ var TRACKED_CHANGES_ADAPTER_OPTIONAL_METHODS = ["clearActiveTrackedChangeTargetIfMatches"];
7970
+ var isAdapterDocumentId = (value) => value === void 0 || value === null || typeof value === "string" || typeof value === "number";
7971
+ var hasAdapterShape = (value, required, optional) => require__plugin_vue_export_helper.isPlainObject(value) && isAdapterDocumentId(value.documentId) && required.every((member) => typeof value[member] === "function") && optional.every((member) => value[member] === void 0 || typeof value[member] === "function");
7972
+ function isV2CommentsAdapter(value) {
7973
+ return hasAdapterShape(value, COMMENTS_ADAPTER_METHODS, COMMENTS_ADAPTER_OPTIONAL_METHODS);
7974
+ }
7975
+ function isV2TrackedChangesAdapter(value) {
7976
+ return hasAdapterShape(value, TRACKED_CHANGES_ADAPTER_METHODS, TRACKED_CHANGES_ADAPTER_OPTIONAL_METHODS);
7977
+ }
7978
+ //#endregion
7979
+ //#region src/stores/comments-store.ts
7980
+ var requireHost = (superdoc, action) => {
7981
+ if (!superdoc) throw new TypeError(`${action} requires superdoc`);
7982
+ return superdoc;
7983
+ };
7984
+ var PARENT_LINK_FIELDS = [
7985
+ "trackedChangeThreadParentId",
7986
+ "trackedChangeParentId",
7987
+ "threadingParentCommentId",
7988
+ "parentCommentId"
7989
+ ];
7990
+ var isPresent = (value) => Boolean(value);
7991
+ var errorDetail = (error) => (error == null ? void 0 : Reflect.get(Object(error), "message")) ?? String(error);
7811
7992
  var useCommentsStore = defineStore("comments", () => {
7812
7993
  const superdocStore = useSuperdocStore();
7994
+ const findDocument = (documentId) => typeof documentId === "string" ? superdocStore.getDocument(documentId) : void 0;
7813
7995
  const commentsConfig = require__plugin_vue_export_helper.reactive({
7814
7996
  name: "comments",
7815
7997
  readOnly: false,
@@ -7839,6 +8021,7 @@ var useCommentsStore = defineStore("comments", () => {
7839
8021
  });
7840
8022
  const debounceTimers = {};
7841
8023
  const trackedChangeResolutionSnapshots = /* @__PURE__ */ new WeakMap();
8024
+ let cachedTrackedChangeBatchIdentityIndex = null;
7842
8025
  const isBenchmarkCommentsDebugEnabled = () => {
7843
8026
  if (typeof window === "undefined") return false;
7844
8027
  return "__labsSuperDocV2BenchmarkDebug" in window;
@@ -7876,7 +8059,7 @@ var useCommentsStore = defineStore("comments", () => {
7876
8059
  });
7877
8060
  const getTrackedChangeThread = (parentComment) => {
7878
8061
  const id = parentComment?.commentId ?? null;
7879
- return (id != null ? trackedChangeThreadIndex.value.get(id) : null) ?? [parentComment].filter(Boolean);
8062
+ return (id != null ? trackedChangeThreadIndex.value.get(id) : null) ?? (parentComment ? [parentComment] : []);
7880
8063
  };
7881
8064
  let priorTrackedChangeDecisionLinkIndex = /* @__PURE__ */ new Map();
7882
8065
  const trackedChangeDecisionLinkIndex = require__plugin_vue_export_helper.computed(() => {
@@ -7887,6 +8070,7 @@ var useCommentsStore = defineStore("comments", () => {
7887
8070
  const isCommentsListVisible = require__plugin_vue_export_helper.ref(false);
7888
8071
  const isReviewDirectoryActive = require__plugin_vue_export_helper.ref(false);
7889
8072
  const isReviewDirectoryLoading = require__plugin_vue_export_helper.ref(false);
8073
+ const reviewAnalysisProgress = require__plugin_vue_export_helper.ref(null);
7890
8074
  const editorCommentIds = require__plugin_vue_export_helper.ref([]);
7891
8075
  const editorCommentPositions = require__plugin_vue_export_helper.ref({});
7892
8076
  const isCommentHighlighted = require__plugin_vue_export_helper.ref(false);
@@ -7908,29 +8092,28 @@ var useCommentsStore = defineStore("comments", () => {
7908
8092
  });
7909
8093
  const v2CommentsAdapter = require__plugin_vue_export_helper.shallowRef(null);
7910
8094
  const setV2CommentsAdapter = (adapter) => {
7911
- if (v2CommentsAdapter.value !== (adapter ?? null)) pendingCommentRequest += 1;
7912
- v2CommentsAdapter.value = adapter ?? null;
8095
+ const next = isV2CommentsAdapter(adapter) ? adapter : null;
8096
+ if (v2CommentsAdapter.value !== next) pendingCommentRequest += 1;
8097
+ v2CommentsAdapter.value = next;
7913
8098
  };
7914
8099
  const getV2CommentsAdapter = (superdoc) => {
7915
- const fromFacade = superdoc?.activeEditor?.v2Comments ?? null;
7916
- if (fromFacade) return fromFacade;
8100
+ const fromFacade = superdoc?.activeEditor?.v2Comments;
8101
+ if (isV2CommentsAdapter(fromFacade)) return fromFacade;
7917
8102
  return v2CommentsAdapter.value;
7918
8103
  };
7919
8104
  const isV2EditorActive = (superdoc) => superdoc?.activeEditor?.editorVersion === 2 || v2CommentsAdapter.value !== null;
7920
8105
  const v2TrackedChangesAdapter = require__plugin_vue_export_helper.shallowRef(null);
7921
8106
  const setV2TrackedChangesAdapter = (adapter) => {
7922
- v2TrackedChangesAdapter.value = adapter ?? null;
8107
+ cachedTrackedChangeBatchIdentityIndex = null;
8108
+ v2TrackedChangesAdapter.value = isV2TrackedChangesAdapter(adapter) ? adapter : null;
7923
8109
  };
7924
8110
  const getV2TrackedChangesAdapter = (superdoc) => {
7925
- const fromFacade = superdoc?.activeEditor?.v2TrackedChanges ?? null;
7926
- if (fromFacade) return fromFacade;
8111
+ const fromFacade = superdoc?.activeEditor?.v2TrackedChanges;
8112
+ if (isV2TrackedChangesAdapter(fromFacade)) return fromFacade;
7927
8113
  return v2TrackedChangesAdapter.value;
7928
8114
  };
7929
8115
  /**
7930
8116
  * Initialize the store
7931
- *
7932
- * @param {Object} config The comments module config from SuperDoc
7933
- * @returns {void}
7934
8117
  */
7935
8118
  const init = (config = {}) => {
7936
8119
  const updatedConfig = {
@@ -7962,12 +8145,17 @@ var useCommentsStore = defineStore("comments", () => {
7962
8145
  comment.trackedChangeDecision = null;
7963
8146
  };
7964
8147
  const getCommentEventPayload = (comment) => typeof comment?.getValues === "function" ? comment.getValues() : { ...comment };
8148
+ const getDeletedCommentChanges = (sources, fileId) => {
8149
+ const commentId = [...sources.map((source) => source?.commentId), ...sources.map((source) => source?.importedId)].map(normalizeCommentId).find(Boolean);
8150
+ return commentId ? [{
8151
+ key: "deleted",
8152
+ commentId,
8153
+ fileId
8154
+ }] : [];
8155
+ };
7965
8156
  /**
7966
8157
  * Check if a comment originated from the document editor (or has no explicit source).
7967
8158
  * Comments without a source are assumed to be editor-backed for backward compatibility.
7968
- *
7969
- * @param {Object} comment - The comment to check
7970
- * @returns {boolean} True if the comment is editor-backed
7971
8159
  */
7972
8160
  const isEditorBackedComment = (comment) => {
7973
8161
  const source = comment?.selection?.source;
@@ -8001,9 +8189,6 @@ var useCommentsStore = defineStore("comments", () => {
8001
8189
  }, { deep: false });
8002
8190
  /**
8003
8191
  * Get the editor position data for a comment.
8004
- *
8005
- * @param {Object | string} commentOrId The comment object or comment ID
8006
- * @returns {Object | null} The position data from editorCommentPositions
8007
8192
  */
8008
8193
  const getCommentPosition = (commentOrId) => {
8009
8194
  return resolveCommentPositionEntry(commentOrId).entry ?? null;
@@ -8016,10 +8201,6 @@ var useCommentsStore = defineStore("comments", () => {
8016
8201
  };
8017
8202
  /**
8018
8203
  * Set the active comment or clear all active comments
8019
- *
8020
- * @param {Object | undefined | null} superdoc The SuperDoc instance holding the active editor
8021
- * @param {string | undefined | null} id The comment ID
8022
- * @returns {void}
8023
8204
  */
8024
8205
  const setActiveComment = (superdoc, id) => {
8025
8206
  const activeEditor = superdoc?.activeEditor;
@@ -8030,7 +8211,7 @@ var useCommentsStore = defineStore("comments", () => {
8030
8211
  v2Adapter.setActiveComment(null);
8031
8212
  return;
8032
8213
  }
8033
- activeEditor?.commands?.setActiveComment({ commentId: null });
8214
+ activeEditor?.commands?.setActiveComment?.({ commentId: null });
8034
8215
  return;
8035
8216
  }
8036
8217
  const comment = getComment(id);
@@ -8041,18 +8222,13 @@ var useCommentsStore = defineStore("comments", () => {
8041
8222
  v2Adapter.setActiveComment(comment.commentId);
8042
8223
  return;
8043
8224
  }
8044
- activeEditor?.commands?.setActiveComment({ commentId: activeComment.value });
8225
+ activeEditor?.commands?.setActiveComment?.({ commentId: activeComment.value });
8045
8226
  };
8046
8227
  /**
8047
8228
  * Called when a tracked change is updated. Creates a new comment if necessary,
8048
8229
  * or updates an existing tracked-change comment.
8049
- *
8050
- * @param {Object} param0
8051
- * @param {Object} param0.superdoc The SuperDoc instance
8052
- * @param {Object} param0.params The tracked change params
8053
- * @returns {void}
8054
8230
  */
8055
- const handleTrackedChangeUpdate = ({ superdoc, params, broadcastChanges = true, trackedChangeIdentityIndex = null }) => {
8231
+ const handleTrackedChangeUpdate = ({ superdoc, params, broadcastChanges = true, trackedChangeIdentityIndex = null, preserveDraft = false }) => {
8056
8232
  const span = startInteractionSpan("store.trackedChanges.handleUpdate", "store-reconciliation", {
8057
8233
  event: params?.event ?? null,
8058
8234
  changeId: params?.changeId ?? null,
@@ -8077,9 +8253,11 @@ var useCommentsStore = defineStore("comments", () => {
8077
8253
  const normalizedTrackedChangeLabel = typeof trackedChangeLabel === "string" && trackedChangeLabel.length > 0 ? trackedChangeLabel : null;
8078
8254
  const normalizedTrackedChangeDetailLines = normalizeTrackedChangeDetailLines(trackedChangeDetailLines);
8079
8255
  const normalizedTrackedChangeImagePreview = normalizeTrackedChangeImagePreview(trackedChangeImagePreview);
8256
+ const hasDraft = preserveDraft && (pendingComment.value !== null || currentCommentText.value !== "" || currentCommentMentions.value.length > 0);
8080
8257
  const comment = getPendingComment({
8081
8258
  documentId,
8082
- commentId: changeId,
8259
+ ...hasDraft ? { commentText: "" } : {},
8260
+ commentId: normalizedChangeId ?? void 0,
8083
8261
  trackedChange: true,
8084
8262
  trackedChangeText,
8085
8263
  trackedChangeType,
@@ -8131,7 +8309,7 @@ var useCommentsStore = defineStore("comments", () => {
8131
8309
  commentCanonicalId,
8132
8310
  commentAnchorKey,
8133
8311
  ...commentAliasIds
8134
- ].filter(Boolean).some((id) => incomingAliasIds.has(id))) return true;
8312
+ ].some((id) => id != null && incomingAliasIds.has(id))) return true;
8135
8313
  const hasSyntheticSideIdentity = normalizedTrackedChangeCanonicalId && normalizedTrackedChangeCanonicalId !== normalizedChangeId || commentCanonicalId && commentCanonicalId !== commentId;
8136
8314
  if (hasCompatibleStory && commentId === normalizedChangeId) return true;
8137
8315
  if (hasCompatibleStory && !hasSyntheticSideIdentity && (importedId === normalizedChangeId || normalizedImportedId !== void 0 && normalizedImportedId != null && importedId === normalizedImportedId)) return true;
@@ -8155,7 +8333,7 @@ var useCommentsStore = defineStore("comments", () => {
8155
8333
  };
8156
8334
  const emitTrackedChangeEvent = (event) => {
8157
8335
  if (!broadcastChanges) return;
8158
- syncCommentsToClients(superdoc, event);
8336
+ syncCommentsToClients(requireHost(superdoc, "handleTrackedChangeUpdate"), event);
8159
8337
  debounceEmit(changeId, event, superdoc);
8160
8338
  };
8161
8339
  const setIfChanged = (target, key, value) => {
@@ -8198,8 +8376,9 @@ var useCommentsStore = defineStore("comments", () => {
8198
8376
  target?.trackedChangeCanonicalId,
8199
8377
  target?.trackedChangeAnchorKey,
8200
8378
  ...getCommentAliasIds(target)
8201
- ].map((id) => normalizeCommentId(id)).filter(Boolean));
8202
- fields.trackedChangePositionAliases = normalizedTrackedChangePositionAliases.some((alias) => targetIdentityIds.has(alias)) ? normalizeTrackedChangePositionAliases([...Array.isArray(target?.trackedChangePositionAliases) ? target.trackedChangePositionAliases : [], ...normalizedTrackedChangePositionAliases]) : normalizedTrackedChangePositionAliases;
8379
+ ].map((id) => normalizeCommentId(id)).filter(isPresent));
8380
+ const incomingAliases = normalizedTrackedChangePositionAliases ?? [];
8381
+ fields.trackedChangePositionAliases = incomingAliases.some((alias) => targetIdentityIds.has(alias)) ? normalizeTrackedChangePositionAliases([...Array.isArray(target?.trackedChangePositionAliases) ? target.trackedChangePositionAliases : [], ...incomingAliases]) : incomingAliases;
8203
8382
  }
8204
8383
  let didChange = false;
8205
8384
  for (const [key, value] of Object.entries(fields)) didChange = setIfChanged(target, key, value) || didChange;
@@ -8208,7 +8387,7 @@ var useCommentsStore = defineStore("comments", () => {
8208
8387
  didChange = true;
8209
8388
  }
8210
8389
  if (hasCustomAttributes && target && !trackedChangeCustomAttributesEqual(target.customAttributes, normalizedCustomAttributes)) {
8211
- target.customAttributes = normalizedCustomAttributes;
8390
+ target.customAttributes = normalizedCustomAttributes ?? null;
8212
8391
  didChange = true;
8213
8392
  }
8214
8393
  return applyStoryMetadata(target) || didChange;
@@ -8230,9 +8409,10 @@ var useCommentsStore = defineStore("comments", () => {
8230
8409
  return;
8231
8410
  }
8232
8411
  addHydratedComment({
8233
- superdoc,
8412
+ superdoc: requireHost(superdoc, "handleTrackedChangeUpdate"),
8234
8413
  comment,
8235
- broadcastChanges
8414
+ broadcastChanges,
8415
+ preserveDraft: hasDraft
8236
8416
  });
8237
8417
  trackedChangeIdentityIndex?.add(comment);
8238
8418
  } else if (event === "update") {
@@ -8250,11 +8430,12 @@ var useCommentsStore = defineStore("comments", () => {
8250
8430
  id: params.resolvedById ?? superdoc?.user?.id ?? null,
8251
8431
  email: params.resolvedByEmail ?? superdoc?.user?.email ?? null,
8252
8432
  name: params.resolvedByName ?? superdoc?.user?.name ?? null,
8253
- decision: params.decision ?? null,
8433
+ decision: params.decision ?? void 0,
8254
8434
  superdoc
8255
8435
  };
8256
8436
  if (existingTrackedChange && !existingTrackedChange.resolvedTime) existingTrackedChange.resolveComment({
8257
8437
  ...resolveArgs,
8438
+ superdoc: requireHost(superdoc, "handleTrackedChangeUpdate"),
8258
8439
  reconciliationToken: COMMENT_RECONCILIATION_TOKEN
8259
8440
  });
8260
8441
  }
@@ -8279,10 +8460,11 @@ var useCommentsStore = defineStore("comments", () => {
8279
8460
  instantSidebarAlignmentInstanceId.value = null;
8280
8461
  };
8281
8462
  const debounceEmit = (commentId, event, superdoc, delay = 1e3) => {
8282
- if (debounceTimers[commentId]) clearTimeout(debounceTimers[commentId]);
8283
- debounceTimers[commentId] = setTimeout(() => {
8463
+ const timerKey = String(commentId);
8464
+ if (debounceTimers[timerKey]) clearTimeout(debounceTimers[timerKey]);
8465
+ debounceTimers[timerKey] = setTimeout(() => {
8284
8466
  if (superdoc) superdoc.emit("comments-update", event);
8285
- delete debounceTimers[commentId];
8467
+ delete debounceTimers[timerKey];
8286
8468
  }, delay);
8287
8469
  };
8288
8470
  const showAddComment = (superdoc, targetClientY = null) => {
@@ -8294,7 +8476,7 @@ var useCommentsStore = defineStore("comments", () => {
8294
8476
  if (!ownsRequest()) return;
8295
8477
  superdoc.emit("comments-update", {
8296
8478
  type: COMMENT_EVENTS.PENDING,
8297
- pendingSelection: superdoc?.activeEditor === editor ? snapshot : null
8479
+ pendingSelection: superdoc?.activeEditor === editor && require__plugin_vue_export_helper.isPlainObject(snapshot) ? snapshot : null
8298
8480
  });
8299
8481
  };
8300
8482
  if (v2Adapter) {
@@ -8339,12 +8521,11 @@ var useCommentsStore = defineStore("comments", () => {
8339
8521
  }).catch((err) => ({
8340
8522
  ok: false,
8341
8523
  reason: "selection-capture-failed",
8342
- detail: err?.message ?? String(err)
8524
+ detail: errorDetail(err)
8343
8525
  }));
8344
8526
  }
8345
8527
  const pendingSelection = getPendingSelectionSnapshot(superdoc, superdocStore.activeSelection);
8346
- const asyncSnapshot = typeof pendingSelection?.then === "function";
8347
- if (!asyncSnapshot) emitPending(pendingSelection);
8528
+ if (!require__plugin_vue_export_helper.isThenable(pendingSelection)) emitPending(pendingSelection);
8348
8529
  const showLegacyPending = (selection) => {
8349
8530
  selection.selectionBounds = { ...selection.selectionBounds };
8350
8531
  if (superdocStore.selectionPosition?.source && superdocStore.selectionPosition.source !== "pdf") superdocStore.selectionPosition.source = null;
@@ -8354,7 +8535,7 @@ var useCommentsStore = defineStore("comments", () => {
8354
8535
  parentCommentId: null
8355
8536
  });
8356
8537
  if (!superdoc.config.isInternal) pendingComment.value.isInternal = false;
8357
- if (superdoc.activeEditor?.commands) superdoc.activeEditor.commands.insertComment({
8538
+ if (superdoc.activeEditor?.commands) superdoc.activeEditor.commands.insertComment?.({
8358
8539
  ...pendingComment.value.getValues(),
8359
8540
  commentId: "pending",
8360
8541
  skipEmit: true
@@ -8365,10 +8546,10 @@ var useCommentsStore = defineStore("comments", () => {
8365
8546
  activeComment.value = pendingComment.value.commentId;
8366
8547
  return { ok: true };
8367
8548
  };
8368
- if (asyncSnapshot) {
8549
+ if (require__plugin_vue_export_helper.isThenable(pendingSelection)) {
8369
8550
  const selection = { ...superdocStore.activeSelection };
8370
8551
  selection.selectionBounds = { ...selection.selectionBounds };
8371
- return pendingSelection.then((snapshot) => {
8552
+ return Promise.resolve(pendingSelection).then((snapshot) => {
8372
8553
  if (!ownsRequest()) return {
8373
8554
  ok: false,
8374
8555
  reason: "pending-comment-superseded"
@@ -8400,12 +8581,6 @@ var useCommentsStore = defineStore("comments", () => {
8400
8581
  });
8401
8582
  /**
8402
8583
  * Get a new pending comment
8403
- *
8404
- * @param {Object} param0
8405
- * @param {Object} [param0.selection] The selection object
8406
- * @param {string | null} [param0.documentId] The document ID
8407
- * @param {string | null} [param0.parentCommentId] The parent comment
8408
- * @returns {ReturnType<typeof useComment>} The new comment object
8409
8584
  */
8410
8585
  const getPendingComment = ({ selection, documentId, parentCommentId, ...options }) => {
8411
8586
  return _getNewcomment({
@@ -8417,17 +8592,11 @@ var useCommentsStore = defineStore("comments", () => {
8417
8592
  };
8418
8593
  /**
8419
8594
  * Get the new comment object
8420
- *
8421
- * @param {Object} param0
8422
- * @param {Object} param0.selection The selection object
8423
- * @param {String} param0.documentId The document ID
8424
- * @param {String} param0.parentCommentId The parent comment ID
8425
- * @returns {Object} The new comment object
8426
8595
  */
8427
8596
  const _getNewcomment = ({ selection, documentId, parentCommentId, ...options }) => {
8428
8597
  let activeDocument;
8429
- if (documentId) activeDocument = superdocStore.getDocument(documentId);
8430
- else if (selection) activeDocument = superdocStore.getDocument(selection.documentId);
8598
+ if (documentId) activeDocument = findDocument(documentId);
8599
+ else if (selection) activeDocument = findDocument(selection.documentId);
8431
8600
  if (!activeDocument) activeDocument = superdocStore.documents[0];
8432
8601
  return useComment({
8433
8602
  fileId: activeDocument.id,
@@ -8444,8 +8613,6 @@ var useCommentsStore = defineStore("comments", () => {
8444
8613
  };
8445
8614
  /**
8446
8615
  * Remove the pending comment
8447
- *
8448
- * @returns {void}
8449
8616
  */
8450
8617
  const resetPendingCommentState = (superdoc, clearViewingSelection = true) => {
8451
8618
  const hadPending = !!pendingComment.value;
@@ -8461,7 +8628,7 @@ var useCommentsStore = defineStore("comments", () => {
8461
8628
  } catch {}
8462
8629
  return;
8463
8630
  }
8464
- superdoc?.activeEditor?.commands?.removeComment({ commentId: "pending" });
8631
+ superdoc?.activeEditor?.commands?.removeComment?.({ commentId: "pending" });
8465
8632
  };
8466
8633
  const removePendingComment = (superdoc) => {
8467
8634
  pendingCommentRequest += 1;
@@ -8474,16 +8641,8 @@ var useCommentsStore = defineStore("comments", () => {
8474
8641
  };
8475
8642
  /**
8476
8643
  * Add a new comment to the document
8477
- *
8478
- * @param {Object} param0
8479
- * @param {Object} param0.superdoc The SuperDoc instance
8480
- * @param {ReturnType<typeof useComment>} param0.comment The comment to add
8481
- * @param {boolean} [param0.skipEditorUpdate] Skip writing the comment into the editor
8482
- * @param {boolean} [param0.broadcastChanges] Emit and sync the change
8483
- * @param {boolean} [param0.isHydration] The comment comes from import or collaboration
8484
- * @returns {{ ok: boolean, reason?: string, detail?: string, code?: string } | Promise<{ ok: boolean, reason?: string, detail?: string, code?: string }>} The mutation outcome
8485
8644
  */
8486
- const addCommentInternal = ({ superdoc, comment, skipEditorUpdate = false, broadcastChanges = true, isHydration = false }) => {
8645
+ const addCommentInternal = ({ superdoc, comment, skipEditorUpdate = false, broadcastChanges = true, isHydration = false, preserveDraft = false }) => {
8487
8646
  if (commentsAreReadOnly() && !isHydration) return readOnlyMutationOutcome();
8488
8647
  const v2Adapter = !skipEditorUpdate && !isHydration ? getV2CommentsAdapter(superdoc) : null;
8489
8648
  if (v2Adapter && !comment.trackedChange) {
@@ -8511,7 +8670,7 @@ var useCommentsStore = defineStore("comments", () => {
8511
8670
  return {
8512
8671
  ok: false,
8513
8672
  reason: "adapter-threw",
8514
- detail: err?.message ?? String(err)
8673
+ detail: errorDetail(err)
8515
8674
  };
8516
8675
  }
8517
8676
  })().then((outcome) => {
@@ -8562,28 +8721,30 @@ var useCommentsStore = defineStore("comments", () => {
8562
8721
  };
8563
8722
  });
8564
8723
  }
8565
- let parentComment = commentsList.value.find((c) => c.commentId === activeComment.value);
8724
+ const activeId = activeComment.value;
8725
+ const parent = require__plugin_vue_export_helper.toRaw(commentsList.value).find((c) => require__plugin_vue_export_helper.unref(c.commentId) === activeId);
8726
+ let parentComment = parent ? require__plugin_vue_export_helper.reactive(parent) : void 0;
8566
8727
  if (!parentComment) parentComment = comment;
8567
8728
  const newComment = useComment(comment.getValues());
8568
- if (pendingComment.value) newComment.setText({
8569
- text: currentCommentText.value,
8570
- suppressUpdate: true
8571
- });
8572
- else newComment.setText({
8573
- text: comment.commentText,
8729
+ const text = pendingComment.value && !preserveDraft ? currentCommentText.value : comment.commentText;
8730
+ if (text === "") {
8731
+ newComment.commentText = "";
8732
+ newComment.mentions = [];
8733
+ } else newComment.setText({
8734
+ text,
8574
8735
  suppressUpdate: true
8575
8736
  });
8576
- newComment.selection.source = pendingComment.value?.selection?.source ?? newComment.selection.source;
8737
+ if (!preserveDraft) newComment.selection.source = pendingComment.value?.selection?.source ?? newComment.selection.source;
8577
8738
  if (parentComment) newComment.isInternal = parentComment.isInternal;
8578
8739
  if (!superdoc.config.isInternal) newComment.isInternal = false;
8579
8740
  commentsList.value.push(newComment);
8580
- const hadPendingComment = !!pendingComment.value;
8581
- removePendingComment(superdoc);
8741
+ const hadPendingComment = !preserveDraft && !!pendingComment.value;
8742
+ if (!preserveDraft) removePendingComment(superdoc);
8582
8743
  if (hadPendingComment) {
8583
8744
  activeComment.value = newComment.commentId;
8584
8745
  setActiveFloatingCommentInstance(newComment.commentId);
8585
8746
  }
8586
- if (!skipEditorUpdate && !comment.trackedChange && superdoc.activeEditor?.commands && !comment.parentCommentId) superdoc.activeEditor.commands.insertComment({
8747
+ if (!skipEditorUpdate && !comment.trackedChange && superdoc.activeEditor?.commands && !comment.parentCommentId) superdoc.activeEditor.commands.insertComment?.({
8587
8748
  ...newComment.getValues(),
8588
8749
  skipEmit: true
8589
8750
  });
@@ -8606,12 +8767,13 @@ var useCommentsStore = defineStore("comments", () => {
8606
8767
  skipEditorUpdate,
8607
8768
  broadcastChanges
8608
8769
  });
8609
- const addHydratedComment = ({ superdoc, comment, skipEditorUpdate = false, broadcastChanges = true }) => addCommentInternal({
8770
+ const addHydratedComment = ({ superdoc, comment, skipEditorUpdate = false, broadcastChanges = true, preserveDraft = false }) => addCommentInternal({
8610
8771
  superdoc,
8611
8772
  comment,
8612
8773
  skipEditorUpdate,
8613
8774
  broadcastChanges,
8614
- isHydration: true
8775
+ isHydration: true,
8776
+ preserveDraft
8615
8777
  });
8616
8778
  const deleteComment = ({ commentId: commentIdToDelete, superdoc }) => {
8617
8779
  if (commentsAreReadOnly()) return Promise.resolve(readOnlyMutationOutcome());
@@ -8631,7 +8793,7 @@ var useCommentsStore = defineStore("comments", () => {
8631
8793
  return {
8632
8794
  ok: false,
8633
8795
  reason: "adapter-threw",
8634
- detail: err?.message ?? String(err)
8796
+ detail: errorDetail(err)
8635
8797
  };
8636
8798
  }
8637
8799
  })().then((outcome) => {
@@ -8651,16 +8813,16 @@ var useCommentsStore = defineStore("comments", () => {
8651
8813
  });
8652
8814
  return outcome;
8653
8815
  }
8654
- const removedIds = new Set((Array.isArray(outcome.removedCommentIds) ? outcome.removedCommentIds : []).map(normalizeCommentId).filter(Boolean));
8655
- if (removedIds.has(normalizeCommentId(commentId))) {
8656
- if (importedId != null) removedIds.add(normalizeCommentId(importedId));
8816
+ const removedIds = new Set((Array.isArray(outcome.removedCommentIds) ? outcome.removedCommentIds : []).map(normalizeCommentId).filter(isPresent));
8817
+ if (removedIds.has(normalizeCommentId(commentId) ?? "")) {
8818
+ if (importedId != null) removedIds.add(String(importedId));
8657
8819
  let addedChild = true;
8658
8820
  while (addedChild) {
8659
8821
  addedChild = false;
8660
8822
  for (const row of commentsList.value) {
8661
8823
  const parentId = normalizeCommentId(row?.parentCommentId);
8662
8824
  if (!parentId || !removedIds.has(parentId)) continue;
8663
- for (const alias of [row?.commentId, row?.importedId].map(normalizeCommentId).filter(Boolean)) {
8825
+ for (const alias of [row?.commentId, row?.importedId].map(normalizeCommentId).filter(isPresent)) {
8664
8826
  if (removedIds.has(alias)) continue;
8665
8827
  removedIds.add(alias);
8666
8828
  addedChild = true;
@@ -8668,9 +8830,9 @@ var useCommentsStore = defineStore("comments", () => {
8668
8830
  }
8669
8831
  }
8670
8832
  commentsList.value = commentsList.value.filter((row) => {
8671
- return ![row?.commentId, row?.importedId].map(normalizeCommentId).filter(Boolean).some((alias) => removedIds.has(alias));
8833
+ return ![row?.commentId, row?.importedId].map(normalizeCommentId).filter(isPresent).some((alias) => removedIds.has(alias));
8672
8834
  });
8673
- if (removedIds.has(normalizeCommentId(activeComment.value))) clearActiveCommentSelection();
8835
+ if (removedIds.has(normalizeCommentId(activeComment.value) ?? "")) clearActiveCommentSelection();
8674
8836
  } else reconcileCommentsFromV2({
8675
8837
  superdoc,
8676
8838
  adapter: v2Adapter,
@@ -8678,18 +8840,18 @@ var useCommentsStore = defineStore("comments", () => {
8678
8840
  items: outcome.items ?? [],
8679
8841
  pruneStale: outcome.complete !== false
8680
8842
  });
8843
+ const payload = comment.getValues();
8681
8844
  superdoc.emit("comments-update", {
8682
8845
  type: COMMENT_EVENTS.DELETED,
8683
- comment: comment.getValues(),
8684
- changes: [{
8685
- key: "deleted",
8846
+ comment: payload,
8847
+ changes: getDeletedCommentChanges([{
8686
8848
  commentId,
8687
- fileId
8688
- }]
8849
+ importedId
8850
+ }, payload], fileId)
8689
8851
  });
8690
8852
  return { ok: true };
8691
8853
  });
8692
- superdoc.activeEditor?.commands?.removeComment({
8854
+ superdoc.activeEditor?.commands?.removeComment?.({
8693
8855
  commentId,
8694
8856
  importedId
8695
8857
  });
@@ -8697,14 +8859,14 @@ var useCommentsStore = defineStore("comments", () => {
8697
8859
  const childCommentIds = commentsList.value.filter((c) => c.parentCommentId === commentId).map((c) => c.commentId || c.importedId);
8698
8860
  commentsList.value = commentsList.value.filter((c) => !childCommentIds.includes(c.commentId));
8699
8861
  if (activeComment.value === commentId || childCommentIds.includes(activeComment.value)) clearActiveCommentSelection();
8862
+ const payload = comment.getValues();
8700
8863
  const event = {
8701
8864
  type: COMMENT_EVENTS.DELETED,
8702
- comment: comment.getValues(),
8703
- changes: [{
8704
- key: "deleted",
8865
+ comment: payload,
8866
+ changes: getDeletedCommentChanges([{
8705
8867
  commentId,
8706
- fileId
8707
- }]
8868
+ importedId
8869
+ }, payload], fileId)
8708
8870
  };
8709
8871
  superdoc.emit("comments-update", event);
8710
8872
  syncCommentsToClients(superdoc, event);
@@ -8716,7 +8878,7 @@ var useCommentsStore = defineStore("comments", () => {
8716
8878
  * otherwise keep the source bubble after the document comment is gone.
8717
8879
  */
8718
8880
  const dropCommentsFromMutationImpact = ({ superdoc, documentId, removedIds } = {}) => {
8719
- const seedIds = new Set([...removedIds instanceof Set ? removedIds : removedIds ?? []].map((id) => normalizeCommentId(id)).filter(Boolean));
8881
+ const seedIds = new Set([...removedIds instanceof Set ? removedIds : removedIds ?? []].map((id) => normalizeCommentId(id)).filter(isPresent));
8720
8882
  if (!seedIds.size) return {
8721
8883
  ok: true,
8722
8884
  removedIds: []
@@ -8740,7 +8902,7 @@ var useCommentsStore = defineStore("comments", () => {
8740
8902
  if (!isDroppableCommentRow(row)) continue;
8741
8903
  const aliases = getCommentAliasIds(row);
8742
8904
  const alreadyDropped = aliases.some((alias) => droppedIds.has(alias));
8743
- const parentDropped = droppedIds.has(normalizeCommentId(row?.parentCommentId)) || droppedIds.has(normalizeCommentId(row?.threadingParentCommentId));
8905
+ const parentDropped = droppedIds.has(normalizeCommentId(row?.parentCommentId) ?? "") || droppedIds.has(normalizeCommentId(row?.threadingParentCommentId) ?? "");
8744
8906
  if (!alreadyDropped && !parentDropped) continue;
8745
8907
  for (const alias of aliases) {
8746
8908
  if (droppedIds.has(alias)) continue;
@@ -8768,11 +8930,7 @@ var useCommentsStore = defineStore("comments", () => {
8768
8930
  const event = {
8769
8931
  type: COMMENT_EVENTS.DELETED,
8770
8932
  comment: payload,
8771
- changes: [{
8772
- key: "deleted",
8773
- commentId: payload?.commentId,
8774
- fileId: payload?.fileId
8775
- }]
8933
+ changes: getDeletedCommentChanges([payload, comment], payload?.fileId)
8776
8934
  };
8777
8935
  superdoc?.emit?.("comments-update", event);
8778
8936
  });
@@ -8794,7 +8952,7 @@ var useCommentsStore = defineStore("comments", () => {
8794
8952
  const byAlias = /* @__PURE__ */ new Map();
8795
8953
  const childrenByParentAlias = /* @__PURE__ */ new Map();
8796
8954
  for (const row of rows) {
8797
- for (const alias of [row.commentId, row.importedId].map(normalizeCommentId).filter(Boolean)) if (!byAlias.has(alias)) byAlias.set(alias, row);
8955
+ for (const alias of [row.commentId, row.importedId].map(normalizeCommentId).filter(isPresent)) if (!byAlias.has(alias)) byAlias.set(alias, row);
8798
8956
  const parentId = normalizeCommentId(row.parentCommentId);
8799
8957
  if (parentId) {
8800
8958
  const children = childrenByParentAlias.get(parentId) ?? [];
@@ -8821,7 +8979,7 @@ var useCommentsStore = defineStore("comments", () => {
8821
8979
  if (!row || visited.has(row)) continue;
8822
8980
  visited.add(row);
8823
8981
  family.push(row);
8824
- for (const alias of [row.commentId, row.importedId].map(normalizeCommentId).filter(Boolean)) queue.push(...childrenByParentAlias.get(alias) ?? []);
8982
+ for (const alias of [row.commentId, row.importedId].map(normalizeCommentId).filter(isPresent)) queue.push(...childrenByParentAlias.get(alias) ?? []);
8825
8983
  }
8826
8984
  const isResolved = status === "resolved";
8827
8985
  const resolvedAt = Date.now();
@@ -8844,7 +9002,7 @@ var useCommentsStore = defineStore("comments", () => {
8844
9002
  outcome = {
8845
9003
  ok: false,
8846
9004
  reason: "adapter-threw",
8847
- detail: err?.message ?? String(err)
9005
+ detail: errorDetail(err)
8848
9006
  };
8849
9007
  }
8850
9008
  if (!isCurrentV2CommentsAdapter(adapter)) return {
@@ -8916,14 +9074,14 @@ var useCommentsStore = defineStore("comments", () => {
8916
9074
  return (outcome.items ?? []).map((item) => adapter.mapV2CommentToUseCommentInput(item, {
8917
9075
  fileId,
8918
9076
  fileType: null
8919
- })).filter(Boolean);
9077
+ })).filter(isPresent);
8920
9078
  };
8921
9079
  const isBlankDirectoryCommentText = (value) => typeof value !== "string" || value.replace(/<[^>]*>/g, "").replace(/&nbsp;/gi, " ").trim().length === 0;
8922
9080
  const trackedChangeDirectoryComment = (params) => {
8923
9081
  if (!params || params.event === "omit" || params.changeId == null) return null;
8924
9082
  return getPendingComment({
8925
9083
  documentId: params.documentId,
8926
- commentId: params.changeId,
9084
+ commentId: String(params.changeId),
8927
9085
  trackedChange: true,
8928
9086
  trackedChangeText: params.trackedChangeText,
8929
9087
  trackedChangeType: params.trackedChangeType,
@@ -8970,7 +9128,7 @@ var useCommentsStore = defineStore("comments", () => {
8970
9128
  const mappedCommentInputs = commentItems.filter((item) => !require_create_super_doc_ui.isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
8971
9129
  fileId: documentId,
8972
9130
  fileType: document?.type ?? null
8973
- })).filter(Boolean);
9131
+ })).filter(isPresent);
8974
9132
  const trackedParentBySidecarId = /* @__PURE__ */ new Map();
8975
9133
  for (const input of mappedCommentInputs) if (input.trackedChange !== true && input.trackedChangeThreadParentId && !input.parentCommentId && isBlankDirectoryCommentText(input.commentText)) trackedParentBySidecarId.set(String(input.commentId), String(input.trackedChangeThreadParentId));
8976
9134
  const commentRows = mappedCommentInputs.filter((input) => !trackedParentBySidecarId.has(String(input.commentId))).map((input) => {
@@ -8981,7 +9139,7 @@ var useCommentsStore = defineStore("comments", () => {
8981
9139
  }
8982
9140
  return useComment(input);
8983
9141
  });
8984
- const trackedRows = trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).map((params) => trackedChangeDirectoryComment(params)).filter(Boolean);
9142
+ const trackedRows = trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).map((params) => trackedChangeDirectoryComment(params)).filter(isPresent);
8985
9143
  reviewDirectoryList.value = [...commentRows, ...trackedRows];
8986
9144
  return {
8987
9145
  ok: true,
@@ -9062,13 +9220,6 @@ var useCommentsStore = defineStore("comments", () => {
9062
9220
  * - successful mutation refreshes from v2 list and reconciles
9063
9221
  * - rejection preserves rows and emits a rejected `comments-update` event
9064
9222
  * - committed-but-refresh-failed surfaces honestly (no reconcile with `[]`)
9065
- *
9066
- * @param {Object} [param0]
9067
- * @param {Object} [param0.superdoc] The SuperDoc instance.
9068
- * @param {string | null} [param0.parentCommentId] The comment or tracked-change row being replied to.
9069
- * @param {string} [param0.text] The reply text.
9070
- * @param {Array<{ name?: string | null, email?: string | null }>} [param0.mentions] Users mentioned in the reply.
9071
- * @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.
9072
9223
  */
9073
9224
  const replyCommentV2 = async ({ superdoc, parentCommentId, text, mentions = [], trackedChangeTarget } = {}) => {
9074
9225
  if (commentsAreReadOnly()) return readOnlyMutationOutcome();
@@ -9343,10 +9494,15 @@ var useCommentsStore = defineStore("comments", () => {
9343
9494
  row?.threadingParentCommentId,
9344
9495
  row?.trackedChangeParentId,
9345
9496
  row?.trackedChangeThreadParentId
9346
- ].map((value) => normalizeCommentId(value)).filter(Boolean));
9497
+ ].map((value) => normalizeCommentId(value)).filter(isPresent));
9347
9498
  /** Keep one mounted review window plus rows whose interaction is still live. */
9348
9499
  const boundReviewPresentationRows = ({ documentId, visibleIds }) => {
9349
9500
  const currentRows = commentsList.value;
9501
+ if (currentRows.every((row) => {
9502
+ if (!belongsToTrackedChangeSyncDocument(row, documentId)) return true;
9503
+ const id = normalizeCommentId(row.commentId);
9504
+ return Boolean(id && visibleIds.has(id));
9505
+ })) return;
9350
9506
  const identityIdsByRow = /* @__PURE__ */ new Map();
9351
9507
  const identityIdsFor = (row) => {
9352
9508
  let ids = identityIdsByRow.get(row);
@@ -9364,7 +9520,7 @@ var useCommentsStore = defineStore("comments", () => {
9364
9520
  pendingComment.value?.commentId,
9365
9521
  instantSidebarAlignmentThreadId.value,
9366
9522
  instantSidebarAlignmentInstanceId.value
9367
- ].map((value) => normalizeCommentId(value)).filter(Boolean));
9523
+ ].map((value) => normalizeCommentId(value)).filter(isPresent));
9368
9524
  let changed = true;
9369
9525
  while (changed) {
9370
9526
  changed = false;
@@ -9432,7 +9588,8 @@ var useCommentsStore = defineStore("comments", () => {
9432
9588
  if (preparedReview?.reuseCatalog === true && committedReviewPresentations.value[normalizedDocumentId]) {
9433
9589
  const previous = committedReviewPresentations.value[normalizedDocumentId];
9434
9590
  const next = preparedReview.presentation;
9435
- 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 {
9591
+ const catalogVersion = preparedReview.catalogVersion ?? NaN;
9592
+ 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 {
9436
9593
  ok: false,
9437
9594
  reason: "review-catalog-stale"
9438
9595
  };
@@ -9478,8 +9635,8 @@ var useCommentsStore = defineStore("comments", () => {
9478
9635
  previousPresentation.updateContext = presentation.updateContext;
9479
9636
  return {
9480
9637
  ok: true,
9481
- commentItems: preparedReview.comments.length,
9482
- trackedItems: preparedReview.trackedChanges.length
9638
+ commentItems: preparedReview?.comments.length,
9639
+ trackedItems: preparedReview?.trackedChanges.length
9483
9640
  };
9484
9641
  }
9485
9642
  let effectiveCommentItems = commentItems;
@@ -9493,7 +9650,7 @@ var useCommentsStore = defineStore("comments", () => {
9493
9650
  reason: seeded?.reason ?? "comment-catalog-seed-failed"
9494
9651
  };
9495
9652
  const selected = commentsAdapter.selectVisibleReviewComments?.();
9496
- if (selected?.ok === true && Array.isArray(selected.items) && (!Array.isArray(selected.unresolvedIds) || selected.unresolvedIds.length === 0)) effectiveCommentItems = selected.items;
9653
+ if (!require__plugin_vue_export_helper.isThenable(selected) && selected?.ok === true && Array.isArray(selected.items) && (!Array.isArray(selected.unresolvedIds) || selected.unresolvedIds.length === 0)) effectiveCommentItems = selected.items;
9497
9654
  }
9498
9655
  const fileType = (normalizedDocumentId ? superdocStore.getDocument(normalizedDocumentId) : null)?.type ?? null;
9499
9656
  let preparedInputs;
@@ -9510,16 +9667,16 @@ var useCommentsStore = defineStore("comments", () => {
9510
9667
  })) : effectiveCommentItems.filter((item) => !require_create_super_doc_ui.isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
9511
9668
  fileId: normalizedDocumentId,
9512
9669
  fileType
9513
- })).filter(Boolean);
9670
+ })).filter(isPresent);
9514
9671
  preparedParams = preparedReview ? preparedReview.trackedChanges.map((params) => ({
9515
9672
  ...params,
9516
9673
  documentId: normalizedDocumentId
9517
- })) : trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).filter(Boolean);
9674
+ })) : trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).filter(isPresent);
9518
9675
  } catch (error) {
9519
9676
  return {
9520
9677
  ok: false,
9521
9678
  reason: "mapper-failed",
9522
- detail: error?.message ?? String(error)
9679
+ detail: errorDetail(error)
9523
9680
  };
9524
9681
  }
9525
9682
  if (typeof patch !== "function") return {
@@ -9547,7 +9704,7 @@ var useCommentsStore = defineStore("comments", () => {
9547
9704
  params?.trackedChangeCanonicalId,
9548
9705
  params?.trackedChangeAnchorKey
9549
9706
  ])
9550
- ].map((value) => normalizeCommentId(value)).filter(Boolean));
9707
+ ].map((value) => normalizeCommentId(value)).filter(isPresent));
9551
9708
  patch(() => {
9552
9709
  const previousPreparedPresentation = committedReviewPresentations.value[normalizedDocumentId] ?? null;
9553
9710
  if (preparedReview?.presentation) committedReviewPresentations.value = {
@@ -9562,7 +9719,8 @@ var useCommentsStore = defineStore("comments", () => {
9562
9719
  preparedParams,
9563
9720
  preparedPresentation: preparedReview?.presentation ?? null,
9564
9721
  previousPreparedPresentation,
9565
- pruneStale: false
9722
+ pruneStale: false,
9723
+ preserveDraft: true
9566
9724
  });
9567
9725
  reconcileCommentsFromV2({
9568
9726
  superdoc,
@@ -9595,7 +9753,7 @@ var useCommentsStore = defineStore("comments", () => {
9595
9753
  };
9596
9754
  const effectiveDocumentId = documentId ?? adapter.documentId ?? null;
9597
9755
  const normalizedEffectiveDocumentId = effectiveDocumentId != null ? String(effectiveDocumentId) : null;
9598
- const fileType = (effectiveDocumentId ? superdocStore.getDocument(effectiveDocumentId) : null)?.type ?? null;
9756
+ const fileType = (effectiveDocumentId ? findDocument(effectiveDocumentId) : null)?.type ?? null;
9599
9757
  const isBlankV2CommentText = (value) => {
9600
9758
  if (typeof value !== "string") return true;
9601
9759
  return value.replace(/<[^>]*>/g, "").replace(/&nbsp;/gi, " ").trim().length === 0;
@@ -9615,7 +9773,7 @@ var useCommentsStore = defineStore("comments", () => {
9615
9773
  const candidateInputs = Array.isArray(preparedInputs) ? preparedInputs : items.filter((item) => !require_create_super_doc_ui.isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => adapter.mapV2CommentToUseCommentInput(item, {
9616
9774
  fileId: effectiveDocumentId,
9617
9775
  fileType
9618
- })).filter(Boolean);
9776
+ })).filter(isPresent);
9619
9777
  for (const input of candidateInputs) {
9620
9778
  if (isV2TrackedChangeSidecarCommentInput(input)) {
9621
9779
  trackedChangeSidecarParentByCommentId.set(String(input.commentId), String(input.trackedChangeThreadParentId));
@@ -9668,7 +9826,7 @@ var useCommentsStore = defineStore("comments", () => {
9668
9826
  existing.mentions = Array.isArray(input.mentions) ? input.mentions : [];
9669
9827
  existing.isInternal = typeof input.isInternal === "boolean" ? input.isInternal : existing.isInternal;
9670
9828
  if (typeof input.resolvedTime === "number") {
9671
- existing.resolvedTime = input.resolvedTimeWasSynthesized === true && typeof existing.resolvedTime === "number" ? existing.resolvedTime : input.resolvedTime;
9829
+ existing.resolvedTime = input.resolvedTimeWasSynthesized === true && typeof existing.resolvedTime === "number" ? existing.resolvedTime : input.resolvedTime ?? null;
9672
9830
  existing.resolvedByEmail = input.resolvedByEmail ?? null;
9673
9831
  existing.resolvedByName = input.resolvedByName ?? null;
9674
9832
  } else {
@@ -9742,7 +9900,7 @@ var useCommentsStore = defineStore("comments", () => {
9742
9900
  return {
9743
9901
  ok: false,
9744
9902
  reason: "comment-read-failed",
9745
- detail: err?.message ?? String(err)
9903
+ detail: errorDetail(err)
9746
9904
  };
9747
9905
  }
9748
9906
  if (!isCurrentV2CommentsAdapter(adapter)) return {
@@ -9768,8 +9926,6 @@ var useCommentsStore = defineStore("comments", () => {
9768
9926
  };
9769
9927
  /**
9770
9928
  * Cancel the pending comment
9771
- *
9772
- * @returns {void}
9773
9929
  */
9774
9930
  const cancelComment = (superdoc) => {
9775
9931
  removePendingComment(superdoc);
@@ -9779,9 +9935,6 @@ var useCommentsStore = defineStore("comments", () => {
9779
9935
  *
9780
9936
  * Replace-file swaps call this before hydrating the replacement so stale
9781
9937
  * comments never survive long enough to render beside the new content.
9782
- *
9783
- * @param {string | null | undefined} documentId
9784
- * @returns {void}
9785
9938
  */
9786
9939
  function removeCommentsForDocument(documentId) {
9787
9940
  const activeDocumentId = documentId != null ? String(documentId) : null;
@@ -9812,20 +9965,16 @@ var useCommentsStore = defineStore("comments", () => {
9812
9965
  const remapTrackedChangeReferences = (droppedCommentIds, activeDocumentId) => {
9813
9966
  const remapSelection = (selection) => {
9814
9967
  const selectedId = normalizeCommentId(selection.value);
9815
- if (selectedId && droppedCommentIds.has(selectedId)) selection.value = droppedCommentIds.get(selectedId);
9968
+ const replacementId = selectedId ? droppedCommentIds.get(selectedId) : void 0;
9969
+ if (replacementId) selection.value = replacementId;
9816
9970
  };
9817
9971
  remapSelection(activeComment);
9818
9972
  remapSelection(activeFloatingCommentInstanceId);
9819
9973
  commentsList.value.forEach((row) => {
9820
9974
  if (!belongsToTrackedChangeSyncDocument(row, activeDocumentId)) return;
9821
9975
  const rowIdentityUpdates = {};
9822
- for (const field of [
9823
- "trackedChangeThreadParentId",
9824
- "trackedChangeParentId",
9825
- "threadingParentCommentId",
9826
- "parentCommentId"
9827
- ]) {
9828
- const replacementId = droppedCommentIds.get(normalizeCommentId(row?.[field]));
9976
+ for (const field of PARENT_LINK_FIELDS) {
9977
+ const replacementId = droppedCommentIds.get(normalizeCommentId(row?.[field]) ?? "");
9829
9978
  if (!replacementId) continue;
9830
9979
  row[field] = replacementId;
9831
9980
  rowIdentityUpdates[field] = replacementId;
@@ -9846,10 +9995,6 @@ var useCommentsStore = defineStore("comments", () => {
9846
9995
  *
9847
9996
  * Pruning is scoped to the active editor document so replay in one document does not
9848
9997
  * delete tracked-change comments from other open documents.
9849
- *
9850
- * @param {Set<string>} liveTrackedChangeIds IDs currently present in editor marks.
9851
- * @param {string | null} activeDocumentId Document currently being synced.
9852
- * @returns {void}
9853
9998
  */
9854
9999
  const pruneStaleTrackedChangeComments = (liveTrackedChangeIds, liveTrackedChangeAnchorKeys, activeDocumentId, superdoc = null, { broadcastChanges = true } = {}) => withInteractionSpan("store.trackedChanges.pruneStaleComments", "store-reconciliation", {
9855
10000
  activeDocumentId: activeDocumentId ?? null,
@@ -9894,7 +10039,7 @@ var useCommentsStore = defineStore("comments", () => {
9894
10039
  comment: payload
9895
10040
  };
9896
10041
  if (broadcastChanges) {
9897
- syncCommentsToClients(superdoc, event);
10042
+ syncCommentsToClients(requireHost(superdoc, "pruneStaleTrackedChangeComments"), event);
9898
10043
  superdoc?.emit?.("comments-update", event);
9899
10044
  }
9900
10045
  });
@@ -9926,14 +10071,10 @@ var useCommentsStore = defineStore("comments", () => {
9926
10071
  const event = {
9927
10072
  type: COMMENT_EVENTS.DELETED,
9928
10073
  comment: payload,
9929
- changes: [{
9930
- key: "deleted",
9931
- commentId: payload.commentId,
9932
- fileId: payload.fileId
9933
- }]
10074
+ changes: getDeletedCommentChanges([payload, comment], payload.fileId)
9934
10075
  };
9935
10076
  if (broadcastChanges) {
9936
- syncCommentsToClients(superdoc, event);
10077
+ syncCommentsToClients(requireHost(superdoc, "pruneStaleTrackedChangeComments"), event);
9937
10078
  superdoc?.emit?.("comments-update", event);
9938
10079
  }
9939
10080
  });
@@ -9956,7 +10097,6 @@ var useCommentsStore = defineStore("comments", () => {
9956
10097
  const resolveArgs = {
9957
10098
  email: superdoc?.user?.email ?? null,
9958
10099
  name: superdoc?.user?.name ?? null,
9959
- superdoc,
9960
10100
  reconciliationToken: COMMENT_RECONCILIATION_TOKEN
9961
10101
  };
9962
10102
  let resolvedCount = 0;
@@ -9967,9 +10107,12 @@ var useCommentsStore = defineStore("comments", () => {
9967
10107
  trackedChangeThreadParentIdForComment(linkedComment),
9968
10108
  linkedComment.threadingParentCommentId,
9969
10109
  linkedComment.parentCommentId
9970
- ].map((id) => normalizeCommentId(id)).filter(Boolean).some((id) => trackedChangeIds.has(id))) return;
10110
+ ].map((id) => normalizeCommentId(id)).filter(isPresent).some((id) => trackedChangeIds.has(id))) return;
9971
10111
  if (typeof linkedComment.resolveComment === "function") {
9972
- linkedComment.resolveComment(resolveArgs);
10112
+ linkedComment.resolveComment({
10113
+ ...resolveArgs,
10114
+ superdoc: requireHost(superdoc, "resolveLinkedCommentsForTrackedChangeDecision")
10115
+ });
9973
10116
  resolvedCount += 1;
9974
10117
  }
9975
10118
  });
@@ -10066,7 +10209,7 @@ var useCommentsStore = defineStore("comments", () => {
10066
10209
  if (previousCommentId) anchorSourceIds.add(previousCommentId);
10067
10210
  if (previousCanonicalId) anchorSourceIds.add(previousCanonicalId);
10068
10211
  }
10069
- if (comment.trackedChangeAnchorKey == null || anchorSourceIds.has(currentAnchorParts?.rawId)) comment.trackedChangeAnchorKey = toAnchor;
10212
+ if (comment.trackedChangeAnchorKey == null || anchorSourceIds.has(currentAnchorParts?.rawId ?? "")) comment.trackedChangeAnchorKey = toAnchor;
10070
10213
  comment.trackedChangePositionAliases = normalizeTrackedChangePositionAliases([...Array.isArray(comment.trackedChangePositionAliases) ? comment.trackedChangePositionAliases : [], fromId]);
10071
10214
  const identitySourceIds = /* @__PURE__ */ new Set([fromId]);
10072
10215
  if (advancesRemapLineage) {
@@ -10077,33 +10220,28 @@ var useCommentsStore = defineStore("comments", () => {
10077
10220
  if (row === comment) return;
10078
10221
  if (!belongsToTrackedChangeSyncDocument(row, normalizedDocumentId)) return;
10079
10222
  const rowIdentityUpdates = {};
10080
- for (const field of [
10081
- "trackedChangeThreadParentId",
10082
- "trackedChangeParentId",
10083
- "threadingParentCommentId",
10084
- "parentCommentId"
10085
- ]) {
10086
- if (!identitySourceIds.has(normalizeCommentId(row?.[field]))) continue;
10223
+ for (const field of PARENT_LINK_FIELDS) {
10224
+ if (!identitySourceIds.has(normalizeCommentId(row?.[field]) ?? "")) continue;
10087
10225
  row[field] = toId;
10088
10226
  rowIdentityUpdates[field] = toId;
10089
10227
  }
10090
10228
  if (Object.keys(rowIdentityUpdates).length && typeof row.updateIdentityValues === "function") row.updateIdentityValues(rowIdentityUpdates);
10091
10229
  });
10092
10230
  if (Object.keys(commentIdentityUpdates).length && typeof comment.updateIdentityValues === "function") comment.updateIdentityValues(commentIdentityUpdates);
10093
- const previousEntry = previousPositionEntry ?? [positions[fromId], positions[buildBodyTrackedChangeAnchorKey(fromId)]].find((entry) => entry && positionEntryMatchesTrackedChangeStory(entry, comment));
10231
+ const previousEntry = previousPositionEntry ?? [positions[fromId], positions[String(buildBodyTrackedChangeAnchorKey(fromId))]].find((entry) => entry && positionEntryMatchesTrackedChangeStory(entry, comment));
10094
10232
  if (previousEntry) {
10095
- for (const key of [toId, toAnchor]) if (positions[key] === void 0) {
10233
+ for (const key of [toId, String(toAnchor)]) if (positions[key] === void 0) {
10096
10234
  positions[key] = previousEntry;
10097
10235
  positionsChanged = true;
10098
10236
  }
10099
10237
  }
10100
- if (identitySourceIds.has(normalizeCommentId(activeComment.value))) activeComment.value = toId;
10101
- if (identitySourceIds.has(normalizeCommentId(activeFloatingCommentInstanceId.value))) activeFloatingCommentInstanceId.value = toId;
10238
+ if (identitySourceIds.has(normalizeCommentId(activeComment.value) ?? "")) activeComment.value = toId;
10239
+ if (identitySourceIds.has(normalizeCommentId(activeFloatingCommentInstanceId.value) ?? "")) activeFloatingCommentInstanceId.value = toId;
10102
10240
  }
10103
10241
  if (positionsChanged) editorCommentPositions.value = { ...positions };
10104
10242
  };
10105
10243
  const pruneDecidedTrackedChangeRow = ({ superdoc, comment: targetComment, decidedId, decidedIds, documentId } = {}) => {
10106
- const normalizedDecidedIds = new Set((Array.isArray(decidedIds) || decidedIds instanceof Set ? Array.from(decidedIds) : [decidedId]).map((id) => normalizeCommentId(id)).filter(Boolean));
10244
+ const normalizedDecidedIds = new Set((Array.isArray(decidedIds) || decidedIds instanceof Set ? Array.from(decidedIds) : [decidedId]).map((id) => normalizeCommentId(id)).filter(isPresent));
10107
10245
  const normalizedDocumentId = normalizeCommentId(documentId);
10108
10246
  if (!normalizedDecidedIds.size || !normalizedDocumentId) return {
10109
10247
  removed: false,
@@ -10163,12 +10301,6 @@ var useCommentsStore = defineStore("comments", () => {
10163
10301
  };
10164
10302
  /**
10165
10303
  * Accept or reject the tracked change behind a sidebar row.
10166
- *
10167
- * @param {Object} param0
10168
- * @param {Object} param0.superdoc The SuperDoc instance.
10169
- * @param {Object} param0.comment The tracked-change row.
10170
- * @param {string} param0.decision `'accept'` or `'reject'`.
10171
- * @returns {{ ok: boolean, success?: boolean, reason?: string } | Promise<{ ok: boolean, success?: boolean, reason?: string }>} The decision outcome.
10172
10304
  */
10173
10305
  const decideTrackedChangeFromSidebar = ({ superdoc, comment, decision }) => {
10174
10306
  const disabledReason = trackedChangeDecisionDisabledReason(superdoc);
@@ -10196,7 +10328,7 @@ var useCommentsStore = defineStore("comments", () => {
10196
10328
  return {
10197
10329
  ok: false,
10198
10330
  reason: "adapter-threw",
10199
- detail: err?.message ?? String(err)
10331
+ detail: errorDetail(err)
10200
10332
  };
10201
10333
  }
10202
10334
  })().then(async (outcome) => {
@@ -10249,9 +10381,11 @@ var useCommentsStore = defineStore("comments", () => {
10249
10381
  if (!activeEditor) return { ok: false };
10250
10382
  const id = comment.commentId ?? comment.importedId;
10251
10383
  if (!id) return { ok: false };
10252
- const story = comment.trackedChangeStory ?? void 0;
10384
+ const rawStory = comment.trackedChangeStory ?? void 0;
10385
+ const story = require_create_super_doc_ui.isStoryLocator(rawStory) ? rawStory : void 0;
10253
10386
  const documentApi = typeof activeEditor.doc === "object" ? activeEditor.doc : null;
10254
10387
  if (documentApi?.trackChanges?.decide) try {
10388
+ if (rawStory && !story) throw new TypeError("trackedChangeStory must be a valid StoryLocator");
10255
10389
  const target = story ? {
10256
10390
  id,
10257
10391
  story
@@ -10264,7 +10398,7 @@ var useCommentsStore = defineStore("comments", () => {
10264
10398
  success: Boolean(receipt?.success)
10265
10399
  }));
10266
10400
  } catch (error) {
10267
- if (story) return {
10401
+ if (rawStory) return {
10268
10402
  ok: false,
10269
10403
  error
10270
10404
  };
@@ -10311,9 +10445,9 @@ var useCommentsStore = defineStore("comments", () => {
10311
10445
  documentId: effectiveDocumentId
10312
10446
  })).removed
10313
10447
  };
10314
- const normalizedRemovedIds = new Set(Array.from(removedIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean));
10315
- const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean))];
10316
- const remappedSourceIds = new Set((Array.isArray(remappedPairs) ? remappedPairs : []).map((pair) => normalizeCommentId(pair?.from)).filter(Boolean));
10448
+ const normalizedRemovedIds = new Set(Array.from(removedIds ?? []).map((id) => normalizeCommentId(id)).filter(isPresent));
10449
+ const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(isPresent))];
10450
+ const remappedSourceIds = new Set((Array.isArray(remappedPairs) ? remappedPairs : []).map((pair) => normalizeCommentId(pair?.from)).filter(isPresent));
10317
10451
  const locallyRemovedIds = new Set([...normalizedRemovedIds].filter((id) => !remappedSourceIds.has(id)));
10318
10452
  if (locallyRemovedIds.size > 0 && effectiveDocumentId != null) pruneDecidedTrackedChangeRow({
10319
10453
  superdoc,
@@ -10332,6 +10466,10 @@ var useCommentsStore = defineStore("comments", () => {
10332
10466
  const byIdentityAlias = /* @__PURE__ */ new Map();
10333
10467
  const aliasesByRow = /* @__PURE__ */ new Map();
10334
10468
  const indexedRows = /* @__PURE__ */ new Set();
10469
+ const indexedIdentities = [];
10470
+ let building = true;
10471
+ let reusable = true;
10472
+ let reused = false;
10335
10473
  let aliasLookups = 0;
10336
10474
  let candidateVisits = 0;
10337
10475
  let rowAliasBuilds = 0;
@@ -10355,6 +10493,7 @@ var useCommentsStore = defineStore("comments", () => {
10355
10493
  index.set(alias, bucket);
10356
10494
  };
10357
10495
  const add = (comment) => {
10496
+ if (!building) reusable = false;
10358
10497
  if (!comment?.trackedChange) return;
10359
10498
  if (documentId && !belongsToTrackedChangeSyncDocument(comment, documentId)) return;
10360
10499
  indexedRows.add(comment);
@@ -10366,19 +10505,49 @@ var useCommentsStore = defineStore("comments", () => {
10366
10505
  rowAliasBuilds += 1;
10367
10506
  aliasesByRow.set(comment, aliases);
10368
10507
  aliases.forEach((alias) => addAlias(byIdentityAlias, alias, comment));
10508
+ if (building) indexedIdentities.push({
10509
+ row: comment,
10510
+ aliases,
10511
+ canonicalId: normalizeCommentId(comment.trackedChangeCanonicalId)
10512
+ });
10369
10513
  };
10370
10514
  commentsList.value.forEach(add);
10515
+ building = false;
10371
10516
  return {
10517
+ inUse: false,
10372
10518
  add,
10373
10519
  aliasesFor,
10374
- work: () => ({
10375
- trackedRowsIndexed: indexedRows.size,
10376
- invalidatedIdMembershipChecks: 0,
10377
- incomingAliasLookups: aliasLookups,
10378
- candidateVisits,
10379
- rowAliasBuilds,
10380
- rowAliasCacheHits
10381
- }),
10520
+ isCurrent: () => {
10521
+ if (!reusable) return false;
10522
+ let index = 0;
10523
+ for (const row of commentsList.value) {
10524
+ if (!row?.trackedChange || documentId && !belongsToTrackedChangeSyncDocument(row, documentId)) continue;
10525
+ const previous = indexedIdentities[index++];
10526
+ if (!previous || previous.row !== row || previous.canonicalId !== normalizeCommentId(row.trackedChangeCanonicalId)) return false;
10527
+ const aliases = getCommentAliasIds(row);
10528
+ rowAliasBuilds += 1;
10529
+ if (aliases.length !== previous.aliases.length || aliases.some((alias, i) => alias !== previous.aliases[i])) return false;
10530
+ }
10531
+ reused = index === indexedIdentities.length;
10532
+ return reused;
10533
+ },
10534
+ work: () => {
10535
+ const result = {
10536
+ trackedRowsIndexed: indexedRows.size,
10537
+ invalidatedIdMembershipChecks: 0,
10538
+ incomingAliasLookups: aliasLookups,
10539
+ candidateVisits,
10540
+ rowAliasBuilds,
10541
+ rowAliasCacheHits,
10542
+ indexReused: Number(reused)
10543
+ };
10544
+ aliasLookups = 0;
10545
+ candidateVisits = 0;
10546
+ rowAliasBuilds = 0;
10547
+ rowAliasCacheHits = 0;
10548
+ reused = false;
10549
+ return result;
10550
+ },
10382
10551
  candidates({ changeId, importedId, anchorKey, canonicalId, positionAliases = [] } = {}) {
10383
10552
  const out = /* @__PURE__ */ new Set();
10384
10553
  const include = (value) => {
@@ -10401,7 +10570,30 @@ var useCommentsStore = defineStore("comments", () => {
10401
10570
  }
10402
10571
  };
10403
10572
  };
10404
- const reconcileTrackedChangesFromV2 = ({ superdoc, adapter, documentId, items, pruneStale = true, preparedParams = null, preparedPresentation = null, previousPreparedPresentation = null, liveIds: suppliedLiveIds = null, liveAnchorKeys: suppliedLiveAnchorKeys = null, hydrationGeneration } = {}) => withInteractionSpan("store.trackedChanges.reconcile", "store-reconciliation", {
10573
+ const getTrackedChangeBatchIdentityIndex = (documentId) => {
10574
+ const cached = cachedTrackedChangeBatchIdentityIndex;
10575
+ if (cached?.documentId === documentId && !cached.index.inUse) {
10576
+ cached.index.inUse = true;
10577
+ let current = false;
10578
+ try {
10579
+ current = cached.index.isCurrent() && cachedTrackedChangeBatchIdentityIndex === cached;
10580
+ } finally {
10581
+ if (!current) {
10582
+ cached.index.inUse = false;
10583
+ if (cachedTrackedChangeBatchIdentityIndex === cached) cachedTrackedChangeBatchIdentityIndex = null;
10584
+ }
10585
+ }
10586
+ if (current) return cached.index;
10587
+ }
10588
+ const index = createTrackedChangeBatchIdentityIndex(documentId);
10589
+ index.inUse = true;
10590
+ cachedTrackedChangeBatchIdentityIndex = {
10591
+ documentId,
10592
+ index
10593
+ };
10594
+ return index;
10595
+ };
10596
+ const reconcileTrackedChangesFromV2 = ({ superdoc, adapter, documentId, items, pruneStale = true, preparedParams = null, preserveDraft = false, preparedPresentation = null, previousPreparedPresentation = null, liveIds: suppliedLiveIds = null, liveAnchorKeys: suppliedLiveAnchorKeys = null, hydrationGeneration } = {}) => withInteractionSpan("store.trackedChanges.reconcile", "store-reconciliation", {
10405
10597
  documentId: documentId ?? null,
10406
10598
  itemCount: Array.isArray(items) ? items.length : null,
10407
10599
  pruneStale,
@@ -10413,33 +10605,36 @@ var useCommentsStore = defineStore("comments", () => {
10413
10605
  const liveAnchorKeys = suppliedLiveAnchorKeys instanceof Set ? suppliedLiveAnchorKeys : /* @__PURE__ */ new Set();
10414
10606
  const liveIds = suppliedLiveIds instanceof Set ? suppliedLiveIds : /* @__PURE__ */ new Set();
10415
10607
  let appliedCount = 0;
10416
- const trackedChangeIdentityIndex = createTrackedChangeBatchIdentityIndex(effectiveDocumentId == null ? null : String(effectiveDocumentId));
10608
+ const trackedChangeIdentityIndex = getTrackedChangeBatchIdentityIndex(effectiveDocumentId == null ? null : String(effectiveDocumentId));
10417
10609
  const identitySpan = startInteractionSpan("store.trackedChanges.batchIdentity", "store-reconciliation", { itemCount: items.length });
10418
- const paramsList = Array.isArray(preparedParams) ? preparedParams : items.map((item) => adapter.mapV2TrackedChangeToCommentParams(item));
10419
- const previousCoordinate = previousPreparedPresentation?.coordinate;
10420
- const coordinate = preparedPresentation?.coordinate;
10421
- const retainUnchangedRows = !pruneStale && previousCoordinate && coordinate && coordinate.documentEpoch === preparedPresentation.epoch && coordinate.documentEpoch === previousPreparedPresentation.epoch && coordinate.documentEpoch === previousCoordinate.documentEpoch && typeof coordinate.mountId === "string" && coordinate.mountId.length > 0 && coordinate.mountId === previousCoordinate.mountId && preparedPresentation.baseVersion === previousPreparedPresentation.version && preparedPresentation.version === preparedPresentation.baseVersion + 1 && coordinate.reviewVersion === preparedPresentation.version && previousCoordinate.reviewVersion === previousPreparedPresentation.version && Number.isSafeInteger(coordinate.commitSequence) && Number.isSafeInteger(previousCoordinate.commitSequence) && coordinate.commitSequence >= previousCoordinate.commitSequence;
10610
+ let enteredBatch = false;
10422
10611
  try {
10612
+ const paramsList = Array.isArray(preparedParams) ? preparedParams : items.map((item) => adapter.mapV2TrackedChangeToCommentParams(item));
10613
+ const previousCoordinate = previousPreparedPresentation?.coordinate;
10614
+ const coordinate = preparedPresentation?.coordinate;
10615
+ const retainUnchangedRows = !pruneStale && previousCoordinate && coordinate && coordinate.documentEpoch === preparedPresentation.epoch && coordinate.documentEpoch === previousPreparedPresentation.epoch && coordinate.documentEpoch === previousCoordinate.documentEpoch && typeof coordinate.mountId === "string" && coordinate.mountId.length > 0 && coordinate.mountId === previousCoordinate.mountId && preparedPresentation.baseVersion === previousPreparedPresentation.version && preparedPresentation.version === preparedPresentation.baseVersion + 1 && coordinate.reviewVersion === preparedPresentation.version && previousCoordinate.reviewVersion === previousPreparedPresentation.version && Number.isSafeInteger(coordinate.commitSequence) && Number.isSafeInteger(previousCoordinate.commitSequence) && coordinate.commitSequence >= previousCoordinate.commitSequence;
10616
+ enteredBatch = true;
10423
10617
  for (const params of paramsList) {
10424
10618
  if (!params) continue;
10425
10619
  let retainUnchangedRow = false;
10620
+ const anchorParts = parseTrackedChangeAnchorKey(params.trackedChangeAnchorKey);
10621
+ const anchorStoryKey = anchorParts ? normalizeTrackedChangeStoryKey(anchorParts.storyKey) : void 0;
10426
10622
  if (preparedPresentation) {
10427
10623
  if (preparedPresentation !== committedReviewPresentations.value[String(effectiveDocumentId)]) continue;
10428
10624
  if (params.documentId != null && String(params.documentId) !== String(effectiveDocumentId)) continue;
10429
10625
  const id = normalizeCommentId(params.changeId);
10430
- const version = preparedPresentation.rowVersions?.[id];
10626
+ const version = preparedPresentation.rowVersions?.[String(id)] ?? NaN;
10431
10627
  if (!id || !Number.isSafeInteger(version) || version < 1) continue;
10432
10628
  const existing = [...trackedChangeIdentityIndex.candidates({ changeId: id })].filter((row) => String(row.fileId) === String(effectiveDocumentId) && String(row.commentId) === id);
10433
10629
  if (existing.length > 1) continue;
10434
10630
  const row = existing[0];
10435
10631
  if (row && row.trackedChangeCanonicalId !== params.trackedChangeCanonicalId) {
10436
- const previousVersion = previousPreparedPresentation?.rowVersions?.[id];
10437
- 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;
10632
+ const previousVersion = previousPreparedPresentation?.rowVersions?.[id] ?? NaN;
10633
+ 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;
10438
10634
  row.trackedChangeCanonicalId = params.trackedChangeCanonicalId;
10439
- row.updateIdentityValues?.({ trackedChangeCanonicalId: params.trackedChangeCanonicalId });
10440
10635
  trackedChangeIdentityIndex.add(row);
10441
10636
  }
10442
- 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;
10637
+ 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;
10443
10638
  }
10444
10639
  if (params.event === "omit") {
10445
10640
  if (params.changeId != null) liveIds.add(String(params.changeId));
@@ -10453,12 +10648,15 @@ var useCommentsStore = defineStore("comments", () => {
10453
10648
  superdoc,
10454
10649
  params,
10455
10650
  broadcastChanges: false,
10456
- trackedChangeIdentityIndex
10651
+ trackedChangeIdentityIndex,
10652
+ preserveDraft
10457
10653
  });
10458
10654
  appliedCount += 1;
10459
10655
  }
10460
10656
  } finally {
10461
- endInteractionSpan(identitySpan, trackedChangeIdentityIndex.work());
10657
+ const work = trackedChangeIdentityIndex.work();
10658
+ trackedChangeIdentityIndex.inUse = false;
10659
+ if (enteredBatch) endInteractionSpan(identitySpan, work);
10462
10660
  }
10463
10661
  if (!effectiveDocumentId || !pruneStale) {
10464
10662
  if (effectiveDocumentId) compactDuplicateBodyTrackedChangeComments(String(effectiveDocumentId));
@@ -10480,22 +10678,18 @@ var useCommentsStore = defineStore("comments", () => {
10480
10678
  return commentsList.value.filter((comment) => {
10481
10679
  if (comment?.trackedChange !== true) return false;
10482
10680
  if (normalizedDocumentId == null) return true;
10483
- const rowDocumentId = comment?.documentId ?? comment?.fileId ?? null;
10681
+ const rowDocumentId = Reflect.get(comment, "documentId") ?? comment?.fileId ?? null;
10484
10682
  return rowDocumentId != null && String(rowDocumentId) === normalizedDocumentId;
10485
10683
  }).length;
10486
10684
  };
10487
10685
  /**
10488
10686
  * Triggered when the editor locations are updated
10489
10687
  * Updates floating comment locations from the editor
10490
- *
10491
- * @param {Object} allCommentPositions The collected geometry keyed by comment / tracked-change id
10492
- * @param {{ retainMissingTrackedChangeGeometry?: boolean, retainedTrackedChangeIds?: Iterable<string> }} options Geometry reconciliation options
10493
- * @returns {void}
10494
10688
  */
10495
10689
  const handleEditorLocationsUpdate = (allCommentPositions, options = {}) => {
10496
10690
  if (allCommentPositions == null) return;
10497
10691
  const retainMissingTrackedChangeGeometry = options?.retainMissingTrackedChangeGeometry === true;
10498
- const retainedTrackedChangeIds = retainMissingTrackedChangeGeometry && options?.retainedTrackedChangeIds != null ? new Set(Array.from(options.retainedTrackedChangeIds, (id) => normalizeCommentId(id)).filter(Boolean)) : null;
10692
+ const retainedTrackedChangeIds = retainMissingTrackedChangeGeometry && options?.retainedTrackedChangeIds != null ? new Set(Array.from(options.retainedTrackedChangeIds, (id) => normalizeCommentId(id)).filter(isPresent)) : null;
10499
10693
  const previousPositions = editorCommentPositions.value || {};
10500
10694
  const normalizedPositions = {};
10501
10695
  Object.entries(allCommentPositions).forEach(([key, entry]) => {
@@ -10522,7 +10716,7 @@ var useCommentsStore = defineStore("comments", () => {
10522
10716
  return null;
10523
10717
  };
10524
10718
  const writeTrackedChangeAliases = (entry, comment, extraKeys = []) => {
10525
- const keys = [...aliasesFor(comment), ...extraKeys.map((id) => normalizeCommentId(id)).filter(Boolean)];
10719
+ const keys = [...aliasesFor(comment), ...extraKeys.map((id) => normalizeCommentId(id)).filter(isPresent)];
10526
10720
  for (const key of new Set(keys)) if (normalizedPositions[key] === void 0) normalizedPositions[key] = entry;
10527
10721
  };
10528
10722
  commentsList.value.forEach((comment) => {
@@ -10610,6 +10804,7 @@ var useCommentsStore = defineStore("comments", () => {
10610
10804
  isCommentsListVisible,
10611
10805
  isReviewDirectoryActive,
10612
10806
  isReviewDirectoryLoading,
10807
+ reviewAnalysisProgress,
10613
10808
  generalCommentIds,
10614
10809
  editorCommentIds,
10615
10810
  commentsParentElement,
@@ -10837,7 +11032,7 @@ var _hoisted_3$21 = {
10837
11032
  key: 0,
10838
11033
  class: "comments-dropdown__option-icon"
10839
11034
  };
10840
- var _hoisted_4$15 = ["innerHTML"];
11035
+ var _hoisted_4$16 = ["innerHTML"];
10841
11036
  var _hoisted_5$11 = { class: "comments-dropdown__option-label" };
10842
11037
  var CommentsDropdown_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require__plugin_vue_export_helper.defineComponent({
10843
11038
  __name: "CommentsDropdown",
@@ -10981,7 +11176,7 @@ var CommentsDropdown_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
10981
11176
  }, [hasIcon(option) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("span", _hoisted_3$21, [option.iconString ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("span", {
10982
11177
  key: 0,
10983
11178
  innerHTML: option.iconString
10984
- }, null, 8, _hoisted_4$15)) : (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(require__plugin_vue_export_helper.unref(OptionIcon), {
11179
+ }, null, 8, _hoisted_4$16)) : (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(require__plugin_vue_export_helper.unref(OptionIcon), {
10985
11180
  key: 1,
10986
11181
  option
10987
11182
  }, null, 8, ["option"]))])) : require__plugin_vue_export_helper.createCommentVNode("", true), require__plugin_vue_export_helper.createElementVNode("span", _hoisted_5$11, require__plugin_vue_export_helper.toDisplayString(option.label), 1)], 10, _hoisted_2$24);
@@ -10997,7 +11192,7 @@ var CommentsDropdown_default = /* @__PURE__ */ require__plugin_vue_export_helper
10997
11192
  var _hoisted_1$30 = { class: "sd-comment-option" };
10998
11193
  var _hoisted_2$23 = ["innerHTML"];
10999
11194
  var _hoisted_3$20 = { class: "sd-option-state" };
11000
- var _hoisted_4$14 = ["innerHTML"];
11195
+ var _hoisted_4$15 = ["innerHTML"];
11001
11196
  var InternalDropdown_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require__plugin_vue_export_helper.defineComponent({
11002
11197
  __name: "InternalDropdown",
11003
11198
  props: {
@@ -11074,7 +11269,7 @@ var InternalDropdown_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
11074
11269
  require__plugin_vue_export_helper.createElementVNode("div", {
11075
11270
  class: "sd-dropdown-caret",
11076
11271
  innerHTML: require__plugin_vue_export_helper.unref(superdocIcons).caretDown
11077
- }, null, 8, _hoisted_4$14)
11272
+ }, null, 8, _hoisted_4$15)
11078
11273
  ])]),
11079
11274
  _: 1
11080
11275
  }, 8, ["disabled", "content-style"])], 4);
@@ -11153,7 +11348,7 @@ var Avatar_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_v
11153
11348
  var _hoisted_1$28 = { class: "card-section comment-header" };
11154
11349
  var _hoisted_2$21 = { class: "comment-header-left" };
11155
11350
  var _hoisted_3$18 = { class: "user-info" };
11156
- var _hoisted_4$13 = { class: "user-name" };
11351
+ var _hoisted_4$14 = { class: "user-name" };
11157
11352
  var _hoisted_5$10 = {
11158
11353
  key: 0,
11159
11354
  class: "imported-tag"
@@ -11414,7 +11609,7 @@ var CommentHeader_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
11414
11609
  return require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_1$28, [require__plugin_vue_export_helper.createElementVNode("div", _hoisted_2$21, [require__plugin_vue_export_helper.createVNode(Avatar_default, {
11415
11610
  user: getCurrentUser.value,
11416
11611
  class: "avatar"
11417
- }, null, 8, ["user"]), require__plugin_vue_export_helper.createElementVNode("div", _hoisted_3$18, [require__plugin_vue_export_helper.createElementVNode("div", _hoisted_4$13, [require__plugin_vue_export_helper.createTextVNode(require__plugin_vue_export_helper.toDisplayString(getCurrentUser.value.name), 1), isImported.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("span", _hoisted_5$10, "IMPORTED")) : require__plugin_vue_export_helper.createCommentVNode("", true)]), props.comment.createdTime ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_6$8, require__plugin_vue_export_helper.toDisplayString(require__plugin_vue_export_helper.unref(formatDate)(props.comment.createdTime)), 1)) : require__plugin_vue_export_helper.createCommentVNode("", true)])]), require__plugin_vue_export_helper.createElementVNode("div", { class: require__plugin_vue_export_helper.normalizeClass(["overflow-menu", { "is-visible": props.isActive }]) }, [
11612
+ }, null, 8, ["user"]), require__plugin_vue_export_helper.createElementVNode("div", _hoisted_3$18, [require__plugin_vue_export_helper.createElementVNode("div", _hoisted_4$14, [require__plugin_vue_export_helper.createTextVNode(require__plugin_vue_export_helper.toDisplayString(getCurrentUser.value.name), 1), isImported.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("span", _hoisted_5$10, "IMPORTED")) : require__plugin_vue_export_helper.createCommentVNode("", true)]), props.comment.createdTime ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_6$8, require__plugin_vue_export_helper.toDisplayString(require__plugin_vue_export_helper.unref(formatDate)(props.comment.createdTime)), 1)) : require__plugin_vue_export_helper.createCommentVNode("", true)])]), require__plugin_vue_export_helper.createElementVNode("div", { class: require__plugin_vue_export_helper.normalizeClass(["overflow-menu", { "is-visible": props.isActive }]) }, [
11418
11613
  allowResolve.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
11419
11614
  key: 0,
11420
11615
  class: require__plugin_vue_export_helper.normalizeClass(["overflow-menu__icon", { "sd-is-disabled": Boolean(__props.resolveDisabledReason) }]),
@@ -11472,7 +11667,7 @@ var _hoisted_3$17 = [
11472
11667
  "aria-controls",
11473
11668
  "aria-activedescendant"
11474
11669
  ];
11475
- var _hoisted_4$12 = [
11670
+ var _hoisted_4$13 = [
11476
11671
  "id",
11477
11672
  "aria-selected",
11478
11673
  "data-sd-comment-mention-option",
@@ -11679,7 +11874,7 @@ var CommentInput_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ req
11679
11874
  "aria-selected": index === highlightedMentionIndex.value,
11680
11875
  "data-sd-comment-mention-option": userKey(user),
11681
11876
  onMousedown: require__plugin_vue_export_helper.withModifiers(($event) => selectMention(user), ["prevent"])
11682
- }, [require__plugin_vue_export_helper.createElementVNode("span", _hoisted_5$9, require__plugin_vue_export_helper.toDisplayString(displayName(user)), 1), user.email ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("span", _hoisted_6$7, require__plugin_vue_export_helper.toDisplayString(user.email), 1)) : require__plugin_vue_export_helper.createCommentVNode("", true)], 42, _hoisted_4$12);
11877
+ }, [require__plugin_vue_export_helper.createElementVNode("span", _hoisted_5$9, require__plugin_vue_export_helper.toDisplayString(displayName(user)), 1), user.email ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("span", _hoisted_6$7, require__plugin_vue_export_helper.toDisplayString(user.email), 1)) : require__plugin_vue_export_helper.createCommentVNode("", true)], 42, _hoisted_4$13);
11683
11878
  }), 128))])) : require__plugin_vue_export_helper.createCommentVNode("", true)])], 2)]);
11684
11879
  };
11685
11880
  }
@@ -11808,7 +12003,7 @@ var _hoisted_2$19 = {
11808
12003
  class: "existing-internal-input"
11809
12004
  };
11810
12005
  var _hoisted_3$16 = { class: "new-comment-input-wrapper" };
11811
- var _hoisted_4$11 = {
12006
+ var _hoisted_4$12 = {
11812
12007
  key: 1,
11813
12008
  class: "tracked-change-decision-alert",
11814
12009
  role: "alert"
@@ -11833,8 +12028,8 @@ var _hoisted_11$3 = {
11833
12028
  key: 0,
11834
12029
  class: "card-section comment-body"
11835
12030
  };
11836
- var _hoisted_12$2 = ["data-track-change-semantic-color-key"];
11837
- var _hoisted_13$2 = { key: 0 };
12031
+ var _hoisted_12$3 = ["data-track-change-semantic-color-key"];
12032
+ var _hoisted_13$3 = { key: 0 };
11838
12033
  var _hoisted_14$2 = { class: "change-type tracked-change-label" };
11839
12034
  var _hoisted_15$2 = {
11840
12035
  key: 0,
@@ -12617,6 +12812,16 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
12617
12812
  }
12618
12813
  handleResolve();
12619
12814
  };
12815
+ const ownsTrackedChangeDecision = (superdoc, editor) => editor !== null && proxy.$superdoc === superdoc && superdoc.activeEditor === editor;
12816
+ const restoreTrackedChangeDecisionFocus = (superdoc, editor) => {
12817
+ require__plugin_vue_export_helper.nextTick(() => {
12818
+ if (!ownsTrackedChangeDecision(superdoc, editor)) return;
12819
+ commentsStore.lastUpdate = /* @__PURE__ */ new Date();
12820
+ activeComment.value = null;
12821
+ commentsStore.setActiveComment(superdoc, activeComment.value);
12822
+ if (ownsTrackedChangeDecision(superdoc, editor)) superdoc.focus?.({ preventScroll: true });
12823
+ });
12824
+ };
12620
12825
  const handleReject = async () => {
12621
12826
  if (props.comment.trackedChange) {
12622
12827
  if (trackedChangeDecisionsAreDisabled()) return trackedChangeDecisionDisabledOutcome();
@@ -12624,23 +12829,21 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
12624
12829
  const customHandler = proxy.$superdoc.config.onTrackedChangeBubbleReject;
12625
12830
  if (props.comment.trackedChange) {
12626
12831
  if (isV2Mode.value) {
12832
+ const superdoc = proxy.$superdoc;
12833
+ const editor = superdoc.activeEditor;
12627
12834
  const outcome = await commentsStore.decideTrackedChangeFromSidebar({
12628
- superdoc: proxy.$superdoc,
12835
+ superdoc,
12629
12836
  comment: props.comment,
12630
12837
  decision: "reject"
12631
12838
  });
12839
+ if (!ownsTrackedChangeDecision(superdoc, editor)) return;
12632
12840
  if (!outcome?.ok || outcome.success === false) {
12633
12841
  reportTrackedChangeDecisionFailure("reject", outcome);
12634
12842
  return;
12635
12843
  }
12636
12844
  clearTrackedChangeDecisionFailure();
12637
- if (typeof customHandler === "function") customHandler(props.comment, proxy.$superdoc.activeEditor);
12638
- require__plugin_vue_export_helper.nextTick(() => {
12639
- commentsStore.lastUpdate = /* @__PURE__ */ new Date();
12640
- activeComment.value = null;
12641
- commentsStore.setActiveComment(proxy.$superdoc, activeComment.value);
12642
- proxy.$superdoc.focus?.({ preventScroll: true });
12643
- });
12845
+ if (typeof customHandler === "function") customHandler(props.comment, editor);
12846
+ restoreTrackedChangeDecisionFocus(superdoc, editor);
12644
12847
  return;
12645
12848
  }
12646
12849
  let decisionApplied = true;
@@ -12686,23 +12889,21 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
12686
12889
  }
12687
12890
  const customHandler = proxy.$superdoc.config.onTrackedChangeBubbleAccept;
12688
12891
  if (props.comment.trackedChange && isV2Mode.value) {
12892
+ const superdoc = proxy.$superdoc;
12893
+ const editor = superdoc.activeEditor;
12689
12894
  const outcome = await commentsStore.decideTrackedChangeFromSidebar({
12690
- superdoc: proxy.$superdoc,
12895
+ superdoc,
12691
12896
  comment: props.comment,
12692
12897
  decision: "accept"
12693
12898
  });
12899
+ if (!ownsTrackedChangeDecision(superdoc, editor)) return;
12694
12900
  if (!outcome?.ok || outcome.success === false) {
12695
12901
  reportTrackedChangeDecisionFailure("accept", outcome);
12696
12902
  return;
12697
12903
  }
12698
12904
  clearTrackedChangeDecisionFailure();
12699
- if (typeof customHandler === "function") customHandler(props.comment, proxy.$superdoc.activeEditor);
12700
- require__plugin_vue_export_helper.nextTick(() => {
12701
- commentsStore.lastUpdate = /* @__PURE__ */ new Date();
12702
- activeComment.value = null;
12703
- commentsStore.setActiveComment(proxy.$superdoc, activeComment.value);
12704
- proxy.$superdoc.focus?.({ preventScroll: true });
12705
- });
12905
+ if (typeof customHandler === "function") customHandler(props.comment, editor);
12906
+ restoreTrackedChangeDecisionFocus(superdoc, editor);
12706
12907
  return;
12707
12908
  }
12708
12909
  let v1TrackedChangeDecisionApplied = true;
@@ -12801,14 +13002,15 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
12801
13002
  if (session?.owner === editSessionOwner) session.submission = submission;
12802
13003
  const editorOwner = captureCommentEditorOwner();
12803
13004
  const documentId = editorOwner.adapter.documentId;
12804
- const thread = props.comment;
13005
+ const threadId = props.comment.commentId;
13006
+ const fileId = props.comment.fileId;
12805
13007
  const draft = currentCommentText.value;
12806
13008
  const outcome = await commentsStore.editCommentV2({
12807
13009
  superdoc: proxy.$superdoc,
12808
13010
  commentId: comment.commentId,
12809
13011
  text: draft
12810
13012
  });
12811
- if (session?.owner !== editSessionOwner || commentEditSessions.get(commentsStore) !== session || session.submission !== submission || !editorOwner.isCurrent() || editorOwner.adapter.documentId !== documentId || props.comment !== thread || editingCommentId.value !== comment.commentId || currentCommentText.value !== draft) return outcome;
13013
+ if (session?.owner !== editSessionOwner || commentEditSessions.get(commentsStore) !== session || session.submission !== submission || !editorOwner.isCurrent() || editorOwner.adapter.documentId !== documentId || props.comment.commentId !== threadId || props.comment.fileId !== fileId || editingCommentId.value !== comment.commentId || currentCommentText.value !== draft) return outcome;
12812
13014
  if (!outcome?.ok) {
12813
13015
  commentSaveFailure.value = commentSaveFailureMessage(outcome);
12814
13016
  return outcome;
@@ -12947,7 +13149,7 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
12947
13149
  "config",
12948
13150
  "comment"
12949
13151
  ])]),
12950
- commentSaveFailure.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("p", _hoisted_4$11, require__plugin_vue_export_helper.toDisplayString(commentSaveFailure.value), 1)) : require__plugin_vue_export_helper.createCommentVNode("", true),
13152
+ commentSaveFailure.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("p", _hoisted_4$12, require__plugin_vue_export_helper.toDisplayString(commentSaveFailure.value), 1)) : require__plugin_vue_export_helper.createCommentVNode("", true),
12951
13153
  require__plugin_vue_export_helper.createElementVNode("div", _hoisted_5$8, [require__plugin_vue_export_helper.createElementVNode("button", {
12952
13154
  class: "sd-button reply-btn-cancel",
12953
13155
  onClick: require__plugin_vue_export_helper.withModifiers(handleCancel, ["stop", "prevent"])
@@ -13006,7 +13208,7 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
13006
13208
  "data-track-change-semantic-color-key": comment.semanticColorKey ?? void 0,
13007
13209
  ref_for: true,
13008
13210
  ref: index === 0 ? setParentBodyRef : void 0
13009
- }, [comment.trackedChangeLabel ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_13$2, [require__plugin_vue_export_helper.createElementVNode("span", _hoisted_14$2, require__plugin_vue_export_helper.toDisplayString(comment.trackedChangeLabel), 1), comment.trackedChangeDetailLines && comment.trackedChangeDetailLines.length ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_15$2, [(require__plugin_vue_export_helper.openBlock(true), require__plugin_vue_export_helper.createElementBlock(require__plugin_vue_export_helper.Fragment, null, require__plugin_vue_export_helper.renderList(comment.trackedChangeDetailLines, (line, lineIndex) => {
13211
+ }, [comment.trackedChangeLabel ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_13$3, [require__plugin_vue_export_helper.createElementVNode("span", _hoisted_14$2, require__plugin_vue_export_helper.toDisplayString(comment.trackedChangeLabel), 1), comment.trackedChangeDetailLines && comment.trackedChangeDetailLines.length ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_15$2, [(require__plugin_vue_export_helper.openBlock(true), require__plugin_vue_export_helper.createElementBlock(require__plugin_vue_export_helper.Fragment, null, require__plugin_vue_export_helper.renderList(comment.trackedChangeDetailLines, (line, lineIndex) => {
13010
13212
  return require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
13011
13213
  key: lineIndex,
13012
13214
  class: "tracked-change-detail-line"
@@ -13031,7 +13233,7 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
13031
13233
  style: require__plugin_vue_export_helper.normalizeStyle(trackedChangeImagePreviewStyle(comment.trackedChangeImagePreview)),
13032
13234
  loading: "lazy",
13033
13235
  decoding: "async"
13034
- }, null, 12, _hoisted_38)], 8, _hoisted_37)) : require__plugin_vue_export_helper.createCommentVNode("", true)], 10, _hoisted_12$2),
13236
+ }, null, 12, _hoisted_38)], 8, _hoisted_37)) : require__plugin_vue_export_helper.createCommentVNode("", true)], 10, _hoisted_12$3),
13035
13237
  shouldTruncate.value && isTextOverflowing.value && index === 0 ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
13036
13238
  key: 0,
13037
13239
  class: "show-more-toggle",
@@ -13128,7 +13330,7 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
13128
13330
  });
13129
13331
  //#endregion
13130
13332
  //#region src/components/CommentsLayer/CommentDialog.vue
13131
- var CommentDialog_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(CommentDialog_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-4220ce17"]]);
13333
+ var CommentDialog_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(CommentDialog_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-919d450b"]]);
13132
13334
  //#endregion
13133
13335
  //#region src/components/CommentsLayer/commentsList/ReviewDirectoryListItem.vue?vue&type=script&setup=true&lang.ts
13134
13336
  var _hoisted_1$25 = [
@@ -13284,8 +13486,14 @@ var ReviewDirectoryListItem_default = /* @__PURE__ */ require__plugin_vue_export
13284
13486
  //#endregion
13285
13487
  //#region src/components/CommentsLayer/commentsList/commentsList.vue?vue&type=script&setup=true&lang.ts
13286
13488
  var _hoisted_1$24 = ["aria-busy"];
13287
- var _hoisted_2$18 = { key: 0 };
13489
+ var _hoisted_2$18 = {
13490
+ key: 0,
13491
+ class: "review-analysis",
13492
+ role: "status",
13493
+ "data-testid": "review-analysis-progress"
13494
+ };
13288
13495
  var _hoisted_3$15 = { key: 1 };
13496
+ var _hoisted_4$11 = { key: 2 };
13289
13497
  var REPEATED_HEADER_FOOTER_STORY_TYPE = "headerFooterPart";
13290
13498
  var commentsList_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require__plugin_vue_export_helper.defineComponent({
13291
13499
  __name: "commentsList",
@@ -13307,7 +13515,12 @@ var commentsList_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ req
13307
13515
  const rovingIndex = require__plugin_vue_export_helper.ref(0);
13308
13516
  const isCommentsListVisible = require__plugin_vue_export_helper.toRef(commentsStore, "isCommentsListVisible");
13309
13517
  const floatingCommentInstances = require__plugin_vue_export_helper.computed(() => commentsStore.getFloatingCommentInstances);
13310
- const { activeComment, activeFloatingCommentInstanceId, commentsList, reviewDirectoryList, isReviewDirectoryActive, isReviewDirectoryLoading } = storeToRefs(piniaCommentsStore);
13518
+ const { activeComment, activeFloatingCommentInstanceId, commentsList, reviewDirectoryList, isReviewDirectoryActive, isReviewDirectoryLoading, reviewAnalysisProgress } = storeToRefs(piniaCommentsStore);
13519
+ const reviewAnalysisLabel = require__plugin_vue_export_helper.computed(() => {
13520
+ const progress = reviewAnalysisProgress.value;
13521
+ if (!isReviewDirectoryActive.value || !isReviewDirectoryLoading.value || !progress) return null;
13522
+ return `Analysing changes… ${Math.min(progress.done, progress.total)} of ${progress.total}`;
13523
+ });
13311
13524
  const groupedComments = require__plugin_vue_export_helper.computed(() => isReviewDirectoryActive.value ? commentsStore.getGroupedReviewDirectory : commentsStore.getGroupedComments);
13312
13525
  const reviewRows = require__plugin_vue_export_helper.computed(() => isReviewDirectoryActive.value ? reviewDirectoryList.value : commentsList.value);
13313
13526
  const commentAliases = (comment) => [
@@ -13420,45 +13633,53 @@ var commentsList_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ req
13420
13633
  class: "comments-list",
13421
13634
  role: "list",
13422
13635
  "aria-busy": require__plugin_vue_export_helper.unref(isReviewDirectoryActive) && require__plugin_vue_export_helper.unref(isReviewDirectoryLoading) ? "true" : "false"
13423
- }, [__props.showMainComments ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_2$18, [(require__plugin_vue_export_helper.openBlock(true), require__plugin_vue_export_helper.createElementBlock(require__plugin_vue_export_helper.Fragment, null, require__plugin_vue_export_helper.renderList(parentCommentDisplayItems.value, (item, index) => {
13424
- return require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(ReviewDirectoryListItem_default, {
13425
- key: item.id,
13426
- item,
13427
- index,
13428
- total: totalDisplayItems.value,
13429
- "tab-index": index === rovingIndex.value ? 0 : -1,
13430
- onFocusItem: _cache[0] || (_cache[0] = ($event) => rovingIndex.value = $event),
13431
- onNavigate: focusDirectoryItem
13432
- }, null, 8, [
13433
- "item",
13434
- "index",
13435
- "total",
13436
- "tab-index"
13437
- ]);
13438
- }), 128))])) : require__plugin_vue_export_helper.createCommentVNode("", true), shouldShowResolvedComments.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_3$15, [_cache[2] || (_cache[2] = require__plugin_vue_export_helper.createElementVNode("div", { class: "comment-title" }, "Resolved", -1)), (require__plugin_vue_export_helper.openBlock(true), require__plugin_vue_export_helper.createElementBlock(require__plugin_vue_export_helper.Fragment, null, require__plugin_vue_export_helper.renderList(resolvedCommentDisplayItems.value, (item, index) => {
13439
- return require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(ReviewDirectoryListItem_default, {
13440
- key: item.id,
13441
- item,
13442
- index: displayedMainCount.value + index,
13443
- total: totalDisplayItems.value,
13444
- "tab-index": displayedMainCount.value + index === rovingIndex.value ? 0 : -1,
13445
- onFocusItem: _cache[1] || (_cache[1] = ($event) => rovingIndex.value = $event),
13446
- onNavigate: focusDirectoryItem
13447
- }, null, 8, [
13448
- "item",
13449
- "index",
13450
- "total",
13451
- "tab-index"
13452
- ]);
13453
- }), 128))])) : require__plugin_vue_export_helper.createCommentVNode("", true)], 8, _hoisted_1$24);
13636
+ }, [
13637
+ reviewAnalysisLabel.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_2$18, require__plugin_vue_export_helper.toDisplayString(reviewAnalysisLabel.value), 1)) : require__plugin_vue_export_helper.createCommentVNode("", true),
13638
+ __props.showMainComments ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_3$15, [(require__plugin_vue_export_helper.openBlock(true), require__plugin_vue_export_helper.createElementBlock(require__plugin_vue_export_helper.Fragment, null, require__plugin_vue_export_helper.renderList(parentCommentDisplayItems.value, (item, index) => {
13639
+ return require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(ReviewDirectoryListItem_default, {
13640
+ key: item.id,
13641
+ item,
13642
+ index,
13643
+ total: totalDisplayItems.value,
13644
+ "tab-index": index === rovingIndex.value ? 0 : -1,
13645
+ onFocusItem: _cache[0] || (_cache[0] = ($event) => rovingIndex.value = $event),
13646
+ onNavigate: focusDirectoryItem
13647
+ }, null, 8, [
13648
+ "item",
13649
+ "index",
13650
+ "total",
13651
+ "tab-index"
13652
+ ]);
13653
+ }), 128))])) : require__plugin_vue_export_helper.createCommentVNode("", true),
13654
+ shouldShowResolvedComments.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_4$11, [_cache[2] || (_cache[2] = require__plugin_vue_export_helper.createElementVNode("div", { class: "comment-title" }, "Resolved", -1)), (require__plugin_vue_export_helper.openBlock(true), require__plugin_vue_export_helper.createElementBlock(require__plugin_vue_export_helper.Fragment, null, require__plugin_vue_export_helper.renderList(resolvedCommentDisplayItems.value, (item, index) => {
13655
+ return require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(ReviewDirectoryListItem_default, {
13656
+ key: item.id,
13657
+ item,
13658
+ index: displayedMainCount.value + index,
13659
+ total: totalDisplayItems.value,
13660
+ "tab-index": displayedMainCount.value + index === rovingIndex.value ? 0 : -1,
13661
+ onFocusItem: _cache[1] || (_cache[1] = ($event) => rovingIndex.value = $event),
13662
+ onNavigate: focusDirectoryItem
13663
+ }, null, 8, [
13664
+ "item",
13665
+ "index",
13666
+ "total",
13667
+ "tab-index"
13668
+ ]);
13669
+ }), 128))])) : require__plugin_vue_export_helper.createCommentVNode("", true)
13670
+ ], 8, _hoisted_1$24);
13454
13671
  };
13455
13672
  }
13456
13673
  });
13457
13674
  //#endregion
13458
13675
  //#region src/components/CommentsLayer/commentsList/commentsList.vue
13459
- var commentsList_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(commentsList_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-8eca48e1"]]);
13676
+ var commentsList_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(commentsList_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-641eba1e"]]);
13460
13677
  //#endregion
13461
13678
  //#region src/components/CommentsLayer/commentsList/super-comments-list.ts
13679
+ var reviewAnalysisOf = (snapshot) => {
13680
+ const analysis = snapshot?.analysis;
13681
+ return analysis && typeof analysis.done === "number" && typeof analysis.total === "number" && analysis.total > 0 ? analysis : null;
13682
+ };
13462
13683
  var isReviewDirectoryStore = (value) => require__plugin_vue_export_helper.isPlainObject(value) && typeof value.setReviewDirectoryFromV2 === "function" && typeof value.clearReviewDirectory === "function";
13463
13684
  var readCommentsStore = (superdoc) => {
13464
13685
  const store = superdoc?.["commentsStore"];
@@ -13516,9 +13737,16 @@ var SuperComments = class extends require_eventemitter3.import_eventemitter3.def
13516
13737
  if (generation !== this.directoryGeneration) return;
13517
13738
  if ((snapshot?.listStatus ?? snapshot?.status) !== "ready") {
13518
13739
  const store = readCommentsStore(this.superdoc);
13519
- if (store) store.isReviewDirectoryLoading = true;
13740
+ if (store) {
13741
+ store.isReviewDirectoryLoading = true;
13742
+ if (family === "trackChanges") store.reviewAnalysisProgress = reviewAnalysisOf(snapshot);
13743
+ }
13520
13744
  return;
13521
13745
  }
13746
+ if (family === "trackChanges") {
13747
+ const store = readCommentsStore(this.superdoc);
13748
+ if (store) store.reviewAnalysisProgress = null;
13749
+ }
13522
13750
  const items = Array.isArray(snapshot.items) ? snapshot.items : [];
13523
13751
  if (this.directoryItemRefs[family] === items) {
13524
13752
  const store = readCommentsStore(this.superdoc);
@@ -13577,6 +13805,7 @@ var SuperComments = class extends require_eventemitter3.import_eventemitter3.def
13577
13805
  store.clearReviewDirectory();
13578
13806
  store.isReviewDirectoryActive = false;
13579
13807
  store.isReviewDirectoryLoading = false;
13808
+ store.reviewAnalysisProgress = null;
13580
13809
  }
13581
13810
  if (this.app) {
13582
13811
  this.app.unmount();
@@ -13897,124 +14126,6 @@ var composeAuthorColorResolver = (config) => {
13897
14126
  };
13898
14127
  };
13899
14128
  //#endregion
13900
- //#region ../document-api/src/errors.ts
13901
- /**
13902
- * Structured validation error thrown by document-api execute* functions.
13903
- *
13904
- * Consumers should prefer checking `error.code` over `instanceof` for resilience
13905
- * across package boundaries and bundling scenarios.
13906
- */
13907
- var DocumentApiValidationError = class DocumentApiValidationError extends Error {
13908
- constructor(code, message, details) {
13909
- super(message);
13910
- this.name = "DocumentApiValidationError";
13911
- this.code = code;
13912
- this.details = details;
13913
- Object.setPrototypeOf(this, DocumentApiValidationError.prototype);
13914
- }
13915
- };
13916
- //#endregion
13917
- //#region ../document-api/src/export/export.types.ts
13918
- /** Canonical tracked-change export modes accepted by every public export surface. */
13919
- var SD_EXPORT_MODES = [
13920
- "review-preserving",
13921
- "final",
13922
- "original"
13923
- ];
13924
- /** Default mode for modeful DOCX export. */
13925
- var DEFAULT_SD_EXPORT_MODE = "review-preserving";
13926
- var SD_COMMENT_EXPORT_POLICIES = [
13927
- "preserve",
13928
- "resolve",
13929
- "remove"
13930
- ];
13931
- var SD_CONTENT_CONTROL_EXPORT_POLICIES = ["preserve", "flatten"];
13932
- //#endregion
13933
- //#region ../document-api/src/export/export.ts
13934
- /**
13935
- * Export namespace: engine-agnostic public API and adapter contract.
13936
- *
13937
- * Validates the requested mode at the public boundary so an invalid mode fails
13938
- * closed with `INVALID_INPUT` before any document work happens, then delegates
13939
- * byte production and degradation reporting to the engine adapter.
13940
- */
13941
- function resolveMode$1(input) {
13942
- if (input != null && !require_create_super_doc_ui.isRecord(input)) throw new DocumentApiValidationError("INVALID_INPUT", "export.toDocx input must be an object.");
13943
- const raw = input?.mode;
13944
- if (raw === void 0) return DEFAULT_SD_EXPORT_MODE;
13945
- 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(", ")}.`, {
13946
- mode: raw,
13947
- allowedModes: [...SD_EXPORT_MODES]
13948
- });
13949
- return raw;
13950
- }
13951
- function resolveDocxExportPolicies(input = {}) {
13952
- if (!require_create_super_doc_ui.isRecord(input)) throw new DocumentApiValidationError("INVALID_INPUT", "DOCX export input must be an object.");
13953
- const mode = resolveMode$1(input);
13954
- const policies = input.policies;
13955
- if (policies !== void 0 && !require_create_super_doc_ui.isRecord(policies)) throw new DocumentApiValidationError("INVALID_INPUT", "DOCX export policies must be an object.");
13956
- const allowed = {
13957
- trackedChanges: SD_EXPORT_MODES,
13958
- comments: SD_COMMENT_EXPORT_POLICIES,
13959
- contentControls: SD_CONTENT_CONTROL_EXPORT_POLICIES
13960
- };
13961
- 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}.`, {
13962
- policy: key,
13963
- value
13964
- });
13965
- const requested = policies;
13966
- if (input.mode !== void 0 && requested?.trackedChanges !== void 0 && mode !== requested.trackedChanges) throw new DocumentApiValidationError("INVALID_INPUT", "mode conflicts with policies.trackedChanges.");
13967
- return {
13968
- trackedChanges: requested?.trackedChanges ?? mode,
13969
- comments: requested?.comments ?? "preserve",
13970
- contentControls: requested?.contentControls ?? "preserve"
13971
- };
13972
- }
13973
- //#endregion
13974
- //#region ../document-api/src/permission-ranges/permission-ranges.ts
13975
- function editorKey(editor) {
13976
- return editor.id?.trim() ? editor.id : editor.email?.trim() || void 0;
13977
- }
13978
- function editorMatches(editor, id) {
13979
- return Boolean(id.trim()) && (editor.id === id || Boolean(editor.email?.trim() && editor.email.trim().toLowerCase() === id.toLowerCase()));
13980
- }
13981
- /**
13982
- * Resolve a host ID or imported Word email against an explicitly supplied roster.
13983
- * Word receives the email when available; opaque IDs remain host-specific.
13984
- * Duplicate IDs/emails are ambiguous. This does not authenticate a user.
13985
- */
13986
- function resolvePermissionRangeEditor(editors, id) {
13987
- const matches = editors.filter((editor) => editorMatches(editor, id));
13988
- if (matches.length === 0) return {
13989
- status: "unknown",
13990
- id
13991
- };
13992
- if (matches.length !== 1) return {
13993
- status: "ambiguous",
13994
- id
13995
- };
13996
- const editor = matches[0];
13997
- const wordId = editor.email?.trim() || editor.id;
13998
- if (editors.filter((candidate) => editorMatches(candidate, wordId)).length !== 1) return {
13999
- status: "ambiguous",
14000
- id
14001
- };
14002
- return {
14003
- status: "resolved",
14004
- id,
14005
- editor: { ...editor },
14006
- principal: {
14007
- kind: "editor",
14008
- id: wordId
14009
- }
14010
- };
14011
- }
14012
- /** Enumerate the host roster for an editor picker, including ambiguous entries. */
14013
- function listPermissionRangeEditors(editors) {
14014
- const ids = new Set(editors.map(editorKey).filter((id) => id !== void 0));
14015
- return Array.from(ids, (id) => resolvePermissionRangeEditor(editors, id));
14016
- }
14017
- //#endregion
14018
14129
  //#region src/core/superdoc-ui-owner.ts
14019
14130
  var ownerIds = /* @__PURE__ */ new WeakMap();
14020
14131
  function getSuperDocUiOwnerId(superdoc) {
@@ -14125,18 +14236,42 @@ var FloatingComments_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
14125
14236
  const { activeComment, activeFloatingCommentInstanceId, editorCommentPositions, pendingComment, editingCommentId, instantSidebarAlignmentTargetY, instantSidebarAlignmentThreadId, instantSidebarAlignmentInstanceId } = storeToRefs(piniaCommentsStore);
14126
14237
  const { activeZoom } = storeToRefs(superdocStore);
14127
14238
  const committedPresentation = require__plugin_vue_export_helper.computed(() => commentsStore.committedReviewPresentations?.[String(props.currentDocument?.id)] ?? null);
14239
+ const committedInstanceCache = /* @__PURE__ */ new Map();
14240
+ let committedInstanceCacheOwner = "";
14241
+ const sameReviewInstance = (cached, next) => {
14242
+ const before = Object.entries(cached).filter(([key]) => key !== "comment" && key !== "rowVersion");
14243
+ const after = Object.entries(next);
14244
+ if (before.length !== after.length) return false;
14245
+ const previous = new Map(before);
14246
+ return after.every(([key, value]) => previous.has(key) && (key === "positionEntry" ? JSON.stringify(previous.get(key)) === JSON.stringify(value) : Object.is(previous.get(key), value)));
14247
+ };
14128
14248
  const committedInstances = require__plugin_vue_export_helper.computed(() => {
14129
14249
  const presentation = committedPresentation.value;
14130
14250
  if (!presentation) return null;
14131
14251
  const rows = new Map(commentsStore.commentsList.filter((row) => String(row.fileId) === String(props.currentDocument?.id)).map((row) => [String(row.commentId), row]));
14132
- return presentation.instances.flatMap((instance) => {
14252
+ const owner = `${presentation.epoch}\u0000${presentation.coordinate?.mountId ?? ""}`;
14253
+ if (owner !== committedInstanceCacheOwner) {
14254
+ committedInstanceCache.clear();
14255
+ committedInstanceCacheOwner = owner;
14256
+ }
14257
+ const live = /* @__PURE__ */ new Set();
14258
+ const instances = presentation.instances.flatMap((instance) => {
14133
14259
  const comment = instance.rowId === "pending" ? pendingComment.value : rows.get(instance.rowId);
14134
- return comment ? [{
14260
+ if (!comment) return [];
14261
+ const rowVersion = presentation.rowVersions[instance.rowId] ?? 0;
14262
+ live.add(instance.id);
14263
+ const cached = committedInstanceCache.get(instance.id);
14264
+ if (cached && cached.comment === comment && cached.rowVersion === rowVersion && sameReviewInstance(cached, instance)) return [cached];
14265
+ const merged = {
14135
14266
  ...instance,
14136
14267
  comment,
14137
- rowVersion: presentation.rowVersions[instance.rowId] ?? 0
14138
- }] : [];
14268
+ rowVersion
14269
+ };
14270
+ committedInstanceCache.set(instance.id, merged);
14271
+ return [merged];
14139
14272
  });
14273
+ for (const id of committedInstanceCache.keys()) if (!live.has(id)) committedInstanceCache.delete(id);
14274
+ return instances;
14140
14275
  });
14141
14276
  const floatingCommentInstances = require__plugin_vue_export_helper.computed(() => {
14142
14277
  if (committedInstances.value) return committedInstances.value.filter((instance) => instance.rowId !== "pending");
@@ -14552,6 +14687,18 @@ var FloatingComments_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
14552
14687
  scheduleDeferredRemeasure(() => allPositions.value.map((pos) => pos.id));
14553
14688
  });
14554
14689
  };
14690
+ const resizeHandlers = /* @__PURE__ */ new Map();
14691
+ const resizeHandlerFor = (id) => {
14692
+ let handler = resizeHandlers.get(id);
14693
+ if (!handler) {
14694
+ handler = () => {
14695
+ const position = allPositions.value.find((candidate) => candidate.id === id);
14696
+ if (position) handleResize(position);
14697
+ };
14698
+ resizeHandlers.set(id, handler);
14699
+ }
14700
+ return handler;
14701
+ };
14555
14702
  const setInstantLayoutTransitionsDisabled = (disabled) => {
14556
14703
  if (!disabled && directDecisionContinuityTargetId.value != null) return;
14557
14704
  disableInstantLayoutTransitions.value = disabled;
@@ -14956,6 +15103,7 @@ var FloatingComments_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
14956
15103
  visibilityChanged = true;
14957
15104
  }
14958
15105
  if (visibilityChanged) visibleIds.value = newVisible;
15106
+ for (const id of resizeHandlers.keys()) if (!currentIds.has(id)) resizeHandlers.delete(id);
14959
15107
  for (const id of prevPositionIds) if (!currentIds.has(id)) delete _heightsCache[id];
14960
15108
  const setChanged = prevPositionIds.size !== currentIds.size || [...prevPositionIds].some((id) => !currentIds.has(id));
14961
15109
  const orderChanged = renderedPositionOrder.length !== positionOrder.length || renderedPositionOrder.some((id, index) => id !== positionOrder[index]);
@@ -15048,12 +15196,13 @@ var FloatingComments_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
15048
15196
  style: require__plugin_vue_export_helper.normalizeStyle({
15049
15197
  top: (pos.isOffscreenReviewCard && !committedPresentation.value ? pos.top - sidebarOffsetY.value : pos.top) + "px",
15050
15198
  height: pos.height + "px",
15051
- transition: committedPresentation.value || disableInstantLayoutTransitions.value ? "none" : void 0
15199
+ transition: committedPresentation.value || disableInstantLayoutTransitions.value ? "none" : void 0,
15200
+ visibility: pos.instanceRef?.provisional ? "hidden" : void 0
15052
15201
  })
15053
15202
  }, [shouldRenderDialog(pos) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(CommentDialog_default, {
15054
15203
  key: pos.id + commentsRenderKey.value,
15055
15204
  onReady: handleDialog,
15056
- onResize: ($event) => handleResize(pos),
15205
+ onResize: resizeHandlerFor(pos.id),
15057
15206
  class: "floating-comment",
15058
15207
  parent: __props.parent,
15059
15208
  comment: pos.commentRef,
@@ -15076,7 +15225,7 @@ var FloatingComments_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
15076
15225
  });
15077
15226
  //#endregion
15078
15227
  //#region src/components/CommentsLayer/FloatingComments.vue
15079
- var FloatingComments_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(FloatingComments_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-aaf2c537"]]);
15228
+ var FloatingComments_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(FloatingComments_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-1070f5a1"]]);
15080
15229
  //#endregion
15081
15230
  //#region src/components/CommentsLayer/PdfCommentsLayer.vue?vue&type=script&setup=true&lang.ts
15082
15231
  var _hoisted_1$22 = {
@@ -15210,7 +15359,7 @@ var PdfCommentsLayer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
15210
15359
  });
15211
15360
  //#endregion
15212
15361
  //#region src/components/CommentsLayer/PdfCommentsLayer.vue
15213
- var PdfCommentsLayer_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(PdfCommentsLayer_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-86166837"]]);
15362
+ var PdfCommentsLayer_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(PdfCommentsLayer_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-e3ae65fa"]]);
15214
15363
  //#endregion
15215
15364
  //#region src/components/Whiteboard/use-whiteboard.ts
15216
15365
  var useWhiteboard = ({ proxy, layers, documents, modules }) => {
@@ -15806,6 +15955,21 @@ function useCommentSmallScreen({ commentsModuleConfig, superdocRoot, layers }) {
15806
15955
  };
15807
15956
  }
15808
15957
  //#endregion
15958
+ //#region src/composables/use-comment-sidebar.ts
15959
+ function useCommentSidebar({ documentKey, enabled, hasContent }) {
15960
+ const reserved = require__plugin_vue_export_helper.ref(false);
15961
+ require__plugin_vue_export_helper.watch(documentKey, () => {
15962
+ reserved.value = false;
15963
+ }, { flush: "sync" });
15964
+ require__plugin_vue_export_helper.watch(() => enabled.value && hasContent.value, (visible) => {
15965
+ if (visible) reserved.value = true;
15966
+ }, {
15967
+ immediate: true,
15968
+ flush: "sync"
15969
+ });
15970
+ return require__plugin_vue_export_helper.computed(() => enabled.value && (hasContent.value || reserved.value));
15971
+ }
15972
+ //#endregion
15809
15973
  //#region src/composables/use-compact-comment-popover.ts
15810
15974
  var POPOVER_WIDTH_PX = 320;
15811
15975
  var SAFE_MARGIN_PX = 12;
@@ -16184,6 +16348,117 @@ function resolveV2ReviewTargetCommentId(target, getComment) {
16184
16348
  return commentId != null ? String(commentId) : null;
16185
16349
  }
16186
16350
  //#endregion
16351
+ //#region src/helpers/v2-review-geometry-delivery.ts
16352
+ function createReviewGeometryDelivery(deliver) {
16353
+ const packets = /* @__PURE__ */ new Map();
16354
+ let stageOwners = /* @__PURE__ */ new WeakMap();
16355
+ let active = null;
16356
+ const documentOf = (current) => current.documentId != null ? String(current.documentId) : current.mountContainer?.dataset.superdocV2DocumentId ?? null;
16357
+ const matches = (documentId, current) => current.mountContainer?.dataset.superdocV2DocumentId === documentId && (current.documentId == null || String(current.documentId) === documentId);
16358
+ const matchesPaint = (packet, current) => typeof packet.payload.paintEpoch !== "number" || packet.payload.paintEpoch === current.epoch;
16359
+ const observe = (current) => {
16360
+ const stage = current.mountContainer;
16361
+ const documentId = documentOf(current);
16362
+ if (!stage || !documentId || !matches(documentId, current)) return;
16363
+ const previousOwner = stageOwners.get(stage);
16364
+ if (previousOwner && previousOwner !== documentId) packets.delete(previousOwner);
16365
+ stageOwners.set(stage, documentId);
16366
+ const packet = packets.get(documentId);
16367
+ if (packet?.stage && packet.stage !== stage) packets.delete(documentId);
16368
+ else if (packet && (!active || active.stage !== stage || active.documentId !== documentId || active.epoch !== current.epoch)) packet.delivered = false;
16369
+ active = {
16370
+ documentId,
16371
+ stage,
16372
+ epoch: current.epoch
16373
+ };
16374
+ };
16375
+ const dispatch = (documentId, packet, current, replay = false) => {
16376
+ if (!current.mountContainer || !matches(documentId, current) || !matchesPaint(packet, current)) return false;
16377
+ if (packet.stage && packet.stage !== current.mountContainer || packet.delivering || packet.delivered && !replay) return false;
16378
+ const wasDelivered = packet.delivered;
16379
+ const previousStage = packet.stage;
16380
+ packet.stage = current.mountContainer;
16381
+ packet.delivered = true;
16382
+ packet.delivering = true;
16383
+ try {
16384
+ deliver(current, packet.payload);
16385
+ } catch (error) {
16386
+ packet.delivered = wasDelivered;
16387
+ packet.stage = previousStage;
16388
+ throw error;
16389
+ } finally {
16390
+ packet.delivering = false;
16391
+ }
16392
+ return true;
16393
+ };
16394
+ const notify = (documentId, payload, current, ready = true) => {
16395
+ if (!payload.reviewGeometry || !Array.isArray(payload.pageIndices)) return false;
16396
+ let key;
16397
+ try {
16398
+ key = JSON.stringify([
16399
+ documentId,
16400
+ payload.documentEpoch,
16401
+ payload.paintEpoch,
16402
+ payload.commitSequence,
16403
+ payload.generation,
16404
+ payload.pageIndices,
16405
+ payload.zoomScale,
16406
+ payload.reviewGeometry
16407
+ ]);
16408
+ } catch {
16409
+ return false;
16410
+ }
16411
+ const isCurrent = current != null && matches(documentId, current);
16412
+ if (isCurrent) observe(current);
16413
+ const previous = packets.get(documentId);
16414
+ if (previous && previous.payload.documentEpoch === payload.documentEpoch && typeof previous.payload.paintEpoch === "number" && typeof payload.paintEpoch === "number" && payload.paintEpoch < previous.payload.paintEpoch) return false;
16415
+ if (previous && previous.payload.documentEpoch === payload.documentEpoch && typeof previous.payload.commitSequence === "number" && typeof payload.commitSequence === "number" && payload.commitSequence < previous.payload.commitSequence) return false;
16416
+ if (previous && previous.payload.documentEpoch === payload.documentEpoch && typeof previous.payload.generation === "number" && typeof payload.generation === "number" && payload.generation < previous.payload.generation) return false;
16417
+ if (isCurrent && typeof payload.paintEpoch === "number" && typeof current.epoch === "number" && payload.paintEpoch < current.epoch) return false;
16418
+ if (previous?.key === key) {
16419
+ previous.payload = payload;
16420
+ return isCurrent && ready ? dispatch(documentId, previous, current) : false;
16421
+ }
16422
+ const packet = {
16423
+ payload,
16424
+ key,
16425
+ stage: isCurrent ? current.mountContainer : previous && previous.payload.documentEpoch === payload.documentEpoch ? previous.stage : null,
16426
+ delivered: false,
16427
+ delivering: false
16428
+ };
16429
+ packets.set(documentId, packet);
16430
+ if (!isCurrent || !ready) return false;
16431
+ try {
16432
+ return dispatch(documentId, packet, current);
16433
+ } catch (error) {
16434
+ if (previous) packets.set(documentId, previous);
16435
+ else packets.delete(documentId);
16436
+ throw error;
16437
+ }
16438
+ };
16439
+ return {
16440
+ notify,
16441
+ observe,
16442
+ flush(current) {
16443
+ observe(current);
16444
+ const documentId = documentOf(current);
16445
+ const packet = documentId == null ? null : packets.get(documentId);
16446
+ return documentId && packet ? dispatch(documentId, packet, current) : false;
16447
+ },
16448
+ replay(current) {
16449
+ observe(current);
16450
+ const documentId = documentOf(current);
16451
+ const packet = documentId == null ? null : packets.get(documentId);
16452
+ return documentId && packet ? dispatch(documentId, packet, current, true) : false;
16453
+ },
16454
+ reset() {
16455
+ packets.clear();
16456
+ stageOwners = /* @__PURE__ */ new WeakMap();
16457
+ active = null;
16458
+ }
16459
+ };
16460
+ }
16461
+ //#endregion
16187
16462
  //#region src/helpers/v2-mutation-exception.ts
16188
16463
  var FAILURE_SOURCES = /* @__PURE__ */ new Set(["shell", "receipt"]);
16189
16464
  var DIAGNOSTIC_CODE = /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/;
@@ -16553,7 +16828,7 @@ function usePasswordPrompt({ getSurfaceManager, getPasswordPromptConfig, onUnhan
16553
16828
  })
16554
16829
  });
16555
16830
  } else {
16556
- const mod = await Promise.resolve().then(() => require("./chunks/PasswordPromptSurface-x8GNmMJT.cjs"));
16831
+ const mod = await Promise.resolve().then(() => require("./chunks/PasswordPromptSurface-VFPnQOML.cjs"));
16557
16832
  const surfaceId = `password-prompt-${doc.id}`;
16558
16833
  handle = manager.open({
16559
16834
  id: surfaceId,
@@ -17213,7 +17488,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17213
17488
  } else {
17214
17489
  let FindReplaceSurface;
17215
17490
  try {
17216
- FindReplaceSurface = (await Promise.resolve().then(() => require("./chunks/FindReplaceSurface-fVHxPXtn.cjs"))).default;
17491
+ FindReplaceSurface = (await Promise.resolve().then(() => require("./chunks/FindReplaceSurface-DWXgWgg7.cjs"))).default;
17217
17492
  } catch {
17218
17493
  opening = false;
17219
17494
  return;
@@ -17587,11 +17862,11 @@ var _hoisted_8$3 = { class: "input-row url-input-row" };
17587
17862
  var _hoisted_9$2 = ["innerHTML"];
17588
17863
  var _hoisted_10$2 = ["readonly", "onKeydown"];
17589
17864
  var _hoisted_11$2 = ["innerHTML"];
17590
- var _hoisted_12$1 = {
17865
+ var _hoisted_12$2 = {
17591
17866
  key: 0,
17592
17867
  class: "input-row link-buttons"
17593
17868
  };
17594
- var _hoisted_13$1 = ["disabled"];
17869
+ var _hoisted_13$2 = ["disabled"];
17595
17870
  var _hoisted_14$1 = ["innerHTML"];
17596
17871
  var _hoisted_15$1 = ["disabled"];
17597
17872
  var _hoisted_16$1 = {
@@ -17864,7 +18139,7 @@ var LinkInput_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ requir
17864
18139
  "data-item": "btn-link-open"
17865
18140
  }, null, 10, _hoisted_11$2)
17866
18141
  ]),
17867
- !isViewingMode.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_12$1, [isEditing.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("button", {
18142
+ !isViewingMode.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_12$2, [isEditing.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("button", {
17868
18143
  key: 0,
17869
18144
  class: "remove-btn",
17870
18145
  onClick: handleRemove,
@@ -17873,7 +18148,7 @@ var LinkInput_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ requir
17873
18148
  }, [require__plugin_vue_export_helper.createElementVNode("div", {
17874
18149
  class: "remove-btn__icon",
17875
18150
  innerHTML: require__plugin_vue_export_helper.unref(toolbarIcons).removeLink
17876
- }, null, 8, _hoisted_14$1), _cache[6] || (_cache[6] = require__plugin_vue_export_helper.createTextVNode(" Remove ", -1))], 8, _hoisted_13$1)) : require__plugin_vue_export_helper.createCommentVNode("", true), require__plugin_vue_export_helper.createElementVNode("button", {
18151
+ }, null, 8, _hoisted_14$1), _cache[6] || (_cache[6] = require__plugin_vue_export_helper.createTextVNode(" Remove ", -1))], 8, _hoisted_13$2)) : require__plugin_vue_export_helper.createCommentVNode("", true), require__plugin_vue_export_helper.createElementVNode("button", {
17877
18152
  class: require__plugin_vue_export_helper.normalizeClass(["sd-submit-btn", { "disable-btn": isDisabled.value }]),
17878
18153
  onClick: handleSubmit,
17879
18154
  disabled: isSubmitting.value,
@@ -18786,6 +19061,7 @@ function createV2EditorRuntimeAdapter(options) {
18786
19061
  const { id, documentId, root, host, getLegacyEditorProjection, onUnregister } = options;
18787
19062
  let snapshot = host.getSnapshot();
18788
19063
  let didDispose = false;
19064
+ let detached = false;
18789
19065
  let unregistered = false;
18790
19066
  let mountedFocusHandle = null;
18791
19067
  let tokenRevision = 0;
@@ -19082,7 +19358,17 @@ function createV2EditorRuntimeAdapter(options) {
19082
19358
  };
19083
19359
  }
19084
19360
  }
19361
+ function releaseHostSubscriptions() {
19362
+ hostUnsubscribe?.();
19363
+ hostUnsubscribe = null;
19364
+ pageMetricsUnsubscribe?.();
19365
+ pageMetricsUnsubscribe = null;
19366
+ selectionUnsubscribe?.();
19367
+ selectionUnsubscribe = null;
19368
+ activeSelectionController = null;
19369
+ }
19085
19370
  function handleHostSnapshot(next) {
19371
+ if (detached || didDispose) return;
19086
19372
  snapshot = next;
19087
19373
  invalidatePositionTokens();
19088
19374
  syncSelectionSubscription();
@@ -19096,10 +19382,7 @@ function createV2EditorRuntimeAdapter(options) {
19096
19382
  });
19097
19383
  if (next.state === "disposed" && !didDispose) {
19098
19384
  didDispose = true;
19099
- hostUnsubscribe?.();
19100
- pageMetricsUnsubscribe?.();
19101
- selectionUnsubscribe?.();
19102
- selectionUnsubscribe = null;
19385
+ releaseHostSubscriptions();
19103
19386
  emit({ type: "disposed" });
19104
19387
  listeners.clear();
19105
19388
  if (!unregistered) {
@@ -19246,10 +19529,7 @@ function createV2EditorRuntimeAdapter(options) {
19246
19529
  await host.dispose();
19247
19530
  if (!didDispose) {
19248
19531
  didDispose = true;
19249
- hostUnsubscribe?.();
19250
- pageMetricsUnsubscribe?.();
19251
- selectionUnsubscribe?.();
19252
- selectionUnsubscribe = null;
19532
+ releaseHostSubscriptions();
19253
19533
  emit({ type: "disposed" });
19254
19534
  listeners.clear();
19255
19535
  if (!unregistered) {
@@ -19328,8 +19608,17 @@ function createV2EditorRuntimeAdapter(options) {
19328
19608
  }
19329
19609
  },
19330
19610
  attachMountHandle(handle) {
19611
+ if (detached || didDispose) return;
19331
19612
  mountedFocusHandle = handle;
19332
19613
  syncSelectionSubscription();
19614
+ },
19615
+ detach() {
19616
+ if (detached) return;
19617
+ detached = true;
19618
+ unregistered = true;
19619
+ releaseHostSubscriptions();
19620
+ mountedFocusHandle = null;
19621
+ listeners.clear();
19333
19622
  }
19334
19623
  };
19335
19624
  }
@@ -20498,7 +20787,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
20498
20787
  __name: "SuperDoc",
20499
20788
  emits: ["selection-update"],
20500
20789
  setup(__props, { emit: __emit }) {
20501
- const PdfViewer = require__plugin_vue_export_helper.defineAsyncComponent(() => Promise.resolve().then(() => require("./chunks/PdfViewer-CkrAHpC-.cjs")));
20790
+ const PdfViewer = require__plugin_vue_export_helper.defineAsyncComponent(() => Promise.resolve().then(() => require("./chunks/PdfViewer-DYTbPVUr.cjs")));
20502
20791
  const getDocumentLoadPassword = (doc) => doc.password ?? proxy.$superdoc.config.password;
20503
20792
  const resolveDocumentV2Collaboration = (doc) => {
20504
20793
  const rawConfig = doc?.v2Collaboration;
@@ -20583,7 +20872,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
20583
20872
  if (commentsConfig.highlightHoverColor) vars["--sd-comments-highlight-hover"] = commentsConfig.highlightHoverColor;
20584
20873
  const activeTrackChangeColors = {
20585
20874
  ...commentsConfig.trackChangeHighlightColors || {},
20586
- ...commentsConfig.trackChangeActiveHighlightColors || {}
20875
+ ...commentsConfig.trackChangeActiveHighlightColors
20587
20876
  };
20588
20877
  if (activeTrackChangeColors.insertBorder) vars["--sd-tracked-changes-insert-border"] = activeTrackChangeColors.insertBorder;
20589
20878
  if (activeTrackChangeColors.insertBackground) vars["--sd-tracked-changes-insert-background"] = activeTrackChangeColors.insertBackground;
@@ -20612,6 +20901,8 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
20612
20901
  const v2ReviewSidebarUnlocked = require__plugin_vue_export_helper.ref(false);
20613
20902
  const v2GeometryEpoch = require__plugin_vue_export_helper.ref(null);
20614
20903
  let v2GeometryRafHandle = 0;
20904
+ let v2GeometryReplayRafHandle = 0;
20905
+ let v2GeometryReplayGeneration = 0;
20615
20906
  let v2TrackedChangeRestampRetainPaints = 0;
20616
20907
  let v2TrackedChangeRestampRetentionReason = null;
20617
20908
  const activeEditorRef = require__plugin_vue_export_helper.computed(() => proxy.$superdoc.activeEditor);
@@ -20795,6 +21086,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
20795
21086
  if (document && provider?.sendStateless) document.provider = null;
20796
21087
  const entry = v2Runtimes.get(documentId);
20797
21088
  if (!entry) return;
21089
+ entry.adapter.detach();
20798
21090
  proxy.$superdoc["unregisterEditorRuntime"](entry.runtimeId);
20799
21091
  v2Runtimes.delete(documentId);
20800
21092
  const hostRoot = subDocumentRoots.get(documentId);
@@ -20948,7 +21240,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
20948
21240
  if (loadingDocument?.loadScope && !loadingDocument.loadScope.finish()) return;
20949
21241
  clearV2EditorFailure(payload.documentId ?? null);
20950
21242
  clearV2MutationRejectionNotifications(payload.documentId ?? null);
20951
- const { host, firstPartyCommandMutation, mount, documentId, capabilities, editCommands, bindEditShortcuts, bindSessionShortcuts, commentsAdapter, trackedChangesAdapter, contextMenu, documentApi, documentMutationReadiness, documentApiUnavailableReason, pageMetrics, pageLayout, pageFurniture, presence, lock, connection, collaborationProvider, fonts, replaceFile, upgradeToCollaboration, documentOpenToken } = payload;
21243
+ const { host, firstPartyCommandMutation, mount, documentId, capabilities, editCommands, bindEditShortcuts, bindSessionShortcuts, commentsAdapter, trackedChangesAdapter, contextMenu, viewportQuery, documentApi, documentMutationReadiness, documentApiUnavailableReason, pageMetrics, pageLayout, pageFurniture, presence, lock, connection, collaborationProvider, fonts, replaceFile, upgradeToCollaboration, documentOpenToken } = payload;
20952
21244
  documentOpenTelemetry?.trackDocumentOpen(documentOpenToken ?? null, documentId ?? null);
20953
21245
  const saveV2Bytes = createV2SaveQueue(async (saveOptions = {}) => {
20954
21246
  if (!host || typeof host.save !== "function") throw new Error("v2-editor: save unavailable");
@@ -21044,21 +21336,21 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21044
21336
  };
21045
21337
  };
21046
21338
  const currentV2AuthoringMode = () => proxy.$superdoc.config.documentMode === "suggesting" ? "tracked" : "direct";
21047
- const readV2CurrentSelectionTarget = () => {
21048
- const selection = (host?.getHandles?.()?.editing ?? null)?.selection ?? null;
21049
- if (typeof selection?.toSelectionTarget !== "function") return createV2AuthoringFailure("selection-target-unavailable", "editing.selection.toSelectionTarget is not available");
21050
- const result = selection.toSelectionTarget();
21051
- if (!result || result.kind === "rejected") {
21052
- const rejection = result?.rejection ?? {};
21053
- return createV2AuthoringFailure(rejection.reason ?? rejection.code ?? "selection-target-rejected", rejection.detail);
21339
+ const confirmedV2SelectionTarget = async () => {
21340
+ const selectionTargets = host?.getHandles?.()?.editing?.selectionTargets;
21341
+ if (typeof selectionTargets?.confirmedSelectionTarget !== "function") return createV2AuthoringFailure("selection-confirmation-unavailable");
21342
+ try {
21343
+ const result = await selectionTargets.confirmedSelectionTarget();
21344
+ if (!result?.ok || !result.target) return createV2AuthoringFailure(result?.reason ?? "selection-unconfirmed");
21345
+ return {
21346
+ ok: true,
21347
+ target: result.target,
21348
+ mode: result.mode,
21349
+ story: result.story
21350
+ };
21351
+ } catch (error) {
21352
+ return createV2AuthoringFailure("selection-unconfirmed", error instanceof Error ? error.message : String(error));
21054
21353
  }
21055
- if (!result.target || typeof result.target !== "object") return createV2AuthoringFailure("selection-target-rejected", "selection target was not produced");
21056
- return {
21057
- ok: true,
21058
- target: result.target,
21059
- mode: result.mode ?? "range",
21060
- story: result.story
21061
- };
21062
21354
  };
21063
21355
  const failureFromV2Receipt = (receipt, fallbackReason) => {
21064
21356
  const failure = receipt?.failure ?? receipt;
@@ -21132,7 +21424,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21132
21424
  },
21133
21425
  serializeSelectionToClipboard: async ({ includeHtml = true } = {}) => {
21134
21426
  if (!documentApi?.clipboard || typeof documentApi.clipboard.serializeSelection !== "function") return createV2AuthoringFailure("clipboard-serialize-unavailable", "activeEditor.doc.clipboard.serializeSelection is not available");
21135
- const selected = readV2CurrentSelectionTarget();
21427
+ const selected = await confirmedV2SelectionTarget();
21136
21428
  if (!selected.ok) return selected;
21137
21429
  let serialized;
21138
21430
  try {
@@ -21158,7 +21450,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21158
21450
  let pasteTarget = target;
21159
21451
  let selectionMode = null;
21160
21452
  if (!pasteTarget) {
21161
- const selected = readV2CurrentSelectionTarget();
21453
+ const selected = await confirmedV2SelectionTarget();
21162
21454
  if (!selected.ok) return selected;
21163
21455
  pasteTarget = selected.target;
21164
21456
  selectionMode = selected.mode;
@@ -21219,6 +21511,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21219
21511
  v2Comments: commentsAdapter ?? null,
21220
21512
  v2TrackedChanges: trackedChangesAdapter ?? null,
21221
21513
  contextMenu: contextMenu ?? null,
21514
+ viewportQuery: viewportQuery ?? null,
21222
21515
  pageMetrics: pageMetrics ?? null,
21223
21516
  pageLayout: pageLayout ?? null,
21224
21517
  pageFurniture: pageFurniture ?? null,
@@ -21560,10 +21853,11 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21560
21853
  const publishV2PendingPositionEntry = (entry) => {
21561
21854
  if (!entry) return;
21562
21855
  handleEditorLocationsUpdate({
21563
- ...editorCommentPositions.value ?? {},
21856
+ ...editorCommentPositions.value,
21564
21857
  pending: entry
21565
21858
  });
21566
21859
  };
21860
+ let scheduleV2GeometryReplay = () => {};
21567
21861
  const v2GeometryPublisher = resolvedEditorIntegration.createGeometryPublisher({
21568
21862
  getLayersContainer: () => layers.value ?? null,
21569
21863
  isCommentsEnabled: () => Boolean(commentsModuleConfig.value) && shouldRenderCommentsInViewing.value,
@@ -21579,7 +21873,8 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21579
21873
  v2ReviewSidebarUnlocked.value = true;
21580
21874
  v2GeometryEpoch.value = v2GeometryPublisher.getLastEpoch();
21581
21875
  }
21582
- }
21876
+ },
21877
+ onCommittedPagePaint: ({ payload }) => scheduleV2GeometryReplay(payload)
21583
21878
  });
21584
21879
  const armV2TrackedChangeRestampGeometryRetention = (reason = "tracked-change-restamp") => {
21585
21880
  v2TrackedChangeRestampRetainPaints = Math.max(v2TrackedChangeRestampRetainPaints, 3);
@@ -21599,20 +21894,53 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21599
21894
  v2TrackedChangeRestampRetentionReason = null;
21600
21895
  };
21601
21896
  const resolveV2GeometryPublishOptions = (options) => typeof options === "function" ? options() : options;
21897
+ const isV2GeometryReady = (current) => {
21898
+ if (!current?.mountContainer || !commentsModuleConfig.value || !shouldRenderCommentsInViewing.value) return false;
21899
+ const mount = current.mount;
21900
+ let liveEpoch;
21901
+ try {
21902
+ liveEpoch = mount?.getRenderEpoch?.();
21903
+ } catch {
21904
+ return false;
21905
+ }
21906
+ return v2GeometryPublisher.getLastPayload() === current && v2GeometryPublisher.getLastEpoch() != null && v2GeometryPublisher.getLastEpoch() === current.epoch && (liveEpoch == null || liveEpoch === current.epoch);
21907
+ };
21602
21908
  const scheduleV2GeometryPublish = (payload, options = void 0) => {
21603
21909
  v2GeometryRender.value = payload;
21604
21910
  if (v2GeometryRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryRafHandle);
21911
+ const publish = () => {
21912
+ const current = v2GeometryRender.value;
21913
+ Promise.resolve(v2GeometryPublisher.publish(current, resolveV2GeometryPublishOptions(options))).then(() => {
21914
+ if (current === v2GeometryRender.value && isV2GeometryReady(current)) v2ReviewGeometryDelivery.flush(current);
21915
+ });
21916
+ };
21605
21917
  if (typeof requestAnimationFrame !== "function") {
21606
- v2GeometryPublisher.publish(v2GeometryRender.value, resolveV2GeometryPublishOptions(options));
21918
+ publish();
21607
21919
  return;
21608
21920
  }
21609
21921
  v2GeometryRafHandle = requestAnimationFrame(() => {
21610
21922
  v2GeometryRafHandle = 0;
21611
- v2GeometryPublisher.publish(v2GeometryRender.value, resolveV2GeometryPublishOptions(options));
21923
+ publish();
21612
21924
  });
21613
21925
  };
21926
+ const v2ReviewGeometryDelivery = createReviewGeometryDelivery((current, packet) => {
21927
+ v2GeometryPublisher.noteCommittedPageWindow?.(packet);
21928
+ scheduleV2GeometryPublish(current);
21929
+ });
21930
+ scheduleV2GeometryReplay = (payload) => {
21931
+ if (!payload || payload !== v2GeometryRender.value) return;
21932
+ const generation = ++v2GeometryReplayGeneration;
21933
+ if (v2GeometryReplayRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryReplayRafHandle);
21934
+ const replay = () => {
21935
+ v2GeometryReplayRafHandle = 0;
21936
+ if (generation === v2GeometryReplayGeneration && payload === v2GeometryRender.value && isV2GeometryReady(payload)) v2ReviewGeometryDelivery.replay(payload);
21937
+ };
21938
+ if (typeof requestAnimationFrame === "function") v2GeometryReplayRafHandle = requestAnimationFrame(replay);
21939
+ else queueMicrotask(replay);
21940
+ };
21614
21941
  const onV2Render = (payload) => {
21615
21942
  if (!payload) return;
21943
+ v2ReviewGeometryDelivery.observe(payload);
21616
21944
  scheduleV2GeometryPublish(payload, () => takeV2TrackedChangeRestampGeometryRetention());
21617
21945
  if (v2HasRangeSelection.value) scheduleV2SelectionToolbarStateSync();
21618
21946
  };
@@ -21645,7 +21973,11 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21645
21973
  cancelScheduledV2SelectionToolbarSync();
21646
21974
  if (v2GeometryRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryRafHandle);
21647
21975
  v2GeometryRafHandle = 0;
21976
+ if (v2GeometryReplayRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryReplayRafHandle);
21977
+ v2GeometryReplayRafHandle = 0;
21978
+ v2GeometryReplayGeneration++;
21648
21979
  v2GeometryRender.value = null;
21980
+ v2ReviewGeometryDelivery.reset();
21649
21981
  v2GeometryEpoch.value = null;
21650
21982
  v2GeometryAvailable.value = false;
21651
21983
  v2ReviewSidebarUnlocked.value = false;
@@ -21793,14 +22125,15 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21793
22125
  if (!isV2Mode.value) return;
21794
22126
  if (!v2GeometryPublisher.getLastPayload()) return;
21795
22127
  if (v2GeometryRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryRafHandle);
21796
- if (typeof requestAnimationFrame !== "function") {
21797
- v2GeometryPublisher.recollect(resolveV2GeometryPublishOptions(options));
21798
- return;
21799
- }
21800
- v2GeometryRafHandle = requestAnimationFrame(() => {
22128
+ const recollect = () => {
21801
22129
  v2GeometryRafHandle = 0;
21802
- v2GeometryPublisher.recollect(resolveV2GeometryPublishOptions(options));
21803
- });
22130
+ const current = v2GeometryRender.value;
22131
+ Promise.resolve(v2GeometryPublisher.recollect(resolveV2GeometryPublishOptions(options))).then(() => {
22132
+ if (current === v2GeometryRender.value && isV2GeometryReady(current)) v2ReviewGeometryDelivery.replay(current);
22133
+ });
22134
+ };
22135
+ if (typeof requestAnimationFrame !== "function") recollect();
22136
+ else v2GeometryRafHandle = requestAnimationFrame(recollect);
21804
22137
  };
21805
22138
  const handleV2DocumentModeChange = () => {
21806
22139
  if (!isV2Mode.value) return;
@@ -22023,18 +22356,25 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
22023
22356
  onCommentsUpdate: onEditorCommentsUpdate,
22024
22357
  onFontsResolved: onFontsResolvedFn,
22025
22358
  onPageCountKnown: proxy.$superdoc.config.onPageCountKnown ?? null,
22359
+ onReviewAnalysisProgress: (progress) => {
22360
+ v2ReviewWindowController.noteAnalysisProgress?.(progress);
22361
+ },
22026
22362
  onReviewWindowCommitted: (payload) => {
22027
22363
  v2ReviewWindowController.onCommittedPagePaint?.({
22028
22364
  ...payload,
22029
22365
  documentId: doc.id
22030
22366
  });
22031
- v2GeometryPublisher.noteCommittedPageWindow?.(payload);
22367
+ if (payload.reviewGeometry && Array.isArray(payload.pageIndices)) {
22368
+ const current = v2GeometryRender.value;
22369
+ v2ReviewGeometryDelivery.notify(String(doc.id), payload, current, isV2GeometryReady(current));
22370
+ } else v2GeometryPublisher.noteCommittedPageWindow?.(payload);
22032
22371
  },
22033
22372
  fontAssets: proxy.$superdoc.config.fonts,
22034
22373
  workerUrls: proxy.$superdoc.config.workerUrls,
22035
22374
  fieldContext: doc.fieldContext,
22036
22375
  fieldUpdatePolicy: proxy.$superdoc.config.fieldUpdatePolicy,
22037
22376
  workerStartupTimeoutMs: proxy.$superdoc.config.workerStartupTimeoutMs,
22377
+ ...proxy.$superdoc.config.changes ? { changes: proxy.$superdoc.config.changes } : {},
22038
22378
  proofing: resolvedProofingConfig.value,
22039
22379
  isNewFile,
22040
22380
  password: getDocumentLoadPassword(doc),
@@ -22055,7 +22395,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
22055
22395
  documentLoading: proxy.$superdoc.uiConfig.loading.enabled,
22056
22396
  linkPopoverResolver: getHyperlinkActivationHandler(),
22057
22397
  layoutEngineOptions: useLayoutEngine ? {
22058
- ...proxy.$superdoc.config.layoutEngineOptions || {},
22398
+ ...proxy.$superdoc.config.layoutEngineOptions,
22059
22399
  proofing: resolvedProofingConfig.value,
22060
22400
  debugLabel: proxy.$superdoc.config.layoutEngineOptions?.debugLabel ?? doc.name ?? doc.id,
22061
22401
  zoom: (activeZoom.value ?? 100) / 100,
@@ -22318,11 +22658,16 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
22318
22658
  if (layout !== "auto") return true;
22319
22659
  return !isCompactCommentsMode.value;
22320
22660
  });
22321
- const showCommentsSidebar = require__plugin_vue_export_helper.computed(() => {
22661
+ const sidebarEnabled = require__plugin_vue_export_helper.computed(() => {
22322
22662
  if (isV2Mode.value && !v2GeometryAvailable.value && !v2ReviewSidebarUnlocked.value && !hasPdfFloatingComments.value) return false;
22323
22663
  if (!shouldRenderCommentsInViewing.value) return false;
22324
22664
  if (!shouldUseSidebarComments.value) return false;
22325
- return pendingComment.value || (floatingComments.value.length > 0 || hasOpenTrackedChanges.value) && isReady.value && layers.value && isCommentsEnabled.value && !isCommentsListVisible.value;
22665
+ return Boolean(pendingComment.value || isReady.value && layers.value && isCommentsEnabled.value && !isCommentsListVisible.value);
22666
+ });
22667
+ const showCommentsSidebar = useCommentSidebar({
22668
+ documentKey: require__plugin_vue_export_helper.computed(() => JSON.stringify(documents.value.map((document) => document.id))),
22669
+ enabled: sidebarEnabled,
22670
+ hasContent: require__plugin_vue_export_helper.computed(() => Boolean(pendingComment.value || floatingComments.value.length || hasOpenTrackedChanges.value))
22326
22671
  });
22327
22672
  const activeCompactComment = require__plugin_vue_export_helper.computed(() => {
22328
22673
  if (showCommentsSidebar.value) return null;
@@ -22946,7 +23291,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
22946
23291
  ref_key: "superdocRoot",
22947
23292
  ref: superdocRoot,
22948
23293
  class: require__plugin_vue_export_helper.normalizeClass(["superdoc", {
22949
- "superdoc--with-sidebar": showCommentsSidebar.value,
23294
+ "superdoc--with-sidebar": require__plugin_vue_export_helper.unref(showCommentsSidebar),
22950
23295
  "superdoc--web-layout": require__plugin_vue_export_helper.unref(proxy).$superdoc.config.viewOptions?.layout === "web",
22951
23296
  "superdoc--contained": require__plugin_vue_export_helper.unref(proxy).$superdoc.config.contained,
22952
23297
  "high-contrast": require__plugin_vue_export_helper.unref(isHighContrastMode)
@@ -23094,12 +23439,12 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
23094
23439
  onAnchorActivate: handleHighlightClick
23095
23440
  })) : require__plugin_vue_export_helper.createCommentVNode("", true)
23096
23441
  ])], 512),
23097
- showCommentsSidebar.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
23442
+ require__plugin_vue_export_helper.unref(showCommentsSidebar) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
23098
23443
  key: 0,
23099
23444
  ref_key: "rightSidebarRef",
23100
23445
  ref: rightSidebarRef,
23101
23446
  class: "superdoc__right-sidebar right-sidebar"
23102
- }, [require__plugin_vue_export_helper.createElementVNode("div", _hoisted_4$9, [require__plugin_vue_export_helper.unref(hasInitializedLocations) && (floatingComments.value.length > 0 || require__plugin_vue_export_helper.unref(pendingComment)) ? (require__plugin_vue_export_helper.openBlock(true), require__plugin_vue_export_helper.createElementBlock(require__plugin_vue_export_helper.Fragment, { key: 0 }, require__plugin_vue_export_helper.renderList(require__plugin_vue_export_helper.unref(documentsWithConverations), (doc) => {
23447
+ }, [require__plugin_vue_export_helper.createElementVNode("div", _hoisted_4$9, [require__plugin_vue_export_helper.unref(hasInitializedLocations) ? (require__plugin_vue_export_helper.openBlock(true), require__plugin_vue_export_helper.createElementBlock(require__plugin_vue_export_helper.Fragment, { key: 0 }, require__plugin_vue_export_helper.renderList(require__plugin_vue_export_helper.unref(documentsWithConverations), (doc) => {
23103
23448
  return require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(FloatingComments_default, {
23104
23449
  parent: layers.value,
23105
23450
  "current-document": doc
@@ -23120,7 +23465,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
23120
23465
  });
23121
23466
  //#endregion
23122
23467
  //#region src/SuperDoc.vue
23123
- var SuperDoc_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(SuperDoc_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-d2540ad3"]]);
23468
+ var SuperDoc_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(SuperDoc_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-9bae9abb"]]);
23124
23469
  //#endregion
23125
23470
  //#region src/core/create-app.ts
23126
23471
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
@@ -35697,6 +36042,16 @@ var SurfaceManager = class {
35697
36042
  };
35698
36043
  //#endregion
35699
36044
  //#region src/helpers/deprecation.ts
36045
+ /**
36046
+ * Deprecation utilities for steering consumers toward the Document API.
36047
+ *
36048
+ * ProseMirror will be removed in a future major version. These helpers emit
36049
+ * one-time console warnings when deprecated properties are accessed on an
36050
+ * editor instance handed to consumer callbacks (onEditorCreate, etc.).
36051
+ *
36052
+ * Internal code continues to use the raw Editor — only the consumer-facing
36053
+ * boundary wraps the editor with a Proxy.
36054
+ */
35700
36055
  var MIGRATION_URL = "https://docs.superdoc.dev/guides/migration/document-api";
35701
36056
  var _warned = /* @__PURE__ */ new Set();
35702
36057
  /**
@@ -35885,7 +36240,8 @@ function normalizeSuperDocUser(user) {
35885
36240
  if (!user || typeof user !== "object") return { ...DEFAULT_SUPERDOC_USER };
35886
36241
  return {
35887
36242
  ...DEFAULT_SUPERDOC_USER,
35888
- ...user
36243
+ ...user,
36244
+ ...user.commentIdentity !== void 0 ? { commentIdentity: normalizeCommentIdentity(user.commentIdentity) } : {}
35889
36245
  };
35890
36246
  }
35891
36247
  //#endregion
@@ -36404,6 +36760,7 @@ function normalizeUiConfig(config = {}) {
36404
36760
  * Keeping policy separate means an application can disable the built-in
36405
36761
  * comments UI without losing the rules enforced by its custom UI.
36406
36762
  */
36763
+ var isAutocapitalize = (value) => value === "off" || value === "sentences" || value === "words" || value === "characters";
36407
36764
  var isCommentInteractionLevel = (value) => value === "read" || value === "write" || value === "resolve";
36408
36765
  /**
36409
36766
  * Resolve the effective interaction policy.
@@ -36423,14 +36780,26 @@ function normalizeInteractionConfig(config = {}) {
36423
36780
  const canonicalLevel = interactionComments.level;
36424
36781
  const hasCanonicalLevel = isCommentInteractionLevel(canonicalLevel);
36425
36782
  const level = hasCanonicalLevel ? canonicalLevel : legacyReadOnly ? "read" : legacyAllowResolve ? "resolve" : "write";
36783
+ const interactionWritingAssistance = require__plugin_vue_export_helper.isPlainObject(interaction.writingAssistance) ? interaction.writingAssistance : {};
36784
+ const autocorrect = interactionWritingAssistance.autocorrect === true;
36785
+ const autocapitalize = isAutocapitalize(interactionWritingAssistance.autocapitalize) ? interactionWritingAssistance.autocapitalize : "off";
36786
+ const spellcheck = interactionWritingAssistance.spellcheck === true;
36426
36787
  const canonicalAllowDecisions = interactionTrackedChanges.allowDecisions;
36788
+ const interactionTouch = require__plugin_vue_export_helper.isPlainObject(interaction.touch) ? interaction.touch : {};
36427
36789
  return {
36428
36790
  comments: {
36429
36791
  level,
36430
36792
  readOnly: hasCanonicalLevel ? level === "read" : legacyReadOnly,
36431
36793
  allowResolve: hasCanonicalLevel ? level === "resolve" : legacyAllowResolve
36432
36794
  },
36433
- trackedChanges: { allowDecisions: typeof canonicalAllowDecisions === "boolean" ? canonicalAllowDecisions : !legacyReadOnly }
36795
+ trackedChanges: { allowDecisions: typeof canonicalAllowDecisions === "boolean" ? canonicalAllowDecisions : !legacyReadOnly },
36796
+ touch: { gestures: interactionTouch.gestures === true },
36797
+ writingAssistance: {
36798
+ enabled: autocorrect || autocapitalize !== "off" || spellcheck,
36799
+ autocorrect,
36800
+ autocapitalize,
36801
+ spellcheck
36802
+ }
36434
36803
  };
36435
36804
  }
36436
36805
  //#endregion
@@ -37087,26 +37456,39 @@ var ToolbarButtonIcon_default = /* @__PURE__ */ require__plugin_vue_export_helpe
37087
37456
  var _hoisted_1$15 = [
37088
37457
  "role",
37089
37458
  "aria-label",
37090
- "aria-disabled"
37459
+ "aria-disabled",
37460
+ "aria-busy"
37091
37461
  ];
37092
37462
  var _hoisted_2$11 = ["data-item"];
37093
37463
  var _hoisted_3$10 = ["data-item"];
37094
37464
  var _hoisted_4$8 = {
37095
- key: 1,
37465
+ key: 0,
37466
+ role: "status",
37467
+ "aria-label": "Applying list",
37468
+ style: { "font-weight": "700" }
37469
+ };
37470
+ var _hoisted_5$6 = {
37471
+ key: 2,
37096
37472
  class: "sd-button-label"
37097
37473
  };
37098
- var _hoisted_5$6 = ["data-item", "aria-label"];
37099
- var _hoisted_6$4 = ["innerHTML"];
37100
- var _hoisted_7$2 = { key: 2 };
37101
- var _hoisted_8$2 = ["onKeydown", "id"];
37102
- var _hoisted_9$1 = [
37474
+ var _hoisted_6$4 = ["data-item", "aria-label"];
37475
+ var _hoisted_7$2 = ["innerHTML"];
37476
+ var _hoisted_8$2 = {
37477
+ key: 0,
37478
+ role: "status",
37479
+ "aria-label": "Applying list",
37480
+ style: { "font-weight": "700" }
37481
+ };
37482
+ var _hoisted_9$1 = { key: 3 };
37483
+ var _hoisted_10$1 = ["onKeydown", "id"];
37484
+ var _hoisted_11$1 = [
37103
37485
  "placeholder",
37104
37486
  "onKeydown",
37105
37487
  "id",
37106
37488
  "data-item"
37107
37489
  ];
37108
- var _hoisted_10$1 = ["data-item", "innerHTML"];
37109
- var _hoisted_11$1 = {
37490
+ var _hoisted_12$1 = ["data-item", "innerHTML"];
37491
+ var _hoisted_13$1 = {
37110
37492
  "aria-live": "polite",
37111
37493
  class: "sd-visually-hidden"
37112
37494
  };
@@ -37277,6 +37659,7 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37277
37659
  role: __props.isOverflowItem ? "menuitem" : "button",
37278
37660
  "aria-label": require__plugin_vue_export_helper.unref(attributes).ariaLabel,
37279
37661
  "aria-disabled": require__plugin_vue_export_helper.unref(disabled) ? "true" : void 0,
37662
+ "aria-busy": require__plugin_vue_export_helper.unref(attributes).ariaBusy || void 0,
37280
37663
  "data-sd-part": "toolbar-item",
37281
37664
  onClick: handleOuterClick,
37282
37665
  onKeydown: _cache[9] || (_cache[9] = require__plugin_vue_export_helper.withKeys(($event) => onEnterKeydown($event), ["enter"])),
@@ -37298,17 +37681,21 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37298
37681
  class: "sd-toolbar-button__main",
37299
37682
  "data-item": `btn-${require__plugin_vue_export_helper.unref(name) || ""}-main`,
37300
37683
  onClick: _cache[0] || (_cache[0] = ($event) => handleSplitMainClick($event))
37301
- }, [require__plugin_vue_export_helper.unref(icon) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(ToolbarButtonIcon_default, {
37302
- key: 0,
37303
- color: require__plugin_vue_export_helper.unref(iconColor),
37304
- class: "sd-toolbar-icon",
37305
- icon: require__plugin_vue_export_helper.unref(icon),
37306
- name: require__plugin_vue_export_helper.unref(name)
37307
- }, null, 8, [
37308
- "color",
37309
- "icon",
37310
- "name"
37311
- ])) : require__plugin_vue_export_helper.createCommentVNode("", true), require__plugin_vue_export_helper.unref(label) && !require__plugin_vue_export_helper.unref(hideLabel) && !require__plugin_vue_export_helper.unref(inlineTextInputVisible) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_4$8, require__plugin_vue_export_helper.toDisplayString(require__plugin_vue_export_helper.unref(label)), 1)) : require__plugin_vue_export_helper.createCommentVNode("", true)], 8, _hoisted_3$10)) : require__plugin_vue_export_helper.createCommentVNode("", true),
37684
+ }, [
37685
+ require__plugin_vue_export_helper.unref(attributes).ariaBusy && isSplit.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("span", _hoisted_4$8, "…")) : require__plugin_vue_export_helper.createCommentVNode("", true),
37686
+ require__plugin_vue_export_helper.unref(icon) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(ToolbarButtonIcon_default, {
37687
+ key: 1,
37688
+ color: require__plugin_vue_export_helper.unref(iconColor),
37689
+ class: "sd-toolbar-icon",
37690
+ icon: require__plugin_vue_export_helper.unref(icon),
37691
+ name: require__plugin_vue_export_helper.unref(name)
37692
+ }, null, 8, [
37693
+ "color",
37694
+ "icon",
37695
+ "name"
37696
+ ])) : require__plugin_vue_export_helper.createCommentVNode("", true),
37697
+ require__plugin_vue_export_helper.unref(label) && !require__plugin_vue_export_helper.unref(hideLabel) && !require__plugin_vue_export_helper.unref(inlineTextInputVisible) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_5$6, require__plugin_vue_export_helper.toDisplayString(require__plugin_vue_export_helper.unref(label)), 1)) : require__plugin_vue_export_helper.createCommentVNode("", true)
37698
+ ], 8, _hoisted_3$10)) : require__plugin_vue_export_helper.createCommentVNode("", true),
37312
37699
  isSplit.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
37313
37700
  key: 1,
37314
37701
  class: "sd-toolbar-button__caret",
@@ -37319,9 +37706,10 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37319
37706
  class: "sd-dropdown-caret",
37320
37707
  innerHTML: caretIcon.value,
37321
37708
  style: require__plugin_vue_export_helper.normalizeStyle({ opacity: require__plugin_vue_export_helper.unref(disabled) ? .6 : 1 })
37322
- }, null, 12, _hoisted_6$4)], 8, _hoisted_5$6)) : (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock(require__plugin_vue_export_helper.Fragment, { key: 2 }, [
37709
+ }, null, 12, _hoisted_7$2)], 8, _hoisted_6$4)) : (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock(require__plugin_vue_export_helper.Fragment, { key: 2 }, [
37710
+ require__plugin_vue_export_helper.unref(attributes).ariaBusy ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("span", _hoisted_8$2, "…")) : require__plugin_vue_export_helper.createCommentVNode("", true),
37323
37711
  require__plugin_vue_export_helper.unref(icon) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(ToolbarButtonIcon_default, {
37324
- key: 0,
37712
+ key: 1,
37325
37713
  color: require__plugin_vue_export_helper.unref(iconColor),
37326
37714
  class: "sd-toolbar-icon",
37327
37715
  icon: require__plugin_vue_export_helper.unref(icon),
@@ -37332,10 +37720,10 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37332
37720
  "name"
37333
37721
  ])) : require__plugin_vue_export_helper.createCommentVNode("", true),
37334
37722
  require__plugin_vue_export_helper.unref(label) && !require__plugin_vue_export_helper.unref(hideLabel) && (!require__plugin_vue_export_helper.unref(inlineTextInputVisible) || itemName.value === "fontFamily") ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
37335
- key: 1,
37723
+ key: 2,
37336
37724
  class: require__plugin_vue_export_helper.normalizeClass(["sd-button-label", { "sd-visually-hidden": require__plugin_vue_export_helper.unref(inlineTextInputVisible) && itemName.value === "fontFamily" }])
37337
37725
  }, require__plugin_vue_export_helper.toDisplayString(require__plugin_vue_export_helper.unref(label)), 3)) : require__plugin_vue_export_helper.createCommentVNode("", true),
37338
- require__plugin_vue_export_helper.unref(inlineTextInputVisible) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("span", _hoisted_7$2, [require__plugin_vue_export_helper.unref(name) === "fontSize" ? require__plugin_vue_export_helper.withDirectives((require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("input", {
37726
+ require__plugin_vue_export_helper.unref(inlineTextInputVisible) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("span", _hoisted_9$1, [require__plugin_vue_export_helper.unref(name) === "fontSize" ? require__plugin_vue_export_helper.withDirectives((require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("input", {
37339
37727
  key: 0,
37340
37728
  "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => inlineTextInput.value = $event),
37341
37729
  onKeydown: [require__plugin_vue_export_helper.withKeys(require__plugin_vue_export_helper.withModifiers(handleInlineInputEnter, ["stop", "prevent"]), ["enter"]), _cache[2] || (_cache[2] = require__plugin_vue_export_helper.withKeys(($event) => handleInlineInputTab($event), ["tab"]))],
@@ -37347,7 +37735,7 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37347
37735
  autocomplete: "off",
37348
37736
  ref_key: "inlineInput",
37349
37737
  ref: inlineInput
37350
- }, null, 42, _hoisted_8$2)), [[require__plugin_vue_export_helper.vModelText, inlineTextInput.value]]) : require__plugin_vue_export_helper.withDirectives((require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("input", {
37738
+ }, null, 42, _hoisted_10$1)), [[require__plugin_vue_export_helper.vModelText, inlineTextInput.value]]) : require__plugin_vue_export_helper.withDirectives((require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("input", {
37351
37739
  key: 1,
37352
37740
  "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => inlineTextInput.value = $event),
37353
37741
  placeholder: require__plugin_vue_export_helper.unref(label),
@@ -37364,23 +37752,23 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37364
37752
  autocomplete: "off",
37365
37753
  ref_key: "inlineInput",
37366
37754
  ref: inlineInput
37367
- }, null, 42, _hoisted_9$1)), [[require__plugin_vue_export_helper.vModelText, inlineTextInput.value]])])) : require__plugin_vue_export_helper.createCommentVNode("", true),
37755
+ }, null, 42, _hoisted_11$1)), [[require__plugin_vue_export_helper.vModelText, inlineTextInput.value]])])) : require__plugin_vue_export_helper.createCommentVNode("", true),
37368
37756
  require__plugin_vue_export_helper.unref(hasCaret) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
37369
- key: 3,
37757
+ key: 4,
37370
37758
  class: "sd-dropdown-caret",
37371
37759
  "data-item": `btn-${require__plugin_vue_export_helper.unref(name) || ""}-toggle`,
37372
37760
  innerHTML: caretIcon.value,
37373
37761
  style: require__plugin_vue_export_helper.normalizeStyle({ opacity: require__plugin_vue_export_helper.unref(disabled) ? .6 : 1 })
37374
- }, null, 12, _hoisted_10$1)) : require__plugin_vue_export_helper.createCommentVNode("", true)
37762
+ }, null, 12, _hoisted_12$1)) : require__plugin_vue_export_helper.createCommentVNode("", true)
37375
37763
  ], 64)),
37376
- require__plugin_vue_export_helper.createElementVNode("div", _hoisted_11$1, require__plugin_vue_export_helper.toDisplayString(`${require__plugin_vue_export_helper.unref(attributes).ariaLabel} ${require__plugin_vue_export_helper.unref(active) ? "selected" : "unset"}`), 1)
37764
+ require__plugin_vue_export_helper.createElementVNode("div", _hoisted_13$1, require__plugin_vue_export_helper.toDisplayString(`${require__plugin_vue_export_helper.unref(attributes).ariaLabel} ${require__plugin_vue_export_helper.unref(active) ? "selected" : "unset"}`), 1)
37377
37765
  ], 10, _hoisted_2$11)], 46, _hoisted_1$15);
37378
37766
  };
37379
37767
  }
37380
37768
  });
37381
37769
  //#endregion
37382
37770
  //#region src/internal/toolbar/built-in/ToolbarButton.vue
37383
- var ToolbarButton_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(ToolbarButton_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-e599674c"]]);
37771
+ var ToolbarButton_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(ToolbarButton_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-b49ede12"]]);
37384
37772
  //#endregion
37385
37773
  //#region src/internal/toolbar/built-in/ToolbarSeparator.vue?vue&type=script&setup=true&lang.ts
37386
37774
  var _hoisted_1$14 = {
@@ -38676,7 +39064,7 @@ var SdTooltip_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ requir
38676
39064
  ]);
38677
39065
  const contentStyle = require__plugin_vue_export_helper.computed(() => ({
38678
39066
  ...props.contentStyle,
38679
- ...attrs.style || {},
39067
+ ...attrs.style,
38680
39068
  position: "fixed",
38681
39069
  top: position.value.top,
38682
39070
  left: position.value.left,
@@ -38848,7 +39236,7 @@ var SdTooltip_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ requir
38848
39236
  });
38849
39237
  //#endregion
38850
39238
  //#region src/internal/toolbar/built-in/SdTooltip.vue
38851
- var SdTooltip_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(SdTooltip_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-dfd6f1d1"]]);
39239
+ var SdTooltip_default = /* @__PURE__ */ require__plugin_vue_export_helper._plugin_vue_export_helper_default(SdTooltip_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-87967ab1"]]);
38852
39240
  //#endregion
38853
39241
  //#region src/internal/toolbar/built-in/ButtonGroup.vue?vue&type=script&setup=true&lang.ts
38854
39242
  var _hoisted_1$11 = [
@@ -41698,6 +42086,8 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons: configuredIcons, toolbarTe
41698
42086
  attributes: { ariaLabel: "Bold" }
41699
42087
  });
41700
42088
  const fontOptions = (toolbarFonts ?? TOOLBAR_FONTS).map(normalizeFontOption);
42089
+ const isFontOption = (option) => typeof option.label === "string" && typeof option.key === "string";
42090
+ const currentFontOptions = () => fontButton.nestedOptions.value.filter(isFontOption);
41701
42091
  const fontButton = useToolbarItem({
41702
42092
  type: "dropdown",
41703
42093
  name: "fontFamily",
@@ -41724,8 +42114,9 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons: configuredIcons, toolbarTe
41724
42114
  if (typeof fontFamily !== "string" || !fontFamily) return;
41725
42115
  const family = fontFamily.split(",")[0];
41726
42116
  const optionValue = (option) => option.value ?? option.label;
41727
- const defaultFont = fontOptions.find((option) => optionValue(option) === fontButton.defaultLabel.value);
41728
- const foundFont = fontOptions.find((option) => optionValue(option) === family);
42117
+ const options = currentFontOptions();
42118
+ const defaultFont = options.find((option) => optionValue(option) === fontButton.defaultLabel.value);
42119
+ const foundFont = options.find((option) => optionValue(option) === family);
41729
42120
  if (foundFont) {
41730
42121
  fontButton.label.value = foundFont.label;
41731
42122
  fontButton.selectedValue.value = foundFont.key;
@@ -41738,7 +42129,7 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons: configuredIcons, toolbarTe
41738
42129
  }
41739
42130
  },
41740
42131
  onDeactivate: () => {
41741
- const defaultFont = fontOptions.find((option) => (option.value ?? option.label) === fontButton.defaultLabel.value);
42132
+ const defaultFont = currentFontOptions().find((option) => (option.value ?? option.label) === fontButton.defaultLabel.value);
41742
42133
  fontButton.label.value = defaultFont?.label ?? fontButton.defaultLabel.value;
41743
42134
  fontButton.selectedValue.value = defaultFont?.key ?? "";
41744
42135
  }
@@ -42950,7 +43341,7 @@ var V1_ENABLED_ON_SELECTION_REASON = /* @__PURE__ */ new Set([
42950
43341
  ]);
42951
43342
  var V1_ENABLED_ON_EMPTY_REASON = /* @__PURE__ */ new Set(["list", "numberedlist"]);
42952
43343
  function shouldKeepEnabledForV1ToolbarParity(name, commandState) {
42953
- if (name === "fontFamily" || name === "fontSize") return true;
43344
+ if (name === "fontFamily" || name === "fontSize") return commandState?.reason !== "document-readonly" && commandState?.reason !== "deleted-text-formatting-unsupported";
42954
43345
  if (V1_ENABLED_ON_EMPTY_REASON.has(name) && commandState?.disabled && commandState?.supported !== false && commandState?.reason == null) return true;
42955
43346
  if (!V1_ENABLED_ON_SELECTION_REASON.has(name)) return false;
42956
43347
  const reason = commandState?.reason;
@@ -42984,6 +43375,7 @@ var RESERVED_TOOLBAR_ITEM_NAMES = /* @__PURE__ */ new Set([
42984
43375
  * proof lane, and behavior tests resolve against a real object.
42985
43376
  */
42986
43377
  var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.default {
43378
+ #formattingRefusalTooltips;
42987
43379
  #imagePicker;
42988
43380
  #groupItems;
42989
43381
  #highlightOptions;
@@ -43015,6 +43407,7 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
43015
43407
  showPageNumberButton: false
43016
43408
  };
43017
43409
  this.toolbarItems = [];
43410
+ this.#formattingRefusalTooltips = /* @__PURE__ */ new WeakMap();
43018
43411
  this.overflowItems = [];
43019
43412
  this.pendingSelectionCapture = void 0;
43020
43413
  this.#imagePicker = null;
@@ -43141,7 +43534,7 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
43141
43534
  const signature = this.#fontOptionsSignature();
43142
43535
  if (signature !== this._lastFontOptionsSignature) {
43143
43536
  this._lastFontOptionsSignature = signature;
43144
- this.#rebuildToolbarItems();
43537
+ if (!this.#refreshFontFamilyOptions()) this.#rebuildToolbarItems();
43145
43538
  }
43146
43539
  this.updateToolbarState();
43147
43540
  };
@@ -43289,6 +43682,13 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
43289
43682
  this.#makeToolbarItems();
43290
43683
  this.emit("toolbar-items-changed");
43291
43684
  }
43685
+ #refreshFontFamilyOptions() {
43686
+ const items = this.#getAllToolbarItems().filter((item) => item.name.value === "fontFamily");
43687
+ if (!items.length) return false;
43688
+ const options = (this.#resolveToolbarFonts(this.config.fonts) ?? TOOLBAR_FONTS).map(normalizeFontOption);
43689
+ for (const item of items) item.nestedOptions.value = options.map((option) => ({ ...option }));
43690
+ return true;
43691
+ }
43292
43692
  #fontOptionsSignature() {
43293
43693
  if (this.config.fonts) return "custom-fonts";
43294
43694
  const options = this.superdoc?.fonts?.getFontFamilyOptions?.() ?? [];
@@ -43468,6 +43868,17 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
43468
43868
  const commandId = resolveBuiltInItemCommandId(name);
43469
43869
  if (!commandId) return false;
43470
43870
  const commandState = this.snapshot?.commands?.[commandId];
43871
+ if (commandState?.reason === "deleted-text-formatting-unsupported") {
43872
+ if (!this.#formattingRefusalTooltips.has(item)) this.#formattingRefusalTooltips.set(item, item.tooltip.value);
43873
+ item.tooltip.value = "Select kept text, or accept or reject the deletion before formatting.";
43874
+ } else if (this.#formattingRefusalTooltips.has(item)) {
43875
+ item.tooltip.value = this.#formattingRefusalTooltips.get(item);
43876
+ this.#formattingRefusalTooltips.delete(item);
43877
+ }
43878
+ if ((name === "list" || name === "numberedlist") && item.attributes.value.ariaBusy !== Boolean(commandState?.pending)) item.attributes.value = {
43879
+ ...item.attributes.value,
43880
+ ariaBusy: Boolean(commandState?.pending)
43881
+ };
43471
43882
  if (shouldKeepEnabledForV1ToolbarParity(name, commandState)) item.setDisabled(false);
43472
43883
  else item.setDisabled(Boolean(commandState?.disabled));
43473
43884
  const handlers = {
@@ -43935,6 +44346,9 @@ function deriveCollaborationConnectionKind(snapshot, hasSyncedBefore) {
43935
44346
  default: return hasSyncedBefore ? "reconnecting" : "initial";
43936
44347
  }
43937
44348
  }
44349
+ function destroyedDuringUpgradeError() {
44350
+ return /* @__PURE__ */ new Error("SuperDoc: instance was destroyed during upgrade");
44351
+ }
43938
44352
  function isV2ActiveEditorFacade(editor) {
43939
44353
  return Boolean(editor && typeof editor === "object" && editor.editorVersion === 2);
43940
44354
  }
@@ -43984,6 +44398,7 @@ function runtimeUserOf(user) {
43984
44398
  const text = (value) => typeof value === "string" && value ? value : void 0;
43985
44399
  return {
43986
44400
  name: text(user.name) ?? "",
44401
+ ...user.commentIdentity !== void 0 ? { commentIdentity: user.commentIdentity ? { ...user.commentIdentity } : null } : {},
43987
44402
  ...text(user.id) ? { id: text(user.id) } : {},
43988
44403
  ...text(user.email) ? { email: text(user.email) } : {},
43989
44404
  ...text(user.initials) ? { initials: text(user.initials) } : {}
@@ -43995,10 +44410,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
43995
44410
  }
43996
44411
  #destroyed;
43997
44412
  #diagnostics;
43998
- #isUpgrading;
43999
44413
  #pendingUpgrade;
44000
- /** Aborts an in-flight upgrade (sync wait or ready wait). */
44001
- #abortUpgrade;
44002
44414
  /**
44003
44415
  * Unsubscribe handle for the v2 presence → `awareness-update` bridge. Set
44004
44416
  * while a v2 collaborative editor is mounted and torn down on remount,
@@ -44012,6 +44424,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44012
44424
  #v2ConnectionUnsub;
44013
44425
  /** Latest `collaboration-connection-change` payload, for late readers. */
44014
44426
  #v2ConnectionState;
44427
+ #v2ConnectionFailureReported;
44015
44428
  /** Whether the current collaboration room has completed a sync at least once. */
44016
44429
  #v2ConnectionHasSynced;
44017
44430
  /** Lock state captured before a local → v2 collaboration promotion remount. */
@@ -44107,13 +44520,12 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44107
44520
  constructor(config) {
44108
44521
  super();
44109
44522
  this.#destroyed = false;
44110
- this.#isUpgrading = false;
44111
44523
  this.#pendingUpgrade = null;
44112
- this.#abortUpgrade = null;
44113
44524
  this.#v2AwarenessUnsub = null;
44114
44525
  this.#v2LockUnsub = null;
44115
44526
  this.#v2ConnectionUnsub = null;
44116
44527
  this.#v2ConnectionState = null;
44528
+ this.#v2ConnectionFailureReported = false;
44117
44529
  this.#v2ConnectionHasSynced = false;
44118
44530
  this.#pendingV2LockSeed = null;
44119
44531
  this.#mountWrapper = null;
@@ -44228,6 +44640,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44228
44640
  if (!config.selector) throw new Error("SuperDoc: selector is required");
44229
44641
  const container = typeof config.selector === "string" ? document.querySelector(config.selector) : config.selector;
44230
44642
  if (!(container instanceof HTMLElement)) throw new Error("SuperDoc: selector must be a valid CSS selector string or DOM element");
44643
+ const user = normalizeSuperDocUser(config.user);
44231
44644
  this.#diagnostics = require_create_super_doc_ui.createInteractionHistory(this, config, () => this.version, container);
44232
44645
  this.#surfaceManager = new SurfaceManager({ getModuleConfig: () => this.#surfacesConfig });
44233
44646
  this.#contentControlClickRoot = container;
@@ -44237,7 +44650,10 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44237
44650
  container.addEventListener("keydown", this.#handleContentControlKeyDown, true);
44238
44651
  container.addEventListener("click", this.#handleContentControlClick, true);
44239
44652
  this.#onConfig("content-control:active-change", config.onContentControlActiveChange);
44240
- this.#init(config, container);
44653
+ this.#init({
44654
+ ...config,
44655
+ user
44656
+ }, container);
44241
44657
  this.#contentControlEventBridgeReady = true;
44242
44658
  if (this.listenerCount("content-control:active-change") > 0) this.ui;
44243
44659
  }
@@ -44290,7 +44706,6 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44290
44706
  }
44291
44707
  this.editorVersion = 2;
44292
44708
  this.#validateLegacyConfig();
44293
- this.config.user = normalizeSuperDocUser(this.config.user);
44294
44709
  if (!this.config.layoutEngineOptions.virtualization) this.config.layoutEngineOptions.virtualization = {
44295
44710
  enabled: true,
44296
44711
  window: 5,
@@ -44316,7 +44731,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44316
44731
  }
44317
44732
  this.userColorMap = /* @__PURE__ */ new Map();
44318
44733
  this.colorIndex = 0;
44319
- this.version = "2.29.0";
44734
+ this.version = "2.30.0-next.19";
44320
44735
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
44321
44736
  this.superdocId = config.superdocId || require_uuid.v4();
44322
44737
  this.colors = this.config.colors ?? [];
@@ -44735,6 +45150,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44735
45150
  if (this.#v2ConnectionState?.documentId !== documentId) {
44736
45151
  this.#v2ConnectionState = null;
44737
45152
  this.#v2ConnectionHasSynced = false;
45153
+ this.#v2ConnectionFailureReported = false;
44738
45154
  }
44739
45155
  const publish = (snapshot) => {
44740
45156
  if (this.#destroyed) return;
@@ -44749,8 +45165,21 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44749
45165
  superdoc: this
44750
45166
  };
44751
45167
  this.#v2ConnectionState = payload;
44752
- if (snapshot.state === "synced") this.#v2ConnectionHasSynced = true;
44753
- this.emit("collaboration-connection-change", payload);
45168
+ if (snapshot.state === "synced") {
45169
+ this.#v2ConnectionHasSynced = true;
45170
+ this.#v2ConnectionFailureReported = false;
45171
+ }
45172
+ const firstFailure = snapshot.state === "failed" && !this.#v2ConnectionFailureReported;
45173
+ if (firstFailure) this.#v2ConnectionFailureReported = true;
45174
+ const reportFailure = firstFailure && snapshot.detail !== "worker-transport-failed";
45175
+ try {
45176
+ this.emit("collaboration-connection-change", payload);
45177
+ } finally {
45178
+ if (reportFailure) {
45179
+ const exception = createCollaborationException("collaboration-connection-failed", documentId);
45180
+ if (exception) this.emit("exception", exception);
45181
+ }
45182
+ }
44754
45183
  };
44755
45184
  try {
44756
45185
  const initialSnapshot = connection.getSnapshot?.();
@@ -44863,41 +45292,43 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44863
45292
  throw Object.assign(/* @__PURE__ */ new Error("SuperDoc: a different collaboration target is already upgrading"), { code: "collaboration-upgrade-target-conflict" });
44864
45293
  }
44865
45294
  const { target } = this.#validateUpgradePrerequisites(options);
44866
- this.#isUpgrading = true;
44867
- const operation = (async () => {
44868
- try {
44869
- const localSource = await this.#captureCurrentDocxSourceForUpgrade();
44870
- const rollback = this.#snapshotV2UpgradeState();
44871
- const lockSeed = this.#snapshotV2LockSeed();
44872
- this.#assertNotDestroyed();
44873
- try {
44874
- this.#pendingV2LockSeed = lockSeed;
44875
- const promotion = await this.#promoteSingleDocumentToV2Collaboration(target, localSource);
44876
- this.#finalizeSingleDocumentV2Collaboration(promotion, target);
44877
- this.#pendingV2LockSeed = null;
44878
- const attachedDocumentId = this.activeEditor?.options?.documentId;
44879
- const attachedRoomId = promotion.configDoc.v2Collaboration?.documentId;
44880
- if (typeof attachedDocumentId !== "string" || attachedDocumentId !== promotion.configDoc.id || typeof attachedRoomId !== "string" || attachedRoomId !== target.documentId) throw new Error("SuperDoc: collaboration upgrade did not attach the expected room and document");
44881
- return {
44882
- roomId: attachedRoomId,
44883
- documentId: attachedDocumentId
44884
- };
44885
- } catch (err) {
44886
- this.#rollbackV2UpgradeState(rollback);
44887
- throw err;
44888
- }
44889
- } finally {
44890
- this.#abortUpgrade = null;
44891
- this.#isUpgrading = false;
44892
- this.#pendingUpgrade = null;
44893
- }
44894
- })();
45295
+ let rejectOnDestroy = () => {};
45296
+ const destroyed = new Promise((_resolve, reject) => {
45297
+ rejectOnDestroy = reject;
45298
+ });
45299
+ const operation = Promise.race([this.#runUpgrade(target), destroyed]).finally(() => {
45300
+ this.#pendingUpgrade = null;
45301
+ });
44895
45302
  this.#pendingUpgrade = {
44896
45303
  target,
44897
- promise: operation
45304
+ promise: operation,
45305
+ rejectOnDestroy
44898
45306
  };
44899
45307
  return operation;
44900
45308
  }
45309
+ async #runUpgrade(target) {
45310
+ const localSource = await this.#captureCurrentDocxSourceForUpgrade();
45311
+ const rollback = this.#snapshotV2UpgradeState();
45312
+ const lockSeed = this.#snapshotV2LockSeed();
45313
+ this.#assertNotDestroyed();
45314
+ try {
45315
+ this.#pendingV2LockSeed = lockSeed;
45316
+ const promotion = await this.#promoteSingleDocumentToV2Collaboration(target, localSource);
45317
+ this.#assertNotDestroyed();
45318
+ this.#finalizeSingleDocumentV2Collaboration(promotion, target);
45319
+ this.#pendingV2LockSeed = null;
45320
+ const attachedDocumentId = this.activeEditor?.options?.documentId;
45321
+ const attachedRoomId = promotion.configDoc.v2Collaboration?.documentId;
45322
+ if (typeof attachedDocumentId !== "string" || attachedDocumentId !== promotion.configDoc.id || typeof attachedRoomId !== "string" || attachedRoomId !== target.documentId) throw new Error("SuperDoc: collaboration upgrade did not attach the expected room and document");
45323
+ return {
45324
+ roomId: attachedRoomId,
45325
+ documentId: attachedDocumentId
45326
+ };
45327
+ } catch (err) {
45328
+ if (!this.#destroyed) this.#rollbackV2UpgradeState(rollback);
45329
+ throw err;
45330
+ }
45331
+ }
44901
45332
  #sameUpgradeTarget(left, right) {
44902
45333
  if (left.token !== right.token) return false;
44903
45334
  const seen = /* @__PURE__ */ new WeakMap();
@@ -44962,7 +45393,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44962
45393
  * async waits inside upgradeToCollaboration().
44963
45394
  */
44964
45395
  #assertNotDestroyed() {
44965
- if (this.#destroyed) throw new Error("SuperDoc: instance was destroyed during upgrade");
45396
+ if (this.#destroyed) throw destroyedDuringUpgradeError();
44966
45397
  }
44967
45398
  /**
44968
45399
  * Return the superdoc store, throwing a clear lifecycle error if
@@ -45061,6 +45492,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
45061
45492
  this.#writeStoreDocV2Collaboration(storeDoc, v2Collaboration);
45062
45493
  this.isCollaborative = true;
45063
45494
  await editor.upgradeToCollaboration(localSource, v2Collaboration);
45495
+ this.#assertNotDestroyed();
45064
45496
  configDoc.data = localSource;
45065
45497
  storeDoc.data = localSource;
45066
45498
  return {
@@ -45120,7 +45552,6 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
45120
45552
  */
45121
45553
  #validateUpgradePrerequisites(options) {
45122
45554
  if (this.#destroyed) throw new Error("SuperDoc: cannot upgrade a destroyed instance");
45123
- if (this.#isUpgrading) throw new Error("SuperDoc: upgrade already in progress");
45124
45555
  if (this.isCollaborative) throw new Error("SuperDoc: instance is already collaborative");
45125
45556
  const cfg = this.config;
45126
45557
  const docxDocs = cfg.documents.filter((d) => d.type === DOCX);
@@ -45175,10 +45606,15 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
45175
45606
  this.#requireReady("setUser");
45176
45607
  const name = typeof user?.name === "string" ? user.name.trim() : "";
45177
45608
  if (!name) return Promise.reject(/* @__PURE__ */ new TypeError("SuperDoc.setUser requires a non-empty user name."));
45178
- const next = normalizeSuperDocUser({
45179
- ...user,
45180
- name
45181
- });
45609
+ let next;
45610
+ try {
45611
+ next = normalizeSuperDocUser({
45612
+ ...user,
45613
+ name
45614
+ });
45615
+ } catch (error) {
45616
+ return Promise.reject(error);
45617
+ }
45182
45618
  const applied = this.#userSwitch.then(() => this.#applyUser(next));
45183
45619
  this.#userSwitch = applied.catch(() => {});
45184
45620
  return applied;
@@ -45192,14 +45628,15 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
45192
45628
  "email",
45193
45629
  "image",
45194
45630
  "initials"
45195
- ].every((key) => (current[key] ?? null) === (next[key] ?? null))) return;
45631
+ ].every((key) => (current[key] ?? null) === (next[key] ?? null)) && current.commentIdentity === void 0 === (next.commentIdentity === void 0) && (current.commentIdentity?.providerId ?? null) === (next.commentIdentity?.providerId ?? null) && (current.commentIdentity?.userId ?? null) === (next.commentIdentity?.userId ?? null)) return;
45196
45632
  const runtimes = this.#editorRuntimeRegistry.getAll();
45197
45633
  const results = await Promise.allSettled(runtimes.map((runtime) => runtime.setUser?.(runtimeUserOf(next))));
45198
45634
  const failure = results.find((result) => result.status === "rejected");
45199
45635
  if (failure) {
45200
- if (current) await Promise.allSettled(runtimes.filter((_, index) => results[index].status === "fulfilled").map((runtime) => runtime.setUser?.(runtimeUserOf(current))));
45636
+ if (current && !this.#destroyed) await Promise.allSettled(runtimes.filter((_, index) => results[index].status === "fulfilled").map((runtime) => runtime.setUser?.(runtimeUserOf(current))));
45201
45637
  throw failure.reason;
45202
45638
  }
45639
+ if (this.#destroyed) return;
45203
45640
  const target = this.config.user ?? (this.config.user = {});
45204
45641
  for (const key of Object.keys(target)) delete target[key];
45205
45642
  Object.assign(target, next);
@@ -46371,7 +46808,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
46371
46808
  const result = await activeEditor.replaceFile(replacementSource);
46372
46809
  const state = result && typeof result === "object" ? result.state : null;
46373
46810
  const confirmed = state === null || state === "review-ready" || state === "editing-ready";
46374
- if (confirmed) {
46811
+ if (confirmed && !this.#destroyed) {
46375
46812
  this.#replaceActiveDocumentData(activeEditor, replacementSource);
46376
46813
  this.emit("document-replaced", {
46377
46814
  editor: activeEditor,
@@ -46527,10 +46964,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
46527
46964
  this.#contentControlClickRoot = null;
46528
46965
  this.#contentControlActiveChangeUnsub?.();
46529
46966
  this.#contentControlActiveChangeUnsub = null;
46530
- if (this.#abortUpgrade) {
46531
- this.#abortUpgrade();
46532
- this.#abortUpgrade = null;
46533
- }
46967
+ this.#pendingUpgrade?.rejectOnDestroy(destroyedDuringUpgradeError());
46534
46968
  if (this.#surfaceManager) this.#surfaceManager.destroy();
46535
46969
  this.toolbar?.destroy?.();
46536
46970
  this.commentsList?.close();