superdoc 2.29.0 → 2.30.0-next.13

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 (131) 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-Dc8dQAMc.es.js → create-super-doc-ui--o8TcYYv.es.js} +6080 -5456
  11. package/dist/chunks/{create-super-doc-ui-L60GhVAX.cjs → create-super-doc-ui-CvK1JdZv.cjs} +3567 -2937
  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/contract/command-catalog.d.ts +1 -1
  19. package/dist/document-api/src/contract/operation-definitions.d.ts +76 -8
  20. package/dist/document-api/src/contract/operation-registry.d.ts +2 -2
  21. package/dist/document-api/src/contract/schemas.d.ts +7 -1
  22. package/dist/document-api/src/create/create.d.ts +0 -4
  23. package/dist/document-api/src/diff/diff.types.d.ts +6 -6
  24. package/dist/document-api/src/header-footers/header-footers.d.ts +1 -3
  25. package/dist/document-api/src/index.d.ts +13 -1
  26. package/dist/document-api/src/inline-semantics/token-sets.d.ts +0 -2
  27. package/dist/document-api/src/insert/insert.d.ts +0 -1
  28. package/dist/document-api/src/paragraphs/paragraphs.d.ts +8 -3
  29. package/dist/document-api/src/paragraphs/paragraphs.types.d.ts +49 -5
  30. package/dist/document-api/src/tables/data/table-preset-appearance.generated.d.ts +2 -0
  31. package/dist/document-api/src/tables/table-style-presets.d.ts +73 -0
  32. package/dist/document-api/src/tables/tables.d.ts +4 -1
  33. package/dist/document-api/src/types/sd-props.d.ts +7 -0
  34. package/dist/document-api/src/types/table-operations.types.d.ts +12 -15
  35. package/dist/layout-engine/contracts/src/index.d.ts +4 -1
  36. package/dist/layout-engine/contracts/src/table-cell-vector-overlay.d.ts +2 -0
  37. package/dist/layout-engine/dom-contract/src/data-attrs.d.ts +4 -0
  38. package/dist/layout-engine/layout-bridge/src/coupled-footnote-pagination.d.ts +1 -0
  39. package/dist/layout-engine/layout-bridge/src/footnote-page-certificate.d.ts +1 -0
  40. package/dist/layout-engine/layout-bridge/src/footnote-page-planner.d.ts +5 -0
  41. package/dist/layout-engine/layout-bridge/src/incrementalLayout.d.ts +1 -0
  42. package/dist/layout-engine/layout-engine/src/footnote-page-flow.d.ts +3 -0
  43. package/dist/layout-engine/layout-engine/src/index.d.ts +7 -0
  44. package/dist/layout-engine/layout-engine/src/layout-paragraph.d.ts +5 -0
  45. package/dist/layout-engine/layout-engine/src/layout-table.d.ts +3 -1
  46. package/dist/layout-engine/layout-engine/src/layout-utils.d.ts +0 -4
  47. package/dist/layout-engine/layout-engine/src/paginator.d.ts +4 -0
  48. package/dist/layout-engine/painters/dom/src/index.d.ts +2 -0
  49. package/dist/layout-engine/painters/dom/src/paragraph/list-marker.d.ts +1 -1
  50. package/dist/layout-engine/painters/dom/src/paragraph/renderParagraphContent.d.ts +1 -1
  51. package/dist/layout-engine/painters/dom/src/styles.d.ts +0 -10
  52. package/dist/layout-engine/painters/dom/src/svg-utils.d.ts +1 -13
  53. package/dist/public/ui-react.cjs +1 -1
  54. package/dist/public/ui-react.es.js +1 -1
  55. package/dist/public/ui-vue.cjs +1 -1
  56. package/dist/public/ui-vue.es.js +1 -1
  57. package/dist/public/ui.cjs +1 -1
  58. package/dist/public/ui.es.js +1 -1
  59. package/dist/shared/common/Telemetry.d.ts +50 -0
  60. package/dist/shared/common/event-types.d.ts +13 -0
  61. package/dist/shared/font-system/src/bundled-face-names.d.ts +1 -0
  62. package/dist/shared/font-system/src/resolver.d.ts +1 -0
  63. package/dist/style.css +1 -1
  64. package/dist/style.layered.css +1 -1
  65. package/dist/superdoc/src/components/CommentsLayer/CommentDialog.vue.d.ts +17 -33
  66. package/dist/superdoc/src/components/CommentsLayer/collect-tracked-change-thread.d.ts +6 -4
  67. package/dist/superdoc/src/components/CommentsLayer/comment-params.d.ts +84 -0
  68. package/dist/superdoc/src/components/CommentsLayer/use-comment.d.ts +15 -95
  69. package/dist/superdoc/src/components/Whiteboard/use-whiteboard.d.ts +30 -9
  70. package/dist/superdoc/src/composables/use-comment-sidebar.d.ts +6 -0
  71. package/dist/superdoc/src/composables/use-document.d.ts +51 -99
  72. package/dist/superdoc/src/core/SuperDoc.d.ts +12 -3
  73. package/dist/superdoc/src/core/collaboration/cdn-collaboration-comment-map.d.ts +1 -0
  74. package/dist/superdoc/src/core/collaboration/collaboration-comment-map.d.ts +2 -0
  75. package/dist/superdoc/src/core/collaboration/helpers.d.ts +1 -1
  76. package/dist/superdoc/src/core/config/normalize-interaction-config.d.ts +10 -1
  77. package/dist/superdoc/src/core/document-open-telemetry.d.ts +1 -1
  78. package/dist/superdoc/src/core/editor-runtime/types.d.ts +4 -0
  79. package/dist/superdoc/src/core/editor-runtime/v2/v2-editor-runtime-adapter.d.ts +1 -0
  80. package/dist/superdoc/src/core/types/index.d.ts +110 -39
  81. package/dist/superdoc/src/helpers/deprecation.d.ts +12 -3
  82. package/dist/superdoc/src/helpers/object-guards.d.ts +1 -0
  83. package/dist/superdoc/src/helpers/v2-review-geometry-delivery.d.ts +22 -0
  84. package/dist/superdoc/src/internal/toolbar/built-in/ToolbarButton.vue.d.ts +1 -0
  85. package/dist/superdoc/src/public/content-control-ref.d.ts +12 -0
  86. package/dist/superdoc/src/public/index.d.cts +20 -0
  87. package/dist/superdoc/src/public/index.d.ts +4 -1
  88. package/dist/superdoc/src/public/ui/async-read-cache.d.ts +32 -0
  89. package/dist/superdoc/src/public/ui/block-commands.d.ts +39 -0
  90. package/dist/superdoc/src/public/ui/command-results.d.ts +39 -0
  91. package/dist/superdoc/src/public/ui/commands.d.ts +5 -1
  92. package/dist/superdoc/src/public/ui/create-super-doc-ui.d.ts +1 -17
  93. package/dist/superdoc/src/public/ui/document-change-records.d.ts +7 -0
  94. package/dist/superdoc/src/public/ui/entity-rows.d.ts +14 -0
  95. package/dist/superdoc/src/public/ui/font-options.d.ts +7 -0
  96. package/dist/superdoc/src/public/ui/format-painter-paragraph.d.ts +7 -0
  97. package/dist/superdoc/src/public/ui/format-painter-state.d.ts +29 -0
  98. package/dist/superdoc/src/public/ui/heavy-read-policy.d.ts +11 -0
  99. package/dist/superdoc/src/public/ui/host-events.d.ts +3 -0
  100. package/dist/superdoc/src/public/ui/inline-values.d.ts +80 -0
  101. package/dist/superdoc/src/public/ui/loose-record.d.ts +4 -0
  102. package/dist/superdoc/src/public/ui/reasons.d.ts +1 -0
  103. package/dist/superdoc/src/public/ui/selection-targets.d.ts +53 -0
  104. package/dist/superdoc/src/public/ui/shared-tracked-changes-catalog.d.ts +16 -0
  105. package/dist/superdoc/src/public/ui/tracked-change-ids.d.ts +51 -0
  106. package/dist/superdoc/src/public/ui/types.d.ts +25 -2
  107. package/dist/superdoc/src/public/ui/ui-bench-timing.d.ts +9 -0
  108. package/dist/superdoc/src/public/ui.d.cts +18 -0
  109. package/dist/superdoc/src/public/ui.d.ts +1 -1
  110. package/dist/superdoc/src/stores/comments-store.d.ts +23022 -546
  111. package/dist/superdoc/src/stores/helpers/comment-grouping.d.ts +1 -1
  112. package/dist/superdoc/src/stores/helpers/comment-identity.d.ts +10 -10
  113. package/dist/superdoc/src/stores/helpers/review-presentation.d.ts +91 -0
  114. package/dist/superdoc/src/stores/helpers/tracked-change-identity.d.ts +4 -2
  115. package/dist/superdoc/src/stores/helpers/tracked-change-row-values.d.ts +1 -8
  116. package/dist/superdoc/src/stores/helpers/tracked-change-thread-index.d.ts +1 -1
  117. package/dist/superdoc/src/stores/helpers/v2-review-adapters.d.ts +147 -0
  118. package/dist/superdoc/src/stores/superdoc-store.d.ts +12 -2895
  119. package/dist/superdoc.cjs +915 -516
  120. package/dist/superdoc.es.js +915 -516
  121. package/dist/word-layout/src/marker-utils.d.ts +0 -5
  122. package/dist/word-layout/src/unit-conversions.d.ts +0 -1
  123. package/dist-cdn/style.layered.css +1 -1
  124. package/dist-cdn/superdoc.min.css +1 -1
  125. package/dist-cdn/superdoc.min.js +38 -42
  126. package/package.json +12 -3
  127. package/dist/document-api/src/diff/index.d.ts +0 -3
  128. package/dist/document-api/src/write/locator.d.ts +0 -24
  129. package/dist/layout-engine/painters/dom/src/features/feature-registry.d.ts +0 -41
  130. package/dist/superdoc/src/internal/toolbar/built-in/format-negation.d.ts +0 -11
  131. 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-CvK1JdZv.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.13";
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(() => {
@@ -7908,29 +8091,28 @@ var useCommentsStore = defineStore("comments", () => {
7908
8091
  });
7909
8092
  const v2CommentsAdapter = require__plugin_vue_export_helper.shallowRef(null);
7910
8093
  const setV2CommentsAdapter = (adapter) => {
7911
- if (v2CommentsAdapter.value !== (adapter ?? null)) pendingCommentRequest += 1;
7912
- v2CommentsAdapter.value = adapter ?? null;
8094
+ const next = isV2CommentsAdapter(adapter) ? adapter : null;
8095
+ if (v2CommentsAdapter.value !== next) pendingCommentRequest += 1;
8096
+ v2CommentsAdapter.value = next;
7913
8097
  };
7914
8098
  const getV2CommentsAdapter = (superdoc) => {
7915
- const fromFacade = superdoc?.activeEditor?.v2Comments ?? null;
7916
- if (fromFacade) return fromFacade;
8099
+ const fromFacade = superdoc?.activeEditor?.v2Comments;
8100
+ if (isV2CommentsAdapter(fromFacade)) return fromFacade;
7917
8101
  return v2CommentsAdapter.value;
7918
8102
  };
7919
8103
  const isV2EditorActive = (superdoc) => superdoc?.activeEditor?.editorVersion === 2 || v2CommentsAdapter.value !== null;
7920
8104
  const v2TrackedChangesAdapter = require__plugin_vue_export_helper.shallowRef(null);
7921
8105
  const setV2TrackedChangesAdapter = (adapter) => {
7922
- v2TrackedChangesAdapter.value = adapter ?? null;
8106
+ cachedTrackedChangeBatchIdentityIndex = null;
8107
+ v2TrackedChangesAdapter.value = isV2TrackedChangesAdapter(adapter) ? adapter : null;
7923
8108
  };
7924
8109
  const getV2TrackedChangesAdapter = (superdoc) => {
7925
- const fromFacade = superdoc?.activeEditor?.v2TrackedChanges ?? null;
7926
- if (fromFacade) return fromFacade;
8110
+ const fromFacade = superdoc?.activeEditor?.v2TrackedChanges;
8111
+ if (isV2TrackedChangesAdapter(fromFacade)) return fromFacade;
7927
8112
  return v2TrackedChangesAdapter.value;
7928
8113
  };
7929
8114
  /**
7930
8115
  * Initialize the store
7931
- *
7932
- * @param {Object} config The comments module config from SuperDoc
7933
- * @returns {void}
7934
8116
  */
7935
8117
  const init = (config = {}) => {
7936
8118
  const updatedConfig = {
@@ -7962,12 +8144,17 @@ var useCommentsStore = defineStore("comments", () => {
7962
8144
  comment.trackedChangeDecision = null;
7963
8145
  };
7964
8146
  const getCommentEventPayload = (comment) => typeof comment?.getValues === "function" ? comment.getValues() : { ...comment };
8147
+ const getDeletedCommentChanges = (sources, fileId) => {
8148
+ const commentId = [...sources.map((source) => source?.commentId), ...sources.map((source) => source?.importedId)].map(normalizeCommentId).find(Boolean);
8149
+ return commentId ? [{
8150
+ key: "deleted",
8151
+ commentId,
8152
+ fileId
8153
+ }] : [];
8154
+ };
7965
8155
  /**
7966
8156
  * Check if a comment originated from the document editor (or has no explicit source).
7967
8157
  * 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
8158
  */
7972
8159
  const isEditorBackedComment = (comment) => {
7973
8160
  const source = comment?.selection?.source;
@@ -8001,9 +8188,6 @@ var useCommentsStore = defineStore("comments", () => {
8001
8188
  }, { deep: false });
8002
8189
  /**
8003
8190
  * 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
8191
  */
8008
8192
  const getCommentPosition = (commentOrId) => {
8009
8193
  return resolveCommentPositionEntry(commentOrId).entry ?? null;
@@ -8016,10 +8200,6 @@ var useCommentsStore = defineStore("comments", () => {
8016
8200
  };
8017
8201
  /**
8018
8202
  * 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
8203
  */
8024
8204
  const setActiveComment = (superdoc, id) => {
8025
8205
  const activeEditor = superdoc?.activeEditor;
@@ -8030,7 +8210,7 @@ var useCommentsStore = defineStore("comments", () => {
8030
8210
  v2Adapter.setActiveComment(null);
8031
8211
  return;
8032
8212
  }
8033
- activeEditor?.commands?.setActiveComment({ commentId: null });
8213
+ activeEditor?.commands?.setActiveComment?.({ commentId: null });
8034
8214
  return;
8035
8215
  }
8036
8216
  const comment = getComment(id);
@@ -8041,18 +8221,13 @@ var useCommentsStore = defineStore("comments", () => {
8041
8221
  v2Adapter.setActiveComment(comment.commentId);
8042
8222
  return;
8043
8223
  }
8044
- activeEditor?.commands?.setActiveComment({ commentId: activeComment.value });
8224
+ activeEditor?.commands?.setActiveComment?.({ commentId: activeComment.value });
8045
8225
  };
8046
8226
  /**
8047
8227
  * Called when a tracked change is updated. Creates a new comment if necessary,
8048
8228
  * 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
8229
  */
8055
- const handleTrackedChangeUpdate = ({ superdoc, params, broadcastChanges = true, trackedChangeIdentityIndex = null }) => {
8230
+ const handleTrackedChangeUpdate = ({ superdoc, params, broadcastChanges = true, trackedChangeIdentityIndex = null, preserveDraft = false }) => {
8056
8231
  const span = startInteractionSpan("store.trackedChanges.handleUpdate", "store-reconciliation", {
8057
8232
  event: params?.event ?? null,
8058
8233
  changeId: params?.changeId ?? null,
@@ -8077,9 +8252,11 @@ var useCommentsStore = defineStore("comments", () => {
8077
8252
  const normalizedTrackedChangeLabel = typeof trackedChangeLabel === "string" && trackedChangeLabel.length > 0 ? trackedChangeLabel : null;
8078
8253
  const normalizedTrackedChangeDetailLines = normalizeTrackedChangeDetailLines(trackedChangeDetailLines);
8079
8254
  const normalizedTrackedChangeImagePreview = normalizeTrackedChangeImagePreview(trackedChangeImagePreview);
8255
+ const hasDraft = preserveDraft && (pendingComment.value !== null || currentCommentText.value !== "" || currentCommentMentions.value.length > 0);
8080
8256
  const comment = getPendingComment({
8081
8257
  documentId,
8082
- commentId: changeId,
8258
+ ...hasDraft ? { commentText: "" } : {},
8259
+ commentId: normalizedChangeId ?? void 0,
8083
8260
  trackedChange: true,
8084
8261
  trackedChangeText,
8085
8262
  trackedChangeType,
@@ -8131,7 +8308,7 @@ var useCommentsStore = defineStore("comments", () => {
8131
8308
  commentCanonicalId,
8132
8309
  commentAnchorKey,
8133
8310
  ...commentAliasIds
8134
- ].filter(Boolean).some((id) => incomingAliasIds.has(id))) return true;
8311
+ ].some((id) => id != null && incomingAliasIds.has(id))) return true;
8135
8312
  const hasSyntheticSideIdentity = normalizedTrackedChangeCanonicalId && normalizedTrackedChangeCanonicalId !== normalizedChangeId || commentCanonicalId && commentCanonicalId !== commentId;
8136
8313
  if (hasCompatibleStory && commentId === normalizedChangeId) return true;
8137
8314
  if (hasCompatibleStory && !hasSyntheticSideIdentity && (importedId === normalizedChangeId || normalizedImportedId !== void 0 && normalizedImportedId != null && importedId === normalizedImportedId)) return true;
@@ -8155,7 +8332,7 @@ var useCommentsStore = defineStore("comments", () => {
8155
8332
  };
8156
8333
  const emitTrackedChangeEvent = (event) => {
8157
8334
  if (!broadcastChanges) return;
8158
- syncCommentsToClients(superdoc, event);
8335
+ syncCommentsToClients(requireHost(superdoc, "handleTrackedChangeUpdate"), event);
8159
8336
  debounceEmit(changeId, event, superdoc);
8160
8337
  };
8161
8338
  const setIfChanged = (target, key, value) => {
@@ -8198,8 +8375,9 @@ var useCommentsStore = defineStore("comments", () => {
8198
8375
  target?.trackedChangeCanonicalId,
8199
8376
  target?.trackedChangeAnchorKey,
8200
8377
  ...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;
8378
+ ].map((id) => normalizeCommentId(id)).filter(isPresent));
8379
+ const incomingAliases = normalizedTrackedChangePositionAliases ?? [];
8380
+ fields.trackedChangePositionAliases = incomingAliases.some((alias) => targetIdentityIds.has(alias)) ? normalizeTrackedChangePositionAliases([...Array.isArray(target?.trackedChangePositionAliases) ? target.trackedChangePositionAliases : [], ...incomingAliases]) : incomingAliases;
8203
8381
  }
8204
8382
  let didChange = false;
8205
8383
  for (const [key, value] of Object.entries(fields)) didChange = setIfChanged(target, key, value) || didChange;
@@ -8208,7 +8386,7 @@ var useCommentsStore = defineStore("comments", () => {
8208
8386
  didChange = true;
8209
8387
  }
8210
8388
  if (hasCustomAttributes && target && !trackedChangeCustomAttributesEqual(target.customAttributes, normalizedCustomAttributes)) {
8211
- target.customAttributes = normalizedCustomAttributes;
8389
+ target.customAttributes = normalizedCustomAttributes ?? null;
8212
8390
  didChange = true;
8213
8391
  }
8214
8392
  return applyStoryMetadata(target) || didChange;
@@ -8230,9 +8408,10 @@ var useCommentsStore = defineStore("comments", () => {
8230
8408
  return;
8231
8409
  }
8232
8410
  addHydratedComment({
8233
- superdoc,
8411
+ superdoc: requireHost(superdoc, "handleTrackedChangeUpdate"),
8234
8412
  comment,
8235
- broadcastChanges
8413
+ broadcastChanges,
8414
+ preserveDraft: hasDraft
8236
8415
  });
8237
8416
  trackedChangeIdentityIndex?.add(comment);
8238
8417
  } else if (event === "update") {
@@ -8250,11 +8429,12 @@ var useCommentsStore = defineStore("comments", () => {
8250
8429
  id: params.resolvedById ?? superdoc?.user?.id ?? null,
8251
8430
  email: params.resolvedByEmail ?? superdoc?.user?.email ?? null,
8252
8431
  name: params.resolvedByName ?? superdoc?.user?.name ?? null,
8253
- decision: params.decision ?? null,
8432
+ decision: params.decision ?? void 0,
8254
8433
  superdoc
8255
8434
  };
8256
8435
  if (existingTrackedChange && !existingTrackedChange.resolvedTime) existingTrackedChange.resolveComment({
8257
8436
  ...resolveArgs,
8437
+ superdoc: requireHost(superdoc, "handleTrackedChangeUpdate"),
8258
8438
  reconciliationToken: COMMENT_RECONCILIATION_TOKEN
8259
8439
  });
8260
8440
  }
@@ -8279,10 +8459,11 @@ var useCommentsStore = defineStore("comments", () => {
8279
8459
  instantSidebarAlignmentInstanceId.value = null;
8280
8460
  };
8281
8461
  const debounceEmit = (commentId, event, superdoc, delay = 1e3) => {
8282
- if (debounceTimers[commentId]) clearTimeout(debounceTimers[commentId]);
8283
- debounceTimers[commentId] = setTimeout(() => {
8462
+ const timerKey = String(commentId);
8463
+ if (debounceTimers[timerKey]) clearTimeout(debounceTimers[timerKey]);
8464
+ debounceTimers[timerKey] = setTimeout(() => {
8284
8465
  if (superdoc) superdoc.emit("comments-update", event);
8285
- delete debounceTimers[commentId];
8466
+ delete debounceTimers[timerKey];
8286
8467
  }, delay);
8287
8468
  };
8288
8469
  const showAddComment = (superdoc, targetClientY = null) => {
@@ -8294,7 +8475,7 @@ var useCommentsStore = defineStore("comments", () => {
8294
8475
  if (!ownsRequest()) return;
8295
8476
  superdoc.emit("comments-update", {
8296
8477
  type: COMMENT_EVENTS.PENDING,
8297
- pendingSelection: superdoc?.activeEditor === editor ? snapshot : null
8478
+ pendingSelection: superdoc?.activeEditor === editor && require__plugin_vue_export_helper.isPlainObject(snapshot) ? snapshot : null
8298
8479
  });
8299
8480
  };
8300
8481
  if (v2Adapter) {
@@ -8339,12 +8520,11 @@ var useCommentsStore = defineStore("comments", () => {
8339
8520
  }).catch((err) => ({
8340
8521
  ok: false,
8341
8522
  reason: "selection-capture-failed",
8342
- detail: err?.message ?? String(err)
8523
+ detail: errorDetail(err)
8343
8524
  }));
8344
8525
  }
8345
8526
  const pendingSelection = getPendingSelectionSnapshot(superdoc, superdocStore.activeSelection);
8346
- const asyncSnapshot = typeof pendingSelection?.then === "function";
8347
- if (!asyncSnapshot) emitPending(pendingSelection);
8527
+ if (!require__plugin_vue_export_helper.isThenable(pendingSelection)) emitPending(pendingSelection);
8348
8528
  const showLegacyPending = (selection) => {
8349
8529
  selection.selectionBounds = { ...selection.selectionBounds };
8350
8530
  if (superdocStore.selectionPosition?.source && superdocStore.selectionPosition.source !== "pdf") superdocStore.selectionPosition.source = null;
@@ -8354,7 +8534,7 @@ var useCommentsStore = defineStore("comments", () => {
8354
8534
  parentCommentId: null
8355
8535
  });
8356
8536
  if (!superdoc.config.isInternal) pendingComment.value.isInternal = false;
8357
- if (superdoc.activeEditor?.commands) superdoc.activeEditor.commands.insertComment({
8537
+ if (superdoc.activeEditor?.commands) superdoc.activeEditor.commands.insertComment?.({
8358
8538
  ...pendingComment.value.getValues(),
8359
8539
  commentId: "pending",
8360
8540
  skipEmit: true
@@ -8365,10 +8545,10 @@ var useCommentsStore = defineStore("comments", () => {
8365
8545
  activeComment.value = pendingComment.value.commentId;
8366
8546
  return { ok: true };
8367
8547
  };
8368
- if (asyncSnapshot) {
8548
+ if (require__plugin_vue_export_helper.isThenable(pendingSelection)) {
8369
8549
  const selection = { ...superdocStore.activeSelection };
8370
8550
  selection.selectionBounds = { ...selection.selectionBounds };
8371
- return pendingSelection.then((snapshot) => {
8551
+ return Promise.resolve(pendingSelection).then((snapshot) => {
8372
8552
  if (!ownsRequest()) return {
8373
8553
  ok: false,
8374
8554
  reason: "pending-comment-superseded"
@@ -8400,12 +8580,6 @@ var useCommentsStore = defineStore("comments", () => {
8400
8580
  });
8401
8581
  /**
8402
8582
  * 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
8583
  */
8410
8584
  const getPendingComment = ({ selection, documentId, parentCommentId, ...options }) => {
8411
8585
  return _getNewcomment({
@@ -8417,17 +8591,11 @@ var useCommentsStore = defineStore("comments", () => {
8417
8591
  };
8418
8592
  /**
8419
8593
  * 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
8594
  */
8427
8595
  const _getNewcomment = ({ selection, documentId, parentCommentId, ...options }) => {
8428
8596
  let activeDocument;
8429
- if (documentId) activeDocument = superdocStore.getDocument(documentId);
8430
- else if (selection) activeDocument = superdocStore.getDocument(selection.documentId);
8597
+ if (documentId) activeDocument = findDocument(documentId);
8598
+ else if (selection) activeDocument = findDocument(selection.documentId);
8431
8599
  if (!activeDocument) activeDocument = superdocStore.documents[0];
8432
8600
  return useComment({
8433
8601
  fileId: activeDocument.id,
@@ -8444,8 +8612,6 @@ var useCommentsStore = defineStore("comments", () => {
8444
8612
  };
8445
8613
  /**
8446
8614
  * Remove the pending comment
8447
- *
8448
- * @returns {void}
8449
8615
  */
8450
8616
  const resetPendingCommentState = (superdoc, clearViewingSelection = true) => {
8451
8617
  const hadPending = !!pendingComment.value;
@@ -8461,7 +8627,7 @@ var useCommentsStore = defineStore("comments", () => {
8461
8627
  } catch {}
8462
8628
  return;
8463
8629
  }
8464
- superdoc?.activeEditor?.commands?.removeComment({ commentId: "pending" });
8630
+ superdoc?.activeEditor?.commands?.removeComment?.({ commentId: "pending" });
8465
8631
  };
8466
8632
  const removePendingComment = (superdoc) => {
8467
8633
  pendingCommentRequest += 1;
@@ -8474,16 +8640,8 @@ var useCommentsStore = defineStore("comments", () => {
8474
8640
  };
8475
8641
  /**
8476
8642
  * 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
8643
  */
8486
- const addCommentInternal = ({ superdoc, comment, skipEditorUpdate = false, broadcastChanges = true, isHydration = false }) => {
8644
+ const addCommentInternal = ({ superdoc, comment, skipEditorUpdate = false, broadcastChanges = true, isHydration = false, preserveDraft = false }) => {
8487
8645
  if (commentsAreReadOnly() && !isHydration) return readOnlyMutationOutcome();
8488
8646
  const v2Adapter = !skipEditorUpdate && !isHydration ? getV2CommentsAdapter(superdoc) : null;
8489
8647
  if (v2Adapter && !comment.trackedChange) {
@@ -8511,7 +8669,7 @@ var useCommentsStore = defineStore("comments", () => {
8511
8669
  return {
8512
8670
  ok: false,
8513
8671
  reason: "adapter-threw",
8514
- detail: err?.message ?? String(err)
8672
+ detail: errorDetail(err)
8515
8673
  };
8516
8674
  }
8517
8675
  })().then((outcome) => {
@@ -8562,28 +8720,30 @@ var useCommentsStore = defineStore("comments", () => {
8562
8720
  };
8563
8721
  });
8564
8722
  }
8565
- let parentComment = commentsList.value.find((c) => c.commentId === activeComment.value);
8723
+ const activeId = activeComment.value;
8724
+ const parent = require__plugin_vue_export_helper.toRaw(commentsList.value).find((c) => require__plugin_vue_export_helper.unref(c.commentId) === activeId);
8725
+ let parentComment = parent ? require__plugin_vue_export_helper.reactive(parent) : void 0;
8566
8726
  if (!parentComment) parentComment = comment;
8567
8727
  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,
8728
+ const text = pendingComment.value && !preserveDraft ? currentCommentText.value : comment.commentText;
8729
+ if (text === "") {
8730
+ newComment.commentText = "";
8731
+ newComment.mentions = [];
8732
+ } else newComment.setText({
8733
+ text,
8574
8734
  suppressUpdate: true
8575
8735
  });
8576
- newComment.selection.source = pendingComment.value?.selection?.source ?? newComment.selection.source;
8736
+ if (!preserveDraft) newComment.selection.source = pendingComment.value?.selection?.source ?? newComment.selection.source;
8577
8737
  if (parentComment) newComment.isInternal = parentComment.isInternal;
8578
8738
  if (!superdoc.config.isInternal) newComment.isInternal = false;
8579
8739
  commentsList.value.push(newComment);
8580
- const hadPendingComment = !!pendingComment.value;
8581
- removePendingComment(superdoc);
8740
+ const hadPendingComment = !preserveDraft && !!pendingComment.value;
8741
+ if (!preserveDraft) removePendingComment(superdoc);
8582
8742
  if (hadPendingComment) {
8583
8743
  activeComment.value = newComment.commentId;
8584
8744
  setActiveFloatingCommentInstance(newComment.commentId);
8585
8745
  }
8586
- if (!skipEditorUpdate && !comment.trackedChange && superdoc.activeEditor?.commands && !comment.parentCommentId) superdoc.activeEditor.commands.insertComment({
8746
+ if (!skipEditorUpdate && !comment.trackedChange && superdoc.activeEditor?.commands && !comment.parentCommentId) superdoc.activeEditor.commands.insertComment?.({
8587
8747
  ...newComment.getValues(),
8588
8748
  skipEmit: true
8589
8749
  });
@@ -8606,12 +8766,13 @@ var useCommentsStore = defineStore("comments", () => {
8606
8766
  skipEditorUpdate,
8607
8767
  broadcastChanges
8608
8768
  });
8609
- const addHydratedComment = ({ superdoc, comment, skipEditorUpdate = false, broadcastChanges = true }) => addCommentInternal({
8769
+ const addHydratedComment = ({ superdoc, comment, skipEditorUpdate = false, broadcastChanges = true, preserveDraft = false }) => addCommentInternal({
8610
8770
  superdoc,
8611
8771
  comment,
8612
8772
  skipEditorUpdate,
8613
8773
  broadcastChanges,
8614
- isHydration: true
8774
+ isHydration: true,
8775
+ preserveDraft
8615
8776
  });
8616
8777
  const deleteComment = ({ commentId: commentIdToDelete, superdoc }) => {
8617
8778
  if (commentsAreReadOnly()) return Promise.resolve(readOnlyMutationOutcome());
@@ -8631,7 +8792,7 @@ var useCommentsStore = defineStore("comments", () => {
8631
8792
  return {
8632
8793
  ok: false,
8633
8794
  reason: "adapter-threw",
8634
- detail: err?.message ?? String(err)
8795
+ detail: errorDetail(err)
8635
8796
  };
8636
8797
  }
8637
8798
  })().then((outcome) => {
@@ -8651,16 +8812,16 @@ var useCommentsStore = defineStore("comments", () => {
8651
8812
  });
8652
8813
  return outcome;
8653
8814
  }
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));
8815
+ const removedIds = new Set((Array.isArray(outcome.removedCommentIds) ? outcome.removedCommentIds : []).map(normalizeCommentId).filter(isPresent));
8816
+ if (removedIds.has(normalizeCommentId(commentId) ?? "")) {
8817
+ if (importedId != null) removedIds.add(String(importedId));
8657
8818
  let addedChild = true;
8658
8819
  while (addedChild) {
8659
8820
  addedChild = false;
8660
8821
  for (const row of commentsList.value) {
8661
8822
  const parentId = normalizeCommentId(row?.parentCommentId);
8662
8823
  if (!parentId || !removedIds.has(parentId)) continue;
8663
- for (const alias of [row?.commentId, row?.importedId].map(normalizeCommentId).filter(Boolean)) {
8824
+ for (const alias of [row?.commentId, row?.importedId].map(normalizeCommentId).filter(isPresent)) {
8664
8825
  if (removedIds.has(alias)) continue;
8665
8826
  removedIds.add(alias);
8666
8827
  addedChild = true;
@@ -8668,9 +8829,9 @@ var useCommentsStore = defineStore("comments", () => {
8668
8829
  }
8669
8830
  }
8670
8831
  commentsList.value = commentsList.value.filter((row) => {
8671
- return ![row?.commentId, row?.importedId].map(normalizeCommentId).filter(Boolean).some((alias) => removedIds.has(alias));
8832
+ return ![row?.commentId, row?.importedId].map(normalizeCommentId).filter(isPresent).some((alias) => removedIds.has(alias));
8672
8833
  });
8673
- if (removedIds.has(normalizeCommentId(activeComment.value))) clearActiveCommentSelection();
8834
+ if (removedIds.has(normalizeCommentId(activeComment.value) ?? "")) clearActiveCommentSelection();
8674
8835
  } else reconcileCommentsFromV2({
8675
8836
  superdoc,
8676
8837
  adapter: v2Adapter,
@@ -8678,18 +8839,18 @@ var useCommentsStore = defineStore("comments", () => {
8678
8839
  items: outcome.items ?? [],
8679
8840
  pruneStale: outcome.complete !== false
8680
8841
  });
8842
+ const payload = comment.getValues();
8681
8843
  superdoc.emit("comments-update", {
8682
8844
  type: COMMENT_EVENTS.DELETED,
8683
- comment: comment.getValues(),
8684
- changes: [{
8685
- key: "deleted",
8845
+ comment: payload,
8846
+ changes: getDeletedCommentChanges([{
8686
8847
  commentId,
8687
- fileId
8688
- }]
8848
+ importedId
8849
+ }, payload], fileId)
8689
8850
  });
8690
8851
  return { ok: true };
8691
8852
  });
8692
- superdoc.activeEditor?.commands?.removeComment({
8853
+ superdoc.activeEditor?.commands?.removeComment?.({
8693
8854
  commentId,
8694
8855
  importedId
8695
8856
  });
@@ -8697,14 +8858,14 @@ var useCommentsStore = defineStore("comments", () => {
8697
8858
  const childCommentIds = commentsList.value.filter((c) => c.parentCommentId === commentId).map((c) => c.commentId || c.importedId);
8698
8859
  commentsList.value = commentsList.value.filter((c) => !childCommentIds.includes(c.commentId));
8699
8860
  if (activeComment.value === commentId || childCommentIds.includes(activeComment.value)) clearActiveCommentSelection();
8861
+ const payload = comment.getValues();
8700
8862
  const event = {
8701
8863
  type: COMMENT_EVENTS.DELETED,
8702
- comment: comment.getValues(),
8703
- changes: [{
8704
- key: "deleted",
8864
+ comment: payload,
8865
+ changes: getDeletedCommentChanges([{
8705
8866
  commentId,
8706
- fileId
8707
- }]
8867
+ importedId
8868
+ }, payload], fileId)
8708
8869
  };
8709
8870
  superdoc.emit("comments-update", event);
8710
8871
  syncCommentsToClients(superdoc, event);
@@ -8716,7 +8877,7 @@ var useCommentsStore = defineStore("comments", () => {
8716
8877
  * otherwise keep the source bubble after the document comment is gone.
8717
8878
  */
8718
8879
  const dropCommentsFromMutationImpact = ({ superdoc, documentId, removedIds } = {}) => {
8719
- const seedIds = new Set([...removedIds instanceof Set ? removedIds : removedIds ?? []].map((id) => normalizeCommentId(id)).filter(Boolean));
8880
+ const seedIds = new Set([...removedIds instanceof Set ? removedIds : removedIds ?? []].map((id) => normalizeCommentId(id)).filter(isPresent));
8720
8881
  if (!seedIds.size) return {
8721
8882
  ok: true,
8722
8883
  removedIds: []
@@ -8740,7 +8901,7 @@ var useCommentsStore = defineStore("comments", () => {
8740
8901
  if (!isDroppableCommentRow(row)) continue;
8741
8902
  const aliases = getCommentAliasIds(row);
8742
8903
  const alreadyDropped = aliases.some((alias) => droppedIds.has(alias));
8743
- const parentDropped = droppedIds.has(normalizeCommentId(row?.parentCommentId)) || droppedIds.has(normalizeCommentId(row?.threadingParentCommentId));
8904
+ const parentDropped = droppedIds.has(normalizeCommentId(row?.parentCommentId) ?? "") || droppedIds.has(normalizeCommentId(row?.threadingParentCommentId) ?? "");
8744
8905
  if (!alreadyDropped && !parentDropped) continue;
8745
8906
  for (const alias of aliases) {
8746
8907
  if (droppedIds.has(alias)) continue;
@@ -8768,11 +8929,7 @@ var useCommentsStore = defineStore("comments", () => {
8768
8929
  const event = {
8769
8930
  type: COMMENT_EVENTS.DELETED,
8770
8931
  comment: payload,
8771
- changes: [{
8772
- key: "deleted",
8773
- commentId: payload?.commentId,
8774
- fileId: payload?.fileId
8775
- }]
8932
+ changes: getDeletedCommentChanges([payload, comment], payload?.fileId)
8776
8933
  };
8777
8934
  superdoc?.emit?.("comments-update", event);
8778
8935
  });
@@ -8794,7 +8951,7 @@ var useCommentsStore = defineStore("comments", () => {
8794
8951
  const byAlias = /* @__PURE__ */ new Map();
8795
8952
  const childrenByParentAlias = /* @__PURE__ */ new Map();
8796
8953
  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);
8954
+ for (const alias of [row.commentId, row.importedId].map(normalizeCommentId).filter(isPresent)) if (!byAlias.has(alias)) byAlias.set(alias, row);
8798
8955
  const parentId = normalizeCommentId(row.parentCommentId);
8799
8956
  if (parentId) {
8800
8957
  const children = childrenByParentAlias.get(parentId) ?? [];
@@ -8821,7 +8978,7 @@ var useCommentsStore = defineStore("comments", () => {
8821
8978
  if (!row || visited.has(row)) continue;
8822
8979
  visited.add(row);
8823
8980
  family.push(row);
8824
- for (const alias of [row.commentId, row.importedId].map(normalizeCommentId).filter(Boolean)) queue.push(...childrenByParentAlias.get(alias) ?? []);
8981
+ for (const alias of [row.commentId, row.importedId].map(normalizeCommentId).filter(isPresent)) queue.push(...childrenByParentAlias.get(alias) ?? []);
8825
8982
  }
8826
8983
  const isResolved = status === "resolved";
8827
8984
  const resolvedAt = Date.now();
@@ -8844,7 +9001,7 @@ var useCommentsStore = defineStore("comments", () => {
8844
9001
  outcome = {
8845
9002
  ok: false,
8846
9003
  reason: "adapter-threw",
8847
- detail: err?.message ?? String(err)
9004
+ detail: errorDetail(err)
8848
9005
  };
8849
9006
  }
8850
9007
  if (!isCurrentV2CommentsAdapter(adapter)) return {
@@ -8916,14 +9073,14 @@ var useCommentsStore = defineStore("comments", () => {
8916
9073
  return (outcome.items ?? []).map((item) => adapter.mapV2CommentToUseCommentInput(item, {
8917
9074
  fileId,
8918
9075
  fileType: null
8919
- })).filter(Boolean);
9076
+ })).filter(isPresent);
8920
9077
  };
8921
9078
  const isBlankDirectoryCommentText = (value) => typeof value !== "string" || value.replace(/<[^>]*>/g, "").replace(/&nbsp;/gi, " ").trim().length === 0;
8922
9079
  const trackedChangeDirectoryComment = (params) => {
8923
9080
  if (!params || params.event === "omit" || params.changeId == null) return null;
8924
9081
  return getPendingComment({
8925
9082
  documentId: params.documentId,
8926
- commentId: params.changeId,
9083
+ commentId: String(params.changeId),
8927
9084
  trackedChange: true,
8928
9085
  trackedChangeText: params.trackedChangeText,
8929
9086
  trackedChangeType: params.trackedChangeType,
@@ -8970,7 +9127,7 @@ var useCommentsStore = defineStore("comments", () => {
8970
9127
  const mappedCommentInputs = commentItems.filter((item) => !require_create_super_doc_ui.isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
8971
9128
  fileId: documentId,
8972
9129
  fileType: document?.type ?? null
8973
- })).filter(Boolean);
9130
+ })).filter(isPresent);
8974
9131
  const trackedParentBySidecarId = /* @__PURE__ */ new Map();
8975
9132
  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
9133
  const commentRows = mappedCommentInputs.filter((input) => !trackedParentBySidecarId.has(String(input.commentId))).map((input) => {
@@ -8981,7 +9138,7 @@ var useCommentsStore = defineStore("comments", () => {
8981
9138
  }
8982
9139
  return useComment(input);
8983
9140
  });
8984
- const trackedRows = trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).map((params) => trackedChangeDirectoryComment(params)).filter(Boolean);
9141
+ const trackedRows = trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).map((params) => trackedChangeDirectoryComment(params)).filter(isPresent);
8985
9142
  reviewDirectoryList.value = [...commentRows, ...trackedRows];
8986
9143
  return {
8987
9144
  ok: true,
@@ -9062,13 +9219,6 @@ var useCommentsStore = defineStore("comments", () => {
9062
9219
  * - successful mutation refreshes from v2 list and reconciles
9063
9220
  * - rejection preserves rows and emits a rejected `comments-update` event
9064
9221
  * - 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
9222
  */
9073
9223
  const replyCommentV2 = async ({ superdoc, parentCommentId, text, mentions = [], trackedChangeTarget } = {}) => {
9074
9224
  if (commentsAreReadOnly()) return readOnlyMutationOutcome();
@@ -9343,10 +9493,15 @@ var useCommentsStore = defineStore("comments", () => {
9343
9493
  row?.threadingParentCommentId,
9344
9494
  row?.trackedChangeParentId,
9345
9495
  row?.trackedChangeThreadParentId
9346
- ].map((value) => normalizeCommentId(value)).filter(Boolean));
9496
+ ].map((value) => normalizeCommentId(value)).filter(isPresent));
9347
9497
  /** Keep one mounted review window plus rows whose interaction is still live. */
9348
9498
  const boundReviewPresentationRows = ({ documentId, visibleIds }) => {
9349
9499
  const currentRows = commentsList.value;
9500
+ if (currentRows.every((row) => {
9501
+ if (!belongsToTrackedChangeSyncDocument(row, documentId)) return true;
9502
+ const id = normalizeCommentId(row.commentId);
9503
+ return Boolean(id && visibleIds.has(id));
9504
+ })) return;
9350
9505
  const identityIdsByRow = /* @__PURE__ */ new Map();
9351
9506
  const identityIdsFor = (row) => {
9352
9507
  let ids = identityIdsByRow.get(row);
@@ -9364,7 +9519,7 @@ var useCommentsStore = defineStore("comments", () => {
9364
9519
  pendingComment.value?.commentId,
9365
9520
  instantSidebarAlignmentThreadId.value,
9366
9521
  instantSidebarAlignmentInstanceId.value
9367
- ].map((value) => normalizeCommentId(value)).filter(Boolean));
9522
+ ].map((value) => normalizeCommentId(value)).filter(isPresent));
9368
9523
  let changed = true;
9369
9524
  while (changed) {
9370
9525
  changed = false;
@@ -9432,7 +9587,8 @@ var useCommentsStore = defineStore("comments", () => {
9432
9587
  if (preparedReview?.reuseCatalog === true && committedReviewPresentations.value[normalizedDocumentId]) {
9433
9588
  const previous = committedReviewPresentations.value[normalizedDocumentId];
9434
9589
  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 {
9590
+ const catalogVersion = preparedReview.catalogVersion ?? NaN;
9591
+ 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
9592
  ok: false,
9437
9593
  reason: "review-catalog-stale"
9438
9594
  };
@@ -9478,8 +9634,8 @@ var useCommentsStore = defineStore("comments", () => {
9478
9634
  previousPresentation.updateContext = presentation.updateContext;
9479
9635
  return {
9480
9636
  ok: true,
9481
- commentItems: preparedReview.comments.length,
9482
- trackedItems: preparedReview.trackedChanges.length
9637
+ commentItems: preparedReview?.comments.length,
9638
+ trackedItems: preparedReview?.trackedChanges.length
9483
9639
  };
9484
9640
  }
9485
9641
  let effectiveCommentItems = commentItems;
@@ -9493,7 +9649,7 @@ var useCommentsStore = defineStore("comments", () => {
9493
9649
  reason: seeded?.reason ?? "comment-catalog-seed-failed"
9494
9650
  };
9495
9651
  const selected = commentsAdapter.selectVisibleReviewComments?.();
9496
- if (selected?.ok === true && Array.isArray(selected.items) && (!Array.isArray(selected.unresolvedIds) || selected.unresolvedIds.length === 0)) effectiveCommentItems = selected.items;
9652
+ 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
9653
  }
9498
9654
  const fileType = (normalizedDocumentId ? superdocStore.getDocument(normalizedDocumentId) : null)?.type ?? null;
9499
9655
  let preparedInputs;
@@ -9510,16 +9666,16 @@ var useCommentsStore = defineStore("comments", () => {
9510
9666
  })) : effectiveCommentItems.filter((item) => !require_create_super_doc_ui.isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
9511
9667
  fileId: normalizedDocumentId,
9512
9668
  fileType
9513
- })).filter(Boolean);
9669
+ })).filter(isPresent);
9514
9670
  preparedParams = preparedReview ? preparedReview.trackedChanges.map((params) => ({
9515
9671
  ...params,
9516
9672
  documentId: normalizedDocumentId
9517
- })) : trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).filter(Boolean);
9673
+ })) : trackedChangeItems.map((item) => trackedChangesAdapter.mapV2TrackedChangeToCommentParams(item)).filter(isPresent);
9518
9674
  } catch (error) {
9519
9675
  return {
9520
9676
  ok: false,
9521
9677
  reason: "mapper-failed",
9522
- detail: error?.message ?? String(error)
9678
+ detail: errorDetail(error)
9523
9679
  };
9524
9680
  }
9525
9681
  if (typeof patch !== "function") return {
@@ -9547,7 +9703,7 @@ var useCommentsStore = defineStore("comments", () => {
9547
9703
  params?.trackedChangeCanonicalId,
9548
9704
  params?.trackedChangeAnchorKey
9549
9705
  ])
9550
- ].map((value) => normalizeCommentId(value)).filter(Boolean));
9706
+ ].map((value) => normalizeCommentId(value)).filter(isPresent));
9551
9707
  patch(() => {
9552
9708
  const previousPreparedPresentation = committedReviewPresentations.value[normalizedDocumentId] ?? null;
9553
9709
  if (preparedReview?.presentation) committedReviewPresentations.value = {
@@ -9562,7 +9718,8 @@ var useCommentsStore = defineStore("comments", () => {
9562
9718
  preparedParams,
9563
9719
  preparedPresentation: preparedReview?.presentation ?? null,
9564
9720
  previousPreparedPresentation,
9565
- pruneStale: false
9721
+ pruneStale: false,
9722
+ preserveDraft: true
9566
9723
  });
9567
9724
  reconcileCommentsFromV2({
9568
9725
  superdoc,
@@ -9595,7 +9752,7 @@ var useCommentsStore = defineStore("comments", () => {
9595
9752
  };
9596
9753
  const effectiveDocumentId = documentId ?? adapter.documentId ?? null;
9597
9754
  const normalizedEffectiveDocumentId = effectiveDocumentId != null ? String(effectiveDocumentId) : null;
9598
- const fileType = (effectiveDocumentId ? superdocStore.getDocument(effectiveDocumentId) : null)?.type ?? null;
9755
+ const fileType = (effectiveDocumentId ? findDocument(effectiveDocumentId) : null)?.type ?? null;
9599
9756
  const isBlankV2CommentText = (value) => {
9600
9757
  if (typeof value !== "string") return true;
9601
9758
  return value.replace(/<[^>]*>/g, "").replace(/&nbsp;/gi, " ").trim().length === 0;
@@ -9615,7 +9772,7 @@ var useCommentsStore = defineStore("comments", () => {
9615
9772
  const candidateInputs = Array.isArray(preparedInputs) ? preparedInputs : items.filter((item) => !require_create_super_doc_ui.isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => adapter.mapV2CommentToUseCommentInput(item, {
9616
9773
  fileId: effectiveDocumentId,
9617
9774
  fileType
9618
- })).filter(Boolean);
9775
+ })).filter(isPresent);
9619
9776
  for (const input of candidateInputs) {
9620
9777
  if (isV2TrackedChangeSidecarCommentInput(input)) {
9621
9778
  trackedChangeSidecarParentByCommentId.set(String(input.commentId), String(input.trackedChangeThreadParentId));
@@ -9668,7 +9825,7 @@ var useCommentsStore = defineStore("comments", () => {
9668
9825
  existing.mentions = Array.isArray(input.mentions) ? input.mentions : [];
9669
9826
  existing.isInternal = typeof input.isInternal === "boolean" ? input.isInternal : existing.isInternal;
9670
9827
  if (typeof input.resolvedTime === "number") {
9671
- existing.resolvedTime = input.resolvedTimeWasSynthesized === true && typeof existing.resolvedTime === "number" ? existing.resolvedTime : input.resolvedTime;
9828
+ existing.resolvedTime = input.resolvedTimeWasSynthesized === true && typeof existing.resolvedTime === "number" ? existing.resolvedTime : input.resolvedTime ?? null;
9672
9829
  existing.resolvedByEmail = input.resolvedByEmail ?? null;
9673
9830
  existing.resolvedByName = input.resolvedByName ?? null;
9674
9831
  } else {
@@ -9742,7 +9899,7 @@ var useCommentsStore = defineStore("comments", () => {
9742
9899
  return {
9743
9900
  ok: false,
9744
9901
  reason: "comment-read-failed",
9745
- detail: err?.message ?? String(err)
9902
+ detail: errorDetail(err)
9746
9903
  };
9747
9904
  }
9748
9905
  if (!isCurrentV2CommentsAdapter(adapter)) return {
@@ -9768,8 +9925,6 @@ var useCommentsStore = defineStore("comments", () => {
9768
9925
  };
9769
9926
  /**
9770
9927
  * Cancel the pending comment
9771
- *
9772
- * @returns {void}
9773
9928
  */
9774
9929
  const cancelComment = (superdoc) => {
9775
9930
  removePendingComment(superdoc);
@@ -9779,9 +9934,6 @@ var useCommentsStore = defineStore("comments", () => {
9779
9934
  *
9780
9935
  * Replace-file swaps call this before hydrating the replacement so stale
9781
9936
  * comments never survive long enough to render beside the new content.
9782
- *
9783
- * @param {string | null | undefined} documentId
9784
- * @returns {void}
9785
9937
  */
9786
9938
  function removeCommentsForDocument(documentId) {
9787
9939
  const activeDocumentId = documentId != null ? String(documentId) : null;
@@ -9812,20 +9964,16 @@ var useCommentsStore = defineStore("comments", () => {
9812
9964
  const remapTrackedChangeReferences = (droppedCommentIds, activeDocumentId) => {
9813
9965
  const remapSelection = (selection) => {
9814
9966
  const selectedId = normalizeCommentId(selection.value);
9815
- if (selectedId && droppedCommentIds.has(selectedId)) selection.value = droppedCommentIds.get(selectedId);
9967
+ const replacementId = selectedId ? droppedCommentIds.get(selectedId) : void 0;
9968
+ if (replacementId) selection.value = replacementId;
9816
9969
  };
9817
9970
  remapSelection(activeComment);
9818
9971
  remapSelection(activeFloatingCommentInstanceId);
9819
9972
  commentsList.value.forEach((row) => {
9820
9973
  if (!belongsToTrackedChangeSyncDocument(row, activeDocumentId)) return;
9821
9974
  const rowIdentityUpdates = {};
9822
- for (const field of [
9823
- "trackedChangeThreadParentId",
9824
- "trackedChangeParentId",
9825
- "threadingParentCommentId",
9826
- "parentCommentId"
9827
- ]) {
9828
- const replacementId = droppedCommentIds.get(normalizeCommentId(row?.[field]));
9975
+ for (const field of PARENT_LINK_FIELDS) {
9976
+ const replacementId = droppedCommentIds.get(normalizeCommentId(row?.[field]) ?? "");
9829
9977
  if (!replacementId) continue;
9830
9978
  row[field] = replacementId;
9831
9979
  rowIdentityUpdates[field] = replacementId;
@@ -9846,10 +9994,6 @@ var useCommentsStore = defineStore("comments", () => {
9846
9994
  *
9847
9995
  * Pruning is scoped to the active editor document so replay in one document does not
9848
9996
  * 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
9997
  */
9854
9998
  const pruneStaleTrackedChangeComments = (liveTrackedChangeIds, liveTrackedChangeAnchorKeys, activeDocumentId, superdoc = null, { broadcastChanges = true } = {}) => withInteractionSpan("store.trackedChanges.pruneStaleComments", "store-reconciliation", {
9855
9999
  activeDocumentId: activeDocumentId ?? null,
@@ -9894,7 +10038,7 @@ var useCommentsStore = defineStore("comments", () => {
9894
10038
  comment: payload
9895
10039
  };
9896
10040
  if (broadcastChanges) {
9897
- syncCommentsToClients(superdoc, event);
10041
+ syncCommentsToClients(requireHost(superdoc, "pruneStaleTrackedChangeComments"), event);
9898
10042
  superdoc?.emit?.("comments-update", event);
9899
10043
  }
9900
10044
  });
@@ -9926,14 +10070,10 @@ var useCommentsStore = defineStore("comments", () => {
9926
10070
  const event = {
9927
10071
  type: COMMENT_EVENTS.DELETED,
9928
10072
  comment: payload,
9929
- changes: [{
9930
- key: "deleted",
9931
- commentId: payload.commentId,
9932
- fileId: payload.fileId
9933
- }]
10073
+ changes: getDeletedCommentChanges([payload, comment], payload.fileId)
9934
10074
  };
9935
10075
  if (broadcastChanges) {
9936
- syncCommentsToClients(superdoc, event);
10076
+ syncCommentsToClients(requireHost(superdoc, "pruneStaleTrackedChangeComments"), event);
9937
10077
  superdoc?.emit?.("comments-update", event);
9938
10078
  }
9939
10079
  });
@@ -9956,7 +10096,6 @@ var useCommentsStore = defineStore("comments", () => {
9956
10096
  const resolveArgs = {
9957
10097
  email: superdoc?.user?.email ?? null,
9958
10098
  name: superdoc?.user?.name ?? null,
9959
- superdoc,
9960
10099
  reconciliationToken: COMMENT_RECONCILIATION_TOKEN
9961
10100
  };
9962
10101
  let resolvedCount = 0;
@@ -9967,9 +10106,12 @@ var useCommentsStore = defineStore("comments", () => {
9967
10106
  trackedChangeThreadParentIdForComment(linkedComment),
9968
10107
  linkedComment.threadingParentCommentId,
9969
10108
  linkedComment.parentCommentId
9970
- ].map((id) => normalizeCommentId(id)).filter(Boolean).some((id) => trackedChangeIds.has(id))) return;
10109
+ ].map((id) => normalizeCommentId(id)).filter(isPresent).some((id) => trackedChangeIds.has(id))) return;
9971
10110
  if (typeof linkedComment.resolveComment === "function") {
9972
- linkedComment.resolveComment(resolveArgs);
10111
+ linkedComment.resolveComment({
10112
+ ...resolveArgs,
10113
+ superdoc: requireHost(superdoc, "resolveLinkedCommentsForTrackedChangeDecision")
10114
+ });
9973
10115
  resolvedCount += 1;
9974
10116
  }
9975
10117
  });
@@ -10066,7 +10208,7 @@ var useCommentsStore = defineStore("comments", () => {
10066
10208
  if (previousCommentId) anchorSourceIds.add(previousCommentId);
10067
10209
  if (previousCanonicalId) anchorSourceIds.add(previousCanonicalId);
10068
10210
  }
10069
- if (comment.trackedChangeAnchorKey == null || anchorSourceIds.has(currentAnchorParts?.rawId)) comment.trackedChangeAnchorKey = toAnchor;
10211
+ if (comment.trackedChangeAnchorKey == null || anchorSourceIds.has(currentAnchorParts?.rawId ?? "")) comment.trackedChangeAnchorKey = toAnchor;
10070
10212
  comment.trackedChangePositionAliases = normalizeTrackedChangePositionAliases([...Array.isArray(comment.trackedChangePositionAliases) ? comment.trackedChangePositionAliases : [], fromId]);
10071
10213
  const identitySourceIds = /* @__PURE__ */ new Set([fromId]);
10072
10214
  if (advancesRemapLineage) {
@@ -10077,33 +10219,28 @@ var useCommentsStore = defineStore("comments", () => {
10077
10219
  if (row === comment) return;
10078
10220
  if (!belongsToTrackedChangeSyncDocument(row, normalizedDocumentId)) return;
10079
10221
  const rowIdentityUpdates = {};
10080
- for (const field of [
10081
- "trackedChangeThreadParentId",
10082
- "trackedChangeParentId",
10083
- "threadingParentCommentId",
10084
- "parentCommentId"
10085
- ]) {
10086
- if (!identitySourceIds.has(normalizeCommentId(row?.[field]))) continue;
10222
+ for (const field of PARENT_LINK_FIELDS) {
10223
+ if (!identitySourceIds.has(normalizeCommentId(row?.[field]) ?? "")) continue;
10087
10224
  row[field] = toId;
10088
10225
  rowIdentityUpdates[field] = toId;
10089
10226
  }
10090
10227
  if (Object.keys(rowIdentityUpdates).length && typeof row.updateIdentityValues === "function") row.updateIdentityValues(rowIdentityUpdates);
10091
10228
  });
10092
10229
  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));
10230
+ const previousEntry = previousPositionEntry ?? [positions[fromId], positions[String(buildBodyTrackedChangeAnchorKey(fromId))]].find((entry) => entry && positionEntryMatchesTrackedChangeStory(entry, comment));
10094
10231
  if (previousEntry) {
10095
- for (const key of [toId, toAnchor]) if (positions[key] === void 0) {
10232
+ for (const key of [toId, String(toAnchor)]) if (positions[key] === void 0) {
10096
10233
  positions[key] = previousEntry;
10097
10234
  positionsChanged = true;
10098
10235
  }
10099
10236
  }
10100
- if (identitySourceIds.has(normalizeCommentId(activeComment.value))) activeComment.value = toId;
10101
- if (identitySourceIds.has(normalizeCommentId(activeFloatingCommentInstanceId.value))) activeFloatingCommentInstanceId.value = toId;
10237
+ if (identitySourceIds.has(normalizeCommentId(activeComment.value) ?? "")) activeComment.value = toId;
10238
+ if (identitySourceIds.has(normalizeCommentId(activeFloatingCommentInstanceId.value) ?? "")) activeFloatingCommentInstanceId.value = toId;
10102
10239
  }
10103
10240
  if (positionsChanged) editorCommentPositions.value = { ...positions };
10104
10241
  };
10105
10242
  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));
10243
+ const normalizedDecidedIds = new Set((Array.isArray(decidedIds) || decidedIds instanceof Set ? Array.from(decidedIds) : [decidedId]).map((id) => normalizeCommentId(id)).filter(isPresent));
10107
10244
  const normalizedDocumentId = normalizeCommentId(documentId);
10108
10245
  if (!normalizedDecidedIds.size || !normalizedDocumentId) return {
10109
10246
  removed: false,
@@ -10163,12 +10300,6 @@ var useCommentsStore = defineStore("comments", () => {
10163
10300
  };
10164
10301
  /**
10165
10302
  * 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
10303
  */
10173
10304
  const decideTrackedChangeFromSidebar = ({ superdoc, comment, decision }) => {
10174
10305
  const disabledReason = trackedChangeDecisionDisabledReason(superdoc);
@@ -10196,7 +10327,7 @@ var useCommentsStore = defineStore("comments", () => {
10196
10327
  return {
10197
10328
  ok: false,
10198
10329
  reason: "adapter-threw",
10199
- detail: err?.message ?? String(err)
10330
+ detail: errorDetail(err)
10200
10331
  };
10201
10332
  }
10202
10333
  })().then(async (outcome) => {
@@ -10249,9 +10380,11 @@ var useCommentsStore = defineStore("comments", () => {
10249
10380
  if (!activeEditor) return { ok: false };
10250
10381
  const id = comment.commentId ?? comment.importedId;
10251
10382
  if (!id) return { ok: false };
10252
- const story = comment.trackedChangeStory ?? void 0;
10383
+ const rawStory = comment.trackedChangeStory ?? void 0;
10384
+ const story = require_create_super_doc_ui.isStoryLocator(rawStory) ? rawStory : void 0;
10253
10385
  const documentApi = typeof activeEditor.doc === "object" ? activeEditor.doc : null;
10254
10386
  if (documentApi?.trackChanges?.decide) try {
10387
+ if (rawStory && !story) throw new TypeError("trackedChangeStory must be a valid StoryLocator");
10255
10388
  const target = story ? {
10256
10389
  id,
10257
10390
  story
@@ -10264,7 +10397,7 @@ var useCommentsStore = defineStore("comments", () => {
10264
10397
  success: Boolean(receipt?.success)
10265
10398
  }));
10266
10399
  } catch (error) {
10267
- if (story) return {
10400
+ if (rawStory) return {
10268
10401
  ok: false,
10269
10402
  error
10270
10403
  };
@@ -10311,9 +10444,9 @@ var useCommentsStore = defineStore("comments", () => {
10311
10444
  documentId: effectiveDocumentId
10312
10445
  })).removed
10313
10446
  };
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));
10447
+ const normalizedRemovedIds = new Set(Array.from(removedIds ?? []).map((id) => normalizeCommentId(id)).filter(isPresent));
10448
+ const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(isPresent))];
10449
+ const remappedSourceIds = new Set((Array.isArray(remappedPairs) ? remappedPairs : []).map((pair) => normalizeCommentId(pair?.from)).filter(isPresent));
10317
10450
  const locallyRemovedIds = new Set([...normalizedRemovedIds].filter((id) => !remappedSourceIds.has(id)));
10318
10451
  if (locallyRemovedIds.size > 0 && effectiveDocumentId != null) pruneDecidedTrackedChangeRow({
10319
10452
  superdoc,
@@ -10332,6 +10465,10 @@ var useCommentsStore = defineStore("comments", () => {
10332
10465
  const byIdentityAlias = /* @__PURE__ */ new Map();
10333
10466
  const aliasesByRow = /* @__PURE__ */ new Map();
10334
10467
  const indexedRows = /* @__PURE__ */ new Set();
10468
+ const indexedIdentities = [];
10469
+ let building = true;
10470
+ let reusable = true;
10471
+ let reused = false;
10335
10472
  let aliasLookups = 0;
10336
10473
  let candidateVisits = 0;
10337
10474
  let rowAliasBuilds = 0;
@@ -10355,6 +10492,7 @@ var useCommentsStore = defineStore("comments", () => {
10355
10492
  index.set(alias, bucket);
10356
10493
  };
10357
10494
  const add = (comment) => {
10495
+ if (!building) reusable = false;
10358
10496
  if (!comment?.trackedChange) return;
10359
10497
  if (documentId && !belongsToTrackedChangeSyncDocument(comment, documentId)) return;
10360
10498
  indexedRows.add(comment);
@@ -10366,19 +10504,49 @@ var useCommentsStore = defineStore("comments", () => {
10366
10504
  rowAliasBuilds += 1;
10367
10505
  aliasesByRow.set(comment, aliases);
10368
10506
  aliases.forEach((alias) => addAlias(byIdentityAlias, alias, comment));
10507
+ if (building) indexedIdentities.push({
10508
+ row: comment,
10509
+ aliases,
10510
+ canonicalId: normalizeCommentId(comment.trackedChangeCanonicalId)
10511
+ });
10369
10512
  };
10370
10513
  commentsList.value.forEach(add);
10514
+ building = false;
10371
10515
  return {
10516
+ inUse: false,
10372
10517
  add,
10373
10518
  aliasesFor,
10374
- work: () => ({
10375
- trackedRowsIndexed: indexedRows.size,
10376
- invalidatedIdMembershipChecks: 0,
10377
- incomingAliasLookups: aliasLookups,
10378
- candidateVisits,
10379
- rowAliasBuilds,
10380
- rowAliasCacheHits
10381
- }),
10519
+ isCurrent: () => {
10520
+ if (!reusable) return false;
10521
+ let index = 0;
10522
+ for (const row of commentsList.value) {
10523
+ if (!row?.trackedChange || documentId && !belongsToTrackedChangeSyncDocument(row, documentId)) continue;
10524
+ const previous = indexedIdentities[index++];
10525
+ if (!previous || previous.row !== row || previous.canonicalId !== normalizeCommentId(row.trackedChangeCanonicalId)) return false;
10526
+ const aliases = getCommentAliasIds(row);
10527
+ rowAliasBuilds += 1;
10528
+ if (aliases.length !== previous.aliases.length || aliases.some((alias, i) => alias !== previous.aliases[i])) return false;
10529
+ }
10530
+ reused = index === indexedIdentities.length;
10531
+ return reused;
10532
+ },
10533
+ work: () => {
10534
+ const result = {
10535
+ trackedRowsIndexed: indexedRows.size,
10536
+ invalidatedIdMembershipChecks: 0,
10537
+ incomingAliasLookups: aliasLookups,
10538
+ candidateVisits,
10539
+ rowAliasBuilds,
10540
+ rowAliasCacheHits,
10541
+ indexReused: Number(reused)
10542
+ };
10543
+ aliasLookups = 0;
10544
+ candidateVisits = 0;
10545
+ rowAliasBuilds = 0;
10546
+ rowAliasCacheHits = 0;
10547
+ reused = false;
10548
+ return result;
10549
+ },
10382
10550
  candidates({ changeId, importedId, anchorKey, canonicalId, positionAliases = [] } = {}) {
10383
10551
  const out = /* @__PURE__ */ new Set();
10384
10552
  const include = (value) => {
@@ -10401,7 +10569,30 @@ var useCommentsStore = defineStore("comments", () => {
10401
10569
  }
10402
10570
  };
10403
10571
  };
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", {
10572
+ const getTrackedChangeBatchIdentityIndex = (documentId) => {
10573
+ const cached = cachedTrackedChangeBatchIdentityIndex;
10574
+ if (cached?.documentId === documentId && !cached.index.inUse) {
10575
+ cached.index.inUse = true;
10576
+ let current = false;
10577
+ try {
10578
+ current = cached.index.isCurrent() && cachedTrackedChangeBatchIdentityIndex === cached;
10579
+ } finally {
10580
+ if (!current) {
10581
+ cached.index.inUse = false;
10582
+ if (cachedTrackedChangeBatchIdentityIndex === cached) cachedTrackedChangeBatchIdentityIndex = null;
10583
+ }
10584
+ }
10585
+ if (current) return cached.index;
10586
+ }
10587
+ const index = createTrackedChangeBatchIdentityIndex(documentId);
10588
+ index.inUse = true;
10589
+ cachedTrackedChangeBatchIdentityIndex = {
10590
+ documentId,
10591
+ index
10592
+ };
10593
+ return index;
10594
+ };
10595
+ 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
10596
  documentId: documentId ?? null,
10406
10597
  itemCount: Array.isArray(items) ? items.length : null,
10407
10598
  pruneStale,
@@ -10413,33 +10604,36 @@ var useCommentsStore = defineStore("comments", () => {
10413
10604
  const liveAnchorKeys = suppliedLiveAnchorKeys instanceof Set ? suppliedLiveAnchorKeys : /* @__PURE__ */ new Set();
10414
10605
  const liveIds = suppliedLiveIds instanceof Set ? suppliedLiveIds : /* @__PURE__ */ new Set();
10415
10606
  let appliedCount = 0;
10416
- const trackedChangeIdentityIndex = createTrackedChangeBatchIdentityIndex(effectiveDocumentId == null ? null : String(effectiveDocumentId));
10607
+ const trackedChangeIdentityIndex = getTrackedChangeBatchIdentityIndex(effectiveDocumentId == null ? null : String(effectiveDocumentId));
10417
10608
  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;
10609
+ let enteredBatch = false;
10422
10610
  try {
10611
+ const paramsList = Array.isArray(preparedParams) ? preparedParams : items.map((item) => adapter.mapV2TrackedChangeToCommentParams(item));
10612
+ const previousCoordinate = previousPreparedPresentation?.coordinate;
10613
+ const coordinate = preparedPresentation?.coordinate;
10614
+ 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;
10615
+ enteredBatch = true;
10423
10616
  for (const params of paramsList) {
10424
10617
  if (!params) continue;
10425
10618
  let retainUnchangedRow = false;
10619
+ const anchorParts = parseTrackedChangeAnchorKey(params.trackedChangeAnchorKey);
10620
+ const anchorStoryKey = anchorParts ? normalizeTrackedChangeStoryKey(anchorParts.storyKey) : void 0;
10426
10621
  if (preparedPresentation) {
10427
10622
  if (preparedPresentation !== committedReviewPresentations.value[String(effectiveDocumentId)]) continue;
10428
10623
  if (params.documentId != null && String(params.documentId) !== String(effectiveDocumentId)) continue;
10429
10624
  const id = normalizeCommentId(params.changeId);
10430
- const version = preparedPresentation.rowVersions?.[id];
10625
+ const version = preparedPresentation.rowVersions?.[String(id)] ?? NaN;
10431
10626
  if (!id || !Number.isSafeInteger(version) || version < 1) continue;
10432
10627
  const existing = [...trackedChangeIdentityIndex.candidates({ changeId: id })].filter((row) => String(row.fileId) === String(effectiveDocumentId) && String(row.commentId) === id);
10433
10628
  if (existing.length > 1) continue;
10434
10629
  const row = existing[0];
10435
10630
  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;
10631
+ const previousVersion = previousPreparedPresentation?.rowVersions?.[id] ?? NaN;
10632
+ 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
10633
  row.trackedChangeCanonicalId = params.trackedChangeCanonicalId;
10439
- row.updateIdentityValues?.({ trackedChangeCanonicalId: params.trackedChangeCanonicalId });
10440
10634
  trackedChangeIdentityIndex.add(row);
10441
10635
  }
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;
10636
+ 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
10637
  }
10444
10638
  if (params.event === "omit") {
10445
10639
  if (params.changeId != null) liveIds.add(String(params.changeId));
@@ -10453,12 +10647,15 @@ var useCommentsStore = defineStore("comments", () => {
10453
10647
  superdoc,
10454
10648
  params,
10455
10649
  broadcastChanges: false,
10456
- trackedChangeIdentityIndex
10650
+ trackedChangeIdentityIndex,
10651
+ preserveDraft
10457
10652
  });
10458
10653
  appliedCount += 1;
10459
10654
  }
10460
10655
  } finally {
10461
- endInteractionSpan(identitySpan, trackedChangeIdentityIndex.work());
10656
+ const work = trackedChangeIdentityIndex.work();
10657
+ trackedChangeIdentityIndex.inUse = false;
10658
+ if (enteredBatch) endInteractionSpan(identitySpan, work);
10462
10659
  }
10463
10660
  if (!effectiveDocumentId || !pruneStale) {
10464
10661
  if (effectiveDocumentId) compactDuplicateBodyTrackedChangeComments(String(effectiveDocumentId));
@@ -10480,22 +10677,18 @@ var useCommentsStore = defineStore("comments", () => {
10480
10677
  return commentsList.value.filter((comment) => {
10481
10678
  if (comment?.trackedChange !== true) return false;
10482
10679
  if (normalizedDocumentId == null) return true;
10483
- const rowDocumentId = comment?.documentId ?? comment?.fileId ?? null;
10680
+ const rowDocumentId = Reflect.get(comment, "documentId") ?? comment?.fileId ?? null;
10484
10681
  return rowDocumentId != null && String(rowDocumentId) === normalizedDocumentId;
10485
10682
  }).length;
10486
10683
  };
10487
10684
  /**
10488
10685
  * Triggered when the editor locations are updated
10489
10686
  * 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
10687
  */
10495
10688
  const handleEditorLocationsUpdate = (allCommentPositions, options = {}) => {
10496
10689
  if (allCommentPositions == null) return;
10497
10690
  const retainMissingTrackedChangeGeometry = options?.retainMissingTrackedChangeGeometry === true;
10498
- const retainedTrackedChangeIds = retainMissingTrackedChangeGeometry && options?.retainedTrackedChangeIds != null ? new Set(Array.from(options.retainedTrackedChangeIds, (id) => normalizeCommentId(id)).filter(Boolean)) : null;
10691
+ const retainedTrackedChangeIds = retainMissingTrackedChangeGeometry && options?.retainedTrackedChangeIds != null ? new Set(Array.from(options.retainedTrackedChangeIds, (id) => normalizeCommentId(id)).filter(isPresent)) : null;
10499
10692
  const previousPositions = editorCommentPositions.value || {};
10500
10693
  const normalizedPositions = {};
10501
10694
  Object.entries(allCommentPositions).forEach(([key, entry]) => {
@@ -10522,7 +10715,7 @@ var useCommentsStore = defineStore("comments", () => {
10522
10715
  return null;
10523
10716
  };
10524
10717
  const writeTrackedChangeAliases = (entry, comment, extraKeys = []) => {
10525
- const keys = [...aliasesFor(comment), ...extraKeys.map((id) => normalizeCommentId(id)).filter(Boolean)];
10718
+ const keys = [...aliasesFor(comment), ...extraKeys.map((id) => normalizeCommentId(id)).filter(isPresent)];
10526
10719
  for (const key of new Set(keys)) if (normalizedPositions[key] === void 0) normalizedPositions[key] = entry;
10527
10720
  };
10528
10721
  commentsList.value.forEach((comment) => {
@@ -11833,8 +12026,8 @@ var _hoisted_11$3 = {
11833
12026
  key: 0,
11834
12027
  class: "card-section comment-body"
11835
12028
  };
11836
- var _hoisted_12$2 = ["data-track-change-semantic-color-key"];
11837
- var _hoisted_13$2 = { key: 0 };
12029
+ var _hoisted_12$3 = ["data-track-change-semantic-color-key"];
12030
+ var _hoisted_13$3 = { key: 0 };
11838
12031
  var _hoisted_14$2 = { class: "change-type tracked-change-label" };
11839
12032
  var _hoisted_15$2 = {
11840
12033
  key: 0,
@@ -12617,6 +12810,16 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
12617
12810
  }
12618
12811
  handleResolve();
12619
12812
  };
12813
+ const ownsTrackedChangeDecision = (superdoc, editor) => editor !== null && proxy.$superdoc === superdoc && superdoc.activeEditor === editor;
12814
+ const restoreTrackedChangeDecisionFocus = (superdoc, editor) => {
12815
+ require__plugin_vue_export_helper.nextTick(() => {
12816
+ if (!ownsTrackedChangeDecision(superdoc, editor)) return;
12817
+ commentsStore.lastUpdate = /* @__PURE__ */ new Date();
12818
+ activeComment.value = null;
12819
+ commentsStore.setActiveComment(superdoc, activeComment.value);
12820
+ if (ownsTrackedChangeDecision(superdoc, editor)) superdoc.focus?.({ preventScroll: true });
12821
+ });
12822
+ };
12620
12823
  const handleReject = async () => {
12621
12824
  if (props.comment.trackedChange) {
12622
12825
  if (trackedChangeDecisionsAreDisabled()) return trackedChangeDecisionDisabledOutcome();
@@ -12624,23 +12827,21 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
12624
12827
  const customHandler = proxy.$superdoc.config.onTrackedChangeBubbleReject;
12625
12828
  if (props.comment.trackedChange) {
12626
12829
  if (isV2Mode.value) {
12830
+ const superdoc = proxy.$superdoc;
12831
+ const editor = superdoc.activeEditor;
12627
12832
  const outcome = await commentsStore.decideTrackedChangeFromSidebar({
12628
- superdoc: proxy.$superdoc,
12833
+ superdoc,
12629
12834
  comment: props.comment,
12630
12835
  decision: "reject"
12631
12836
  });
12837
+ if (!ownsTrackedChangeDecision(superdoc, editor)) return;
12632
12838
  if (!outcome?.ok || outcome.success === false) {
12633
12839
  reportTrackedChangeDecisionFailure("reject", outcome);
12634
12840
  return;
12635
12841
  }
12636
12842
  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
- });
12843
+ if (typeof customHandler === "function") customHandler(props.comment, editor);
12844
+ restoreTrackedChangeDecisionFocus(superdoc, editor);
12644
12845
  return;
12645
12846
  }
12646
12847
  let decisionApplied = true;
@@ -12686,23 +12887,21 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
12686
12887
  }
12687
12888
  const customHandler = proxy.$superdoc.config.onTrackedChangeBubbleAccept;
12688
12889
  if (props.comment.trackedChange && isV2Mode.value) {
12890
+ const superdoc = proxy.$superdoc;
12891
+ const editor = superdoc.activeEditor;
12689
12892
  const outcome = await commentsStore.decideTrackedChangeFromSidebar({
12690
- superdoc: proxy.$superdoc,
12893
+ superdoc,
12691
12894
  comment: props.comment,
12692
12895
  decision: "accept"
12693
12896
  });
12897
+ if (!ownsTrackedChangeDecision(superdoc, editor)) return;
12694
12898
  if (!outcome?.ok || outcome.success === false) {
12695
12899
  reportTrackedChangeDecisionFailure("accept", outcome);
12696
12900
  return;
12697
12901
  }
12698
12902
  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
- });
12903
+ if (typeof customHandler === "function") customHandler(props.comment, editor);
12904
+ restoreTrackedChangeDecisionFocus(superdoc, editor);
12706
12905
  return;
12707
12906
  }
12708
12907
  let v1TrackedChangeDecisionApplied = true;
@@ -13006,7 +13205,7 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
13006
13205
  "data-track-change-semantic-color-key": comment.semanticColorKey ?? void 0,
13007
13206
  ref_for: true,
13008
13207
  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) => {
13208
+ }, [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
13209
  return require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
13011
13210
  key: lineIndex,
13012
13211
  class: "tracked-change-detail-line"
@@ -13031,7 +13230,7 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
13031
13230
  style: require__plugin_vue_export_helper.normalizeStyle(trackedChangeImagePreviewStyle(comment.trackedChangeImagePreview)),
13032
13231
  loading: "lazy",
13033
13232
  decoding: "async"
13034
- }, null, 12, _hoisted_38)], 8, _hoisted_37)) : require__plugin_vue_export_helper.createCommentVNode("", true)], 10, _hoisted_12$2),
13233
+ }, null, 12, _hoisted_38)], 8, _hoisted_37)) : require__plugin_vue_export_helper.createCommentVNode("", true)], 10, _hoisted_12$3),
13035
13234
  shouldTruncate.value && isTextOverflowing.value && index === 0 ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
13036
13235
  key: 0,
13037
13236
  class: "show-more-toggle",
@@ -13128,7 +13327,7 @@ var CommentDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
13128
13327
  });
13129
13328
  //#endregion
13130
13329
  //#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"]]);
13330
+ 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-04c17a1a"]]);
13132
13331
  //#endregion
13133
13332
  //#region src/components/CommentsLayer/commentsList/ReviewDirectoryListItem.vue?vue&type=script&setup=true&lang.ts
13134
13333
  var _hoisted_1$25 = [
@@ -13897,124 +14096,6 @@ var composeAuthorColorResolver = (config) => {
13897
14096
  };
13898
14097
  };
13899
14098
  //#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
14099
  //#region src/core/superdoc-ui-owner.ts
14019
14100
  var ownerIds = /* @__PURE__ */ new WeakMap();
14020
14101
  function getSuperDocUiOwnerId(superdoc) {
@@ -14125,18 +14206,42 @@ var FloatingComments_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
14125
14206
  const { activeComment, activeFloatingCommentInstanceId, editorCommentPositions, pendingComment, editingCommentId, instantSidebarAlignmentTargetY, instantSidebarAlignmentThreadId, instantSidebarAlignmentInstanceId } = storeToRefs(piniaCommentsStore);
14126
14207
  const { activeZoom } = storeToRefs(superdocStore);
14127
14208
  const committedPresentation = require__plugin_vue_export_helper.computed(() => commentsStore.committedReviewPresentations?.[String(props.currentDocument?.id)] ?? null);
14209
+ const committedInstanceCache = /* @__PURE__ */ new Map();
14210
+ let committedInstanceCacheOwner = "";
14211
+ const sameReviewInstance = (cached, next) => {
14212
+ const before = Object.entries(cached).filter(([key]) => key !== "comment" && key !== "rowVersion");
14213
+ const after = Object.entries(next);
14214
+ if (before.length !== after.length) return false;
14215
+ const previous = new Map(before);
14216
+ return after.every(([key, value]) => previous.has(key) && (key === "positionEntry" ? JSON.stringify(previous.get(key)) === JSON.stringify(value) : Object.is(previous.get(key), value)));
14217
+ };
14128
14218
  const committedInstances = require__plugin_vue_export_helper.computed(() => {
14129
14219
  const presentation = committedPresentation.value;
14130
14220
  if (!presentation) return null;
14131
14221
  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) => {
14222
+ const owner = `${presentation.epoch}\u0000${presentation.coordinate?.mountId ?? ""}`;
14223
+ if (owner !== committedInstanceCacheOwner) {
14224
+ committedInstanceCache.clear();
14225
+ committedInstanceCacheOwner = owner;
14226
+ }
14227
+ const live = /* @__PURE__ */ new Set();
14228
+ const instances = presentation.instances.flatMap((instance) => {
14133
14229
  const comment = instance.rowId === "pending" ? pendingComment.value : rows.get(instance.rowId);
14134
- return comment ? [{
14230
+ if (!comment) return [];
14231
+ const rowVersion = presentation.rowVersions[instance.rowId] ?? 0;
14232
+ live.add(instance.id);
14233
+ const cached = committedInstanceCache.get(instance.id);
14234
+ if (cached && cached.comment === comment && cached.rowVersion === rowVersion && sameReviewInstance(cached, instance)) return [cached];
14235
+ const merged = {
14135
14236
  ...instance,
14136
14237
  comment,
14137
- rowVersion: presentation.rowVersions[instance.rowId] ?? 0
14138
- }] : [];
14238
+ rowVersion
14239
+ };
14240
+ committedInstanceCache.set(instance.id, merged);
14241
+ return [merged];
14139
14242
  });
14243
+ for (const id of committedInstanceCache.keys()) if (!live.has(id)) committedInstanceCache.delete(id);
14244
+ return instances;
14140
14245
  });
14141
14246
  const floatingCommentInstances = require__plugin_vue_export_helper.computed(() => {
14142
14247
  if (committedInstances.value) return committedInstances.value.filter((instance) => instance.rowId !== "pending");
@@ -14552,6 +14657,18 @@ var FloatingComments_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
14552
14657
  scheduleDeferredRemeasure(() => allPositions.value.map((pos) => pos.id));
14553
14658
  });
14554
14659
  };
14660
+ const resizeHandlers = /* @__PURE__ */ new Map();
14661
+ const resizeHandlerFor = (id) => {
14662
+ let handler = resizeHandlers.get(id);
14663
+ if (!handler) {
14664
+ handler = () => {
14665
+ const position = allPositions.value.find((candidate) => candidate.id === id);
14666
+ if (position) handleResize(position);
14667
+ };
14668
+ resizeHandlers.set(id, handler);
14669
+ }
14670
+ return handler;
14671
+ };
14555
14672
  const setInstantLayoutTransitionsDisabled = (disabled) => {
14556
14673
  if (!disabled && directDecisionContinuityTargetId.value != null) return;
14557
14674
  disableInstantLayoutTransitions.value = disabled;
@@ -14956,6 +15073,7 @@ var FloatingComments_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
14956
15073
  visibilityChanged = true;
14957
15074
  }
14958
15075
  if (visibilityChanged) visibleIds.value = newVisible;
15076
+ for (const id of resizeHandlers.keys()) if (!currentIds.has(id)) resizeHandlers.delete(id);
14959
15077
  for (const id of prevPositionIds) if (!currentIds.has(id)) delete _heightsCache[id];
14960
15078
  const setChanged = prevPositionIds.size !== currentIds.size || [...prevPositionIds].some((id) => !currentIds.has(id));
14961
15079
  const orderChanged = renderedPositionOrder.length !== positionOrder.length || renderedPositionOrder.some((id, index) => id !== positionOrder[index]);
@@ -15048,12 +15166,13 @@ var FloatingComments_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
15048
15166
  style: require__plugin_vue_export_helper.normalizeStyle({
15049
15167
  top: (pos.isOffscreenReviewCard && !committedPresentation.value ? pos.top - sidebarOffsetY.value : pos.top) + "px",
15050
15168
  height: pos.height + "px",
15051
- transition: committedPresentation.value || disableInstantLayoutTransitions.value ? "none" : void 0
15169
+ transition: committedPresentation.value || disableInstantLayoutTransitions.value ? "none" : void 0,
15170
+ visibility: pos.instanceRef?.provisional ? "hidden" : void 0
15052
15171
  })
15053
15172
  }, [shouldRenderDialog(pos) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(CommentDialog_default, {
15054
15173
  key: pos.id + commentsRenderKey.value,
15055
15174
  onReady: handleDialog,
15056
- onResize: ($event) => handleResize(pos),
15175
+ onResize: resizeHandlerFor(pos.id),
15057
15176
  class: "floating-comment",
15058
15177
  parent: __props.parent,
15059
15178
  comment: pos.commentRef,
@@ -15076,7 +15195,7 @@ var FloatingComments_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
15076
15195
  });
15077
15196
  //#endregion
15078
15197
  //#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"]]);
15198
+ 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
15199
  //#endregion
15081
15200
  //#region src/components/CommentsLayer/PdfCommentsLayer.vue?vue&type=script&setup=true&lang.ts
15082
15201
  var _hoisted_1$22 = {
@@ -15210,7 +15329,7 @@ var PdfCommentsLayer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
15210
15329
  });
15211
15330
  //#endregion
15212
15331
  //#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"]]);
15332
+ 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
15333
  //#endregion
15215
15334
  //#region src/components/Whiteboard/use-whiteboard.ts
15216
15335
  var useWhiteboard = ({ proxy, layers, documents, modules }) => {
@@ -15806,6 +15925,21 @@ function useCommentSmallScreen({ commentsModuleConfig, superdocRoot, layers }) {
15806
15925
  };
15807
15926
  }
15808
15927
  //#endregion
15928
+ //#region src/composables/use-comment-sidebar.ts
15929
+ function useCommentSidebar({ documentKey, enabled, hasContent }) {
15930
+ const reserved = require__plugin_vue_export_helper.ref(false);
15931
+ require__plugin_vue_export_helper.watch(documentKey, () => {
15932
+ reserved.value = false;
15933
+ }, { flush: "sync" });
15934
+ require__plugin_vue_export_helper.watch(() => enabled.value && hasContent.value, (visible) => {
15935
+ if (visible) reserved.value = true;
15936
+ }, {
15937
+ immediate: true,
15938
+ flush: "sync"
15939
+ });
15940
+ return require__plugin_vue_export_helper.computed(() => enabled.value && (hasContent.value || reserved.value));
15941
+ }
15942
+ //#endregion
15809
15943
  //#region src/composables/use-compact-comment-popover.ts
15810
15944
  var POPOVER_WIDTH_PX = 320;
15811
15945
  var SAFE_MARGIN_PX = 12;
@@ -16184,6 +16318,116 @@ function resolveV2ReviewTargetCommentId(target, getComment) {
16184
16318
  return commentId != null ? String(commentId) : null;
16185
16319
  }
16186
16320
  //#endregion
16321
+ //#region src/helpers/v2-review-geometry-delivery.ts
16322
+ function createReviewGeometryDelivery(deliver) {
16323
+ const packets = /* @__PURE__ */ new Map();
16324
+ let stageOwners = /* @__PURE__ */ new WeakMap();
16325
+ let active = null;
16326
+ const documentOf = (current) => current.documentId != null ? String(current.documentId) : current.mountContainer?.dataset.superdocV2DocumentId ?? null;
16327
+ const matches = (documentId, current) => current.mountContainer?.dataset.superdocV2DocumentId === documentId && (current.documentId == null || String(current.documentId) === documentId);
16328
+ const matchesPaint = (packet, current) => typeof packet.payload.paintEpoch !== "number" || packet.payload.paintEpoch === current.epoch;
16329
+ const observe = (current) => {
16330
+ const stage = current.mountContainer;
16331
+ const documentId = documentOf(current);
16332
+ if (!stage || !documentId || !matches(documentId, current)) return;
16333
+ const previousOwner = stageOwners.get(stage);
16334
+ if (previousOwner && previousOwner !== documentId) packets.delete(previousOwner);
16335
+ stageOwners.set(stage, documentId);
16336
+ const packet = packets.get(documentId);
16337
+ if (packet?.stage && packet.stage !== stage) packets.delete(documentId);
16338
+ else if (packet && (!active || active.stage !== stage || active.documentId !== documentId || active.epoch !== current.epoch)) packet.delivered = false;
16339
+ active = {
16340
+ documentId,
16341
+ stage,
16342
+ epoch: current.epoch
16343
+ };
16344
+ };
16345
+ const dispatch = (documentId, packet, current, replay = false) => {
16346
+ if (!current.mountContainer || !matches(documentId, current) || !matchesPaint(packet, current)) return false;
16347
+ if (packet.stage && packet.stage !== current.mountContainer || packet.delivering || packet.delivered && !replay) return false;
16348
+ const wasDelivered = packet.delivered;
16349
+ const previousStage = packet.stage;
16350
+ packet.stage = current.mountContainer;
16351
+ packet.delivered = true;
16352
+ packet.delivering = true;
16353
+ try {
16354
+ deliver(current, packet.payload);
16355
+ } catch (error) {
16356
+ packet.delivered = wasDelivered;
16357
+ packet.stage = previousStage;
16358
+ throw error;
16359
+ } finally {
16360
+ packet.delivering = false;
16361
+ }
16362
+ return true;
16363
+ };
16364
+ const notify = (documentId, payload, current, ready = true) => {
16365
+ if (!payload.reviewGeometry || !Array.isArray(payload.pageIndices)) return false;
16366
+ let key;
16367
+ try {
16368
+ key = JSON.stringify([
16369
+ documentId,
16370
+ payload.documentEpoch,
16371
+ payload.paintEpoch,
16372
+ payload.commitSequence,
16373
+ payload.pageIndices,
16374
+ payload.zoomScale,
16375
+ payload.reviewGeometry
16376
+ ]);
16377
+ } catch {
16378
+ return false;
16379
+ }
16380
+ const isCurrent = current != null && matches(documentId, current);
16381
+ if (isCurrent) observe(current);
16382
+ const previous = packets.get(documentId);
16383
+ if (previous && previous.payload.documentEpoch === payload.documentEpoch && typeof previous.payload.paintEpoch === "number" && typeof payload.paintEpoch === "number" && payload.paintEpoch < previous.payload.paintEpoch) return false;
16384
+ if (previous && previous.payload.documentEpoch === payload.documentEpoch && typeof previous.payload.commitSequence === "number" && typeof payload.commitSequence === "number" && payload.commitSequence < previous.payload.commitSequence) return false;
16385
+ if (previous && previous.payload.documentEpoch === payload.documentEpoch && typeof previous.payload.generation === "number" && typeof payload.generation === "number" && payload.generation < previous.payload.generation) return false;
16386
+ if (isCurrent && typeof payload.paintEpoch === "number" && typeof current.epoch === "number" && payload.paintEpoch < current.epoch) return false;
16387
+ if (previous?.key === key) {
16388
+ previous.payload = payload;
16389
+ return isCurrent && ready ? dispatch(documentId, previous, current) : false;
16390
+ }
16391
+ const packet = {
16392
+ payload,
16393
+ key,
16394
+ stage: isCurrent ? current.mountContainer : previous && previous.payload.documentEpoch === payload.documentEpoch ? previous.stage : null,
16395
+ delivered: false,
16396
+ delivering: false
16397
+ };
16398
+ packets.set(documentId, packet);
16399
+ if (!isCurrent || !ready) return false;
16400
+ try {
16401
+ return dispatch(documentId, packet, current);
16402
+ } catch (error) {
16403
+ if (previous) packets.set(documentId, previous);
16404
+ else packets.delete(documentId);
16405
+ throw error;
16406
+ }
16407
+ };
16408
+ return {
16409
+ notify,
16410
+ observe,
16411
+ flush(current) {
16412
+ observe(current);
16413
+ const documentId = documentOf(current);
16414
+ const packet = documentId == null ? null : packets.get(documentId);
16415
+ return documentId && packet ? dispatch(documentId, packet, current) : false;
16416
+ },
16417
+ replay(current) {
16418
+ observe(current);
16419
+ const documentId = documentOf(current);
16420
+ const packet = documentId == null ? null : packets.get(documentId);
16421
+ return documentId && packet ? dispatch(documentId, packet, current, true) : false;
16422
+ },
16423
+ reset() {
16424
+ packets.clear();
16425
+ stageOwners = /* @__PURE__ */ new WeakMap();
16426
+ active = null;
16427
+ }
16428
+ };
16429
+ }
16430
+ //#endregion
16187
16431
  //#region src/helpers/v2-mutation-exception.ts
16188
16432
  var FAILURE_SOURCES = /* @__PURE__ */ new Set(["shell", "receipt"]);
16189
16433
  var DIAGNOSTIC_CODE = /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/;
@@ -16553,7 +16797,7 @@ function usePasswordPrompt({ getSurfaceManager, getPasswordPromptConfig, onUnhan
16553
16797
  })
16554
16798
  });
16555
16799
  } else {
16556
- const mod = await Promise.resolve().then(() => require("./chunks/PasswordPromptSurface-x8GNmMJT.cjs"));
16800
+ const mod = await Promise.resolve().then(() => require("./chunks/PasswordPromptSurface-VFPnQOML.cjs"));
16557
16801
  const surfaceId = `password-prompt-${doc.id}`;
16558
16802
  handle = manager.open({
16559
16803
  id: surfaceId,
@@ -17213,7 +17457,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
17213
17457
  } else {
17214
17458
  let FindReplaceSurface;
17215
17459
  try {
17216
- FindReplaceSurface = (await Promise.resolve().then(() => require("./chunks/FindReplaceSurface-fVHxPXtn.cjs"))).default;
17460
+ FindReplaceSurface = (await Promise.resolve().then(() => require("./chunks/FindReplaceSurface-DWXgWgg7.cjs"))).default;
17217
17461
  } catch {
17218
17462
  opening = false;
17219
17463
  return;
@@ -17587,11 +17831,11 @@ var _hoisted_8$3 = { class: "input-row url-input-row" };
17587
17831
  var _hoisted_9$2 = ["innerHTML"];
17588
17832
  var _hoisted_10$2 = ["readonly", "onKeydown"];
17589
17833
  var _hoisted_11$2 = ["innerHTML"];
17590
- var _hoisted_12$1 = {
17834
+ var _hoisted_12$2 = {
17591
17835
  key: 0,
17592
17836
  class: "input-row link-buttons"
17593
17837
  };
17594
- var _hoisted_13$1 = ["disabled"];
17838
+ var _hoisted_13$2 = ["disabled"];
17595
17839
  var _hoisted_14$1 = ["innerHTML"];
17596
17840
  var _hoisted_15$1 = ["disabled"];
17597
17841
  var _hoisted_16$1 = {
@@ -17864,7 +18108,7 @@ var LinkInput_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ requir
17864
18108
  "data-item": "btn-link-open"
17865
18109
  }, null, 10, _hoisted_11$2)
17866
18110
  ]),
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", {
18111
+ !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
18112
  key: 0,
17869
18113
  class: "remove-btn",
17870
18114
  onClick: handleRemove,
@@ -17873,7 +18117,7 @@ var LinkInput_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ requir
17873
18117
  }, [require__plugin_vue_export_helper.createElementVNode("div", {
17874
18118
  class: "remove-btn__icon",
17875
18119
  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", {
18120
+ }, 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
18121
  class: require__plugin_vue_export_helper.normalizeClass(["sd-submit-btn", { "disable-btn": isDisabled.value }]),
17878
18122
  onClick: handleSubmit,
17879
18123
  disabled: isSubmitting.value,
@@ -18786,6 +19030,7 @@ function createV2EditorRuntimeAdapter(options) {
18786
19030
  const { id, documentId, root, host, getLegacyEditorProjection, onUnregister } = options;
18787
19031
  let snapshot = host.getSnapshot();
18788
19032
  let didDispose = false;
19033
+ let detached = false;
18789
19034
  let unregistered = false;
18790
19035
  let mountedFocusHandle = null;
18791
19036
  let tokenRevision = 0;
@@ -19082,7 +19327,17 @@ function createV2EditorRuntimeAdapter(options) {
19082
19327
  };
19083
19328
  }
19084
19329
  }
19330
+ function releaseHostSubscriptions() {
19331
+ hostUnsubscribe?.();
19332
+ hostUnsubscribe = null;
19333
+ pageMetricsUnsubscribe?.();
19334
+ pageMetricsUnsubscribe = null;
19335
+ selectionUnsubscribe?.();
19336
+ selectionUnsubscribe = null;
19337
+ activeSelectionController = null;
19338
+ }
19085
19339
  function handleHostSnapshot(next) {
19340
+ if (detached || didDispose) return;
19086
19341
  snapshot = next;
19087
19342
  invalidatePositionTokens();
19088
19343
  syncSelectionSubscription();
@@ -19096,10 +19351,7 @@ function createV2EditorRuntimeAdapter(options) {
19096
19351
  });
19097
19352
  if (next.state === "disposed" && !didDispose) {
19098
19353
  didDispose = true;
19099
- hostUnsubscribe?.();
19100
- pageMetricsUnsubscribe?.();
19101
- selectionUnsubscribe?.();
19102
- selectionUnsubscribe = null;
19354
+ releaseHostSubscriptions();
19103
19355
  emit({ type: "disposed" });
19104
19356
  listeners.clear();
19105
19357
  if (!unregistered) {
@@ -19246,10 +19498,7 @@ function createV2EditorRuntimeAdapter(options) {
19246
19498
  await host.dispose();
19247
19499
  if (!didDispose) {
19248
19500
  didDispose = true;
19249
- hostUnsubscribe?.();
19250
- pageMetricsUnsubscribe?.();
19251
- selectionUnsubscribe?.();
19252
- selectionUnsubscribe = null;
19501
+ releaseHostSubscriptions();
19253
19502
  emit({ type: "disposed" });
19254
19503
  listeners.clear();
19255
19504
  if (!unregistered) {
@@ -19328,8 +19577,17 @@ function createV2EditorRuntimeAdapter(options) {
19328
19577
  }
19329
19578
  },
19330
19579
  attachMountHandle(handle) {
19580
+ if (detached || didDispose) return;
19331
19581
  mountedFocusHandle = handle;
19332
19582
  syncSelectionSubscription();
19583
+ },
19584
+ detach() {
19585
+ if (detached) return;
19586
+ detached = true;
19587
+ unregistered = true;
19588
+ releaseHostSubscriptions();
19589
+ mountedFocusHandle = null;
19590
+ listeners.clear();
19333
19591
  }
19334
19592
  };
19335
19593
  }
@@ -20498,7 +20756,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
20498
20756
  __name: "SuperDoc",
20499
20757
  emits: ["selection-update"],
20500
20758
  setup(__props, { emit: __emit }) {
20501
- const PdfViewer = require__plugin_vue_export_helper.defineAsyncComponent(() => Promise.resolve().then(() => require("./chunks/PdfViewer-CkrAHpC-.cjs")));
20759
+ const PdfViewer = require__plugin_vue_export_helper.defineAsyncComponent(() => Promise.resolve().then(() => require("./chunks/PdfViewer-DYTbPVUr.cjs")));
20502
20760
  const getDocumentLoadPassword = (doc) => doc.password ?? proxy.$superdoc.config.password;
20503
20761
  const resolveDocumentV2Collaboration = (doc) => {
20504
20762
  const rawConfig = doc?.v2Collaboration;
@@ -20583,7 +20841,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
20583
20841
  if (commentsConfig.highlightHoverColor) vars["--sd-comments-highlight-hover"] = commentsConfig.highlightHoverColor;
20584
20842
  const activeTrackChangeColors = {
20585
20843
  ...commentsConfig.trackChangeHighlightColors || {},
20586
- ...commentsConfig.trackChangeActiveHighlightColors || {}
20844
+ ...commentsConfig.trackChangeActiveHighlightColors
20587
20845
  };
20588
20846
  if (activeTrackChangeColors.insertBorder) vars["--sd-tracked-changes-insert-border"] = activeTrackChangeColors.insertBorder;
20589
20847
  if (activeTrackChangeColors.insertBackground) vars["--sd-tracked-changes-insert-background"] = activeTrackChangeColors.insertBackground;
@@ -20612,6 +20870,8 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
20612
20870
  const v2ReviewSidebarUnlocked = require__plugin_vue_export_helper.ref(false);
20613
20871
  const v2GeometryEpoch = require__plugin_vue_export_helper.ref(null);
20614
20872
  let v2GeometryRafHandle = 0;
20873
+ let v2GeometryReplayRafHandle = 0;
20874
+ let v2GeometryReplayGeneration = 0;
20615
20875
  let v2TrackedChangeRestampRetainPaints = 0;
20616
20876
  let v2TrackedChangeRestampRetentionReason = null;
20617
20877
  const activeEditorRef = require__plugin_vue_export_helper.computed(() => proxy.$superdoc.activeEditor);
@@ -20795,6 +21055,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
20795
21055
  if (document && provider?.sendStateless) document.provider = null;
20796
21056
  const entry = v2Runtimes.get(documentId);
20797
21057
  if (!entry) return;
21058
+ entry.adapter.detach();
20798
21059
  proxy.$superdoc["unregisterEditorRuntime"](entry.runtimeId);
20799
21060
  v2Runtimes.delete(documentId);
20800
21061
  const hostRoot = subDocumentRoots.get(documentId);
@@ -20948,7 +21209,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
20948
21209
  if (loadingDocument?.loadScope && !loadingDocument.loadScope.finish()) return;
20949
21210
  clearV2EditorFailure(payload.documentId ?? null);
20950
21211
  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;
21212
+ 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
21213
  documentOpenTelemetry?.trackDocumentOpen(documentOpenToken ?? null, documentId ?? null);
20953
21214
  const saveV2Bytes = createV2SaveQueue(async (saveOptions = {}) => {
20954
21215
  if (!host || typeof host.save !== "function") throw new Error("v2-editor: save unavailable");
@@ -21044,21 +21305,21 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21044
21305
  };
21045
21306
  };
21046
21307
  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);
21308
+ const confirmedV2SelectionTarget = async () => {
21309
+ const selectionTargets = host?.getHandles?.()?.editing?.selectionTargets;
21310
+ if (typeof selectionTargets?.confirmedSelectionTarget !== "function") return createV2AuthoringFailure("selection-confirmation-unavailable");
21311
+ try {
21312
+ const result = await selectionTargets.confirmedSelectionTarget();
21313
+ if (!result?.ok || !result.target) return createV2AuthoringFailure(result?.reason ?? "selection-unconfirmed");
21314
+ return {
21315
+ ok: true,
21316
+ target: result.target,
21317
+ mode: result.mode,
21318
+ story: result.story
21319
+ };
21320
+ } catch (error) {
21321
+ return createV2AuthoringFailure("selection-unconfirmed", error instanceof Error ? error.message : String(error));
21054
21322
  }
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
21323
  };
21063
21324
  const failureFromV2Receipt = (receipt, fallbackReason) => {
21064
21325
  const failure = receipt?.failure ?? receipt;
@@ -21132,7 +21393,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21132
21393
  },
21133
21394
  serializeSelectionToClipboard: async ({ includeHtml = true } = {}) => {
21134
21395
  if (!documentApi?.clipboard || typeof documentApi.clipboard.serializeSelection !== "function") return createV2AuthoringFailure("clipboard-serialize-unavailable", "activeEditor.doc.clipboard.serializeSelection is not available");
21135
- const selected = readV2CurrentSelectionTarget();
21396
+ const selected = await confirmedV2SelectionTarget();
21136
21397
  if (!selected.ok) return selected;
21137
21398
  let serialized;
21138
21399
  try {
@@ -21158,7 +21419,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21158
21419
  let pasteTarget = target;
21159
21420
  let selectionMode = null;
21160
21421
  if (!pasteTarget) {
21161
- const selected = readV2CurrentSelectionTarget();
21422
+ const selected = await confirmedV2SelectionTarget();
21162
21423
  if (!selected.ok) return selected;
21163
21424
  pasteTarget = selected.target;
21164
21425
  selectionMode = selected.mode;
@@ -21219,6 +21480,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21219
21480
  v2Comments: commentsAdapter ?? null,
21220
21481
  v2TrackedChanges: trackedChangesAdapter ?? null,
21221
21482
  contextMenu: contextMenu ?? null,
21483
+ viewportQuery: viewportQuery ?? null,
21222
21484
  pageMetrics: pageMetrics ?? null,
21223
21485
  pageLayout: pageLayout ?? null,
21224
21486
  pageFurniture: pageFurniture ?? null,
@@ -21560,10 +21822,11 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21560
21822
  const publishV2PendingPositionEntry = (entry) => {
21561
21823
  if (!entry) return;
21562
21824
  handleEditorLocationsUpdate({
21563
- ...editorCommentPositions.value ?? {},
21825
+ ...editorCommentPositions.value,
21564
21826
  pending: entry
21565
21827
  });
21566
21828
  };
21829
+ let scheduleV2GeometryReplay = () => {};
21567
21830
  const v2GeometryPublisher = resolvedEditorIntegration.createGeometryPublisher({
21568
21831
  getLayersContainer: () => layers.value ?? null,
21569
21832
  isCommentsEnabled: () => Boolean(commentsModuleConfig.value) && shouldRenderCommentsInViewing.value,
@@ -21579,7 +21842,8 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21579
21842
  v2ReviewSidebarUnlocked.value = true;
21580
21843
  v2GeometryEpoch.value = v2GeometryPublisher.getLastEpoch();
21581
21844
  }
21582
- }
21845
+ },
21846
+ onCommittedPagePaint: ({ payload }) => scheduleV2GeometryReplay(payload)
21583
21847
  });
21584
21848
  const armV2TrackedChangeRestampGeometryRetention = (reason = "tracked-change-restamp") => {
21585
21849
  v2TrackedChangeRestampRetainPaints = Math.max(v2TrackedChangeRestampRetainPaints, 3);
@@ -21599,20 +21863,53 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21599
21863
  v2TrackedChangeRestampRetentionReason = null;
21600
21864
  };
21601
21865
  const resolveV2GeometryPublishOptions = (options) => typeof options === "function" ? options() : options;
21866
+ const isV2GeometryReady = (current) => {
21867
+ if (!current?.mountContainer || !commentsModuleConfig.value || !shouldRenderCommentsInViewing.value) return false;
21868
+ const mount = current.mount;
21869
+ let liveEpoch;
21870
+ try {
21871
+ liveEpoch = mount?.getRenderEpoch?.();
21872
+ } catch {
21873
+ return false;
21874
+ }
21875
+ return v2GeometryPublisher.getLastPayload() === current && v2GeometryPublisher.getLastEpoch() != null && v2GeometryPublisher.getLastEpoch() === current.epoch && (liveEpoch == null || liveEpoch === current.epoch);
21876
+ };
21602
21877
  const scheduleV2GeometryPublish = (payload, options = void 0) => {
21603
21878
  v2GeometryRender.value = payload;
21604
21879
  if (v2GeometryRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryRafHandle);
21880
+ const publish = () => {
21881
+ const current = v2GeometryRender.value;
21882
+ Promise.resolve(v2GeometryPublisher.publish(current, resolveV2GeometryPublishOptions(options))).then(() => {
21883
+ if (current === v2GeometryRender.value && isV2GeometryReady(current)) v2ReviewGeometryDelivery.flush(current);
21884
+ });
21885
+ };
21605
21886
  if (typeof requestAnimationFrame !== "function") {
21606
- v2GeometryPublisher.publish(v2GeometryRender.value, resolveV2GeometryPublishOptions(options));
21887
+ publish();
21607
21888
  return;
21608
21889
  }
21609
21890
  v2GeometryRafHandle = requestAnimationFrame(() => {
21610
21891
  v2GeometryRafHandle = 0;
21611
- v2GeometryPublisher.publish(v2GeometryRender.value, resolveV2GeometryPublishOptions(options));
21892
+ publish();
21612
21893
  });
21613
21894
  };
21895
+ const v2ReviewGeometryDelivery = createReviewGeometryDelivery((current, packet) => {
21896
+ v2GeometryPublisher.noteCommittedPageWindow?.(packet);
21897
+ scheduleV2GeometryPublish(current);
21898
+ });
21899
+ scheduleV2GeometryReplay = (payload) => {
21900
+ if (!payload || payload !== v2GeometryRender.value) return;
21901
+ const generation = ++v2GeometryReplayGeneration;
21902
+ if (v2GeometryReplayRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryReplayRafHandle);
21903
+ const replay = () => {
21904
+ v2GeometryReplayRafHandle = 0;
21905
+ if (generation === v2GeometryReplayGeneration && payload === v2GeometryRender.value && isV2GeometryReady(payload)) v2ReviewGeometryDelivery.replay(payload);
21906
+ };
21907
+ if (typeof requestAnimationFrame === "function") v2GeometryReplayRafHandle = requestAnimationFrame(replay);
21908
+ else queueMicrotask(replay);
21909
+ };
21614
21910
  const onV2Render = (payload) => {
21615
21911
  if (!payload) return;
21912
+ v2ReviewGeometryDelivery.observe(payload);
21616
21913
  scheduleV2GeometryPublish(payload, () => takeV2TrackedChangeRestampGeometryRetention());
21617
21914
  if (v2HasRangeSelection.value) scheduleV2SelectionToolbarStateSync();
21618
21915
  };
@@ -21645,7 +21942,11 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21645
21942
  cancelScheduledV2SelectionToolbarSync();
21646
21943
  if (v2GeometryRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryRafHandle);
21647
21944
  v2GeometryRafHandle = 0;
21945
+ if (v2GeometryReplayRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryReplayRafHandle);
21946
+ v2GeometryReplayRafHandle = 0;
21947
+ v2GeometryReplayGeneration++;
21648
21948
  v2GeometryRender.value = null;
21949
+ v2ReviewGeometryDelivery.reset();
21649
21950
  v2GeometryEpoch.value = null;
21650
21951
  v2GeometryAvailable.value = false;
21651
21952
  v2ReviewSidebarUnlocked.value = false;
@@ -21793,14 +22094,15 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
21793
22094
  if (!isV2Mode.value) return;
21794
22095
  if (!v2GeometryPublisher.getLastPayload()) return;
21795
22096
  if (v2GeometryRafHandle && typeof cancelAnimationFrame === "function") cancelAnimationFrame(v2GeometryRafHandle);
21796
- if (typeof requestAnimationFrame !== "function") {
21797
- v2GeometryPublisher.recollect(resolveV2GeometryPublishOptions(options));
21798
- return;
21799
- }
21800
- v2GeometryRafHandle = requestAnimationFrame(() => {
22097
+ const recollect = () => {
21801
22098
  v2GeometryRafHandle = 0;
21802
- v2GeometryPublisher.recollect(resolveV2GeometryPublishOptions(options));
21803
- });
22099
+ const current = v2GeometryRender.value;
22100
+ Promise.resolve(v2GeometryPublisher.recollect(resolveV2GeometryPublishOptions(options))).then(() => {
22101
+ if (current === v2GeometryRender.value && isV2GeometryReady(current)) v2ReviewGeometryDelivery.replay(current);
22102
+ });
22103
+ };
22104
+ if (typeof requestAnimationFrame !== "function") recollect();
22105
+ else v2GeometryRafHandle = requestAnimationFrame(recollect);
21804
22106
  };
21805
22107
  const handleV2DocumentModeChange = () => {
21806
22108
  if (!isV2Mode.value) return;
@@ -22028,13 +22330,17 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
22028
22330
  ...payload,
22029
22331
  documentId: doc.id
22030
22332
  });
22031
- v2GeometryPublisher.noteCommittedPageWindow?.(payload);
22333
+ if (payload.reviewGeometry && Array.isArray(payload.pageIndices)) {
22334
+ const current = v2GeometryRender.value;
22335
+ v2ReviewGeometryDelivery.notify(String(doc.id), payload, current, isV2GeometryReady(current));
22336
+ } else v2GeometryPublisher.noteCommittedPageWindow?.(payload);
22032
22337
  },
22033
22338
  fontAssets: proxy.$superdoc.config.fonts,
22034
22339
  workerUrls: proxy.$superdoc.config.workerUrls,
22035
22340
  fieldContext: doc.fieldContext,
22036
22341
  fieldUpdatePolicy: proxy.$superdoc.config.fieldUpdatePolicy,
22037
22342
  workerStartupTimeoutMs: proxy.$superdoc.config.workerStartupTimeoutMs,
22343
+ ...proxy.$superdoc.config.changes ? { changes: proxy.$superdoc.config.changes } : {},
22038
22344
  proofing: resolvedProofingConfig.value,
22039
22345
  isNewFile,
22040
22346
  password: getDocumentLoadPassword(doc),
@@ -22055,7 +22361,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
22055
22361
  documentLoading: proxy.$superdoc.uiConfig.loading.enabled,
22056
22362
  linkPopoverResolver: getHyperlinkActivationHandler(),
22057
22363
  layoutEngineOptions: useLayoutEngine ? {
22058
- ...proxy.$superdoc.config.layoutEngineOptions || {},
22364
+ ...proxy.$superdoc.config.layoutEngineOptions,
22059
22365
  proofing: resolvedProofingConfig.value,
22060
22366
  debugLabel: proxy.$superdoc.config.layoutEngineOptions?.debugLabel ?? doc.name ?? doc.id,
22061
22367
  zoom: (activeZoom.value ?? 100) / 100,
@@ -22318,11 +22624,16 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
22318
22624
  if (layout !== "auto") return true;
22319
22625
  return !isCompactCommentsMode.value;
22320
22626
  });
22321
- const showCommentsSidebar = require__plugin_vue_export_helper.computed(() => {
22627
+ const sidebarEnabled = require__plugin_vue_export_helper.computed(() => {
22322
22628
  if (isV2Mode.value && !v2GeometryAvailable.value && !v2ReviewSidebarUnlocked.value && !hasPdfFloatingComments.value) return false;
22323
22629
  if (!shouldRenderCommentsInViewing.value) return false;
22324
22630
  if (!shouldUseSidebarComments.value) return false;
22325
- return pendingComment.value || (floatingComments.value.length > 0 || hasOpenTrackedChanges.value) && isReady.value && layers.value && isCommentsEnabled.value && !isCommentsListVisible.value;
22631
+ return Boolean(pendingComment.value || isReady.value && layers.value && isCommentsEnabled.value && !isCommentsListVisible.value);
22632
+ });
22633
+ const showCommentsSidebar = useCommentSidebar({
22634
+ documentKey: require__plugin_vue_export_helper.computed(() => JSON.stringify(documents.value.map((document) => document.id))),
22635
+ enabled: sidebarEnabled,
22636
+ hasContent: require__plugin_vue_export_helper.computed(() => Boolean(pendingComment.value || floatingComments.value.length || hasOpenTrackedChanges.value))
22326
22637
  });
22327
22638
  const activeCompactComment = require__plugin_vue_export_helper.computed(() => {
22328
22639
  if (showCommentsSidebar.value) return null;
@@ -22946,7 +23257,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
22946
23257
  ref_key: "superdocRoot",
22947
23258
  ref: superdocRoot,
22948
23259
  class: require__plugin_vue_export_helper.normalizeClass(["superdoc", {
22949
- "superdoc--with-sidebar": showCommentsSidebar.value,
23260
+ "superdoc--with-sidebar": require__plugin_vue_export_helper.unref(showCommentsSidebar),
22950
23261
  "superdoc--web-layout": require__plugin_vue_export_helper.unref(proxy).$superdoc.config.viewOptions?.layout === "web",
22951
23262
  "superdoc--contained": require__plugin_vue_export_helper.unref(proxy).$superdoc.config.contained,
22952
23263
  "high-contrast": require__plugin_vue_export_helper.unref(isHighContrastMode)
@@ -23094,7 +23405,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
23094
23405
  onAnchorActivate: handleHighlightClick
23095
23406
  })) : require__plugin_vue_export_helper.createCommentVNode("", true)
23096
23407
  ])], 512),
23097
- showCommentsSidebar.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
23408
+ require__plugin_vue_export_helper.unref(showCommentsSidebar) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
23098
23409
  key: 0,
23099
23410
  ref_key: "rightSidebarRef",
23100
23411
  ref: rightSidebarRef,
@@ -23120,7 +23431,7 @@ var SuperDoc_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ require
23120
23431
  });
23121
23432
  //#endregion
23122
23433
  //#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"]]);
23434
+ 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-b5da778c"]]);
23124
23435
  //#endregion
23125
23436
  //#region src/core/create-app.ts
23126
23437
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
@@ -35697,6 +36008,16 @@ var SurfaceManager = class {
35697
36008
  };
35698
36009
  //#endregion
35699
36010
  //#region src/helpers/deprecation.ts
36011
+ /**
36012
+ * Deprecation utilities for steering consumers toward the Document API.
36013
+ *
36014
+ * ProseMirror will be removed in a future major version. These helpers emit
36015
+ * one-time console warnings when deprecated properties are accessed on an
36016
+ * editor instance handed to consumer callbacks (onEditorCreate, etc.).
36017
+ *
36018
+ * Internal code continues to use the raw Editor — only the consumer-facing
36019
+ * boundary wraps the editor with a Proxy.
36020
+ */
35700
36021
  var MIGRATION_URL = "https://docs.superdoc.dev/guides/migration/document-api";
35701
36022
  var _warned = /* @__PURE__ */ new Set();
35702
36023
  /**
@@ -35885,7 +36206,8 @@ function normalizeSuperDocUser(user) {
35885
36206
  if (!user || typeof user !== "object") return { ...DEFAULT_SUPERDOC_USER };
35886
36207
  return {
35887
36208
  ...DEFAULT_SUPERDOC_USER,
35888
- ...user
36209
+ ...user,
36210
+ ...user.commentIdentity !== void 0 ? { commentIdentity: normalizeCommentIdentity(user.commentIdentity) } : {}
35889
36211
  };
35890
36212
  }
35891
36213
  //#endregion
@@ -36404,6 +36726,7 @@ function normalizeUiConfig(config = {}) {
36404
36726
  * Keeping policy separate means an application can disable the built-in
36405
36727
  * comments UI without losing the rules enforced by its custom UI.
36406
36728
  */
36729
+ var isAutocapitalize = (value) => value === "off" || value === "sentences" || value === "words" || value === "characters";
36407
36730
  var isCommentInteractionLevel = (value) => value === "read" || value === "write" || value === "resolve";
36408
36731
  /**
36409
36732
  * Resolve the effective interaction policy.
@@ -36423,14 +36746,26 @@ function normalizeInteractionConfig(config = {}) {
36423
36746
  const canonicalLevel = interactionComments.level;
36424
36747
  const hasCanonicalLevel = isCommentInteractionLevel(canonicalLevel);
36425
36748
  const level = hasCanonicalLevel ? canonicalLevel : legacyReadOnly ? "read" : legacyAllowResolve ? "resolve" : "write";
36749
+ const interactionWritingAssistance = require__plugin_vue_export_helper.isPlainObject(interaction.writingAssistance) ? interaction.writingAssistance : {};
36750
+ const autocorrect = interactionWritingAssistance.autocorrect === true;
36751
+ const autocapitalize = isAutocapitalize(interactionWritingAssistance.autocapitalize) ? interactionWritingAssistance.autocapitalize : "off";
36752
+ const spellcheck = interactionWritingAssistance.spellcheck === true;
36426
36753
  const canonicalAllowDecisions = interactionTrackedChanges.allowDecisions;
36754
+ const interactionTouch = require__plugin_vue_export_helper.isPlainObject(interaction.touch) ? interaction.touch : {};
36427
36755
  return {
36428
36756
  comments: {
36429
36757
  level,
36430
36758
  readOnly: hasCanonicalLevel ? level === "read" : legacyReadOnly,
36431
36759
  allowResolve: hasCanonicalLevel ? level === "resolve" : legacyAllowResolve
36432
36760
  },
36433
- trackedChanges: { allowDecisions: typeof canonicalAllowDecisions === "boolean" ? canonicalAllowDecisions : !legacyReadOnly }
36761
+ trackedChanges: { allowDecisions: typeof canonicalAllowDecisions === "boolean" ? canonicalAllowDecisions : !legacyReadOnly },
36762
+ touch: { gestures: interactionTouch.gestures === true },
36763
+ writingAssistance: {
36764
+ enabled: autocorrect || autocapitalize !== "off" || spellcheck,
36765
+ autocorrect,
36766
+ autocapitalize,
36767
+ spellcheck
36768
+ }
36434
36769
  };
36435
36770
  }
36436
36771
  //#endregion
@@ -37087,26 +37422,39 @@ var ToolbarButtonIcon_default = /* @__PURE__ */ require__plugin_vue_export_helpe
37087
37422
  var _hoisted_1$15 = [
37088
37423
  "role",
37089
37424
  "aria-label",
37090
- "aria-disabled"
37425
+ "aria-disabled",
37426
+ "aria-busy"
37091
37427
  ];
37092
37428
  var _hoisted_2$11 = ["data-item"];
37093
37429
  var _hoisted_3$10 = ["data-item"];
37094
37430
  var _hoisted_4$8 = {
37095
- key: 1,
37431
+ key: 0,
37432
+ role: "status",
37433
+ "aria-label": "Applying list",
37434
+ style: { "font-weight": "700" }
37435
+ };
37436
+ var _hoisted_5$6 = {
37437
+ key: 2,
37096
37438
  class: "sd-button-label"
37097
37439
  };
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 = [
37440
+ var _hoisted_6$4 = ["data-item", "aria-label"];
37441
+ var _hoisted_7$2 = ["innerHTML"];
37442
+ var _hoisted_8$2 = {
37443
+ key: 0,
37444
+ role: "status",
37445
+ "aria-label": "Applying list",
37446
+ style: { "font-weight": "700" }
37447
+ };
37448
+ var _hoisted_9$1 = { key: 3 };
37449
+ var _hoisted_10$1 = ["onKeydown", "id"];
37450
+ var _hoisted_11$1 = [
37103
37451
  "placeholder",
37104
37452
  "onKeydown",
37105
37453
  "id",
37106
37454
  "data-item"
37107
37455
  ];
37108
- var _hoisted_10$1 = ["data-item", "innerHTML"];
37109
- var _hoisted_11$1 = {
37456
+ var _hoisted_12$1 = ["data-item", "innerHTML"];
37457
+ var _hoisted_13$1 = {
37110
37458
  "aria-live": "polite",
37111
37459
  class: "sd-visually-hidden"
37112
37460
  };
@@ -37277,6 +37625,7 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37277
37625
  role: __props.isOverflowItem ? "menuitem" : "button",
37278
37626
  "aria-label": require__plugin_vue_export_helper.unref(attributes).ariaLabel,
37279
37627
  "aria-disabled": require__plugin_vue_export_helper.unref(disabled) ? "true" : void 0,
37628
+ "aria-busy": require__plugin_vue_export_helper.unref(attributes).ariaBusy || void 0,
37280
37629
  "data-sd-part": "toolbar-item",
37281
37630
  onClick: handleOuterClick,
37282
37631
  onKeydown: _cache[9] || (_cache[9] = require__plugin_vue_export_helper.withKeys(($event) => onEnterKeydown($event), ["enter"])),
@@ -37298,17 +37647,21 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37298
37647
  class: "sd-toolbar-button__main",
37299
37648
  "data-item": `btn-${require__plugin_vue_export_helper.unref(name) || ""}-main`,
37300
37649
  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),
37650
+ }, [
37651
+ 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),
37652
+ require__plugin_vue_export_helper.unref(icon) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(ToolbarButtonIcon_default, {
37653
+ key: 1,
37654
+ color: require__plugin_vue_export_helper.unref(iconColor),
37655
+ class: "sd-toolbar-icon",
37656
+ icon: require__plugin_vue_export_helper.unref(icon),
37657
+ name: require__plugin_vue_export_helper.unref(name)
37658
+ }, null, 8, [
37659
+ "color",
37660
+ "icon",
37661
+ "name"
37662
+ ])) : require__plugin_vue_export_helper.createCommentVNode("", true),
37663
+ 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)
37664
+ ], 8, _hoisted_3$10)) : require__plugin_vue_export_helper.createCommentVNode("", true),
37312
37665
  isSplit.value ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
37313
37666
  key: 1,
37314
37667
  class: "sd-toolbar-button__caret",
@@ -37319,9 +37672,10 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37319
37672
  class: "sd-dropdown-caret",
37320
37673
  innerHTML: caretIcon.value,
37321
37674
  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 }, [
37675
+ }, 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 }, [
37676
+ 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
37677
  require__plugin_vue_export_helper.unref(icon) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createBlock(ToolbarButtonIcon_default, {
37324
- key: 0,
37678
+ key: 1,
37325
37679
  color: require__plugin_vue_export_helper.unref(iconColor),
37326
37680
  class: "sd-toolbar-icon",
37327
37681
  icon: require__plugin_vue_export_helper.unref(icon),
@@ -37332,10 +37686,10 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37332
37686
  "name"
37333
37687
  ])) : require__plugin_vue_export_helper.createCommentVNode("", true),
37334
37688
  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,
37689
+ key: 2,
37336
37690
  class: require__plugin_vue_export_helper.normalizeClass(["sd-button-label", { "sd-visually-hidden": require__plugin_vue_export_helper.unref(inlineTextInputVisible) && itemName.value === "fontFamily" }])
37337
37691
  }, 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", {
37692
+ 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
37693
  key: 0,
37340
37694
  "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => inlineTextInput.value = $event),
37341
37695
  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 +37701,7 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37347
37701
  autocomplete: "off",
37348
37702
  ref_key: "inlineInput",
37349
37703
  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", {
37704
+ }, 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
37705
  key: 1,
37352
37706
  "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => inlineTextInput.value = $event),
37353
37707
  placeholder: require__plugin_vue_export_helper.unref(label),
@@ -37364,23 +37718,23 @@ var ToolbarButton_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ re
37364
37718
  autocomplete: "off",
37365
37719
  ref_key: "inlineInput",
37366
37720
  ref: inlineInput
37367
- }, null, 42, _hoisted_9$1)), [[require__plugin_vue_export_helper.vModelText, inlineTextInput.value]])])) : require__plugin_vue_export_helper.createCommentVNode("", true),
37721
+ }, null, 42, _hoisted_11$1)), [[require__plugin_vue_export_helper.vModelText, inlineTextInput.value]])])) : require__plugin_vue_export_helper.createCommentVNode("", true),
37368
37722
  require__plugin_vue_export_helper.unref(hasCaret) ? (require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
37369
- key: 3,
37723
+ key: 4,
37370
37724
  class: "sd-dropdown-caret",
37371
37725
  "data-item": `btn-${require__plugin_vue_export_helper.unref(name) || ""}-toggle`,
37372
37726
  innerHTML: caretIcon.value,
37373
37727
  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)
37728
+ }, null, 12, _hoisted_12$1)) : require__plugin_vue_export_helper.createCommentVNode("", true)
37375
37729
  ], 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)
37730
+ 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
37731
  ], 10, _hoisted_2$11)], 46, _hoisted_1$15);
37378
37732
  };
37379
37733
  }
37380
37734
  });
37381
37735
  //#endregion
37382
37736
  //#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"]]);
37737
+ 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
37738
  //#endregion
37385
37739
  //#region src/internal/toolbar/built-in/ToolbarSeparator.vue?vue&type=script&setup=true&lang.ts
37386
37740
  var _hoisted_1$14 = {
@@ -38676,7 +39030,7 @@ var SdTooltip_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ requir
38676
39030
  ]);
38677
39031
  const contentStyle = require__plugin_vue_export_helper.computed(() => ({
38678
39032
  ...props.contentStyle,
38679
- ...attrs.style || {},
39033
+ ...attrs.style,
38680
39034
  position: "fixed",
38681
39035
  top: position.value.top,
38682
39036
  left: position.value.left,
@@ -38848,7 +39202,7 @@ var SdTooltip_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ requir
38848
39202
  });
38849
39203
  //#endregion
38850
39204
  //#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"]]);
39205
+ 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
39206
  //#endregion
38853
39207
  //#region src/internal/toolbar/built-in/ButtonGroup.vue?vue&type=script&setup=true&lang.ts
38854
39208
  var _hoisted_1$11 = [
@@ -41698,6 +42052,8 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons: configuredIcons, toolbarTe
41698
42052
  attributes: { ariaLabel: "Bold" }
41699
42053
  });
41700
42054
  const fontOptions = (toolbarFonts ?? TOOLBAR_FONTS).map(normalizeFontOption);
42055
+ const isFontOption = (option) => typeof option.label === "string" && typeof option.key === "string";
42056
+ const currentFontOptions = () => fontButton.nestedOptions.value.filter(isFontOption);
41701
42057
  const fontButton = useToolbarItem({
41702
42058
  type: "dropdown",
41703
42059
  name: "fontFamily",
@@ -41724,8 +42080,9 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons: configuredIcons, toolbarTe
41724
42080
  if (typeof fontFamily !== "string" || !fontFamily) return;
41725
42081
  const family = fontFamily.split(",")[0];
41726
42082
  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);
42083
+ const options = currentFontOptions();
42084
+ const defaultFont = options.find((option) => optionValue(option) === fontButton.defaultLabel.value);
42085
+ const foundFont = options.find((option) => optionValue(option) === family);
41729
42086
  if (foundFont) {
41730
42087
  fontButton.label.value = foundFont.label;
41731
42088
  fontButton.selectedValue.value = foundFont.key;
@@ -41738,7 +42095,7 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons: configuredIcons, toolbarTe
41738
42095
  }
41739
42096
  },
41740
42097
  onDeactivate: () => {
41741
- const defaultFont = fontOptions.find((option) => (option.value ?? option.label) === fontButton.defaultLabel.value);
42098
+ const defaultFont = currentFontOptions().find((option) => (option.value ?? option.label) === fontButton.defaultLabel.value);
41742
42099
  fontButton.label.value = defaultFont?.label ?? fontButton.defaultLabel.value;
41743
42100
  fontButton.selectedValue.value = defaultFont?.key ?? "";
41744
42101
  }
@@ -42950,7 +43307,7 @@ var V1_ENABLED_ON_SELECTION_REASON = /* @__PURE__ */ new Set([
42950
43307
  ]);
42951
43308
  var V1_ENABLED_ON_EMPTY_REASON = /* @__PURE__ */ new Set(["list", "numberedlist"]);
42952
43309
  function shouldKeepEnabledForV1ToolbarParity(name, commandState) {
42953
- if (name === "fontFamily" || name === "fontSize") return true;
43310
+ if (name === "fontFamily" || name === "fontSize") return commandState?.reason !== "document-readonly" && commandState?.reason !== "deleted-text-formatting-unsupported";
42954
43311
  if (V1_ENABLED_ON_EMPTY_REASON.has(name) && commandState?.disabled && commandState?.supported !== false && commandState?.reason == null) return true;
42955
43312
  if (!V1_ENABLED_ON_SELECTION_REASON.has(name)) return false;
42956
43313
  const reason = commandState?.reason;
@@ -42984,6 +43341,7 @@ var RESERVED_TOOLBAR_ITEM_NAMES = /* @__PURE__ */ new Set([
42984
43341
  * proof lane, and behavior tests resolve against a real object.
42985
43342
  */
42986
43343
  var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.default {
43344
+ #formattingRefusalTooltips;
42987
43345
  #imagePicker;
42988
43346
  #groupItems;
42989
43347
  #highlightOptions;
@@ -43015,6 +43373,7 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
43015
43373
  showPageNumberButton: false
43016
43374
  };
43017
43375
  this.toolbarItems = [];
43376
+ this.#formattingRefusalTooltips = /* @__PURE__ */ new WeakMap();
43018
43377
  this.overflowItems = [];
43019
43378
  this.pendingSelectionCapture = void 0;
43020
43379
  this.#imagePicker = null;
@@ -43141,7 +43500,7 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
43141
43500
  const signature = this.#fontOptionsSignature();
43142
43501
  if (signature !== this._lastFontOptionsSignature) {
43143
43502
  this._lastFontOptionsSignature = signature;
43144
- this.#rebuildToolbarItems();
43503
+ if (!this.#refreshFontFamilyOptions()) this.#rebuildToolbarItems();
43145
43504
  }
43146
43505
  this.updateToolbarState();
43147
43506
  };
@@ -43289,6 +43648,13 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
43289
43648
  this.#makeToolbarItems();
43290
43649
  this.emit("toolbar-items-changed");
43291
43650
  }
43651
+ #refreshFontFamilyOptions() {
43652
+ const items = this.#getAllToolbarItems().filter((item) => item.name.value === "fontFamily");
43653
+ if (!items.length) return false;
43654
+ const options = (this.#resolveToolbarFonts(this.config.fonts) ?? TOOLBAR_FONTS).map(normalizeFontOption);
43655
+ for (const item of items) item.nestedOptions.value = options.map((option) => ({ ...option }));
43656
+ return true;
43657
+ }
43292
43658
  #fontOptionsSignature() {
43293
43659
  if (this.config.fonts) return "custom-fonts";
43294
43660
  const options = this.superdoc?.fonts?.getFontFamilyOptions?.() ?? [];
@@ -43468,6 +43834,17 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
43468
43834
  const commandId = resolveBuiltInItemCommandId(name);
43469
43835
  if (!commandId) return false;
43470
43836
  const commandState = this.snapshot?.commands?.[commandId];
43837
+ if (commandState?.reason === "deleted-text-formatting-unsupported") {
43838
+ if (!this.#formattingRefusalTooltips.has(item)) this.#formattingRefusalTooltips.set(item, item.tooltip.value);
43839
+ item.tooltip.value = "Select kept text, or accept or reject the deletion before formatting.";
43840
+ } else if (this.#formattingRefusalTooltips.has(item)) {
43841
+ item.tooltip.value = this.#formattingRefusalTooltips.get(item);
43842
+ this.#formattingRefusalTooltips.delete(item);
43843
+ }
43844
+ if ((name === "list" || name === "numberedlist") && item.attributes.value.ariaBusy !== Boolean(commandState?.pending)) item.attributes.value = {
43845
+ ...item.attributes.value,
43846
+ ariaBusy: Boolean(commandState?.pending)
43847
+ };
43471
43848
  if (shouldKeepEnabledForV1ToolbarParity(name, commandState)) item.setDisabled(false);
43472
43849
  else item.setDisabled(Boolean(commandState?.disabled));
43473
43850
  const handlers = {
@@ -43935,6 +44312,9 @@ function deriveCollaborationConnectionKind(snapshot, hasSyncedBefore) {
43935
44312
  default: return hasSyncedBefore ? "reconnecting" : "initial";
43936
44313
  }
43937
44314
  }
44315
+ function destroyedDuringUpgradeError() {
44316
+ return /* @__PURE__ */ new Error("SuperDoc: instance was destroyed during upgrade");
44317
+ }
43938
44318
  function isV2ActiveEditorFacade(editor) {
43939
44319
  return Boolean(editor && typeof editor === "object" && editor.editorVersion === 2);
43940
44320
  }
@@ -43984,6 +44364,7 @@ function runtimeUserOf(user) {
43984
44364
  const text = (value) => typeof value === "string" && value ? value : void 0;
43985
44365
  return {
43986
44366
  name: text(user.name) ?? "",
44367
+ ...user.commentIdentity !== void 0 ? { commentIdentity: user.commentIdentity ? { ...user.commentIdentity } : null } : {},
43987
44368
  ...text(user.id) ? { id: text(user.id) } : {},
43988
44369
  ...text(user.email) ? { email: text(user.email) } : {},
43989
44370
  ...text(user.initials) ? { initials: text(user.initials) } : {}
@@ -43995,10 +44376,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
43995
44376
  }
43996
44377
  #destroyed;
43997
44378
  #diagnostics;
43998
- #isUpgrading;
43999
44379
  #pendingUpgrade;
44000
- /** Aborts an in-flight upgrade (sync wait or ready wait). */
44001
- #abortUpgrade;
44002
44380
  /**
44003
44381
  * Unsubscribe handle for the v2 presence → `awareness-update` bridge. Set
44004
44382
  * while a v2 collaborative editor is mounted and torn down on remount,
@@ -44012,6 +44390,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44012
44390
  #v2ConnectionUnsub;
44013
44391
  /** Latest `collaboration-connection-change` payload, for late readers. */
44014
44392
  #v2ConnectionState;
44393
+ #v2ConnectionFailureReported;
44015
44394
  /** Whether the current collaboration room has completed a sync at least once. */
44016
44395
  #v2ConnectionHasSynced;
44017
44396
  /** Lock state captured before a local → v2 collaboration promotion remount. */
@@ -44107,13 +44486,12 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44107
44486
  constructor(config) {
44108
44487
  super();
44109
44488
  this.#destroyed = false;
44110
- this.#isUpgrading = false;
44111
44489
  this.#pendingUpgrade = null;
44112
- this.#abortUpgrade = null;
44113
44490
  this.#v2AwarenessUnsub = null;
44114
44491
  this.#v2LockUnsub = null;
44115
44492
  this.#v2ConnectionUnsub = null;
44116
44493
  this.#v2ConnectionState = null;
44494
+ this.#v2ConnectionFailureReported = false;
44117
44495
  this.#v2ConnectionHasSynced = false;
44118
44496
  this.#pendingV2LockSeed = null;
44119
44497
  this.#mountWrapper = null;
@@ -44228,6 +44606,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44228
44606
  if (!config.selector) throw new Error("SuperDoc: selector is required");
44229
44607
  const container = typeof config.selector === "string" ? document.querySelector(config.selector) : config.selector;
44230
44608
  if (!(container instanceof HTMLElement)) throw new Error("SuperDoc: selector must be a valid CSS selector string or DOM element");
44609
+ const user = normalizeSuperDocUser(config.user);
44231
44610
  this.#diagnostics = require_create_super_doc_ui.createInteractionHistory(this, config, () => this.version, container);
44232
44611
  this.#surfaceManager = new SurfaceManager({ getModuleConfig: () => this.#surfacesConfig });
44233
44612
  this.#contentControlClickRoot = container;
@@ -44237,7 +44616,10 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44237
44616
  container.addEventListener("keydown", this.#handleContentControlKeyDown, true);
44238
44617
  container.addEventListener("click", this.#handleContentControlClick, true);
44239
44618
  this.#onConfig("content-control:active-change", config.onContentControlActiveChange);
44240
- this.#init(config, container);
44619
+ this.#init({
44620
+ ...config,
44621
+ user
44622
+ }, container);
44241
44623
  this.#contentControlEventBridgeReady = true;
44242
44624
  if (this.listenerCount("content-control:active-change") > 0) this.ui;
44243
44625
  }
@@ -44290,7 +44672,6 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44290
44672
  }
44291
44673
  this.editorVersion = 2;
44292
44674
  this.#validateLegacyConfig();
44293
- this.config.user = normalizeSuperDocUser(this.config.user);
44294
44675
  if (!this.config.layoutEngineOptions.virtualization) this.config.layoutEngineOptions.virtualization = {
44295
44676
  enabled: true,
44296
44677
  window: 5,
@@ -44316,7 +44697,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44316
44697
  }
44317
44698
  this.userColorMap = /* @__PURE__ */ new Map();
44318
44699
  this.colorIndex = 0;
44319
- this.version = "2.29.0";
44700
+ this.version = "2.30.0-next.13";
44320
44701
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
44321
44702
  this.superdocId = config.superdocId || require_uuid.v4();
44322
44703
  this.colors = this.config.colors ?? [];
@@ -44735,6 +45116,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44735
45116
  if (this.#v2ConnectionState?.documentId !== documentId) {
44736
45117
  this.#v2ConnectionState = null;
44737
45118
  this.#v2ConnectionHasSynced = false;
45119
+ this.#v2ConnectionFailureReported = false;
44738
45120
  }
44739
45121
  const publish = (snapshot) => {
44740
45122
  if (this.#destroyed) return;
@@ -44749,8 +45131,21 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44749
45131
  superdoc: this
44750
45132
  };
44751
45133
  this.#v2ConnectionState = payload;
44752
- if (snapshot.state === "synced") this.#v2ConnectionHasSynced = true;
44753
- this.emit("collaboration-connection-change", payload);
45134
+ if (snapshot.state === "synced") {
45135
+ this.#v2ConnectionHasSynced = true;
45136
+ this.#v2ConnectionFailureReported = false;
45137
+ }
45138
+ const firstFailure = snapshot.state === "failed" && !this.#v2ConnectionFailureReported;
45139
+ if (firstFailure) this.#v2ConnectionFailureReported = true;
45140
+ const reportFailure = firstFailure && snapshot.detail !== "worker-transport-failed";
45141
+ try {
45142
+ this.emit("collaboration-connection-change", payload);
45143
+ } finally {
45144
+ if (reportFailure) {
45145
+ const exception = createCollaborationException("collaboration-connection-failed", documentId);
45146
+ if (exception) this.emit("exception", exception);
45147
+ }
45148
+ }
44754
45149
  };
44755
45150
  try {
44756
45151
  const initialSnapshot = connection.getSnapshot?.();
@@ -44863,41 +45258,43 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44863
45258
  throw Object.assign(/* @__PURE__ */ new Error("SuperDoc: a different collaboration target is already upgrading"), { code: "collaboration-upgrade-target-conflict" });
44864
45259
  }
44865
45260
  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
- })();
45261
+ let rejectOnDestroy = () => {};
45262
+ const destroyed = new Promise((_resolve, reject) => {
45263
+ rejectOnDestroy = reject;
45264
+ });
45265
+ const operation = Promise.race([this.#runUpgrade(target), destroyed]).finally(() => {
45266
+ this.#pendingUpgrade = null;
45267
+ });
44895
45268
  this.#pendingUpgrade = {
44896
45269
  target,
44897
- promise: operation
45270
+ promise: operation,
45271
+ rejectOnDestroy
44898
45272
  };
44899
45273
  return operation;
44900
45274
  }
45275
+ async #runUpgrade(target) {
45276
+ const localSource = await this.#captureCurrentDocxSourceForUpgrade();
45277
+ const rollback = this.#snapshotV2UpgradeState();
45278
+ const lockSeed = this.#snapshotV2LockSeed();
45279
+ this.#assertNotDestroyed();
45280
+ try {
45281
+ this.#pendingV2LockSeed = lockSeed;
45282
+ const promotion = await this.#promoteSingleDocumentToV2Collaboration(target, localSource);
45283
+ this.#assertNotDestroyed();
45284
+ this.#finalizeSingleDocumentV2Collaboration(promotion, target);
45285
+ this.#pendingV2LockSeed = null;
45286
+ const attachedDocumentId = this.activeEditor?.options?.documentId;
45287
+ const attachedRoomId = promotion.configDoc.v2Collaboration?.documentId;
45288
+ 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");
45289
+ return {
45290
+ roomId: attachedRoomId,
45291
+ documentId: attachedDocumentId
45292
+ };
45293
+ } catch (err) {
45294
+ if (!this.#destroyed) this.#rollbackV2UpgradeState(rollback);
45295
+ throw err;
45296
+ }
45297
+ }
44901
45298
  #sameUpgradeTarget(left, right) {
44902
45299
  if (left.token !== right.token) return false;
44903
45300
  const seen = /* @__PURE__ */ new WeakMap();
@@ -44962,7 +45359,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44962
45359
  * async waits inside upgradeToCollaboration().
44963
45360
  */
44964
45361
  #assertNotDestroyed() {
44965
- if (this.#destroyed) throw new Error("SuperDoc: instance was destroyed during upgrade");
45362
+ if (this.#destroyed) throw destroyedDuringUpgradeError();
44966
45363
  }
44967
45364
  /**
44968
45365
  * Return the superdoc store, throwing a clear lifecycle error if
@@ -45061,6 +45458,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
45061
45458
  this.#writeStoreDocV2Collaboration(storeDoc, v2Collaboration);
45062
45459
  this.isCollaborative = true;
45063
45460
  await editor.upgradeToCollaboration(localSource, v2Collaboration);
45461
+ this.#assertNotDestroyed();
45064
45462
  configDoc.data = localSource;
45065
45463
  storeDoc.data = localSource;
45066
45464
  return {
@@ -45120,7 +45518,6 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
45120
45518
  */
45121
45519
  #validateUpgradePrerequisites(options) {
45122
45520
  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
45521
  if (this.isCollaborative) throw new Error("SuperDoc: instance is already collaborative");
45125
45522
  const cfg = this.config;
45126
45523
  const docxDocs = cfg.documents.filter((d) => d.type === DOCX);
@@ -45175,10 +45572,15 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
45175
45572
  this.#requireReady("setUser");
45176
45573
  const name = typeof user?.name === "string" ? user.name.trim() : "";
45177
45574
  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
- });
45575
+ let next;
45576
+ try {
45577
+ next = normalizeSuperDocUser({
45578
+ ...user,
45579
+ name
45580
+ });
45581
+ } catch (error) {
45582
+ return Promise.reject(error);
45583
+ }
45182
45584
  const applied = this.#userSwitch.then(() => this.#applyUser(next));
45183
45585
  this.#userSwitch = applied.catch(() => {});
45184
45586
  return applied;
@@ -45192,7 +45594,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
45192
45594
  "email",
45193
45595
  "image",
45194
45596
  "initials"
45195
- ].every((key) => (current[key] ?? null) === (next[key] ?? null))) return;
45597
+ ].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
45598
  const runtimes = this.#editorRuntimeRegistry.getAll();
45197
45599
  const results = await Promise.allSettled(runtimes.map((runtime) => runtime.setUser?.(runtimeUserOf(next))));
45198
45600
  const failure = results.find((result) => result.status === "rejected");
@@ -46527,10 +46929,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
46527
46929
  this.#contentControlClickRoot = null;
46528
46930
  this.#contentControlActiveChangeUnsub?.();
46529
46931
  this.#contentControlActiveChangeUnsub = null;
46530
- if (this.#abortUpgrade) {
46531
- this.#abortUpgrade();
46532
- this.#abortUpgrade = null;
46533
- }
46932
+ this.#pendingUpgrade?.rejectOnDestroy(destroyedDuringUpgradeError());
46534
46933
  if (this.#surfaceManager) this.#surfaceManager.destroy();
46535
46934
  this.toolbar?.destroy?.();
46536
46935
  this.commentsList?.close();