superdoc 2.28.0-next.8 → 2.28.0

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 (64) hide show
  1. package/dist/chunks/{FindReplaceSurface-8V8AjgR4.cjs → FindReplaceSurface-BRs3JrCq.cjs} +1 -1
  2. package/dist/chunks/{FindReplaceSurface-RUiTFDt3.es.js → FindReplaceSurface-Dm043CZP.es.js} +1 -1
  3. package/dist/chunks/{PasswordPromptSurface-B5DHV6qr.cjs → PasswordPromptSurface-BpqP4pUa.cjs} +1 -1
  4. package/dist/chunks/{PasswordPromptSurface-BDpxqdzp.es.js → PasswordPromptSurface-DGjmN7A-.es.js} +1 -1
  5. package/dist/chunks/{PdfViewer-omriDChK.cjs → PdfViewer-Cg072l5d.cjs} +1 -1
  6. package/dist/chunks/{PdfViewer-DjdsIh6n.es.js → PdfViewer-JHXZQZ9x.es.js} +1 -1
  7. package/dist/chunks/{_plugin-vue_export-helper-BUdjGKyl.cjs → _plugin-vue_export-helper-6MBoygIY.cjs} +9 -0
  8. package/dist/chunks/{_plugin-vue_export-helper-7ZiLY07t.es.js → _plugin-vue_export-helper-CsJQtMvr.es.js} +4 -1
  9. package/dist/chunks/{create-super-doc-ui-C4Ka5Ph3.es.js → create-super-doc-ui-BbjHD1ul.es.js} +184 -28
  10. package/dist/chunks/{create-super-doc-ui-B4c55pFy.cjs → create-super-doc-ui-BfgYblyI.cjs} +195 -27
  11. package/dist/collaboration-upgrade-engine.cjs +1 -1
  12. package/dist/collaboration-upgrade-engine.es.js +1 -1
  13. package/dist/document-api/src/contract/operation-definitions.d.ts +2 -2
  14. package/dist/document-api/src/lists/lists.types.d.ts +1 -0
  15. package/dist/document-api/src/sections/sections.types.d.ts +5 -1
  16. package/dist/document-api/src/types/clipboard.d.ts +5 -1
  17. package/dist/document-api/src/types/receipt.d.ts +2 -1
  18. package/dist/layout-engine/contracts/src/index.d.ts +5 -1
  19. package/dist/layout-engine/contracts/src/run-helpers.d.ts +1 -0
  20. package/dist/layout-engine/layout-bridge/src/index.d.ts +1 -0
  21. package/dist/layout-engine/layout-bridge/src/place-inline-drawings.d.ts +9 -0
  22. package/dist/layout-engine/layout-engine/src/layout-drawing.d.ts +2 -1
  23. package/dist/layout-engine/layout-engine/src/paginator.d.ts +1 -0
  24. package/dist/layout-engine/style-engine/src/ooxml/index.d.ts +1 -1
  25. package/dist/public/ui-react.cjs +1 -1
  26. package/dist/public/ui-react.es.js +1 -1
  27. package/dist/public/ui-vue.cjs +1 -1
  28. package/dist/public/ui-vue.es.js +1 -1
  29. package/dist/public/ui.cjs +3 -2
  30. package/dist/public/ui.es.js +3 -2
  31. package/dist/shared/font-system/src/resolver.d.ts +3 -3
  32. package/dist/style.css +1 -1
  33. package/dist/style.layered.css +1 -1
  34. package/dist/superdoc/src/components/CommentsLayer/FloatingComments.vue.d.ts +11 -0
  35. package/dist/superdoc/src/components/CommentsLayer/PdfCommentsLayer.vue.d.ts +4 -0
  36. package/dist/superdoc/src/composables/use-high-contrast-mode.d.ts +8 -4
  37. package/dist/superdoc/src/composables/use-superdoc-instance.d.ts +7 -0
  38. package/dist/superdoc/src/core/SuperDoc.d.ts +1 -5
  39. package/dist/superdoc/src/core/collaboration/helpers.d.ts +0 -5
  40. package/dist/superdoc/src/core/collaboration/resolve-v2-collaboration-target.d.ts +2 -1
  41. package/dist/superdoc/src/core/create-app.d.ts +2 -6
  42. package/dist/superdoc/src/core/editor-runtime/types.d.ts +2 -0
  43. package/dist/superdoc/src/core/editor-runtime/v2/v2-editor-runtime-adapter.d.ts +2 -0
  44. package/dist/superdoc/src/core/types/index.d.ts +14 -0
  45. package/dist/superdoc/src/core/v2-integration/internal-vue-runtime.d.ts +1 -0
  46. package/dist/superdoc/src/core/v2-integration/v2-integration.d.ts +104 -84
  47. package/dist/superdoc/src/helpers/v2-review-mutation-impact.d.ts +2 -0
  48. package/dist/superdoc/src/internal/diagnostics/translate-diagnostic.d.ts +5 -0
  49. package/dist/superdoc/src/internal/toolbar/built-in/constants.d.ts +0 -48
  50. package/dist/superdoc/src/internal/toolbar/built-in-toolbar.d.ts +4 -1
  51. package/dist/superdoc/src/internal/toolbar/compatibility-catalog.d.ts +16 -0
  52. package/dist/superdoc/src/public/ui/types.d.ts +39 -1
  53. package/dist/superdoc/src/public/ui.d.cts +12 -0
  54. package/dist/superdoc/src/public/ui.d.ts +1 -1
  55. package/dist/superdoc/src/stores/comments-store.d.ts +7 -97
  56. package/dist/superdoc/src/stores/superdoc-store.d.ts +21 -291
  57. package/dist/superdoc.cjs +262 -1462
  58. package/dist/superdoc.es.js +538 -12833
  59. package/dist-cdn/style.layered.css +1 -1
  60. package/dist-cdn/superdoc.min.css +1 -1
  61. package/dist-cdn/superdoc.min.js +42 -42
  62. package/package.json +4 -3
  63. package/dist/superdoc/src/core/collaboration/collaboration.d.ts +0 -18
  64. package/dist/superdoc/src/helpers/group-changes.d.ts +0 -1
package/dist/superdoc.cjs CHANGED
@@ -1,16 +1,14 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_rolldown_runtime = require("./chunks/rolldown-runtime-74VwNCz7.cjs");
3
3
  const require_blank_docx = require("./chunks/blank-docx-BuFAbRjs.cjs");
4
- const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-B4c55pFy.cjs");
4
+ const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-BfgYblyI.cjs");
5
5
  const require_eventemitter3 = require("./chunks/eventemitter3-C_TAnXOl.cjs");
6
6
  const require_uuid = require("./chunks/uuid-BhG0ngwk.cjs");
7
- const require__plugin_vue_export_helper = require("./chunks/_plugin-vue_export-helper-BUdjGKyl.cjs");
7
+ const require__plugin_vue_export_helper = require("./chunks/_plugin-vue_export-helper-6MBoygIY.cjs");
8
8
  const require_jszip = require("./chunks/jszip-D8mAFF-r.cjs");
9
9
  const require_constants = require("./chunks/constants-DpXuDx_g.cjs");
10
10
  let superdoc_entry_vue = require("vue");
11
11
  superdoc_entry_vue = require_rolldown_runtime.__toESM(superdoc_entry_vue, 1);
12
- require("y-websocket");
13
- require("@hocuspocus/provider");
14
12
  let yjs = require("yjs");
15
13
  //#region src/core/v2-integration/browser-peer-runtime.js
16
14
  var SUPERDOC_V2_BROWSER_RUNTIME_GLOBAL_KEY = "__SUPERDOC_V2_BROWSER_RUNTIME__";
@@ -260,7 +258,7 @@ var v_click_outside_default = {
260
258
  var DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
261
259
  function getSuperdocVersion() {
262
260
  try {
263
- return "2.28.0-next.8";
261
+ return "2.28.0";
264
262
  } catch {
265
263
  return "unknown";
266
264
  }
@@ -5529,13 +5527,22 @@ function resolveLiveblocksFamily(candidate, authEndpointBaseUrl) {
5529
5527
  reason: "mixed-auth",
5530
5528
  message: "SuperDoc v2 Liveblocks collaboration accepts exactly one auth mode; pass either publicApiKey or authEndpoint, not both."
5531
5529
  };
5530
+ const baseUrlRaw = candidate.baseUrl;
5531
+ const baseUrl = baseUrlRaw === void 0 ? void 0 : normalizeHttpUrl(baseUrlRaw);
5532
+ if (baseUrl === null) return {
5533
+ ok: false,
5534
+ reason: "invalid-base-url",
5535
+ message: "SuperDoc v2 Liveblocks collaboration requires baseUrl to be an absolute http(s) URL."
5536
+ };
5537
+ const baseUrlOption = baseUrl ? { baseUrl } : {};
5532
5538
  if (publicApiKey) return {
5533
5539
  ok: true,
5534
5540
  target: {
5535
5541
  providerFamily: "liveblocks",
5536
5542
  documentId,
5537
5543
  roomMode,
5538
- publicApiKey
5544
+ publicApiKey,
5545
+ ...baseUrlOption
5539
5546
  }
5540
5547
  };
5541
5548
  const authEndpoint = normalizeHttpUrl(authEndpointRaw, authEndpointBaseUrl);
@@ -5550,7 +5557,8 @@ function resolveLiveblocksFamily(candidate, authEndpointBaseUrl) {
5550
5557
  providerFamily: "liveblocks",
5551
5558
  documentId,
5552
5559
  roomMode,
5553
- authEndpoint
5560
+ authEndpoint,
5561
+ ...baseUrlOption
5554
5562
  }
5555
5563
  };
5556
5564
  }
@@ -6833,87 +6841,6 @@ var useSuperdocStore = defineStore("superdoc", () => {
6833
6841
  };
6834
6842
  });
6835
6843
  //#endregion
6836
- //#region src/helpers/group-changes.js
6837
- /**
6838
- * Track changes helper
6839
- * Combines replace transactions which are represented by insertion + deletion
6840
- *
6841
- * @param {Array} changes - Array of tracked changes from the editor
6842
- * @returns {Array} Grouped track changes array with combined replacements
6843
- */
6844
- var groupChanges = (changes) => {
6845
- const markMetaKeys = {
6846
- trackInsert: "insertedMark",
6847
- trackDelete: "deletionMark",
6848
- trackFormat: "formatMark"
6849
- };
6850
- const byId = /* @__PURE__ */ new Map();
6851
- for (const change of changes) {
6852
- const id = change.mark.attrs.id;
6853
- if (!byId.has(id)) byId.set(id, []);
6854
- byId.get(id).push(change);
6855
- }
6856
- const grouped = [];
6857
- for (const [, group] of byId) {
6858
- if (group.length === 1) {
6859
- const c = group[0];
6860
- const key = markMetaKeys[c.mark.type.name];
6861
- grouped.push({
6862
- from: c.from,
6863
- to: c.to,
6864
- [key]: c
6865
- });
6866
- continue;
6867
- }
6868
- const processed = /* @__PURE__ */ new Set();
6869
- for (let i = 0; i < group.length; i++) {
6870
- if (processed.has(i)) continue;
6871
- const c1 = group[i];
6872
- const c1Key = markMetaKeys[c1.mark.type.name];
6873
- const c2 = group[i + 1];
6874
- if (c2 && !processed.has(i + 1) && c1.to === c2.from && c1.mark.type.name !== c2.mark.type.name) {
6875
- const c2Key = markMetaKeys[c2.mark.type.name];
6876
- grouped.push({
6877
- from: c1.from,
6878
- to: c2.to,
6879
- [c1Key]: c1,
6880
- [c2Key]: c2
6881
- });
6882
- processed.add(i);
6883
- processed.add(i + 1);
6884
- continue;
6885
- }
6886
- let foundMatch = false;
6887
- for (let j = i + 1; j < group.length; j++) {
6888
- if (processed.has(j)) continue;
6889
- const cj = group[j];
6890
- if (c1.mark.type.name !== cj.mark.type.name) {
6891
- const cjKey = markMetaKeys[cj.mark.type.name];
6892
- grouped.push({
6893
- from: Math.min(c1.from, cj.from),
6894
- to: Math.max(c1.to, cj.to),
6895
- [c1Key]: c1,
6896
- [cjKey]: cj
6897
- });
6898
- processed.add(i);
6899
- processed.add(j);
6900
- foundMatch = true;
6901
- break;
6902
- }
6903
- }
6904
- if (!foundMatch) {
6905
- grouped.push({
6906
- from: c1.from,
6907
- to: c1.to,
6908
- [c1Key]: c1
6909
- });
6910
- processed.add(i);
6911
- }
6912
- }
6913
- }
6914
- return grouped;
6915
- };
6916
- //#endregion
6917
6844
  //#region src/stores/helpers/floating-comment-instances.js
6918
6845
  var HEADER_FOOTER_TRACKED_CHANGE_STORY_TYPE = "headerFooterPart";
6919
6846
  var toFinitePageIndex = (value) => {
@@ -7055,20 +6982,16 @@ var buildFloatingCommentInstances = ({ comment, positionKey, positionEntry, fall
7055
6982
  })];
7056
6983
  };
7057
6984
  //#endregion
7058
- //#region src/core/v2-integration/v2-integration.js
7059
- /** @type {() => Promise<unknown>} */
6985
+ //#region src/core/v2-integration/v2-integration.ts
7060
6986
  var engineModuleLoader = () => import("@superdoc/docx-engine");
7061
- /** @type {Promise<void> | null} */
7062
6987
  var engineModulePromise = null;
7063
- /** @type {((...args: unknown[]) => unknown) | null} */
7064
6988
  var createSuperDocV2Integration = null;
7065
- /** Load the engine before Vue evaluates the synchronous integration seam. */
7066
6989
  async function loadDefaultV2Integration() {
7067
6990
  if (!engineModulePromise) {
7068
6991
  const loadPromise = Promise.resolve().then(() => engineModuleLoader()).then((module) => {
7069
6992
  const factory = module && typeof module === "object" && "createSuperDocV2Integration" in module ? module.createSuperDocV2Integration : null;
7070
6993
  if (typeof factory !== "function") throw new TypeError("SuperDoc: the DOCX Engine module does not export createSuperDocV2Integration");
7071
- createSuperDocV2Integration = factory;
6994
+ createSuperDocV2Integration = () => factory();
7072
6995
  });
7073
6996
  engineModulePromise = loadPromise;
7074
6997
  loadPromise.catch(() => {
@@ -7080,7 +7003,6 @@ async function loadDefaultV2Integration() {
7080
7003
  }
7081
7004
  await engineModulePromise;
7082
7005
  }
7083
- /** Load the engine for editor startup, preserving the fail-closed stub on failure. */
7084
7006
  async function loadDefaultV2IntegrationOrFallback() {
7085
7007
  try {
7086
7008
  await loadDefaultV2Integration();
@@ -7089,12 +7011,8 @@ async function loadDefaultV2IntegrationOrFallback() {
7089
7011
  }
7090
7012
  }
7091
7013
  var V2_BODY_TRACKED_CHANGE_ANCHOR_PREFIX = "tc::body::";
7092
- /**
7093
- * @param {{ trackedChange?: unknown, trackedChangeAnchorKey?: unknown } | null | undefined} row
7094
- * @returns {boolean}
7095
- */
7096
7014
  function isV2SyntheticTrackedChangeRow(row) {
7097
- if (!row || row.trackedChange !== true) return false;
7015
+ if (!require_create_super_doc_ui.isPlainObject(row) || row.trackedChange !== true) return false;
7098
7016
  const anchorKey = row.trackedChangeAnchorKey;
7099
7017
  return typeof anchorKey === "string" && anchorKey.startsWith(V2_BODY_TRACKED_CHANGE_ANCHOR_PREFIX);
7100
7018
  }
@@ -7144,9 +7062,6 @@ var StubV2EditorComponent = require__plugin_vue_export_helper.defineComponent({
7144
7062
  return () => require__plugin_vue_export_helper.h("div", { class: "superdoc-v2-integration-missing" });
7145
7063
  }
7146
7064
  });
7147
- /**
7148
- * @returns {SuperDocV2Integration}
7149
- */
7150
7065
  function createStubV2Integration() {
7151
7066
  return {
7152
7067
  version: 1,
@@ -7159,30 +7074,25 @@ function createStubV2Integration() {
7159
7074
  isV2SyntheticTrackedChangeRow
7160
7075
  };
7161
7076
  }
7162
- /**
7163
- * Build the default v2 integration the runtime always uses. The v2
7164
- * browser shell is the only source of the editor runtime; no customer-provided
7165
- * integration is consulted. Falls back to the local stub only if the engine
7166
- * shell throws during construction (defensive, never selects v1).
7167
- *
7168
- * @returns {SuperDocV2Integration}
7169
- */
7077
+ function adoptEngineIntegration(engineIntegration) {
7078
+ const stub = createStubV2Integration();
7079
+ if (!require_create_super_doc_ui.isPlainObject(engineIntegration) || !engineIntegration.EditorComponent) return stub;
7080
+ const engine = engineIntegration;
7081
+ return {
7082
+ version: typeof engine.version === "number" ? engine.version : stub.version,
7083
+ capabilities: engine.capabilities ?? stub.capabilities,
7084
+ EditorComponent: engine.EditorComponent ?? stub.EditorComponent,
7085
+ RulerComponent: engine.RulerComponent ?? stub.RulerComponent,
7086
+ createGeometryPublisher: engine.createGeometryPublisher ?? stub.createGeometryPublisher,
7087
+ createReviewWindowController: engine.createReviewWindowController ?? stub.createReviewWindowController,
7088
+ isSyntheticTrackedChangeCommentLaneItem: engine.isSyntheticTrackedChangeCommentLaneItem ?? stub.isSyntheticTrackedChangeCommentLaneItem,
7089
+ isV2SyntheticTrackedChangeRow: engine.isV2SyntheticTrackedChangeRow ?? stub.isV2SyntheticTrackedChangeRow
7090
+ };
7091
+ }
7170
7092
  function createDefaultV2Integration() {
7171
7093
  if (!createSuperDocV2Integration) return createStubV2Integration();
7172
7094
  try {
7173
- const stub = createStubV2Integration();
7174
- const integration = createSuperDocV2Integration();
7175
- if (!integration || typeof integration !== "object" || !integration.EditorComponent) return stub;
7176
- return {
7177
- version: typeof integration.version === "number" ? integration.version : stub.version,
7178
- capabilities: integration.capabilities ?? stub.capabilities,
7179
- EditorComponent: integration.EditorComponent,
7180
- RulerComponent: integration.RulerComponent ?? stub.RulerComponent,
7181
- createGeometryPublisher: integration.createGeometryPublisher ?? stub.createGeometryPublisher,
7182
- createReviewWindowController: integration.createReviewWindowController ?? stub.createReviewWindowController,
7183
- isSyntheticTrackedChangeCommentLaneItem: integration.isSyntheticTrackedChangeCommentLaneItem ?? stub.isSyntheticTrackedChangeCommentLaneItem,
7184
- isV2SyntheticTrackedChangeRow: integration.isV2SyntheticTrackedChangeRow ?? stub.isV2SyntheticTrackedChangeRow
7185
- };
7095
+ return adoptEngineIntegration(createSuperDocV2Integration());
7186
7096
  } catch (error) {
7187
7097
  console.error("[SuperDoc] DOCX Engine integration failed to construct; using the stub.", error);
7188
7098
  return createStubV2Integration();
@@ -7474,20 +7384,6 @@ var buildTrackedChangeDecisionLinkIndex = (allComments, previous = /* @__PURE__
7474
7384
  };
7475
7385
  //#endregion
7476
7386
  //#region src/stores/comments-store.js
7477
- var Editor = class {
7478
- constructor() {
7479
- throw new Error("SuperDoc v2 beta: v1 editor-backed comment import paths are not available.");
7480
- }
7481
- };
7482
- var trackChangesHelpers = {
7483
- getTrackChanges: () => [],
7484
- enumerateStructuralRowChanges: () => []
7485
- };
7486
- var CommentsPluginKey = Symbol("comments-plugin-disabled");
7487
- var getRichTextExtensions = () => [];
7488
- var createOrUpdateTrackedChangeComment = () => null;
7489
- var getTrackedChangeIndex = () => null;
7490
- var resolveTrackedChangeInStory = () => null;
7491
7387
  var makeTrackedChangeAnchorKey = ({ storyKey, rawId }) => `tc::${storyKey}::${rawId}`;
7492
7388
  var COMMENT_DRAFT_BR_TAG = /<br(?:\s[^>]*)?\s*\/?>/gi;
7493
7389
  var COMMENT_DRAFT_BLOCK_BOUNDARY = /(?:<\/(?:p|div|li|blockquote|pre|h[1-6])\s*>\s*)+(?:<(?:p|div|li|blockquote|pre|h[1-6])(?:\s[^>]*)?\s*\/?>)+/gi;
@@ -7632,25 +7528,6 @@ var useCommentsStore = defineStore("comments", () => {
7632
7528
  rawId: String(rawId)
7633
7529
  });
7634
7530
  };
7635
- /**
7636
- * Compute the stable public id for a structural (whole-table) tracked change.
7637
- *
7638
- * This MUST match the public id the document-api projects for the same change
7639
- * (`tracked-change-resolver.groupTrackedChanges`), otherwise the right-rail
7640
- * accept/reject would route `trackChanges.decide` to a change that does not
7641
- * exist. The doc-api derives the public id as `word:structural:<sourceId>`
7642
- * for imported Word revisions, falling back to the per-import logical id.
7643
- * Reuse the exact rule here so the bubble id is the decide id.
7644
- *
7645
- * @param {{ sourceId?: string, id?: string }} structural
7646
- * @returns {string | null}
7647
- */
7648
- const buildStructuralTrackedChangeId = (structural) => {
7649
- if (!structural) return null;
7650
- const sourceId = structural.sourceId ? String(structural.sourceId) : "";
7651
- if (sourceId) return `word:structural:${sourceId}`;
7652
- return structural.id ? String(structural.id) : null;
7653
- };
7654
7531
  const superdocStore = useSuperdocStore();
7655
7532
  const commentsConfig = require__plugin_vue_export_helper.reactive({
7656
7533
  name: "comments",
@@ -7682,8 +7559,6 @@ var useCommentsStore = defineStore("comments", () => {
7682
7559
  const isDebugging = false;
7683
7560
  const debounceTimers = {};
7684
7561
  const trackedChangeResolutionSnapshots = /* @__PURE__ */ new WeakMap();
7685
- const importedTrackedChangeBootstrapTasks = /* @__PURE__ */ new Map();
7686
- let importedTrackedChangeBootstrapGeneration = 0;
7687
7562
  const isBenchmarkCommentsDebugEnabled = () => {
7688
7563
  if (typeof window === "undefined") return false;
7689
7564
  return "__labsSuperDocV2BenchmarkDebug" in window;
@@ -7710,6 +7585,11 @@ var useCommentsStore = defineStore("comments", () => {
7710
7585
  const suppressInternalExternal = require__plugin_vue_export_helper.ref(true);
7711
7586
  const currentCommentText = require__plugin_vue_export_helper.ref("");
7712
7587
  const currentCommentMentions = require__plugin_vue_export_helper.ref([]);
7588
+ let commentDraftRevision = 0;
7589
+ require__plugin_vue_export_helper.watch([currentCommentText, currentCommentMentions], () => commentDraftRevision++, {
7590
+ deep: true,
7591
+ flush: "sync"
7592
+ });
7713
7593
  const commentsList = require__plugin_vue_export_helper.ref([]);
7714
7594
  const reviewDirectoryList = require__plugin_vue_export_helper.shallowRef([]);
7715
7595
  let priorTrackedChangeThreadIndex = /* @__PURE__ */ new Map();
@@ -8043,17 +7923,6 @@ var useCommentsStore = defineStore("comments", () => {
8043
7923
  comment.resolvedByName = null;
8044
7924
  comment.trackedChangeDecision = null;
8045
7925
  };
8046
- const restoreResolvedMetadata = (comment) => {
8047
- if (!comment) return false;
8048
- const snapshot = trackedChangeResolutionSnapshots.get(comment);
8049
- if (!snapshot) return false;
8050
- comment.resolvedTime = snapshot.resolvedTime ?? Date.now();
8051
- comment.resolvedById = snapshot.resolvedById ?? null;
8052
- comment.resolvedByEmail = snapshot.resolvedByEmail ?? null;
8053
- comment.resolvedByName = snapshot.resolvedByName ?? null;
8054
- comment.trackedChangeDecision = snapshot.trackedChangeDecision ?? null;
8055
- return true;
8056
- };
8057
7926
  const getCommentEventPayload = (comment) => typeof comment?.getValues === "function" ? comment.getValues() : { ...comment };
8058
7927
  /**
8059
7928
  * Check if a comment originated from the document editor (or has no explicit source).
@@ -8068,129 +7937,14 @@ var useCommentsStore = defineStore("comments", () => {
8068
7937
  return source === DOCUMENT_EDITOR_SELECTION_SOURCE;
8069
7938
  };
8070
7939
  const isTrackedChangeThread = (comment) => Boolean(comment?.trackedChange) || Boolean(trackedChangeThreadParentIdForComment$1(comment));
8071
- const syncTrackedChangePositionsWithDocument = ({ documentId, editor } = {}) => {
8072
- if (!editor?.state) return 0;
8073
- if (!commentsList.value?.length) return 0;
8074
- const currentPositions = editorCommentPositions.value || {};
8075
- if (!Object.keys(currentPositions).length) return 0;
8076
- const resolveExistingPositionKey = (aliasIds) => aliasIds.find((key) => currentPositions[key] !== void 0) ?? null;
8077
- const candidateRootPositionKeys = /* @__PURE__ */ new Set();
8078
- const rootAliasesByPositionKey = /* @__PURE__ */ new Map();
8079
- commentsList.value.forEach((comment) => {
8080
- if (!comment?.trackedChange) return;
8081
- if (documentId) {
8082
- const resolvedDocumentId = comment?.fileId ?? null;
8083
- if (resolvedDocumentId && resolvedDocumentId !== documentId) return;
8084
- }
8085
- const aliasIds = getCommentAliasIds(comment);
8086
- const normalizedPositionKey = resolveExistingPositionKey(aliasIds);
8087
- if (!normalizedPositionKey) return;
8088
- candidateRootPositionKeys.add(normalizedPositionKey);
8089
- rootAliasesByPositionKey.set(normalizedPositionKey, new Set(aliasIds));
8090
- });
8091
- if (!candidateRootPositionKeys.size) return 0;
8092
- const trackedIds = new Set(trackChangesHelpers.getTrackChanges(editor.state).map(({ mark }) => mark?.attrs?.id).filter((id) => id !== void 0 && id !== null).map((id) => String(id)));
8093
- const trackedChangeIndex = typeof getTrackedChangeIndex === "function" ? getTrackedChangeIndex(editor) : null;
8094
- let liveAnchorKeySource = [];
8095
- try {
8096
- liveAnchorKeySource = trackedChangeIndex?.getAll?.() ?? [];
8097
- } catch {}
8098
- const liveAnchorKeys = new Set(liveAnchorKeySource.map((snapshot) => snapshot?.anchorKey).filter((anchorKey) => typeof anchorKey === "string" && anchorKey.length > 0));
8099
- const staleRootPositionKeys = new Set(Array.from(candidateRootPositionKeys).filter((positionKey) => {
8100
- const aliases = rootAliasesByPositionKey.get(positionKey) ?? /* @__PURE__ */ new Set([positionKey]);
8101
- if (Array.from(aliases).some((alias) => liveAnchorKeys.has(alias))) return false;
8102
- return !Array.from(aliases).some((alias) => trackedIds.has(alias));
8103
- }));
8104
- if (!staleRootPositionKeys.size) return 0;
8105
- const staleRootAliasIds = /* @__PURE__ */ new Set();
8106
- staleRootPositionKeys.forEach((positionKey) => {
8107
- (rootAliasesByPositionKey.get(positionKey) ?? /* @__PURE__ */ new Set([positionKey])).forEach((alias) => staleRootAliasIds.add(alias));
8108
- });
8109
- const stalePositionKeys = new Set(staleRootPositionKeys);
8110
- commentsList.value.forEach((comment) => {
8111
- const aliasIds = getCommentAliasIds(comment);
8112
- const normalizedPositionKey = resolveExistingPositionKey(aliasIds);
8113
- if (!normalizedPositionKey) return;
8114
- if ([
8115
- trackedChangeThreadParentIdForComment$1(comment),
8116
- comment?.threadingParentCommentId,
8117
- comment?.parentCommentId
8118
- ].map((id) => normalizeCommentId(id)).filter(Boolean).some((id) => staleRootAliasIds.has(id))) stalePositionKeys.add(normalizedPositionKey);
8119
- });
8120
- const nextPositions = { ...currentPositions };
8121
- stalePositionKeys.forEach((key) => {
8122
- delete nextPositions[key];
8123
- });
8124
- editorCommentPositions.value = nextPositions;
8125
- if (activeComment.value !== void 0 && activeComment.value !== null) {
8126
- const activeCommentModel = getComment(activeComment.value);
8127
- const activeAliases = new Set(getCommentAliasIds(activeCommentModel ?? activeComment.value));
8128
- const activeParentKeys = [
8129
- trackedChangeThreadParentIdForComment$1(activeCommentModel),
8130
- activeCommentModel?.threadingParentCommentId,
8131
- activeCommentModel?.parentCommentId
8132
- ].map((id) => normalizeCommentId(id)).filter(Boolean);
8133
- if (Array.from(activeAliases).some((id) => staleRootAliasIds.has(id)) || activeParentKeys.some((id) => staleRootAliasIds.has(id))) clearActiveCommentSelection();
8134
- }
8135
- return stalePositionKeys.size;
8136
- };
8137
- const collectStandardCommentDocumentState = (editor) => {
8138
- const doc = editor?.state?.doc;
8139
- if (!doc || typeof doc.descendants !== "function") return null;
8140
- const liveMarkIds = /* @__PURE__ */ new Set();
8141
- const resolvedAnchorIds = /* @__PURE__ */ new Set();
8142
- doc.descendants((node) => {
8143
- node.marks?.filter((mark) => mark?.type?.name === "commentMark").forEach((mark) => {
8144
- [mark.attrs?.commentId, mark.attrs?.importedId].map((id) => normalizeCommentId(id)).filter(Boolean).forEach((id) => liveMarkIds.add(id));
8145
- });
8146
- const typeName = node.type?.name;
8147
- if (typeName !== "commentRangeStart" && typeName !== "commentRangeEnd") return;
8148
- const anchorId = normalizeCommentId(node.attrs?.["w:id"]);
8149
- if (anchorId) resolvedAnchorIds.add(anchorId);
8150
- });
8151
- return {
8152
- liveMarkIds,
8153
- resolvedAnchorIds
8154
- };
8155
- };
8156
- /**
8157
- * Fall back to a live editor when the caller has none (the store watchers).
8158
- * `editorCommentPositions` refreshes asynchronously, so a positions-based
8159
- * decision taken mid-undo/redo can act on STALE keys — e.g. clearing the
8160
- * resolved metadata that the redo restore just wrote back. The document
8161
- * itself is the source of truth; prefer it whenever an editor is reachable.
8162
- */
8163
- const getFallbackCommentsEditor = () => {
8164
- const docs = superdocStore.documents;
8165
- if (!Array.isArray(docs)) return null;
8166
- for (const doc of docs) {
8167
- const editor = typeof doc?.getEditor === "function" ? doc.getEditor() : null;
8168
- if (editor?.state?.doc) return editor;
8169
- }
8170
- return null;
8171
- };
8172
- const syncResolvedCommentsWithDocument = ({ editor } = {}) => {
7940
+ const syncResolvedCommentsWithDocument = () => {
8173
7941
  if (v2CommentsAdapter.value) return 0;
8174
- const effectiveEditor = editor ?? getFallbackCommentsEditor();
8175
- const documentState = collectStandardCommentDocumentState(effectiveEditor);
8176
7942
  const docPositions = editorCommentPositions.value || {};
8177
7943
  const activeKeys = new Set(Object.keys(docPositions));
8178
- if (!documentState && !activeKeys.size) return 0;
7944
+ if (!activeKeys.size) return 0;
8179
7945
  let changed = 0;
8180
7946
  commentsList.value.forEach((comment) => {
8181
7947
  if (!isEditorBackedComment(comment) || isTrackedChangeThread(comment)) return;
8182
- if (documentState) {
8183
- const aliasIds = getCommentAliasIds(comment);
8184
- const hasLiveMark = aliasIds.some((id) => documentState.liveMarkIds.has(id));
8185
- const hasResolvedAnchor = aliasIds.some((id) => documentState.resolvedAnchorIds.has(id));
8186
- if (hasLiveMark && comment.resolvedTime != null) {
8187
- clearResolvedMetadata(comment);
8188
- changed += 1;
8189
- return;
8190
- }
8191
- if (!hasLiveMark && hasResolvedAnchor && comment.resolvedTime == null && restoreResolvedMetadata(comment)) changed += 1;
8192
- return;
8193
- }
8194
7948
  const { key } = resolveCommentPositionEntry(comment);
8195
7949
  if (!key) return;
8196
7950
  if (activeKeys.has(String(key)) && comment.resolvedTime != null) {
@@ -8208,23 +7962,6 @@ var useCommentsStore = defineStore("comments", () => {
8208
7962
  syncResolvedCommentsWithDocument();
8209
7963
  }, { deep: false });
8210
7964
  /**
8211
- * Normalize a position object to a consistent { start, end } format.
8212
- * Handles different editor position schemas (start/end, pos/to, from/to).
8213
- *
8214
- * @param {Object | null | undefined} position The position object
8215
- * @returns {{ start: number, end: number } | null} The normalized range or null
8216
- */
8217
- const getCommentPositionRange = (position) => {
8218
- if (!position) return null;
8219
- const start = position.start ?? position.pos ?? position.from;
8220
- const end = position.end ?? position.to ?? start;
8221
- if (!Number.isFinite(start) || !Number.isFinite(end)) return null;
8222
- return {
8223
- start,
8224
- end
8225
- };
8226
- };
8227
- /**
8228
7965
  * Get the editor position data for a comment.
8229
7966
  *
8230
7967
  * @param {Object | string} commentOrId The comment object or comment ID
@@ -8233,44 +7970,6 @@ var useCommentsStore = defineStore("comments", () => {
8233
7970
  const getCommentPosition = (commentOrId) => {
8234
7971
  return resolveCommentPositionEntry(commentOrId).entry ?? null;
8235
7972
  };
8236
- /**
8237
- * Get the text that a comment is anchored to in the document.
8238
- *
8239
- * @param {Object | string} commentOrId The comment object or comment ID
8240
- * @param {Object} [options] Options for text extraction
8241
- * @param {string} [options.separator=' '] Separator for textBetween when crossing nodes
8242
- * @param {boolean} [options.trim=true] Whether to trim whitespace from the result
8243
- * @returns {string | null} The anchored text or null if unavailable
8244
- */
8245
- const getCommentAnchoredText = (commentOrId, options = {}) => {
8246
- const comment = typeof commentOrId === "object" ? commentOrId : getComment(commentOrId);
8247
- if (!comment) return null;
8248
- const position = resolveCommentPositionEntry(commentOrId).entry ?? null;
8249
- const range = getCommentPositionRange(position);
8250
- if (!range) return null;
8251
- const docNode = (superdocStore.getDocument(comment.fileId)?.getEditor?.())?.state?.doc;
8252
- if (!docNode?.textBetween) return null;
8253
- const separator = options.separator ?? " ";
8254
- const text = docNode.textBetween(range.start, range.end, separator, separator);
8255
- return options.trim === false ? text : text?.trim();
8256
- };
8257
- /**
8258
- * Get both position and anchored text data for a comment.
8259
- *
8260
- * @param {Object | string} commentOrId The comment object or comment ID
8261
- * @param {Object} [options] Options passed to getCommentAnchoredText
8262
- * @param {string} [options.separator=' '] Separator for textBetween when crossing nodes
8263
- * @param {boolean} [options.trim=true] Whether to trim whitespace from the result
8264
- * @returns {{ position: Object, anchoredText: string | null } | null} The anchor data or null
8265
- */
8266
- const getCommentAnchorData = (commentOrId, options = {}) => {
8267
- const position = getCommentPosition(commentOrId);
8268
- if (!position) return null;
8269
- return {
8270
- position,
8271
- anchoredText: getCommentAnchoredText(commentOrId, options)
8272
- };
8273
- };
8274
7973
  const isThreadVisible = (comment) => {
8275
7974
  if (!isViewingMode.value) return true;
8276
7975
  const parent = getThreadParent(comment);
@@ -8315,7 +8014,7 @@ var useCommentsStore = defineStore("comments", () => {
8315
8014
  * @param {Object} param0.params The tracked change params
8316
8015
  * @returns {void}
8317
8016
  */
8318
- const handleTrackedChangeUpdate = ({ superdoc, params, broadcastChanges = true, documentState = void 0, trackedChangeIdentityIndex = null }) => {
8017
+ const handleTrackedChangeUpdate = ({ superdoc, params, broadcastChanges = true, trackedChangeIdentityIndex = null }) => {
8319
8018
  const span = startInteractionSpan("store.trackedChanges.handleUpdate", "store-reconciliation", {
8320
8019
  event: params?.event ?? null,
8321
8020
  changeId: params?.changeId ?? null,
@@ -8332,26 +8031,6 @@ var useCommentsStore = defineStore("comments", () => {
8332
8031
  const normalizedCustomAttributes = hasCustomAttributes ? normalizeTrackedChangeCustomAttributes(customAttributes) : void 0;
8333
8032
  const normalizedImportedId = hasImportedId ? importedId != null ? String(importedId) : null : void 0;
8334
8033
  const normalizedDocumentId = documentId != null ? String(documentId) : null;
8335
- if (!(trackedChangeDisplayType === "tableInsert" || trackedChangeDisplayType === "tableDelete") && normalizedChangeId) {
8336
- const effectiveDocumentId = normalizedDocumentId ?? (superdoc?.activeEditor?.options?.documentId != null ? String(superdoc.activeEditor.options.documentId) : null);
8337
- const docEditor = effectiveDocumentId ? superdocStore.getDocument(effectiveDocumentId)?.getEditor?.() : null;
8338
- const docState = documentState !== void 0 ? documentState : docEditor?.state ?? superdoc?.activeEditor?.state ?? null;
8339
- if (docState) {
8340
- const { ranges: tableRanges, ids: structuralIds } = computeTrackedTableSummaryForState(docState);
8341
- const ranges = trackChangesHelpers.getTrackChanges(docState, normalizedChangeId);
8342
- const inRange = tableRanges.length > 0 && ranges.length > 0 && isInlineRangeInsideTrackedTable(ranges, tableRanges);
8343
- const isStructuralRowIdEcho = ranges.length === 0 && structuralIds.has(normalizedChangeId);
8344
- if (inRange || isStructuralRowIdEcho) {
8345
- pruneSuppressedInlineTableComments({
8346
- suppressedIds: /* @__PURE__ */ new Set([normalizedChangeId]),
8347
- activeDocumentId: effectiveDocumentId,
8348
- superdoc,
8349
- broadcastChanges
8350
- });
8351
- return;
8352
- }
8353
- }
8354
- }
8355
8034
  const hasStoryMetadata = trackedChangeStory !== void 0 || trackedChangeStoryKind !== void 0 || trackedChangeStoryLabel !== void 0 || trackedChangeAnchorKey !== void 0;
8356
8035
  const normalizedTrackedChangeStory = hasStoryMetadata ? trackedChangeStory ?? null : BODY_TRACKED_CHANGE_STORY;
8357
8036
  const normalizedTrackedChangeStoryKind = hasStoryMetadata ? trackedChangeStoryKind ?? null : "body";
@@ -8545,38 +8224,6 @@ var useCommentsStore = defineStore("comments", () => {
8545
8224
  endInteractionSpan(span);
8546
8225
  }
8547
8226
  };
8548
- const collectTrackedChangeMarksByType = (trackedChanges = []) => ({
8549
- insertedMark: trackedChanges.find(({ mark }) => mark?.type?.name === "trackInsert")?.mark ?? null,
8550
- deletionMark: trackedChanges.find(({ mark }) => mark?.type?.name === "trackDelete")?.mark ?? null,
8551
- formatMark: trackedChanges.find(({ mark }) => mark?.type?.name === "trackFormat")?.mark ?? null
8552
- });
8553
- const refreshTrackedChangeCommentsByIds = ({ superdoc, editor, changeIds, broadcastChanges = true }) => {
8554
- if (!superdoc || !editor?.state || !Array.isArray(changeIds) || !changeIds.length) return;
8555
- const documentId = editor?.options?.documentId != null ? String(editor.options.documentId) : null;
8556
- if (!documentId) return;
8557
- for (const changeId of new Set(changeIds.map((id) => id != null ? String(id) : null).filter(Boolean))) {
8558
- const trackedChangesForId = trackChangesHelpers.getTrackChanges(editor.state, changeId);
8559
- if (!trackedChangesForId.length) continue;
8560
- const params = createOrUpdateTrackedChangeComment({
8561
- event: "update",
8562
- marks: collectTrackedChangeMarksByType(trackedChangesForId),
8563
- nodes: [],
8564
- newEditorState: editor.state,
8565
- documentId,
8566
- trackedChangesForId
8567
- });
8568
- if (!params) continue;
8569
- params.trackedChangeStory = BODY_TRACKED_CHANGE_STORY;
8570
- params.trackedChangeStoryKind = "body";
8571
- params.trackedChangeStoryLabel = "";
8572
- params.trackedChangeAnchorKey = buildBodyTrackedChangeAnchorKey(params.changeId ?? changeId);
8573
- handleTrackedChangeUpdate({
8574
- superdoc,
8575
- params,
8576
- broadcastChanges
8577
- });
8578
- }
8579
- };
8580
8227
  const requestInstantSidebarAlignment = (targetY = null, threadId = null, instanceId = null) => {
8581
8228
  const hasTargetY = Number.isFinite(targetY);
8582
8229
  instantSidebarAlignmentTargetY.value = hasTargetY ? targetY : null;
@@ -8912,9 +8559,12 @@ var useCommentsStore = defineStore("comments", () => {
8912
8559
  if (commentsAreReadOnly() && !isHydration) return readOnlyMutationOutcome();
8913
8560
  const v2Adapter = !skipEditorUpdate && !isHydration ? getV2CommentsAdapter(superdoc) : null;
8914
8561
  if (v2Adapter && !comment.trackedChange) {
8915
- const submittedPendingComment = pendingComment.value;
8562
+ const pendingDraft = pendingComment.value;
8563
+ const draftRevision = commentDraftRevision;
8564
+ const draftActiveComment = activeComment.value;
8565
+ const draftActiveInstance = activeFloatingCommentInstanceId.value;
8916
8566
  const submittedPendingRequest = pendingCommentRequest;
8917
- const text = normalizeV2CommentDraftText(submittedPendingComment ? currentCommentText.value : comment.commentText);
8567
+ const text = normalizeV2CommentDraftText(pendingDraft ? currentCommentText.value : comment.commentText);
8918
8568
  const mentions = pendingComment.value ? currentCommentMentions.value : comment.mentions ?? [];
8919
8569
  const target = pendingComment.value ? pendingV2CommentTarget.value : null;
8920
8570
  const parentCommentId = comment.parentCommentId ? String(comment.parentCommentId) : pendingComment.value ? null : null;
@@ -8950,6 +8600,7 @@ var useCommentsStore = defineStore("comments", () => {
8950
8600
  });
8951
8601
  return outcome;
8952
8602
  }
8603
+ const ownsActiveDraftBeforeReconcile = activeComment.value === draftActiveComment && activeFloatingCommentInstanceId.value === draftActiveInstance;
8953
8604
  const reconciled = reconcileCommentsFromV2({
8954
8605
  superdoc,
8955
8606
  adapter: v2Adapter,
@@ -8961,14 +8612,17 @@ var useCommentsStore = defineStore("comments", () => {
8961
8612
  reconciled,
8962
8613
  documentId: v2Adapter.documentId
8963
8614
  });
8964
- const hadPendingComment = submittedPendingComment != null && pendingComment.value?.commentId === submittedPendingComment.commentId;
8615
+ const createdId = created?.commentId ?? null;
8616
+ const createdIsActive = createdId !== null && activeComment.value === createdId;
8617
+ const createdInstance = createdIsActive ? activeFloatingCommentInstanceId.value : null;
8965
8618
  const ownsSubmittedRequest = pendingCommentRequest === submittedPendingRequest;
8966
- if (hadPendingComment) if (ownsSubmittedRequest) removePendingComment(superdoc);
8619
+ const reconciliationClearedActiveDraft = ownsActiveDraftBeforeReconcile && activeComment.value === null && activeFloatingCommentInstanceId.value === null;
8620
+ const ownsPendingDraft = pendingDraft !== null && pendingComment.value === pendingDraft && commentDraftRevision === draftRevision && (createdIsActive || reconciliationClearedActiveDraft || activeComment.value === draftActiveComment && activeFloatingCommentInstanceId.value === draftActiveInstance);
8621
+ if (ownsPendingDraft) if (ownsSubmittedRequest) removePendingComment(superdoc);
8967
8622
  else resetPendingCommentState(superdoc, false);
8968
- const createdId = created?.commentId ?? null;
8969
- if (hadPendingComment && ownsSubmittedRequest && createdId) {
8623
+ if (ownsPendingDraft && ownsSubmittedRequest && createdId) {
8970
8624
  setActiveComment(superdoc, createdId);
8971
- setActiveFloatingCommentInstance(createdId);
8625
+ setActiveFloatingCommentInstance(createdInstance ?? createdId);
8972
8626
  }
8973
8627
  if (broadcastChanges) superdoc.emit("comments-update", {
8974
8628
  type: COMMENT_EVENTS.ADD,
@@ -10186,178 +9840,6 @@ var useCommentsStore = defineStore("comments", () => {
10186
9840
  removePendingComment(superdoc);
10187
9841
  };
10188
9842
  /**
10189
- * Imported DOCX comments can omit the normalized author string.
10190
- * Strip the exporter suffix when present and tolerate missing metadata.
10191
- *
10192
- * @param {string | null | undefined} creatorName
10193
- * @returns {string | null}
10194
- */
10195
- const normalizeImportedCreatorName = (creatorName) => {
10196
- if (typeof creatorName !== "string") return null;
10197
- return creatorName.replace(/\s*\(imported\)\s*$/u, "").trim() || null;
10198
- };
10199
- /**
10200
- * Read the mounted editor view and state without touching `editor.state`.
10201
- * The editor convenience getter delegates to `view.state` and throws after
10202
- * teardown, which is precisely when a deferred import callback can run.
10203
- *
10204
- * @param {Object | null | undefined} editor
10205
- * @returns {{ view: Object, state: Object } | null}
10206
- */
10207
- const readMountedEditorSnapshot = (editor) => {
10208
- if (!editor) return null;
10209
- try {
10210
- if (editor.isDestroyed === true) return null;
10211
- const view = editor.view;
10212
- if (!view || view.isDestroyed === true || view.destroyed === true) return null;
10213
- const state = view.state;
10214
- if (!state) return null;
10215
- return {
10216
- view,
10217
- state
10218
- };
10219
- } catch {
10220
- return null;
10221
- }
10222
- };
10223
- const normalizeBootstrapDocumentId = (documentId) => documentId !== void 0 && documentId !== null && String(documentId).length > 0 ? String(documentId) : null;
10224
- /**
10225
- * Cancel deferred imported tracked-change comment work. Passing a document
10226
- * id cancels only that document; omitting it cancels every pending task.
10227
- *
10228
- * @param {string | null | undefined} documentId
10229
- * @returns {number} number of invalidated tasks
10230
- */
10231
- const cancelImportedTrackedChangeBootstrap = (documentId) => {
10232
- const normalizedDocumentId = normalizeBootstrapDocumentId(documentId);
10233
- const entries = normalizedDocumentId ? [[normalizedDocumentId, importedTrackedChangeBootstrapTasks.get(normalizedDocumentId)]] : Array.from(importedTrackedChangeBootstrapTasks.entries());
10234
- let canceled = 0;
10235
- for (const [taskDocumentId, task] of entries) {
10236
- if (!task) continue;
10237
- importedTrackedChangeBootstrapTasks.delete(taskDocumentId);
10238
- if (task.timeoutHandle !== null && task.timeoutHandle !== void 0) clearTimeout(task.timeoutHandle);
10239
- canceled += 1;
10240
- }
10241
- return canceled;
10242
- };
10243
- const captureImportedTrackedChangeBootstrapSnapshot = (editor) => {
10244
- const mountedEditor = readMountedEditorSnapshot(editor);
10245
- if (!mountedEditor) return null;
10246
- let trackedChanges = [];
10247
- try {
10248
- trackedChanges = trackChangesHelpers.getTrackChanges(mountedEditor.state) ?? [];
10249
- } catch {
10250
- return null;
10251
- }
10252
- const enumerateStructuralChanges = trackChangesHelpers?.enumerateStructuralRowChanges;
10253
- let structuralChanges = [];
10254
- if (typeof enumerateStructuralChanges === "function") try {
10255
- structuralChanges = enumerateStructuralChanges(mountedEditor.state) ?? [];
10256
- } catch {
10257
- structuralChanges = [];
10258
- }
10259
- let storySnapshots = [];
10260
- if (typeof getTrackedChangeIndex === "function") try {
10261
- storySnapshots = getTrackedChangeIndex(editor)?.getAll?.() ?? [];
10262
- } catch {
10263
- storySnapshots = [];
10264
- }
10265
- return {
10266
- ...mountedEditor,
10267
- trackedChanges,
10268
- structuralChanges,
10269
- storySnapshots
10270
- };
10271
- };
10272
- /**
10273
- * Bootstrap tracked-change comment threads after a DOCX import finishes.
10274
- *
10275
- * Initial import historically rebuilt only body tracked-change threads so
10276
- * resolved imported body comments stayed resolved. Header/footer and note
10277
- * tracked changes live outside the body PM state, so they need an additional
10278
- * story-aware bootstrap pass here.
10279
- *
10280
- * We intentionally keep the existing body-only rebuild instead of switching
10281
- * to the broader syncTrackedChangeComments() path so imported resolved body
10282
- * tracked-change threads preserve their initial resolved state.
10283
- *
10284
- * @param {Object | null | undefined} editor
10285
- * @param {Object | null | undefined} superdoc
10286
- * @param {Object | null | undefined} snapshot
10287
- * @returns {boolean}
10288
- */
10289
- const bootstrapImportedTrackedChangeComments = (editor, superdoc, snapshot = null) => {
10290
- if (!editor || !superdoc) return false;
10291
- const captured = snapshot ?? captureImportedTrackedChangeBootstrapSnapshot(editor);
10292
- if (!captured) return false;
10293
- createCommentForTrackChanges(editor, superdoc, captured.trackedChanges, {
10294
- editorState: captured.state,
10295
- editorView: captured.view
10296
- });
10297
- syncStoryTrackedChangeComments({
10298
- superdoc,
10299
- editor,
10300
- snapshots: captured.storySnapshots,
10301
- documentState: captured.state,
10302
- resolveFromEditor: false
10303
- });
10304
- syncStructuralTrackedChangeComments({
10305
- superdoc,
10306
- editor,
10307
- structuralChanges: captured.structuralChanges
10308
- });
10309
- return true;
10310
- };
10311
- const isCurrentImportedTrackedChangeBootstrap = (task) => {
10312
- if (!task) return false;
10313
- if (importedTrackedChangeBootstrapTasks.get(task.documentId) !== task) return false;
10314
- try {
10315
- const editorDocumentId = task.editor?.options?.documentId;
10316
- if (editorDocumentId != null && String(editorDocumentId) !== task.documentId) return false;
10317
- const mountedDocument = superdocStore.getDocument(task.documentId);
10318
- if (!mountedDocument) return false;
10319
- if (mountedDocument.getEditor?.() !== task.editor) return false;
10320
- } catch {
10321
- return false;
10322
- }
10323
- return true;
10324
- };
10325
- const runImportedTrackedChangeBootstrap = (task) => {
10326
- if (!isCurrentImportedTrackedChangeBootstrap(task)) {
10327
- if (importedTrackedChangeBootstrapTasks.get(task?.documentId) === task) importedTrackedChangeBootstrapTasks.delete(task.documentId);
10328
- return false;
10329
- }
10330
- const snapshot = captureImportedTrackedChangeBootstrapSnapshot(task.editor);
10331
- if (!snapshot || !isCurrentImportedTrackedChangeBootstrap(task)) {
10332
- if (importedTrackedChangeBootstrapTasks.get(task.documentId) === task) importedTrackedChangeBootstrapTasks.delete(task.documentId);
10333
- return false;
10334
- }
10335
- try {
10336
- return bootstrapImportedTrackedChangeComments(task.editor, task.superdoc, snapshot);
10337
- } finally {
10338
- if (importedTrackedChangeBootstrapTasks.get(task.documentId) === task) importedTrackedChangeBootstrapTasks.delete(task.documentId);
10339
- }
10340
- };
10341
- const scheduleImportedTrackedChangeBootstrap = ({ editor, superdoc, documentId, defer = true }) => {
10342
- const normalizedDocumentId = normalizeBootstrapDocumentId(documentId);
10343
- if (!normalizedDocumentId || !editor || !superdoc) return false;
10344
- cancelImportedTrackedChangeBootstrap(normalizedDocumentId);
10345
- const task = {
10346
- documentId: normalizedDocumentId,
10347
- editor,
10348
- superdoc,
10349
- generation: ++importedTrackedChangeBootstrapGeneration,
10350
- timeoutHandle: null
10351
- };
10352
- importedTrackedChangeBootstrapTasks.set(normalizedDocumentId, task);
10353
- if (!defer) return runImportedTrackedChangeBootstrap(task);
10354
- task.timeoutHandle = setTimeout(() => {
10355
- task.timeoutHandle = null;
10356
- runImportedTrackedChangeBootstrap(task);
10357
- }, 0);
10358
- return true;
10359
- };
10360
- /**
10361
9843
  * Drop imported threads and cached positions for one document id.
10362
9844
  *
10363
9845
  * Replace-file swaps call this before hydrating the replacement so stale
@@ -10374,7 +9856,6 @@ var useCommentsStore = defineStore("comments", () => {
10374
9856
  delete next[activeDocumentId];
10375
9857
  committedReviewPresentations.value = next;
10376
9858
  }
10377
- cancelImportedTrackedChangeBootstrap(activeDocumentId);
10378
9859
  const removedComments = commentsList.value.filter((comment) => belongsToDocument(comment, activeDocumentId, { allowSingleDocumentMismatch: true }));
10379
9860
  if (!removedComments.length) return;
10380
9861
  const removedAliasIds = /* @__PURE__ */ new Set();
@@ -10392,217 +9873,6 @@ var useCommentsStore = defineStore("comments", () => {
10392
9873
  const activeCommentId = activeComment.value != null ? String(activeComment.value) : null;
10393
9874
  if (activeCommentId && removedAliasIds.has(activeCommentId)) clearActiveCommentSelection();
10394
9875
  }
10395
- function resetCommentsForReplacedDocument(documentId) {
10396
- removeCommentsForDocument(documentId);
10397
- }
10398
- /**
10399
- * Initialize loaded comments into SuperDoc by mapping the imported
10400
- * comment data to SuperDoc useComment objects.
10401
- *
10402
- * Updates the commentsList ref with the new comments.
10403
- *
10404
- * @param {Object} param0
10405
- * @param {Array} param0.comments The comments to be loaded
10406
- * @param {String} param0.documentId The document ID
10407
- * @param {boolean} [param0.replacedFile] Whether this load replaces an existing document in place
10408
- * @returns {void}
10409
- */
10410
- const processLoadedDocxComments = async ({ superdoc, editor, comments, documentId, replacedFile = false }) => {
10411
- const document = superdocStore.getDocument(documentId);
10412
- if (document?.commentThreadingProfile) document.commentThreadingProfile.value = editor?.converter?.commentThreadingProfile || null;
10413
- if (replacedFile) resetCommentsForReplacedDocument(documentId);
10414
- comments.forEach((comment) => {
10415
- const textElements = Array.isArray(comment.elements) ? comment.elements : [];
10416
- const htmlContent = getHtmlFromComment(textElements);
10417
- if (!htmlContent && !comment.trackedChange) return;
10418
- const creatorName = normalizeImportedCreatorName(comment.creatorName);
10419
- const importedName = creatorName ? `${creatorName} (imported)` : null;
10420
- const newComment = useComment({
10421
- fileId: documentId,
10422
- fileType: document.type,
10423
- docxCommentJSON: textElements.length ? textElements : null,
10424
- commentId: comment.commentId,
10425
- isInternal: false,
10426
- parentCommentId: comment.parentCommentId,
10427
- trackedChangeParentId: comment.trackedChangeParentId,
10428
- trackedChangeThreadParentId: comment.trackedChangeThreadParentId,
10429
- creatorId: null,
10430
- creatorName,
10431
- createdTime: comment.createdTime,
10432
- creatorEmail: comment.creatorEmail,
10433
- importedAuthor: {
10434
- ...importedName ? { name: importedName } : {},
10435
- email: comment.creatorEmail
10436
- },
10437
- commentText: htmlContent,
10438
- resolvedTime: comment.isDone ? Date.now() : null,
10439
- resolvedById: null,
10440
- resolvedByEmail: comment.isDone ? comment.creatorEmail : null,
10441
- resolvedByName: comment.isDone ? importedName || "(Imported)" : null,
10442
- trackedChange: comment.trackedChange || false,
10443
- trackedChangeText: comment.trackedChangeText,
10444
- trackedChangeType: comment.trackedChangeType,
10445
- trackedChangeDisplayType: comment.trackedChangeDisplayType,
10446
- deletedText: comment.trackedDeletedText,
10447
- origin: comment.origin || "word",
10448
- threadingMethod: comment.threadingMethod,
10449
- threadingStyleOverride: comment.threadingStyleOverride,
10450
- threadingParentCommentId: comment.threadingParentCommentId,
10451
- originalXmlStructure: comment.originalXmlStructure
10452
- });
10453
- addHydratedComment({
10454
- superdoc,
10455
- comment: newComment
10456
- });
10457
- });
10458
- if (replacedFile) {
10459
- scheduleImportedTrackedChangeBootstrap({
10460
- editor,
10461
- superdoc,
10462
- documentId,
10463
- defer: false
10464
- });
10465
- return;
10466
- }
10467
- scheduleImportedTrackedChangeBootstrap({
10468
- editor,
10469
- superdoc,
10470
- documentId
10471
- });
10472
- };
10473
- const createCommentForTrackChanges = (editor, superdoc, trackedChangesOverride = null, options = {}) => {
10474
- const { reopenResolved = false, refreshExisting = false, broadcastChanges = true, editorState: editorStateOverride = null, editorView: editorViewOverride = null } = options;
10475
- const mountedEditor = editorStateOverride ? {
10476
- state: editorStateOverride,
10477
- view: editorViewOverride
10478
- } : readMountedEditorSnapshot(editor);
10479
- if (!mountedEditor?.state) return;
10480
- const editorState = mountedEditor.state;
10481
- const trackedChanges = trackedChangesOverride ?? trackChangesHelpers.getTrackChanges(editorState);
10482
- const groupedChanges = groupChanges(trackedChanges);
10483
- const activeDocumentId = editor?.options?.documentId != null ? String(editor.options.documentId) : null;
10484
- if (!activeDocumentId) return;
10485
- const skipIds = /* @__PURE__ */ new Set();
10486
- const existingTrackedChangeById = /* @__PURE__ */ new Map();
10487
- commentsList.value.forEach((comment) => {
10488
- if (!comment?.trackedChange) return;
10489
- if (!belongsToTrackedChangeSyncDocument(comment, activeDocumentId)) return;
10490
- if (!isBodyTrackedChangeComment(comment)) return;
10491
- const commentIds = [comment.commentId, comment.importedId].map((id) => id != null ? String(id) : null).filter(Boolean);
10492
- if (comment.resolvedTime) {
10493
- if (!reopenResolved) commentIds.forEach((id) => skipIds.add(id));
10494
- return;
10495
- }
10496
- commentIds.forEach((id) => {
10497
- existingTrackedChangeById.set(id, comment);
10498
- if (!refreshExisting) skipIds.add(id);
10499
- });
10500
- });
10501
- const changesByIdMap = /* @__PURE__ */ new Map();
10502
- for (const change of trackedChanges) {
10503
- const id = change.mark.attrs.id;
10504
- if (!changesByIdMap.has(id)) changesByIdMap.set(id, []);
10505
- changesByIdMap.get(id).push(change);
10506
- }
10507
- const documentId = activeDocumentId;
10508
- const processedIds = /* @__PURE__ */ new Set();
10509
- groupedChanges.forEach(({ insertedMark, deletionMark, formatMark }) => {
10510
- const id = insertedMark?.mark.attrs.id || deletionMark?.mark.attrs.id || formatMark?.mark.attrs.id;
10511
- if (id == null) return;
10512
- const normalizedId = String(id);
10513
- if (processedIds.has(normalizedId)) return;
10514
- processedIds.add(normalizedId);
10515
- if (!refreshExisting && skipIds.has(normalizedId)) return;
10516
- const existingTrackedChange = existingTrackedChangeById.get(normalizedId);
10517
- const marks = {
10518
- ...insertedMark && { insertedMark: insertedMark.mark },
10519
- ...deletionMark && { deletionMark: deletionMark.mark },
10520
- ...formatMark && { formatMark: formatMark.mark }
10521
- };
10522
- const params = createOrUpdateTrackedChangeComment({
10523
- event: existingTrackedChange ? "update" : "add",
10524
- marks,
10525
- nodes: [],
10526
- newEditorState: editorState,
10527
- documentId,
10528
- trackedChangesForId: changesByIdMap.get(id) || []
10529
- });
10530
- if (params) {
10531
- const anchorKey = buildBodyTrackedChangeAnchorKey(params.changeId ?? id);
10532
- params.trackedChangeStory = BODY_TRACKED_CHANGE_STORY;
10533
- params.trackedChangeStoryKind = "body";
10534
- params.trackedChangeStoryLabel = "";
10535
- params.trackedChangeAnchorKey = anchorKey;
10536
- handleTrackedChangeUpdate({
10537
- superdoc,
10538
- params,
10539
- broadcastChanges,
10540
- documentState: editorState
10541
- });
10542
- if (!existingTrackedChange) {
10543
- skipIds.add(normalizedId);
10544
- if (params.changeId != null) skipIds.add(String(params.changeId));
10545
- if (params.importedId != null) skipIds.add(String(params.importedId));
10546
- }
10547
- }
10548
- });
10549
- const editorView = mountedEditor.view;
10550
- try {
10551
- if (!editorView || editor.view !== editorView || editorView.isDestroyed === true || editorView.destroyed === true) return;
10552
- } catch {
10553
- return;
10554
- }
10555
- const { tr } = editorState;
10556
- tr.setMeta(CommentsPluginKey, { type: "force" });
10557
- editorView.dispatch(tr);
10558
- };
10559
- /**
10560
- * Remove inline tracked-change comments whose change id is subsumed by a
10561
- * tracked whole-table change. Mirrors the deletion-event emission of
10562
- * `pruneStaleTrackedChangeComments` but is keyed on an explicit suppressed-id
10563
- * set rather than mark liveness (the mark is still live; only the review item
10564
- * is unwanted). Also clears the active-comment selection if it pointed at a
10565
- * now-suppressed change, so no stale active-on-click dialog can reference it.
10566
- *
10567
- * @param {{ suppressedIds: Set<string>, activeDocumentId: string, superdoc: any, broadcastChanges?: boolean }} input
10568
- */
10569
- const pruneSuppressedInlineTableComments = ({ suppressedIds, activeDocumentId, superdoc = null, broadcastChanges = true }) => {
10570
- if (!(suppressedIds instanceof Set) || !suppressedIds.size || !activeDocumentId) return;
10571
- const removedComments = [];
10572
- commentsList.value = commentsList.value.filter((comment) => {
10573
- if (!comment?.trackedChange) return true;
10574
- if (!isBodyTrackedChangeComment(comment)) return true;
10575
- if (isStructuralTableBubble(comment)) return true;
10576
- if (!belongsToTrackedChangeSyncDocument(comment, activeDocumentId)) return true;
10577
- const commentId = comment.commentId != null ? String(comment.commentId) : null;
10578
- const importedId = comment.importedId != null ? String(comment.importedId) : null;
10579
- if (!(commentId && suppressedIds.has(commentId) || importedId && suppressedIds.has(importedId))) return true;
10580
- removedComments.push(comment);
10581
- return false;
10582
- });
10583
- if (!removedComments.length) return;
10584
- const removedIds = /* @__PURE__ */ new Set();
10585
- removedComments.forEach((comment) => {
10586
- if (comment.commentId != null) removedIds.add(String(comment.commentId));
10587
- if (comment.importedId != null) removedIds.add(String(comment.importedId));
10588
- const payload = getCommentEventPayload(comment);
10589
- const event = {
10590
- type: COMMENT_EVENTS.DELETED,
10591
- comment: payload,
10592
- changes: [{
10593
- key: "deleted",
10594
- commentId: payload.commentId,
10595
- fileId: payload.fileId
10596
- }]
10597
- };
10598
- if (broadcastChanges) {
10599
- syncCommentsToClients(superdoc, event);
10600
- superdoc?.emit?.("comments-update", event);
10601
- }
10602
- });
10603
- const activeCommentId = activeComment.value != null ? String(activeComment.value) : null;
10604
- if (activeCommentId && removedIds.has(activeCommentId)) clearActiveCommentSelection();
10605
- };
10606
9876
  const getCommentDocumentId = (comment) => {
10607
9877
  if (!comment) return null;
10608
9878
  if (comment.fileId != null) return String(comment.fileId);
@@ -11458,286 +10728,6 @@ var useCommentsStore = defineStore("comments", () => {
11458
10728
  return rowDocumentId != null && String(rowDocumentId) === normalizedDocumentId;
11459
10729
  }).length;
11460
10730
  };
11461
- const syncTrackedChangeComments = ({ superdoc, editor, broadcastChanges = true }) => {
11462
- if (!superdoc || !editor) return;
11463
- const activeDocumentId = editor?.options?.documentId != null ? String(editor.options.documentId) : null;
11464
- if (!activeDocumentId) return;
11465
- const captured = captureImportedTrackedChangeBootstrapSnapshot(editor);
11466
- if (!captured) return;
11467
- const trackedChanges = captured.trackedChanges;
11468
- const liveTrackedChangeIds = /* @__PURE__ */ new Set();
11469
- trackedChanges.forEach((change) => {
11470
- const id = change?.mark?.attrs?.id;
11471
- if (id == null) return;
11472
- liveTrackedChangeIds.add(String(id));
11473
- });
11474
- const storySnapshots = captured.storySnapshots;
11475
- const liveTrackedChangeAnchorKeys = new Set(storySnapshots.map((snapshot) => snapshot?.anchorKey).filter((anchorKey) => typeof anchorKey === "string" && anchorKey.length > 0));
11476
- pruneStaleTrackedChangeComments(liveTrackedChangeIds, liveTrackedChangeAnchorKeys, activeDocumentId, superdoc, { broadcastChanges });
11477
- createCommentForTrackChanges(editor, superdoc, trackedChanges, {
11478
- reopenResolved: true,
11479
- refreshExisting: true,
11480
- broadcastChanges,
11481
- editorState: captured.state,
11482
- editorView: captured.view
11483
- });
11484
- syncStoryTrackedChangeComments({
11485
- superdoc,
11486
- editor,
11487
- broadcastChanges,
11488
- snapshots: storySnapshots,
11489
- documentState: captured.state
11490
- });
11491
- syncStructuralTrackedChangeComments({
11492
- superdoc,
11493
- editor,
11494
- broadcastChanges,
11495
- structuralChanges: captured.structuralChanges
11496
- });
11497
- };
11498
- /**
11499
- * Surface decidable whole-table structural tracked changes (table insert /
11500
- * table delete) as right-rail bubbles, mirroring the inline tracked-change
11501
- * path. Structural row revisions live on node attrs (not inline marks), so
11502
- * the inline `getTrackChanges` enumerator never sees them — they are
11503
- * enumerated separately here.
11504
- *
11505
- * Only `decidable` whole-table changes are surfaced; partial/mixed shapes are
11506
- * not actionable (the decision engine fails them closed) so they get no
11507
- * bubble. The bubble id is the document-api public id so accept/reject in the
11508
- * sidebar routes `trackChanges.decide` to the same change.
11509
- *
11510
- * Positioning: the bubble carries a body-story anchorKey (matching the
11511
- * tracked-change index snapshot) and a body PM range (table start/end). The
11512
- * DocumentRendererRuntime position pass emits a body-story position entry for that
11513
- * anchorKey, whose bounds resolve through the same `getRangeRects` path inline
11514
- * body comments/TC use — so it lines up with the table in layout-engine
11515
- * viewing mode.
11516
- */
11517
- const syncStructuralTrackedChangeComments = ({ superdoc, editor, broadcastChanges = true, structuralChanges: structuralChangesOverride = null }) => {
11518
- const activeDocumentId = editor?.options?.documentId != null ? String(editor.options.documentId) : null;
11519
- if (!activeDocumentId) return;
11520
- const enumerate = trackChangesHelpers?.enumerateStructuralRowChanges;
11521
- if (typeof enumerate !== "function") return;
11522
- let structuralChanges = structuralChangesOverride;
11523
- if (!Array.isArray(structuralChanges)) {
11524
- const mountedEditor = readMountedEditorSnapshot(editor);
11525
- if (!mountedEditor) return;
11526
- try {
11527
- structuralChanges = enumerate(mountedEditor.state) ?? [];
11528
- } catch {
11529
- structuralChanges = [];
11530
- }
11531
- }
11532
- for (const structural of structuralChanges) {
11533
- if (!structural?.decidable || !structural?.wholeTable) continue;
11534
- const publicId = buildStructuralTrackedChangeId(structural);
11535
- if (!publicId) continue;
11536
- const anchorKey = buildBodyTrackedChangeAnchorKey(publicId);
11537
- const displayType = structural.subtype === "table-insert" ? "tableInsert" : "tableDelete";
11538
- const trackedChangeType = structural.side === "insertion" ? "trackInsert" : "trackDelete";
11539
- const normalizedPublicId = String(publicId);
11540
- const normalizedAnchorKey = anchorKey != null ? String(anchorKey) : null;
11541
- const params = {
11542
- event: commentsList.value.find((comment) => {
11543
- if (!comment?.trackedChange) return false;
11544
- if (!belongsToTrackedChangeSyncDocument(comment, activeDocumentId)) return false;
11545
- const commentAnchorKey = comment.trackedChangeAnchorKey != null ? String(comment.trackedChangeAnchorKey) : null;
11546
- if (normalizedAnchorKey && commentAnchorKey) return commentAnchorKey === normalizedAnchorKey;
11547
- const commentId = comment.commentId != null ? String(comment.commentId) : null;
11548
- const importedId = comment.importedId != null ? String(comment.importedId) : null;
11549
- return commentId === normalizedPublicId || importedId === normalizedPublicId;
11550
- }) ? "update" : "add",
11551
- changeId: publicId,
11552
- trackedChangeText: "",
11553
- trackedChangeType,
11554
- trackedChangeDisplayType: displayType,
11555
- deletedText: null,
11556
- authorId: null,
11557
- authorEmail: structural.authorEmail || null,
11558
- authorImage: structural.authorImage || null,
11559
- date: structural.date || null,
11560
- author: structural.author || null,
11561
- importedAuthor: structural.importedAuthor ? { name: structural.importedAuthor } : null,
11562
- semanticColorKey: structural.semanticColorKey ?? null,
11563
- semanticColor: structural.semanticColor ?? null,
11564
- documentId: activeDocumentId,
11565
- coords: null,
11566
- trackedChangeStory: BODY_TRACKED_CHANGE_STORY,
11567
- trackedChangeStoryKind: "body",
11568
- trackedChangeStoryLabel: "",
11569
- trackedChangeAnchorKey: anchorKey
11570
- };
11571
- handleTrackedChangeUpdate({
11572
- superdoc,
11573
- params,
11574
- broadcastChanges
11575
- });
11576
- }
11577
- };
11578
- const syncStoryTrackedChangeComments = ({ superdoc, editor, broadcastChanges = true, snapshots = null, documentState = void 0, resolveFromEditor = true }) => {
11579
- const activeDocumentId = editor?.options?.documentId != null ? String(editor.options.documentId) : null;
11580
- if (!activeDocumentId) return;
11581
- let resolvedSnapshots = snapshots;
11582
- if (!Array.isArray(resolvedSnapshots)) {
11583
- if (typeof getTrackedChangeIndex !== "function") return;
11584
- if (!readMountedEditorSnapshot(editor)) return;
11585
- let index = null;
11586
- try {
11587
- index = getTrackedChangeIndex(editor);
11588
- } catch {
11589
- return;
11590
- }
11591
- if (!index) return;
11592
- try {
11593
- resolvedSnapshots = index.getAll();
11594
- } catch {
11595
- return;
11596
- }
11597
- }
11598
- for (const snapshot of resolvedSnapshots) {
11599
- if (snapshot.storyKind === "body") continue;
11600
- upsertStoryTrackedChangeComment({
11601
- superdoc,
11602
- editor,
11603
- snapshot,
11604
- documentId: activeDocumentId,
11605
- broadcastChanges,
11606
- documentState,
11607
- resolveFromEditor
11608
- });
11609
- }
11610
- };
11611
- const getStoryTrackedChangeDisplayType = (snapshot) => {
11612
- if (snapshot?.type !== "structural") return snapshot?.type ?? null;
11613
- return snapshot?.subtype === "table-delete" ? "tableDelete" : "tableInsert";
11614
- };
11615
- const getStoryTrackedChangeType = (snapshot) => {
11616
- if (snapshot?.type !== "structural") return snapshot?.type ?? null;
11617
- return snapshot?.subtype === "table-delete" ? "trackDelete" : "trackInsert";
11618
- };
11619
- const isStoryTrackedChangeDeletion = (snapshot) => {
11620
- if (!snapshot) return false;
11621
- if (snapshot.type === "delete") return true;
11622
- return snapshot.type === "structural" && snapshot.subtype === "table-delete";
11623
- };
11624
- const buildStoryTrackedChangeParams = ({ editor, snapshot, documentId, event, resolveFromEditor = true }) => {
11625
- const trackedChangeType = getStoryTrackedChangeType(snapshot);
11626
- const trackedChangeDisplayType = getStoryTrackedChangeDisplayType(snapshot);
11627
- const fallbackParams = {
11628
- event,
11629
- changeId: snapshot.runtimeRef.rawId,
11630
- trackedChangeText: isStoryTrackedChangeDeletion(snapshot) ? "" : snapshot.excerpt ?? "",
11631
- trackedChangeType,
11632
- trackedChangeDisplayType,
11633
- semanticColorKey: snapshot.semanticColorKey ?? null,
11634
- semanticColor: snapshot.semanticColor ?? null,
11635
- deletedText: isStoryTrackedChangeDeletion(snapshot) ? snapshot.excerpt ?? "" : null,
11636
- authorId: snapshot.authorId,
11637
- authorEmail: snapshot.authorEmail,
11638
- authorImage: snapshot.authorImage,
11639
- date: snapshot.date,
11640
- author: snapshot.author,
11641
- documentId,
11642
- coords: null,
11643
- trackedChangeStory: snapshot.story,
11644
- trackedChangeStoryKind: snapshot.storyKind,
11645
- trackedChangeStoryLabel: snapshot.storyLabel,
11646
- trackedChangeAnchorKey: snapshot.anchorKey
11647
- };
11648
- if (!resolveFromEditor || typeof resolveTrackedChangeInStory !== "function") return fallbackParams;
11649
- let resolvedChange = null;
11650
- try {
11651
- resolvedChange = resolveTrackedChangeInStory(editor, {
11652
- kind: "entity",
11653
- entityType: "trackedChange",
11654
- entityId: snapshot.runtimeRef.rawId,
11655
- story: snapshot.story
11656
- });
11657
- } catch {
11658
- resolvedChange = null;
11659
- }
11660
- const storyEditorState = resolvedChange?.editor?.state ?? null;
11661
- if (!storyEditorState) return fallbackParams;
11662
- let trackedChangesForId = [];
11663
- try {
11664
- trackedChangesForId = trackChangesHelpers.getTrackChanges(storyEditorState, resolvedChange.change.rawId) ?? [];
11665
- } catch {
11666
- trackedChangesForId = [];
11667
- }
11668
- const resolvedParams = createOrUpdateTrackedChangeComment({
11669
- event,
11670
- marks: collectTrackedChangeMarksByType(trackedChangesForId),
11671
- nodes: [],
11672
- newEditorState: storyEditorState,
11673
- documentId,
11674
- trackedChangesForId
11675
- });
11676
- if (!resolvedParams) return fallbackParams;
11677
- if (resolvedParams.semanticColorKey == null && snapshot.semanticColorKey != null) resolvedParams.semanticColorKey = snapshot.semanticColorKey;
11678
- if (resolvedParams.semanticColor == null && snapshot.semanticColor != null) resolvedParams.semanticColor = snapshot.semanticColor;
11679
- resolvedParams.trackedChangeStory = snapshot.story;
11680
- resolvedParams.trackedChangeStoryKind = snapshot.storyKind;
11681
- resolvedParams.trackedChangeStoryLabel = snapshot.storyLabel;
11682
- resolvedParams.trackedChangeAnchorKey = snapshot.anchorKey;
11683
- return resolvedParams;
11684
- };
11685
- const upsertStoryTrackedChangeComment = ({ superdoc, editor, snapshot, documentId, broadcastChanges, documentState, resolveFromEditor }) => {
11686
- if (!snapshot?.runtimeRef?.rawId) return;
11687
- const existingComment = commentsList.value.find((comment) => {
11688
- if (!comment?.trackedChange) return false;
11689
- const commentAnchorKey = comment.trackedChangeAnchorKey != null ? String(comment.trackedChangeAnchorKey) : null;
11690
- if (commentAnchorKey && snapshot.anchorKey) return commentAnchorKey === snapshot.anchorKey;
11691
- if (commentAnchorKey || snapshot.anchorKey) return false;
11692
- return comment.commentId === snapshot.runtimeRef.rawId || comment.importedId === snapshot.runtimeRef.rawId;
11693
- });
11694
- const params = buildStoryTrackedChangeParams({
11695
- editor,
11696
- snapshot,
11697
- documentId,
11698
- event: existingComment ? "update" : "add",
11699
- resolveFromEditor
11700
- });
11701
- handleTrackedChangeUpdate({
11702
- superdoc,
11703
- params,
11704
- broadcastChanges,
11705
- documentState
11706
- });
11707
- if (existingComment) {
11708
- existingComment.trackedChangeStory = snapshot.story;
11709
- existingComment.trackedChangeStoryKind = snapshot.storyKind;
11710
- existingComment.trackedChangeStoryLabel = snapshot.storyLabel;
11711
- existingComment.trackedChangeAnchorKey = snapshot.anchorKey;
11712
- }
11713
- };
11714
- const normalizeDocxSchemaForExport = (value) => {
11715
- if (!value) return [];
11716
- return (Array.isArray(value) ? value : [value]).filter(Boolean);
11717
- };
11718
- const translateCommentsForExport = () => {
11719
- const processedComments = [];
11720
- commentsList.value.forEach((comment) => {
11721
- const values = comment.getValues();
11722
- const richText = values.commentText;
11723
- const docxSchema = normalizeDocxSchemaForExport(values.docxCommentJSON);
11724
- const schema = docxSchema.length ? docxSchema : convertHtmlToSchema(richText);
11725
- processedComments.push({
11726
- ...values,
11727
- commentJSON: schema
11728
- });
11729
- });
11730
- return processedComments;
11731
- };
11732
- const convertHtmlToSchema = (commentHTML) => {
11733
- const json = new Editor({
11734
- mode: "text",
11735
- isHeadless: true,
11736
- content: commentHTML,
11737
- extensions: getRichTextExtensions()
11738
- }).getJSON();
11739
- return Array.isArray(json?.content) ? json.content.filter(Boolean) : [];
11740
- };
11741
10731
  /**
11742
10732
  * Triggered when the editor locations are updated
11743
10733
  * Updates floating comment locations from the editor
@@ -11800,150 +10790,12 @@ var useCommentsStore = defineStore("comments", () => {
11800
10790
  const clearEditorCommentPositions = () => {
11801
10791
  editorCommentPositions.value = {};
11802
10792
  };
11803
- /**
11804
- * Identify the single structural (whole-table) bubble for a tracked table so
11805
- * it is NEVER suppressed by the table-subsume filter below. The structural
11806
- * bubble is the parent "Added table" / "Deleted table" change; its public
11807
- * id is `word:structural:<id>` (or a bare structural fallback) and its display
11808
- * type is `tableInsert` / `tableDelete`.
11809
- *
11810
- * @param {Object} comment
11811
- * @returns {boolean}
11812
- */
11813
- const isStructuralTableBubble = (comment) => {
11814
- if (!comment?.trackedChange) return false;
11815
- const displayType = comment?.trackedChangeDisplayType;
11816
- if (displayType === "tableInsert" || displayType === "tableDelete") return true;
11817
- return [
11818
- comment?.commentId,
11819
- comment?.importedId,
11820
- comment?.trackedChangeAnchorKey
11821
- ].map((id) => id != null ? String(id) : "").filter(Boolean).some((id) => id.includes("word:structural:"));
11822
- };
11823
- /**
11824
- * Compute the decidable whole-table tracked-change ranges for a comment's
11825
- * document, memoized per editor state so the filter does not re-enumerate the
11826
- * document once per comment. Returns `[]` when the document has no tracked
11827
- * whole-table change (the common case), so non-tracked tables and inline-only
11828
- * tracked changes are never affected.
11829
- *
11830
- * @param {string | null | undefined} fileId
11831
- * @returns {Array<{ from: number, to: number }>}
11832
- */
11833
- const trackedTableSummaryCache = /* @__PURE__ */ new WeakMap();
11834
- /**
11835
- * Single source of truth for the decidable whole-table tracked changes in an
11836
- * editor state, enumerated and memoized ONCE per state. Returns:
11837
- * - `ranges`: each tracked whole-table's `{ from, to }` document span. Used to
11838
- * test whether an inline tracked change falls inside a tracked table.
11839
- * - `ids`: every change id associated with those tables (the change's public
11840
- * id / revisionId / revisionGroupId / sourceId plus each row's trackChange
11841
- * id / sourceId). Text typed inside a tracked-inserted row inherits that
11842
- * row's revision id, so such an inline change reports a changeId that is one
11843
- * of these — it must be subsumed by the structural bubble, not get its own
11844
- * review item.
11845
- *
11846
- * @param {object | null | undefined} state
11847
- * @returns {{ ranges: Array<{ from: number, to: number }>, ids: Set<string> }}
11848
- */
11849
- const computeTrackedTableSummaryForState = (state) => {
11850
- if (!state) return {
11851
- ranges: [],
11852
- ids: /* @__PURE__ */ new Set()
11853
- };
11854
- const cached = trackedTableSummaryCache.get(state);
11855
- if (cached) return cached;
11856
- const ranges = [];
11857
- const ids = /* @__PURE__ */ new Set();
11858
- const add = (value) => {
11859
- if (value != null && value !== "") ids.add(String(value));
11860
- };
11861
- const enumerate = trackChangesHelpers?.enumerateStructuralRowChanges;
11862
- if (typeof enumerate === "function") {
11863
- let structuralChanges = [];
11864
- try {
11865
- structuralChanges = enumerate(state) ?? [];
11866
- } catch {
11867
- structuralChanges = [];
11868
- }
11869
- for (const change of structuralChanges) {
11870
- if (!change?.decidable || !change?.wholeTable) continue;
11871
- const from = Number(change.tableFrom);
11872
- const to = Number(change.tableTo);
11873
- if (Number.isFinite(from) && Number.isFinite(to)) ranges.push({
11874
- from,
11875
- to
11876
- });
11877
- add(change.id);
11878
- add(change.revisionId);
11879
- add(change.revisionGroupId);
11880
- add(change.sourceId);
11881
- for (const row of change.rows || []) {
11882
- const tc = row?.node?.attrs?.trackChange;
11883
- add(tc?.id);
11884
- add(tc?.sourceId);
11885
- }
11886
- }
11887
- }
11888
- const summary = {
11889
- ranges,
11890
- ids
11891
- };
11892
- trackedTableSummaryCache.set(state, summary);
11893
- return summary;
11894
- };
11895
- const getTrackedTableRangesForDocument = (fileId) => {
11896
- const editor = superdocStore.getDocument(fileId)?.getEditor?.();
11897
- return computeTrackedTableSummaryForState(editor?.state).ranges;
11898
- };
11899
- /**
11900
- * True when every `{ from, to }` range of an inline tracked change is wholly
11901
- * contained within some decidable whole-table tracked-change range. Such an
11902
- * inline change is subsumed by the structural "Inserted/Deleted table" review
11903
- * item and must NOT become an independent review item (no comment object →
11904
- * no floating bubble and no active-on-click dialog). The underlying
11905
- * trackInsert/trackDelete mark (the green highlight) is untouched — only the
11906
- * review-item comment is suppressed.
11907
- *
11908
- * @param {Array<{ from?: number, to?: number }>} changeRanges
11909
- * @param {Array<{ from: number, to: number }>} tableRanges
11910
- * @returns {boolean}
11911
- */
11912
- const isInlineRangeInsideTrackedTable = (changeRanges, tableRanges) => {
11913
- if (!tableRanges?.length || !changeRanges?.length) return false;
11914
- return changeRanges.every((seg) => {
11915
- const start = Number(seg?.from);
11916
- const end = Number(seg?.to);
11917
- if (!Number.isFinite(start) || !Number.isFinite(end)) return false;
11918
- return tableRanges.some((table) => start >= table.from && end <= table.to);
11919
- });
11920
- };
11921
- /**
11922
- * Suppress an inline tracked-change bubble whose document range falls within a
11923
- * tracked whole-table change's range, so only the structural "Added table"
11924
- * / "Deleted table" bubble shows for that table (matching Word / Google Docs).
11925
- * The structural bubble itself, real user comments, and inline tracked changes
11926
- * inside a NON-tracked table are never suppressed.
11927
- *
11928
- * @param {Object} comment
11929
- * @returns {boolean} True when the bubble must NOT render.
11930
- */
11931
- const isInlineTrackedChangeInsideTrackedTable = (comment) => {
11932
- if (!comment?.trackedChange) return false;
11933
- if (isStructuralTableBubble(comment)) return false;
11934
- const ranges = getTrackedTableRangesForDocument(comment?.fileId);
11935
- if (!ranges.length) return false;
11936
- const entry = resolveCommentPositionEntry(comment).entry;
11937
- const range = getCommentPositionRange(entry);
11938
- if (!range) return false;
11939
- return ranges.some((table) => range.start >= table.from && range.end <= table.to);
11940
- };
11941
10793
  const getFloatingComments = require__plugin_vue_export_helper.computed(() => {
11942
10794
  return getGroupedComments.value?.parentComments.filter((c) => !c.resolvedTime).filter((c) => {
11943
10795
  if (!isEditorBackedComment(c)) return true;
11944
10796
  if (isTrackedChangeThread(c)) return true;
11945
10797
  return Boolean(resolveCommentPositionEntry(c).entry);
11946
- }).filter((c) => !isInlineTrackedChangeInsideTrackedTable(c));
10798
+ });
11947
10799
  });
11948
10800
  const getFloatingCommentInstances = require__plugin_vue_export_helper.computed(() => {
11949
10801
  return getFloatingComments.value.flatMap((comment) => {
@@ -11988,75 +10840,6 @@ var useCommentsStore = defineStore("comments", () => {
11988
10840
  if (typeof commentsVisible === "boolean") viewingVisibility.commentsVisible = commentsVisible;
11989
10841
  if (typeof trackChangesVisible === "boolean") viewingVisibility.trackChangesVisible = trackChangesVisible;
11990
10842
  };
11991
- /**
11992
- * Get HTML content from the comment text JSON (which uses DOCX schema)
11993
- *
11994
- * @param {Object} commentTextJson The comment text JSON
11995
- * @returns {string} The HTML content
11996
- */
11997
- const normalizeCommentForEditor = (node) => {
11998
- if (Array.isArray(node)) return node.map((child) => normalizeCommentForEditor(child)).flat().filter(Boolean);
11999
- if (!node || typeof node !== "object") return node;
12000
- const stripTextStyleAttrs = (attrs) => {
12001
- if (!attrs) return attrs;
12002
- const rest = { ...attrs };
12003
- delete rest.fontSize;
12004
- delete rest.fontFamily;
12005
- delete rest.eastAsiaFontFamily;
12006
- return Object.keys(rest).length ? rest : void 0;
12007
- };
12008
- const normalizeMark = (mark) => {
12009
- if (!mark) return mark;
12010
- const typeName = typeof mark.type === "string" ? mark.type : mark.type?.name;
12011
- const attrs = mark?.attrs ? { ...mark.attrs } : void 0;
12012
- if (typeName === "textStyle" && attrs) return {
12013
- ...mark,
12014
- attrs: stripTextStyleAttrs(attrs)
12015
- };
12016
- return {
12017
- ...mark,
12018
- attrs
12019
- };
12020
- };
12021
- const cloneMarks = (marks) => Array.isArray(marks) ? marks.filter(Boolean).map((mark) => normalizeMark(mark)) : void 0;
12022
- const cloneAttrs = (attrs) => attrs && typeof attrs === "object" ? { ...attrs } : void 0;
12023
- if (!Array.isArray(node.content)) return {
12024
- type: node.type,
12025
- ...node.text !== void 0 ? { text: node.text } : {},
12026
- ...node.attrs ? { attrs: cloneAttrs(node.attrs) } : {},
12027
- ...node.marks ? { marks: cloneMarks(node.marks) } : {}
12028
- };
12029
- const normalizedChildren = node.content.map((child) => normalizeCommentForEditor(child)).flat().filter(Boolean);
12030
- if (node.type === "run") return normalizedChildren;
12031
- return {
12032
- type: node.type,
12033
- ...node.attrs ? { attrs: cloneAttrs(node.attrs) } : {},
12034
- ...node.marks ? { marks: cloneMarks(node.marks) } : {},
12035
- content: normalizedChildren
12036
- };
12037
- };
12038
- const getHtmlFromComment = (commentTextElements) => {
12039
- const elementsArray = Array.isArray(commentTextElements) ? commentTextElements : commentTextElements ? [commentTextElements] : [];
12040
- if (!elementsArray.some((element) => element?.content?.length)) return;
12041
- try {
12042
- const normalizedContent = normalizeCommentForEditor(elementsArray);
12043
- const contentArray = Array.isArray(normalizedContent) ? normalizedContent : normalizedContent ? [normalizedContent] : [];
12044
- if (!contentArray.length) return null;
12045
- return new Editor({
12046
- mode: "text",
12047
- isHeadless: true,
12048
- content: {
12049
- type: "doc",
12050
- content: contentArray
12051
- },
12052
- loadFromSchema: true,
12053
- extensions: getRichTextExtensions()
12054
- }).getHTML();
12055
- } catch (error) {
12056
- console.warn("Failed to convert comment", error);
12057
- return;
12058
- }
12059
- };
12060
10843
  return {
12061
10844
  COMMENT_EVENTS,
12062
10845
  isDebugging,
@@ -12104,8 +10887,6 @@ var useCommentsStore = defineStore("comments", () => {
12104
10887
  getTrackedChangeThread,
12105
10888
  getCommentPositionKey,
12106
10889
  getCommentPosition,
12107
- getCommentAnchoredText,
12108
- getCommentAnchorData,
12109
10890
  resolveCommentPositionEntry,
12110
10891
  getCommentDocumentId,
12111
10892
  belongsToDocument,
@@ -12125,23 +10906,17 @@ var useCommentsStore = defineStore("comments", () => {
12125
10906
  cancelComment,
12126
10907
  deleteComment,
12127
10908
  removePendingComment,
12128
- cancelImportedTrackedChangeBootstrap,
12129
10909
  removeCommentsForDocument,
12130
10910
  setReviewDirectoryFromV2,
12131
10911
  clearReviewDirectory,
12132
- processLoadedDocxComments,
12133
- translateCommentsForExport,
12134
10912
  handleEditorLocationsUpdate,
12135
10913
  clearEditorCommentPositions,
12136
10914
  handleTrackedChangeUpdate,
12137
- refreshTrackedChangeCommentsByIds,
12138
10915
  syncResolvedCommentsWithDocument,
12139
- syncTrackedChangePositionsWithDocument,
12140
10916
  setActiveFloatingCommentInstance,
12141
10917
  requestInstantSidebarAlignment,
12142
10918
  peekInstantSidebarAlignment,
12143
10919
  clearInstantSidebarAlignment,
12144
- syncTrackedChangeComments,
12145
10920
  decideTrackedChangeFromSidebar,
12146
10921
  v2CommentsAdapter,
12147
10922
  setV2CommentsAdapter,
@@ -13484,6 +12259,7 @@ var _hoisted_53 = { class: "reply-input-wrapper" };
13484
12259
  var _hoisted_54 = { class: "reply-actions" };
13485
12260
  var _hoisted_55 = ["disabled", "data-disabled-reason"];
13486
12261
  var commentEditSessions = /* @__PURE__ */ new WeakMap();
12262
+ var commentReplySessions = /* @__PURE__ */ new WeakMap();
13487
12263
  var _sfc_main$33 = {
13488
12264
  __name: "CommentDialog",
13489
12265
  props: {
@@ -13533,9 +12309,13 @@ var _sfc_main$33 = {
13533
12309
  const superdocStore = useSuperdocStore();
13534
12310
  const commentsStore = useCommentsStore();
13535
12311
  const editSessionOwner = {};
12312
+ const replySessionOwner = {};
13536
12313
  const retireOwnEditSession = () => {
13537
12314
  if (commentEditSessions.get(commentsStore)?.owner === editSessionOwner) commentEditSessions.delete(commentsStore);
13538
12315
  };
12316
+ const retireOwnReplySession = () => {
12317
+ if (commentReplySessions.get(commentsStore)?.owner === replySessionOwner) commentReplySessions.delete(commentsStore);
12318
+ };
13539
12319
  const { addComment, cancelComment, deleteComment, getCommentAliasIds, removePendingComment, getCommentDocumentId, requestInstantSidebarAlignment, resolveCommentPositionEntry, clearInstantSidebarAlignment, setActiveFloatingCommentInstance } = commentsStore;
13540
12320
  const { suppressInternalExternal, getConfig, activeComment, activeFloatingCommentInstanceId, floatingCommentsOffset, pendingComment, currentCommentText, currentCommentMentions, isDebugging, editingCommentId, editorCommentPositions, isCommentHighlighted } = storeToRefs(commentsStore);
13541
12321
  const isInternal = require__plugin_vue_export_helper.ref(true);
@@ -13736,6 +12516,11 @@ var _sfc_main$33 = {
13736
12516
  const isReplying = require__plugin_vue_export_helper.ref(false);
13737
12517
  const isSubmittingReply = require__plugin_vue_export_helper.ref(false);
13738
12518
  const isSubmittingNewComment = require__plugin_vue_export_helper.ref(false);
12519
+ let replyDraftRevision = 0;
12520
+ require__plugin_vue_export_helper.watch([currentCommentText, currentCommentMentions], () => replyDraftRevision++, {
12521
+ deep: true,
12522
+ flush: "sync"
12523
+ });
13739
12524
  const commentSaveFailure = require__plugin_vue_export_helper.ref("");
13740
12525
  const commentSaveFailureMessage = (outcome, hasDraft = true) => {
13741
12526
  return `Your comment could not be saved${outcome?.detail === "document-mode-read-only" ? " because the document or connection is read-only" : outcome?.code === "PERMISSION_DENIED" ? " because you do not have permission" : ""}.${hasDraft ? " Your draft is still here. Try again or cancel." : " Try again."}`;
@@ -13746,6 +12531,7 @@ var _sfc_main$33 = {
13746
12531
  ok: false,
13747
12532
  reason: v2WriteCapability.value?.reason ?? "v2-write-unavailable"
13748
12533
  };
12534
+ commentReplySessions.set(commentsStore, { owner: replySessionOwner });
13749
12535
  commentSaveFailure.value = "";
13750
12536
  isReplying.value = true;
13751
12537
  require__plugin_vue_export_helper.nextTick(() => {
@@ -13984,6 +12770,15 @@ var _sfc_main$33 = {
13984
12770
  commentsStore.setActiveComment(proxy.$superdoc, activeComment.value);
13985
12771
  isCommentHighlighted.value = false;
13986
12772
  };
12773
+ const captureCommentEditorOwner = () => {
12774
+ const editor = proxy.$superdoc.activeEditor;
12775
+ const adapter = editor.v2Comments;
12776
+ const document = editor.doc;
12777
+ return {
12778
+ adapter,
12779
+ isCurrent: () => proxy.$superdoc.activeEditor === editor && editor.v2Comments === adapter && editor.doc === document
12780
+ };
12781
+ };
13987
12782
  const handleAddComment = async () => {
13988
12783
  if (commentsAreReadOnly()) return readOnlyMutationOutcome();
13989
12784
  if (isV2WriteDisabled.value) return {
@@ -13998,6 +12793,13 @@ var _sfc_main$33 = {
13998
12793
  isSubmittingReply.value = true;
13999
12794
  commentSaveFailure.value = "";
14000
12795
  try {
12796
+ const session = commentReplySessions.get(commentsStore);
12797
+ const draftRevision = replyDraftRevision;
12798
+ const editorOwner = captureCommentEditorOwner();
12799
+ const threadId = props.comment.commentId;
12800
+ const fileId = props.comment.fileId;
12801
+ const pendingDraft = pendingComment.value;
12802
+ const edit = editingCommentId.value;
14001
12803
  const parentCommentId = getV2ExistingReplyParentId(props.comment);
14002
12804
  const trackedChangeTarget = getV2TrackedChangeCommentTarget(props.comment);
14003
12805
  const outcome = await commentsStore.replyCommentV2({
@@ -14012,6 +12814,8 @@ var _sfc_main$33 = {
14012
12814
  require__plugin_vue_export_helper.nextTick(() => emit("resize"));
14013
12815
  return outcome;
14014
12816
  }
12817
+ if (session?.owner !== replySessionOwner || commentReplySessions.get(commentsStore) !== session || replyDraftRevision !== draftRevision || !editorOwner.isCurrent() || props.comment.commentId !== threadId || props.comment.fileId !== fileId || pendingComment.value !== pendingDraft || editingCommentId.value !== edit || !isDialogActive.value || !isReplying.value) return outcome;
12818
+ retireOwnReplySession();
14015
12819
  isReplying.value = false;
14016
12820
  currentCommentText.value = "";
14017
12821
  currentCommentMentions.value = [];
@@ -14358,10 +13162,8 @@ var _sfc_main$33 = {
14358
13162
  const session = commentEditSessions.get(commentsStore);
14359
13163
  const submission = Symbol("comment-edit-submission");
14360
13164
  if (session?.owner === editSessionOwner) session.submission = submission;
14361
- const editor = proxy.$superdoc.activeEditor;
14362
- const adapter = editor.v2Comments;
14363
- const documentId = adapter.documentId;
14364
- const document = editor.doc;
13165
+ const editorOwner = captureCommentEditorOwner();
13166
+ const documentId = editorOwner.adapter.documentId;
14365
13167
  const thread = props.comment;
14366
13168
  const draft = currentCommentText.value;
14367
13169
  const outcome = await commentsStore.editCommentV2({
@@ -14369,7 +13171,7 @@ var _sfc_main$33 = {
14369
13171
  commentId: comment.commentId,
14370
13172
  text: draft
14371
13173
  });
14372
- if (session?.owner !== editSessionOwner || commentEditSessions.get(commentsStore) !== session || session.submission !== submission || proxy.$superdoc.activeEditor !== editor || editor.v2Comments !== adapter || adapter.documentId !== documentId || editor.doc !== document || props.comment !== thread || editingCommentId.value !== comment.commentId || currentCommentText.value !== draft) return outcome;
13174
+ if (session?.owner !== editSessionOwner || commentEditSessions.get(commentsStore) !== session || session.submission !== submission || !editorOwner.isCurrent() || editorOwner.adapter.documentId !== documentId || props.comment !== thread || editingCommentId.value !== comment.commentId || currentCommentText.value !== draft) return outcome;
14373
13175
  if (!outcome?.ok) {
14374
13176
  commentSaveFailure.value = commentSaveFailureMessage(outcome);
14375
13177
  return outcome;
@@ -14408,6 +13210,7 @@ var _sfc_main$33 = {
14408
13210
  const handleCancel = (comment) => {
14409
13211
  commentSaveFailure.value = "";
14410
13212
  retireOwnEditSession();
13213
+ retireOwnReplySession();
14411
13214
  editingCommentId.value = null;
14412
13215
  isReplying.value = false;
14413
13216
  cancelComment(proxy.$superdoc);
@@ -14434,6 +13237,7 @@ var _sfc_main$33 = {
14434
13237
  });
14435
13238
  require__plugin_vue_export_helper.onBeforeUnmount(() => {
14436
13239
  retireOwnEditSession();
13240
+ retireOwnReplySession();
14437
13241
  proxy.$superdoc?.off?.("active-editor-change", refreshActiveEditor);
14438
13242
  });
14439
13243
  require__plugin_vue_export_helper.watch(showInputSection, (isVisible) => {
@@ -14685,7 +13489,7 @@ var _sfc_main$33 = {
14685
13489
  };
14686
13490
  }
14687
13491
  };
14688
- var CommentDialog_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$33, [["__scopeId", "data-v-f8b79c74"]]);
13492
+ var CommentDialog_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$33, [["__scopeId", "data-v-239e0310"]]);
14689
13493
  //#endregion
14690
13494
  //#region src/components/CommentsLayer/commentsList/ReviewDirectoryListItem.vue
14691
13495
  var _hoisted_1$25 = [
@@ -15337,6 +14141,25 @@ function useViewportFit({ getSuperdoc, superdocContainerWidth, isReady, activeZo
15337
14141
  return { evaluateViewport };
15338
14142
  }
15339
14143
  //#endregion
14144
+ //#region src/composables/use-high-contrast-mode.ts
14145
+ var HIGH_CONTRAST_MODE_KEY = Symbol("superdoc-high-contrast-mode");
14146
+ function createHighContrastMode() {
14147
+ const isHighContrastMode = require__plugin_vue_export_helper.ref(false);
14148
+ return {
14149
+ isHighContrastMode,
14150
+ setHighContrastMode: (value) => {
14151
+ isHighContrastMode.value = value;
14152
+ }
14153
+ };
14154
+ }
14155
+ var HIGH_CONTRAST_OFF = {
14156
+ isHighContrastMode: require__plugin_vue_export_helper.readonly(require__plugin_vue_export_helper.ref(false)),
14157
+ setHighContrastMode: () => {}
14158
+ };
14159
+ function useHighContrastMode() {
14160
+ return require__plugin_vue_export_helper.inject(HIGH_CONTRAST_MODE_KEY, HIGH_CONTRAST_OFF);
14161
+ }
14162
+ //#endregion
15340
14163
  //#region ../layout-engine/contracts/src/author-colors.ts
15341
14164
  /**
15342
14165
  * Curated, high-contrast fallback palette. Used when neither `overrides` nor
@@ -17131,15 +15954,15 @@ var collectRemovedCommentIds = (comments, targetIds, isInScope = () => true) =>
17131
15954
  return removedIds;
17132
15955
  };
17133
15956
  //#endregion
17134
- //#region src/composables/use-high-contrast-mode.js
17135
- var isHighContrastMode = require__plugin_vue_export_helper.ref(false);
17136
- function useHighContrastMode() {
17137
- const setHighContrastMode = (value) => {
17138
- isHighContrastMode.value = value;
17139
- };
15957
+ //#region src/composables/use-superdoc-instance.ts
15958
+ function useSuperDocInstance() {
15959
+ const instance = require__plugin_vue_export_helper.getCurrentInstance();
15960
+ if (!instance?.proxy) throw new Error("useSuperDocInstance() must be called during a component setup function.");
15961
+ const superdoc = instance.appContext.config.globalProperties.$superdoc;
15962
+ if (!superdoc) throw new Error("useSuperDocInstance() found no SuperDoc registered at app.config.globalProperties.$superdoc.");
17140
15963
  return {
17141
- isHighContrastMode,
17142
- setHighContrastMode
15964
+ proxy: instance.proxy,
15965
+ superdoc
17143
15966
  };
17144
15967
  }
17145
15968
  var VALID_COMMENTS_LAYOUTS = /* @__PURE__ */ new Set([
@@ -18122,7 +16945,7 @@ function usePasswordPrompt({ getSurfaceManager, getPasswordPromptConfig, onUnhan
18122
16945
  })
18123
16946
  });
18124
16947
  } else {
18125
- const PasswordPromptSurface = (await Promise.resolve().then(() => require("./chunks/PasswordPromptSurface-B5DHV6qr.cjs"))).default;
16948
+ const PasswordPromptSurface = (await Promise.resolve().then(() => require("./chunks/PasswordPromptSurface-BpqP4pUa.cjs"))).default;
18126
16949
  const surfaceId = `password-prompt-${doc.id}`;
18127
16950
  handle = manager.open({
18128
16951
  id: surfaceId,
@@ -18817,7 +17640,7 @@ function useFindReplace({ getSurfaceManager, getActiveEditor, activeEditorRef, g
18817
17640
  } else {
18818
17641
  let FindReplaceSurface;
18819
17642
  try {
18820
- FindReplaceSurface = (await Promise.resolve().then(() => require("./chunks/FindReplaceSurface-8V8AjgR4.cjs"))).default;
17643
+ FindReplaceSurface = (await Promise.resolve().then(() => require("./chunks/FindReplaceSurface-BRs3JrCq.cjs"))).default;
18821
17644
  } catch {
18822
17645
  opening = false;
18823
17646
  return;
@@ -20812,6 +19635,7 @@ function createV2EditorRuntimeAdapter(options) {
20812
19635
  }
20813
19636
  const result = await host.dispatch(mapped);
20814
19637
  switch (result.status) {
19638
+ case "selected": return { status: "selected" };
20815
19639
  case "committed":
20816
19640
  invalidatePositionTokens();
20817
19641
  return {
@@ -21261,8 +20085,11 @@ function translateRenderReadinessDiagnostic(diag, ctx = {}) {
21261
20085
  else if (severity === "error") diagnosticCode = "RENDER_ERROR";
21262
20086
  else return null;
21263
20087
  const message = reason || code;
20088
+ const error = new Error(typeof diag.error?.message === "string" ? diag.error.message : message);
20089
+ if (typeof diag.error?.name === "string") error.name = diag.error.name;
20090
+ if (typeof diag.error?.stack === "string") error.stack = diag.error.stack;
21264
20091
  return {
21265
- error: new Error(message),
20092
+ error,
21266
20093
  diagnosticCode,
21267
20094
  diagnosticStage: unusableFurniture ? "parse" : "render",
21268
20095
  severity,
@@ -22109,7 +20936,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
22109
20936
  __name: "SuperDoc",
22110
20937
  emits: ["selection-update"],
22111
20938
  setup(__props, { emit: __emit }) {
22112
- const PdfViewer = require__plugin_vue_export_helper.defineAsyncComponent(() => Promise.resolve().then(() => require("./chunks/PdfViewer-omriDChK.cjs")));
20939
+ const PdfViewer = require__plugin_vue_export_helper.defineAsyncComponent(() => Promise.resolve().then(() => require("./chunks/PdfViewer-Cg072l5d.cjs")));
22113
20940
  const getDocumentLoadPassword = (doc) => doc.password ?? proxy.$superdoc.config.password;
22114
20941
  const resolveDocumentV2Collaboration = (doc) => {
22115
20942
  const rawConfig = doc?.v2Collaboration;
@@ -22155,10 +20982,10 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
22155
20982
  }
22156
20983
  });
22157
20984
  const { getConfig, documentsWithConverations, commentsList, pendingComment, activeComment, skipSelectionUpdate, commentsByDocument, isCommentsListVisible, isFloatingCommentsReady, generalCommentIds, hasSyncedCollaborationComments, editorCommentPositions, hasInitializedLocations, isCommentHighlighted, hasOpenTrackedChanges } = storeToRefs(commentsStore);
22158
- const { showAddComment, handleEditorLocationsUpdate, handleTrackedChangeUpdate, refreshTrackedChangeCommentsByIds, syncTrackedChangePositionsWithDocument, syncTrackedChangeComments, addComment, addHydratedComment, getComment, resolveCommentPositionEntry, belongsToDocument, COMMENT_EVENTS, requestInstantSidebarAlignment, peekInstantSidebarAlignment, clearInstantSidebarAlignment } = commentsStore;
22159
- const { proxy } = require__plugin_vue_export_helper.getCurrentInstance();
20985
+ const { showAddComment, handleEditorLocationsUpdate, handleTrackedChangeUpdate, addComment, addHydratedComment, getComment, resolveCommentPositionEntry, belongsToDocument, COMMENT_EVENTS, requestInstantSidebarAlignment, peekInstantSidebarAlignment, clearInstantSidebarAlignment } = commentsStore;
20986
+ const { proxy, superdoc } = useSuperDocInstance();
22160
20987
  commentsStore.proxy = proxy;
22161
- const documentOpenTelemetry = createDocumentOpenTelemetry(proxy.$superdoc.config);
20988
+ const documentOpenTelemetry = createDocumentOpenTelemetry(superdoc.config);
22162
20989
  const resolvedEditorIntegration = createDefaultV2Integration();
22163
20990
  const V2DocumentEditor = require__plugin_vue_export_helper.markRaw(resolvedEditorIntegration.EditorComponent);
22164
20991
  const V2Ruler = resolvedEditorIntegration.RulerComponent ? require__plugin_vue_export_helper.markRaw(resolvedEditorIntegration.RulerComponent) : null;
@@ -22207,7 +21034,6 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
22207
21034
  const layers = require__plugin_vue_export_helper.ref(null);
22208
21035
  const rightSidebarRef = require__plugin_vue_export_helper.ref(null);
22209
21036
  const pdfViewerRef = require__plugin_vue_export_helper.ref(null);
22210
- const pendingReplayTrackedChangeSync = require__plugin_vue_export_helper.ref(false);
22211
21037
  const toolsMenuPosition = require__plugin_vue_export_helper.reactive({
22212
21038
  top: null,
22213
21039
  right: "-25px",
@@ -22269,21 +21095,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
22269
21095
  });
22270
21096
  }
22271
21097
  });
22272
- const pdfConfig = proxy.$superdoc.config.modules?.pdf || {};
22273
- const flushPendingReplayTrackedChangeSync = () => {
22274
- if (!pendingReplayTrackedChangeSync.value) return;
22275
- pendingReplayTrackedChangeSync.value = false;
22276
- syncTrackedChangeComments({
22277
- superdoc: proxy.$superdoc,
22278
- editor: proxy.$superdoc?.activeEditor
22279
- });
22280
- };
22281
- const scheduleReplayTrackedChangeSync = () => {
22282
- pendingReplayTrackedChangeSync.value = true;
22283
- require__plugin_vue_export_helper.nextTick(() => {
22284
- flushPendingReplayTrackedChangeSync();
22285
- });
22286
- };
21098
+ const pdfConfig = superdoc.config.modules?.pdf || {};
22287
21099
  const handleDocumentReady = (documentId, container) => {
22288
21100
  const doc = getDocument(documentId);
22289
21101
  if (!doc || doc.loadScope && !doc.loadScope.finish()) return;
@@ -23283,7 +22095,6 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
23283
22095
  v2PageMetricsSnapshot.value = null;
23284
22096
  const clearedDocumentId = payload?.documentId == null ? null : String(payload.documentId);
23285
22097
  clearV2MutationRejectionNotifications(clearedDocumentId);
23286
- commentsStore.cancelImportedTrackedChangeBootstrap?.(clearedDocumentId ?? void 0);
23287
22098
  if (clearedDocumentId) v2MountStagesByDocumentId.delete(clearedDocumentId);
23288
22099
  if (latestV2MountStage && (clearedDocumentId == null || latestV2MountStage.dataset?.superdocV2DocumentId === clearedDocumentId)) latestV2MountStage = null;
23289
22100
  v2ReviewWindowController.reset("render-cleared");
@@ -23299,7 +22110,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
23299
22110
  ...event,
23300
22111
  documentId: document?.id ?? null
23301
22112
  }));
23302
- if (event.type === "mutation:committed" && event.origin === "extension") return;
22113
+ if (event.type === "mutation:committed" && event.dryRun) return;
23303
22114
  const documentId = document?.id ?? null;
23304
22115
  if (event.type === "history:changed" && event.state) {
23305
22116
  proxy.$superdoc.emit("history-change", {
@@ -23363,7 +22174,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
23363
22174
  if (event.type === "mutation:committed") {
23364
22175
  const bulkDecisionEvent = toV2BulkDecisionEvent(documentId, event.trackedChangeBulkDecision);
23365
22176
  if (bulkDecisionEvent) proxy.$superdoc.emit("tracked-changes:bulk-decision", bulkDecisionEvent);
23366
- emitV2EditorUpdate();
22177
+ if (require_create_super_doc_ui.mutationCommittedEventChangedDocument(event)) emitV2EditorUpdate();
23367
22178
  }
23368
22179
  if (event.type !== "mutation:committed") return;
23369
22180
  if (event.origin === "history") v2ReviewWindowController.refreshCommittedWindow(`history-${event.direction}`);
@@ -23463,6 +22274,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
23463
22274
  case "collaboration-room-corrupt": return "SuperDoc v2 found a structurally invalid collaboration room. No changes were made.";
23464
22275
  case "collaboration-v2-room-missing": return "SuperDoc v2 could not find a committed v2 collaboration room. No changes were made. Use roomMode: \"create\" only when creating a new room.";
23465
22276
  case "collaboration-v2-room-already-exists": return "SuperDoc v2 was asked to create a collaboration room that already exists. Use roomMode: \"join\" to open it.";
22277
+ case "collaboration-session-invalidated": return "This collaboration session is no longer current. Please reopen the shared document with roomMode: \"join\".";
23466
22278
  case "collaboration-v2-room-initializing": return "The SuperDoc v2 collaboration room has not completed initialization. This open attempt did not modify it.";
23467
22279
  case "collaboration-v2-room-bootstrap-stalled": return "The SuperDoc v2 collaboration room bootstrap appears stalled. This open attempt did not modify it. Preserve the room data if a create retry cannot recover it.";
23468
22280
  case "collaboration-open-intent-invalid": return "SuperDoc v2 received an invalid collaboration room mode. No provider was connected.";
@@ -23473,10 +22285,17 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
23473
22285
  const normalizeV2EditorFailureDetail = (detail) => typeof detail === "string" && detail.trim().length > 0 ? detail.trim() : null;
23474
22286
  const onV2EditorFailed = (payload) => {
23475
22287
  documents.value.find((doc) => doc.id === payload?.documentId)?.loadScope?.finish();
23476
- clearActiveV2EditorFacade(payload?.documentId ?? null);
23477
22288
  const reason = typeof payload?.reason === "string" && payload.reason.length > 0 ? payload.reason : "open-failed";
23478
22289
  const detail = normalizeV2EditorFailureDetail(payload?.detail);
23479
22290
  const documentId = typeof payload?.documentId === "string" && payload.documentId.length > 0 ? payload.documentId : null;
22291
+ const retiredDocument = reason === "collaboration-session-invalidated" && documentId ? getDocument(documentId) : null;
22292
+ const retiredExporter = retiredDocument?.getEditor?.();
22293
+ clearActiveV2EditorFacade(payload?.documentId ?? null);
22294
+ if (retiredExporter?.editorVersion === 2 && typeof retiredExporter.exportDocx === "function") {
22295
+ retiredDocument.isReady = false;
22296
+ if (v2Runtimes.has(documentId)) clearV2RuntimeRegistration(documentId);
22297
+ retiredDocument.setEditor?.(retiredExporter);
22298
+ }
23480
22299
  if (reason === "worker-transport-failed") {
23481
22300
  proxy.$superdoc.emit("exception", {
23482
22301
  error: /* @__PURE__ */ new Error("Worker transport failed."),
@@ -23503,7 +22322,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
23503
22322
  ...workerFailure ? { workerFailure } : {},
23504
22323
  reason
23505
22324
  };
23506
- if (reason === "collaboration-v1-config-unsupported" || reason === "collaboration-room-format-unsupported" || reason === "collaboration-room-format-conflict" || reason === "collaboration-room-corrupt" || reason === "collaboration-v2-room-missing" || reason === "collaboration-v2-room-already-exists" || reason === "collaboration-v2-room-initializing" || reason === "collaboration-v2-room-bootstrap-stalled" || reason === "collaboration-open-intent-invalid" || reason === "collaboration-config-invalid") console.warn(`[SuperDoc] ${message}`, logContext);
22325
+ if (reason === "collaboration-v1-config-unsupported" || reason === "collaboration-room-format-unsupported" || reason === "collaboration-room-format-conflict" || reason === "collaboration-room-corrupt" || reason === "collaboration-v2-room-missing" || reason === "collaboration-v2-room-already-exists" || reason === "collaboration-session-invalidated" || reason === "collaboration-v2-room-initializing" || reason === "collaboration-v2-room-bootstrap-stalled" || reason === "collaboration-open-intent-invalid" || reason === "collaboration-config-invalid") console.warn(`[SuperDoc] ${message}`, logContext);
23507
22326
  else console.error(`[SuperDoc] ${message}`, logContext);
23508
22327
  proxy.$superdoc.emit("exception", collaborationException && reason !== "collaboration-config-invalid" ? collaborationException : {
23509
22328
  error: new Error(message),
@@ -23809,7 +22628,6 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
23809
22628
  existingComment: null
23810
22629
  };
23811
22630
  };
23812
- if (type === "replayCompleted") scheduleReplayTrackedChangeSync();
23813
22631
  if (COMMENT_EVENTS?.ADD && type === COMMENT_EVENTS.ADD && commentPayload) {
23814
22632
  commentPayload = normalizeReplayCommentModelPayload(commentPayload);
23815
22633
  const currentUser = proxy.$superdoc?.user;
@@ -24118,7 +22936,6 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
24118
22936
  handleFormattingMarksShortcut(e);
24119
22937
  }
24120
22938
  require__plugin_vue_export_helper.onBeforeUnmount(() => {
24121
- commentsStore.cancelImportedTrackedChangeBootstrap?.();
24122
22939
  passwordPrompt.destroy();
24123
22940
  findReplace.destroy();
24124
22941
  linkPopover.destroy();
@@ -24734,7 +23551,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
24734
23551
  ], 46, _hoisted_1$17);
24735
23552
  };
24736
23553
  }
24737
- }, [["__scopeId", "data-v-f2db957f"]]);
23554
+ }, [["__scopeId", "data-v-ba00d895"]]);
24738
23555
  //#endregion
24739
23556
  //#region src/core/create-app.js
24740
23557
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
@@ -24936,29 +23753,28 @@ var setPiniaDevtoolsSuppressedForApp = (app, isSuppressed) => {
24936
23753
  * @property {import('pinia').Pinia} pinia The Pinia store instance attached to `app`
24937
23754
  * @property {ReturnType<typeof useSuperdocStore>} superdocStore The SuperDoc Pinia store
24938
23755
  * @property {ReturnType<typeof useCommentsStore>} commentsStore The Comments Pinia store
24939
- * @property {ReturnType<typeof useHighContrastMode>} highContrastModeStore The high-contrast mode composable refs (`isHighContrastMode`, `setHighContrastMode`). Named `*Store` for historical reasons; this is a plain Vue composable, not a Pinia store.
24940
23756
  */
24941
23757
  /**
24942
23758
  * Generate the superdoc vue app
24943
23759
  *
24944
- * @param {Object} [options]
23760
+ * @param {Object} options
24945
23761
  * @param {boolean} [options.disablePiniaDevtools=false] Disable Pinia devtools registration for this app instance
24946
- * @returns {SuperdocVueAppRefs} An object containing the vue app, the pinia reference, the superdoc/comments Pinia stores, and the high-contrast mode composable
23762
+ * @param {import('../composables/use-high-contrast-mode').HighContrastMode} options.highContrastMode The owning SuperDoc's high-contrast state, provided to the app
23763
+ * @returns {SuperdocVueAppRefs} An object containing the vue app, the pinia reference, and the superdoc/comments Pinia stores
24947
23764
  */
24948
- var createSuperdocVueApp = ({ disablePiniaDevtools = false } = {}) => {
23765
+ var createSuperdocVueApp = ({ disablePiniaDevtools = false, highContrastMode }) => {
24949
23766
  const app = require__plugin_vue_export_helper.createApp(SuperDoc_default);
24950
23767
  const pinia = createPinia();
24951
23768
  const cleanupPiniaDevtoolsSuppression = setPiniaDevtoolsSuppressedForApp(app, disablePiniaDevtools);
24952
23769
  let superdocStore;
24953
23770
  let commentsStore;
24954
- let highContrastModeStore;
24955
23771
  try {
24956
23772
  app.use(pinia);
24957
23773
  if (disablePiniaDevtools) purgeDevtoolsPluginQueueForApp(app);
24958
23774
  app.directive("click-outside", v_click_outside_default);
24959
23775
  superdocStore = useSuperdocStore();
24960
23776
  commentsStore = useCommentsStore();
24961
- highContrastModeStore = useHighContrastMode();
23777
+ app.provide(HIGH_CONTRAST_MODE_KEY, highContrastMode);
24962
23778
  } catch (error) {
24963
23779
  cleanupPiniaDevtoolsSuppression();
24964
23780
  throw error;
@@ -24974,8 +23790,7 @@ var createSuperdocVueApp = ({ disablePiniaDevtools = false } = {}) => {
24974
23790
  app,
24975
23791
  pinia,
24976
23792
  superdocStore,
24977
- commentsStore,
24978
- highContrastModeStore
23793
+ commentsStore
24979
23794
  };
24980
23795
  };
24981
23796
  //#endregion
@@ -38853,6 +37668,69 @@ var EditorRuntimeRegistry = class {
38853
37668
  } catch {}
38854
37669
  }
38855
37670
  };
37671
+ //#endregion
37672
+ //#region src/internal/toolbar/compatibility-catalog.ts
37673
+ /**
37674
+ * Internal built-in toolbar compatibility catalog (V2 toolbar parity, phase 1).
37675
+ *
37676
+ * This module is the SINGLE place the legacy built-in toolbar item names (the
37677
+ * ids v1 toolbars, `modules.toolbar.groups`, and the docs use) are mapped onto
37678
+ * the V2 world. Later renderers, the toolbar authority, and any proof helper
37679
+ * consume this catalog instead of rediscovering the mapping ad hoc.
37680
+ *
37681
+ * It does NOT re-implement command routing. The V2 command controller catalog
37682
+ * in `../../public/ui/commands.ts` stays the single source of truth for how a
37683
+ * command is backed; every `controller-routed` entry here points at a canonical
37684
+ * command id that must exist there (enforced by `assertCatalogAlignedWithController`).
37685
+ *
37686
+ * Every documented built-in item is classified into exactly one disposition:
37687
+ *
37688
+ * - `controller-routed` → driven by the V2 command controller (a routed
37689
+ * command id in `COMMAND_CATALOG`).
37690
+ * - `host-routed` → driven by a public `SuperDoc`-instance method the built-in
37691
+ * shell owns directly (e.g. formatting-marks toggle), not by the controller
37692
+ * command set.
37693
+ * - `shell-owned` → pure rendered-shell chrome/config the built-in toolbar
37694
+ * owns (custom buttons, layout groups). Has no command and no document
37695
+ * effect of its own.
37696
+ * - `unsupported` → documented built-in item that is explicitly out of V2
37697
+ * functional parity (a signed product decision). The controller command (if
37698
+ * any) fails closed with `command-unsupported`; the docs do not call it
37699
+ * supported.
37700
+ * - `unresolved` → documented today but with no identified public V2 surface
37701
+ * yet. Recorded explicitly so later phases close it deliberately rather than
37702
+ * pretending it already works.
37703
+ *
37704
+ * Nothing here reintroduces a public `headless-toolbar*` export; the catalog is
37705
+ * internal to the `superdoc` package.
37706
+ */
37707
+ var TABLE_ACTION_COMMAND_IDS_BY_OPTION = {
37708
+ addRowBefore: "table-add-row-before",
37709
+ addRowAfter: "table-add-row-after",
37710
+ deleteRow: "table-delete-row",
37711
+ addColumnBefore: "table-add-column-before",
37712
+ addColumnAfter: "table-add-column-after",
37713
+ deleteColumn: "table-delete-column",
37714
+ deleteTable: "table-delete",
37715
+ deleteCellAndTableBorders: "table-remove-borders",
37716
+ mergeCells: "table-merge-cells",
37717
+ splitCell: "table-split-cell",
37718
+ fixTables: "table-fix"
37719
+ };
37720
+ var TABLE_ACTION_COMMAND_IDS = Object.values(TABLE_ACTION_COMMAND_IDS_BY_OPTION);
37721
+ function tableActionCommandId(option) {
37722
+ return TABLE_ACTION_COMMAND_IDS_BY_OPTION[option];
37723
+ }
37724
+ /**
37725
+ * Member controller command ids the legacy `tableActions` dropdown fans out
37726
+ * into. These are real V2 `tables.*` operations now routed through the shared
37727
+ * table-context facade (`ui.tables` / `host.getTableContext()`): each is a
37728
+ * `routed` controller command that enables when a table context is resolvable
37729
+ * and fails closed with `table-context-unavailable` otherwise. The dropdown
37730
+ * itself is shell chrome; its members are controller-routed.
37731
+ */
37732
+ var ROUTED_TABLE_ACTION_COMMAND_IDS = TABLE_ACTION_COMMAND_IDS.filter((commandId) => require_create_super_doc_ui.getCommandDescriptor(commandId)?.disposition === "routed");
37733
+ var UNCATALOGED_ITEM_COMMAND_IDS = { tableOfContents: "table-of-contents-insert" };
38856
37734
  /**
38857
37735
  * The built-in toolbar compatibility catalog. Order groups items the way the
38858
37736
  * docs present them; it is documentation-only.
@@ -39024,18 +37902,7 @@ var BUILT_IN_TOOLBAR_CATALOG = [
39024
37902
  instanceMethod: null,
39025
37903
  disposition: "shell-owned",
39026
37904
  group: "center",
39027
- memberCommandIds: [
39028
- "table-add-row-before",
39029
- "table-add-row-after",
39030
- "table-delete-row",
39031
- "table-add-column-before",
39032
- "table-add-column-after",
39033
- "table-delete-column",
39034
- "table-delete",
39035
- "table-merge-cells",
39036
- "table-split-cell",
39037
- "table-remove-borders"
39038
- ],
37905
+ memberCommandIds: ROUTED_TABLE_ACTION_COMMAND_IDS,
39039
37906
  note: "table editing dropdown is shell chrome; its member commands are controller-routed through the shared table-context facade (ui.tables / host.getTableContext()) and fail closed with table-context-unavailable when the caret is not in a table"
39040
37907
  },
39041
37908
  {
@@ -39135,6 +38002,9 @@ function resolveToolbarCommandId(name) {
39135
38002
  const entry = CATALOG_BY_NAME.get(name);
39136
38003
  return entry?.disposition === "controller-routed" ? entry.commandId : null;
39137
38004
  }
38005
+ function resolveBuiltInItemCommandId(name) {
38006
+ return resolveToolbarCommandId(name) ?? UNCATALOGED_ITEM_COMMAND_IDS[name] ?? null;
38007
+ }
39138
38008
  //#endregion
39139
38009
  //#region src/internal/toolbar/built-in/helpers.js
39140
38010
  var sanitizeNumber = (value, defaultNumber) => {
@@ -41633,54 +40503,6 @@ var RESPONSIVE_BREAKPOINTS = {
41633
40503
  lg: 1280,
41634
40504
  xl: 1410
41635
40505
  };
41636
- var HEADLESS_ITEM_MAP = {
41637
- undo: "undo",
41638
- redo: "redo",
41639
- bold: "bold",
41640
- italic: "italic",
41641
- underline: "underline",
41642
- strike: "strikethrough",
41643
- acceptTrackedChangeBySelection: "track-changes-accept-selection",
41644
- rejectTrackedChangeOnSelection: "track-changes-reject-selection",
41645
- ruler: "ruler",
41646
- formattingMarks: "formatting-marks",
41647
- zoom: "zoom",
41648
- measurementUnit: "measurement-unit",
41649
- documentMode: "document-mode",
41650
- link: "link",
41651
- fontFamily: "font-family",
41652
- fontSize: "font-size",
41653
- list: "bullet-list",
41654
- numberedlist: "numbered-list",
41655
- table: "table-insert",
41656
- image: "image",
41657
- tableOfContents: "table-of-contents-insert",
41658
- color: "text-color",
41659
- highlight: "highlight-color",
41660
- textAlign: "text-align",
41661
- lineHeight: "line-height",
41662
- linkedStyles: "linked-style",
41663
- indentleft: "indent-decrease",
41664
- indentright: "indent-increase",
41665
- directionLtr: "direction-ltr",
41666
- directionRtl: "direction-rtl",
41667
- clearFormatting: "clear-formatting",
41668
- copyFormat: "copy-format"
41669
- };
41670
- var TABLE_ACTION_COMMAND_MAP = {
41671
- addRowBefore: "table-add-row-before",
41672
- addRowAfter: "table-add-row-after",
41673
- deleteRow: "table-delete-row",
41674
- addColumnBefore: "table-add-column-before",
41675
- addColumnAfter: "table-add-column-after",
41676
- deleteColumn: "table-delete-column",
41677
- deleteTable: "table-delete",
41678
- deleteCellAndTableBorders: "table-remove-borders",
41679
- mergeCells: "table-merge-cells",
41680
- splitCell: "table-split-cell",
41681
- fixTables: "table-fix"
41682
- };
41683
- var TABLE_ACTION_COMMAND_IDS = Object.values(TABLE_ACTION_COMMAND_MAP);
41684
40506
  //#endregion
41685
40507
  //#region src/internal/toolbar/built-in/Toolbar.vue
41686
40508
  var _hoisted_1$10 = ["data-superdoc-ui-owner"];
@@ -43132,6 +41954,7 @@ var TableGrid_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_
43132
41954
  //#region src/internal/toolbar/built-in/TableActions.vue
43133
41955
  var _hoisted_1$1 = { class: "toolbar-table-actions" };
43134
41956
  var _hoisted_2$1 = [
41957
+ "aria-disabled",
43135
41958
  "onClick",
43136
41959
  "data-item",
43137
41960
  "ariaLabel"
@@ -43146,12 +41969,17 @@ var _sfc_main$1 = {
43146
41969
  setup(__props, { emit: __emit }) {
43147
41970
  const emit = __emit;
43148
41971
  const handleClick = (item) => {
41972
+ if (item.disabled) return;
43149
41973
  emit("select", { command: item.command });
43150
41974
  };
43151
41975
  return (_ctx, _cache) => {
43152
41976
  return require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", _hoisted_1$1, [(require__plugin_vue_export_helper.openBlock(true), require__plugin_vue_export_helper.createElementBlock(require__plugin_vue_export_helper.Fragment, null, require__plugin_vue_export_helper.renderList(__props.options, (option) => {
43153
41977
  return require__plugin_vue_export_helper.openBlock(), require__plugin_vue_export_helper.createElementBlock("div", {
43154
- class: require__plugin_vue_export_helper.normalizeClass(["toolbar-table-actions__item", { "toolbar-table-actions__item--border": option.bottomBorder }]),
41978
+ class: require__plugin_vue_export_helper.normalizeClass(["toolbar-table-actions__item", {
41979
+ "toolbar-table-actions__item--border": option.bottomBorder,
41980
+ "sd-disabled": option.disabled
41981
+ }]),
41982
+ "aria-disabled": option.disabled ? "true" : void 0,
43155
41983
  onClick: ($event) => handleClick(option),
43156
41984
  "data-item": option.props?.["data-item"] || "",
43157
41985
  ariaLabel: option.props?.ariaLabel,
@@ -43164,7 +41992,7 @@ var _sfc_main$1 = {
43164
41992
  };
43165
41993
  }
43166
41994
  };
43167
- var TableActions_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$1, [["__scopeId", "data-v-0cdad084"]]);
41995
+ var TableActions_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$1, [["__scopeId", "data-v-72123aae"]]);
43168
41996
  //#endregion
43169
41997
  //#region src/internal/toolbar/built-in/PageNumberMenu.vue
43170
41998
  var _hoisted_1 = ["aria-busy"];
@@ -44279,7 +43107,10 @@ var makeDefaultItems = ({ superToolbar, toolbarIcons, toolbarTexts, toolbarFonts
44279
43107
  ];
44280
43108
  function renderTableActions(tableActionsItem) {
44281
43109
  return require__plugin_vue_export_helper.h(TableActions_default, {
44282
- options: tableActionsOptions,
43110
+ options: tableActionsOptions.map((option) => ({
43111
+ ...option,
43112
+ disabled: !superToolbar.isCommandEnabled(tableActionCommandId(option.command))
43113
+ })),
44283
43114
  onSelect: (event) => {
44284
43115
  closeDropdown(tableActionsItem);
44285
43116
  const { command } = event;
@@ -45054,8 +43885,6 @@ var findElementBySelector = (selector) => {
45054
43885
  * Internal only: nothing here is a public package export and no
45055
43886
  * `headless-toolbar*` subpath is reintroduced.
45056
43887
  */
45057
- var HEADLESS_COMMAND_IDS = HEADLESS_ITEM_MAP;
45058
- var TABLE_ACTION_COMMANDS = TABLE_ACTION_COMMAND_MAP;
45059
43888
  function isThenable(value) {
45060
43889
  return (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
45061
43890
  }
@@ -45071,15 +43900,6 @@ function isListenerTarget(value) {
45071
43900
  function hasClosest(target) {
45072
43901
  return target !== null && "closest" in target && typeof target.closest === "function";
45073
43902
  }
45074
- /**
45075
- * Resolve a legacy built-in item name to its canonical V2 controller command
45076
- * id. The compatibility catalog is the primary truth; a small supplemental map
45077
- * (`HEADLESS_ITEM_MAP`) covers documented items the renderer can surface that
45078
- * are not modeled as catalog rows yet (e.g. table-of-contents, direction).
45079
- */
45080
- function resolveCommandId(name) {
45081
- return resolveToolbarCommandId(name) ?? HEADLESS_COMMAND_IDS[name] ?? null;
45082
- }
45083
43903
  function executeCommand(ui, commandId, argument, options = {}) {
45084
43904
  if (!ui?.commands || !commandId) return false;
45085
43905
  if (typeof ui.commands.has === "function" && !ui.commands.has(commandId)) return false;
@@ -45161,7 +43981,8 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
45161
43981
  #groupItems;
45162
43982
  #highlightOptions;
45163
43983
  #painterListenersDetach;
45164
- constructor(config = {}) {
43984
+ #highContrastMode;
43985
+ constructor(config) {
45165
43986
  super();
45166
43987
  this.config = {
45167
43988
  selector: null,
@@ -45203,6 +44024,7 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
45203
44024
  this.#makeToolbarItems();
45204
44025
  this.updateToolbarState();
45205
44026
  };
44027
+ this.#highContrastMode = config.highContrastMode;
45206
44028
  this.config = {
45207
44029
  ...this.config,
45208
44030
  ...config
@@ -45267,6 +44089,7 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
45267
44089
  this.app = require__plugin_vue_export_helper.createApp(Toolbar_default);
45268
44090
  this.app.directive("click-outside", v_click_outside_default);
45269
44091
  this.app.config.globalProperties.$toolbar = this;
44092
+ this.app.provide(HIGH_CONTRAST_MODE_KEY, this.#highContrastMode);
45270
44093
  this.toolbar = this.app.mount(this.toolbarContainer);
45271
44094
  this.updateToolbarState();
45272
44095
  } catch (error) {
@@ -45600,18 +44423,22 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
45600
44423
  #isFontFamilyMixedState(commandState) {
45601
44424
  return Boolean(commandState) && commandState?.disabled === false && commandState?.value == null;
45602
44425
  }
44426
+ isCommandEnabled(commandId) {
44427
+ if (!commandId) return false;
44428
+ const commandState = this.snapshot?.commands?.[commandId];
44429
+ return Boolean(commandState && !commandState.disabled);
44430
+ }
45603
44431
  #applyHeadlessState(item) {
45604
44432
  const name = item.name.value;
45605
44433
  if (item.isCustomToolbarItem && typeof item.command === "string") {
45606
44434
  const commandState = this.snapshot?.commands?.[item.command];
45607
- item.setDisabled(item.initiallyDisabled || !commandState || Boolean(commandState.disabled));
44435
+ item.setDisabled(item.initiallyDisabled || !this.isCommandEnabled(item.command));
45608
44436
  if (commandState?.active) item.activate();
45609
44437
  else item.deactivate();
45610
44438
  return true;
45611
44439
  }
45612
44440
  if (name === "tableActions") {
45613
- const hasAnyEnabled = TABLE_ACTION_COMMAND_IDS.map((commandId) => this.snapshot?.commands?.[commandId]).some((state) => state && !state.disabled);
45614
- item.setDisabled(!hasAnyEnabled);
44441
+ item.setDisabled(!TABLE_ACTION_COMMAND_IDS.some((commandId) => this.isCommandEnabled(commandId)));
45615
44442
  return true;
45616
44443
  }
45617
44444
  if (name === "search") {
@@ -45630,7 +44457,7 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
45630
44457
  else item.deactivate();
45631
44458
  return true;
45632
44459
  }
45633
- const commandId = resolveCommandId(name);
44460
+ const commandId = resolveBuiltInItemCommandId(name);
45634
44461
  if (!commandId) return false;
45635
44462
  const commandState = this.snapshot?.commands?.[commandId];
45636
44463
  if (shouldKeepEnabledForV1ToolbarParity(name, commandState)) item.setDisabled(false);
@@ -45795,12 +44622,12 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
45795
44622
  if (name === "image" && argument == null) return this.#startImagePick();
45796
44623
  if (name === "tableActions") {
45797
44624
  const tableAction = typeof argument === "object" && argument !== null && "command" in argument ? argument.command : void 0;
45798
- const commandId = typeof tableAction === "string" ? TABLE_ACTION_COMMANDS[tableAction] : void 0;
44625
+ const commandId = typeof tableAction === "string" ? tableActionCommandId(tableAction) : void 0;
45799
44626
  if (!commandId) return false;
45800
44627
  return executeCommand(this.ui, commandId, void 0, callbacks);
45801
44628
  }
45802
44629
  if (getBuiltInToolbarItem(name)?.disposition === "unsupported") return true;
45803
- const commandId = resolveCommandId(name);
44630
+ const commandId = resolveBuiltInItemCommandId(name);
45804
44631
  if (!commandId) return false;
45805
44632
  return executeCommand(this.ui, commandId, argument, callbacks);
45806
44633
  }
@@ -46237,6 +45064,10 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
46237
45064
  return this.#surfacesConfig;
46238
45065
  }
46239
45066
  #surfaceManager;
45067
+ /**
45068
+ * @private
45069
+ */
45070
+ #highContrastMode;
46240
45071
  #sourceLifecycleReady;
46241
45072
  #pendingSourceLifecycle;
46242
45073
  /**
@@ -46310,6 +45141,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
46310
45141
  this.users = [];
46311
45142
  this.whiteboard = null;
46312
45143
  this.colors = [];
45144
+ this.#highContrastMode = createHighContrastMode();
46313
45145
  this.readyEditors = 0;
46314
45146
  this.#sourceLifecycleReady = false;
46315
45147
  this.#pendingSourceLifecycle = [];
@@ -46466,7 +45298,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
46466
45298
  }
46467
45299
  this.userColorMap = /* @__PURE__ */ new Map();
46468
45300
  this.colorIndex = 0;
46469
- this.version = "2.28.0-next.8";
45301
+ this.version = "2.28.0";
46470
45302
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
46471
45303
  this.superdocId = config.superdocId || require_uuid.v4();
46472
45304
  this.colors = this.config.colors ?? [];
@@ -46766,7 +45598,10 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
46766
45598
  });
46767
45599
  }
46768
45600
  #initVueApp() {
46769
- const { app, pinia, superdocStore, commentsStore, highContrastModeStore } = createSuperdocVueApp({ disablePiniaDevtools: Boolean(this.config.disablePiniaDevtools) });
45601
+ const { app, pinia, superdocStore, commentsStore } = createSuperdocVueApp({
45602
+ disablePiniaDevtools: Boolean(this.config.disablePiniaDevtools),
45603
+ highContrastMode: this.#highContrastMode
45604
+ });
46770
45605
  this.app = app;
46771
45606
  this.pinia = pinia;
46772
45607
  this.app.config.globalProperties.$config = this.config;
@@ -46775,7 +45610,6 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
46775
45610
  this.app.provide("surfaceManager", this.#surfaceManager);
46776
45611
  this.superdocStore = superdocStore;
46777
45612
  this.commentsStore = commentsStore;
46778
- this.highContrastModeStore = highContrastModeStore;
46779
45613
  if (typeof this.superdocStore.setExceptionHandler === "function") this.superdocStore.setExceptionHandler((payload) => this.emit("exception", payload));
46780
45614
  this.superdocStore.init(this.config);
46781
45615
  const commentsModuleConfig = this.config.modules.comments;
@@ -47173,7 +46007,8 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
47173
46007
  roomMode: target.roomMode,
47174
46008
  ...syncTimeoutOption,
47175
46009
  ...target.publicApiKey ? { publicApiKey: target.publicApiKey } : {},
47176
- ...target.authEndpoint ? { authEndpoint: target.authEndpoint } : {}
46010
+ ...target.authEndpoint ? { authEndpoint: target.authEndpoint } : {},
46011
+ ...target.baseUrl ? { baseUrl: target.baseUrl } : {}
47177
46012
  };
47178
46013
  if (target.providerFamily === "hocuspocus") return {
47179
46014
  providerType: "hocuspocus",
@@ -47879,7 +46714,8 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
47879
46714
  toolbarGroups: toolbarUi.options.toolbarGroups,
47880
46715
  icons: toolbarUi.options.icons,
47881
46716
  texts: toolbarUi.options.texts,
47882
- uiDisplayFallbackFont: this.config.uiDisplayFallbackFont
46717
+ uiDisplayFallbackFont: this.config.uiDisplayFallbackFont,
46718
+ highContrastMode: this.#highContrastMode
47883
46719
  });
47884
46720
  } catch (error) {
47885
46721
  console.warn("[SuperDoc] built-in toolbar mount failed", error);
@@ -48489,23 +47325,6 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
48489
47325
  confirmed
48490
47326
  };
48491
47327
  }
48492
- const legacyReplaceFile = activeEditor && !isV2ActiveEditorFacade(activeEditor) ? activeEditor.replaceFile : null;
48493
- if (typeof legacyReplaceFile === "function") {
48494
- const result = await legacyReplaceFile.call(activeEditor, source);
48495
- const legacyState = result && typeof result === "object" ? result.state : void 0;
48496
- const legacyConfirmed = legacyState === void 0 || legacyState === null || legacyState === "review-ready" || legacyState === "editing-ready";
48497
- if (legacyConfirmed) {
48498
- this.#replaceActiveDocumentData(activeEditor, source);
48499
- this.emit("document-replaced", {
48500
- editor: activeEditor,
48501
- host: activeEditor?.host
48502
- });
48503
- }
48504
- return {
48505
- raw: result,
48506
- confirmed: legacyConfirmed
48507
- };
48508
- }
48509
47328
  throw new Error("SuperDoc: replaceFile is unavailable for the active editor");
48510
47329
  }
48511
47330
  /**
@@ -48518,19 +47337,6 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
48518
47337
  commentsType,
48519
47338
  isFinalDoc
48520
47339
  });
48521
- const uiStoreHydrated = this.config?.modules?.comments !== false;
48522
- let legacyComments;
48523
- let legacyCommentsComputed = false;
48524
- const getLegacyCommentsForV1 = () => {
48525
- if (legacyCommentsComputed) return legacyComments;
48526
- legacyCommentsComputed = true;
48527
- if (commentsType === "clean") legacyComments = [];
48528
- else if (uiStoreHydrated && this.commentsStore && typeof this.commentsStore.translateCommentsForExport === "function") {
48529
- legacyComments = this.commentsStore.translateCommentsForExport();
48530
- if (!Array.isArray(legacyComments)) legacyComments = [];
48531
- }
48532
- return legacyComments;
48533
- };
48534
47340
  const bridgedExportErrors = /* @__PURE__ */ new WeakSet();
48535
47341
  const rememberBridgedExportError = (payload) => {
48536
47342
  if ("editor" in payload && payload.error && typeof payload.error === "object") bridgedExportErrors.add(payload.error);
@@ -48541,7 +47347,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
48541
47347
  if (policies !== void 0 && documents.some((doc) => {
48542
47348
  if (doc.type !== "application/vnd.openxmlformats-officedocument.wordprocessingml.document") return false;
48543
47349
  const editor = doc.getEditor?.();
48544
- return editor?.editorVersion !== 2 || typeof editor.exportDocx !== "function";
47350
+ return !isV2ActiveEditorFacade(editor) || typeof editor.exportDocx !== "function";
48545
47351
  })) throw new Error("Independent DOCX export policies require a mounted V2 exporter for every document.");
48546
47352
  const docxPromises = documents.map(async (doc) => {
48547
47353
  if (!doc || doc.type !== "application/vnd.openxmlformats-officedocument.wordprocessingml.document") return null;
@@ -48554,19 +47360,13 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
48554
47360
  if (isDocumentByteSource(doc.data)) return this.#savedDocxToBlob(doc.data);
48555
47361
  return null;
48556
47362
  };
48557
- if (!editor || typeof editor.exportDocx !== "function") return fallbackDocx();
48558
- const isV2Editor = editor.editorVersion === 2;
47363
+ if (!isV2ActiveEditorFacade(editor) || typeof editor.exportDocx !== "function") return fallbackDocx();
48559
47364
  try {
48560
- const exported = await editor.exportDocx(isV2Editor ? {
47365
+ const exported = await editor.exportDocx({
48561
47366
  isFinalDoc,
48562
47367
  commentsType,
48563
47368
  policies,
48564
47369
  fieldsHighlightColor
48565
- } : {
48566
- isFinalDoc,
48567
- comments: getLegacyCommentsForV1(),
48568
- commentsType,
48569
- fieldsHighlightColor
48570
47370
  });
48571
47371
  if (exported) return exported;
48572
47372
  } catch (error) {
@@ -48580,7 +47380,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
48580
47380
  editor: null
48581
47381
  }));
48582
47382
  }
48583
- if (isV2Editor) throw error;
47383
+ throw error;
48584
47384
  }
48585
47385
  return fallbackDocx();
48586
47386
  });
@@ -48723,7 +47523,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
48723
47523
  setHighContrastMode(isHighContrast) {
48724
47524
  if (!this.activeEditor) return;
48725
47525
  this.activeEditor.setHighContrastMode?.(isHighContrast);
48726
- this.highContrastModeStore?.setHighContrastMode(isHighContrast);
47526
+ this.#highContrastMode.setHighContrastMode(isHighContrast);
48727
47527
  }
48728
47528
  };
48729
47529
  Object.defineProperty(SuperDoc.prototype.canPerformPermission, Symbol.for("superdoc.internal.permission-method.builtin"), { value: true });