superdoc 2.18.0-next.8 → 2.18.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 (29) hide show
  1. package/dist/chunks/{create-super-doc-ui-D9JWPXRh.cjs → create-super-doc-ui-wDvhW9Tz.cjs} +996 -912
  2. package/dist/chunks/{create-super-doc-ui-qTSa0PbD.es.js → create-super-doc-ui-wKzxrztu.es.js} +982 -910
  3. package/dist/collaboration-upgrade-engine.cjs +1 -1
  4. package/dist/collaboration-upgrade-engine.es.js +1 -1
  5. package/dist/document-api/src/diff/diff.types.d.ts +1 -1
  6. package/dist/document-api/src/images/images.types.d.ts +2 -0
  7. package/dist/document-api/src/types/blocks.types.d.ts +2 -0
  8. package/dist/document-api/src/types/clipboard.d.ts +24 -1
  9. package/dist/layout-engine/contracts/src/index.d.ts +4 -0
  10. package/dist/public/ui-react.cjs +1 -1
  11. package/dist/public/ui-react.es.js +1 -1
  12. package/dist/public/ui-vue.cjs +1 -1
  13. package/dist/public/ui-vue.es.js +1 -1
  14. package/dist/public/ui.cjs +1 -1
  15. package/dist/public/ui.es.js +1 -1
  16. package/dist/style.css +22 -22
  17. package/dist/style.layered.css +22 -22
  18. package/dist/superdoc/src/core/SuperDoc.d.ts +10 -3
  19. package/dist/superdoc/src/core/types/index.d.ts +37 -0
  20. package/dist/superdoc/src/internal/toolbar/built-in/LinkInput.vue.d.ts +4 -4
  21. package/dist/superdoc/src/public/index.d.cts +10 -0
  22. package/dist/superdoc/src/public/index.d.ts +5 -0
  23. package/dist/superdoc/src/public/ui/create-super-doc-ui.d.ts +9 -1
  24. package/dist/superdoc.cjs +232 -104
  25. package/dist/superdoc.es.js +232 -104
  26. package/dist-cdn/style.layered.css +1 -1
  27. package/dist-cdn/superdoc.min.css +1 -1
  28. package/dist-cdn/superdoc.min.js +37 -37
  29. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import { i as __toESM$2 } from "./chunks/rolldown-runtime-0pSA04fp.es.js";
2
2
  import { t as blank_default } from "./chunks/blank-docx-DzQccOlW.es.js";
3
- import { a as getV2TrackedChangeMutationImpact, c as createInteractionHistory, i as applyV2CommentInvalidationFromMutation, l as recordInteraction, o as DOM_CLASS_NAMES, s as closeInteractionHistory, t as createSuperDocUI } from "./chunks/create-super-doc-ui-qTSa0PbD.es.js";
3
+ import { a as applyV2CommentInvalidationFromMutation, c as DOM_CLASS_NAMES, d as recordInteraction, l as closeInteractionHistory, n as executeFirstPartyCommandAsync, o as getV2TrackedChangeMutationImpact, r as registerFirstPartyCommandMutation, t as createSuperDocUI, u as createInteractionHistory } from "./chunks/create-super-doc-ui-wKzxrztu.es.js";
4
4
  import { t as import_eventemitter3 } from "./chunks/eventemitter3-DEIiXiH2.es.js";
5
5
  import { t as v4 } from "./chunks/uuid-H0Xcmmhy.es.js";
6
6
  import { a as init_dist$1, i as global, n as init_dist$2, o as Buffer, r as process$1, s as init_dist, t as require_jszip_min } from "./chunks/jszip-BzJ3CyxR.es.js";
@@ -315,7 +315,7 @@ var shuffleArray = (array) => {
315
315
  var DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
316
316
  function getSuperdocVersion() {
317
317
  try {
318
- return "2.18.0-next.8";
318
+ return "2.18.0";
319
319
  } catch {
320
320
  return "unknown";
321
321
  }
@@ -14970,6 +14970,79 @@ function useViewportFit({ getSuperdoc, superdocContainerWidth, isReady, activeZo
14970
14970
  return { evaluateViewport };
14971
14971
  }
14972
14972
  //#endregion
14973
+ //#region ../layout-engine/contracts/src/author-colors.ts
14974
+ /**
14975
+ * Curated, high-contrast fallback palette. Used when neither `overrides` nor
14976
+ * `resolve` produces a color so imported/discovered authors the host did not
14977
+ * configure ahead of time still get a stable, distinct color.
14978
+ */
14979
+ var FALLBACK_PALETTE = [
14980
+ "#1f6feb",
14981
+ "#d1242f",
14982
+ "#8250df",
14983
+ "#bf3989",
14984
+ "#1a7f37",
14985
+ "#9a6700",
14986
+ "#bc4c00",
14987
+ "#0969da",
14988
+ "#cf222e",
14989
+ "#6639ba",
14990
+ "#116329",
14991
+ "#7d4e00"
14992
+ ];
14993
+ /** Stable identity string for an author (used for hashing + dedupe). */
14994
+ var authorIdentityKey = (author) => {
14995
+ if (!author) return "";
14996
+ return `${typeof author.name === "string" ? author.name : ""} ${typeof author.email === "string" ? author.email : ""}`;
14997
+ };
14998
+ /** Deterministic 32-bit FNV-1a hash of a string. */
14999
+ var hashString = (value) => {
15000
+ let hash = 2166136261;
15001
+ for (let i = 0; i < value.length; i += 1) {
15002
+ hash ^= value.charCodeAt(i);
15003
+ hash = Math.imul(hash, 16777619);
15004
+ }
15005
+ return hash >>> 0;
15006
+ };
15007
+ /**
15008
+ * Deterministic fallback color derived from the author identity. The same
15009
+ * identity always maps to the same palette entry, so colors stay stable across
15010
+ * reloads and across the paint / snapshot surfaces.
15011
+ */
15012
+ var fallbackAuthorColor = (author) => {
15013
+ return FALLBACK_PALETTE[hashString(authorIdentityKey(author)) % FALLBACK_PALETTE.length];
15014
+ };
15015
+ var isNonEmptyString = (value) => typeof value === "string" && value.length > 0;
15016
+ /**
15017
+ * Composes the host `authorColors` config into a single resolver.
15018
+ *
15019
+ * Resolution order per author:
15020
+ * 1. `overrides` by identity — `email` first, then `name` (exact match).
15021
+ * 2. `resolve(author)`.
15022
+ * 3. A deterministic fallback color from the author identity.
15023
+ *
15024
+ * Returns `undefined` when colors are disabled (`enabled === false`) or no
15025
+ * config was provided, so callers can leave the existing default palette in
15026
+ * place by simply not threading a resolver.
15027
+ */
15028
+ var composeAuthorColorResolver = (config) => {
15029
+ if (!config || config.enabled === false) return void 0;
15030
+ const overrides = config.overrides && typeof config.overrides === "object" ? config.overrides : void 0;
15031
+ const resolve = typeof config.resolve === "function" ? config.resolve : void 0;
15032
+ return (author) => {
15033
+ const safeAuthor = author ?? {};
15034
+ if (overrides) {
15035
+ if (isNonEmptyString(safeAuthor.email) && isNonEmptyString(overrides[safeAuthor.email])) return overrides[safeAuthor.email];
15036
+ if (isNonEmptyString(safeAuthor.name) && isNonEmptyString(overrides[safeAuthor.name])) return overrides[safeAuthor.name];
15037
+ }
15038
+ if (resolve) try {
15039
+ const resolved = resolve(safeAuthor);
15040
+ if (isNonEmptyString(resolved)) return resolved;
15041
+ } catch {}
15042
+ return fallbackAuthorColor(safeAuthor);
15043
+ };
15044
+ };
15045
+ //#endregion
14973
15046
  //#region src/components/CommentsLayer/floating-comment-positioning.js
14974
15047
  var isPersistentReviewSidebarItem = (comment) => {
14975
15048
  return Boolean(comment?.trackedChange);
@@ -16378,79 +16451,6 @@ var WhiteboardLayer_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
16378
16451
  }
16379
16452
  }, [["__scopeId", "data-v-54546c3d"]]);
16380
16453
  //#endregion
16381
- //#region ../layout-engine/contracts/src/author-colors.ts
16382
- /**
16383
- * Curated, high-contrast fallback palette. Used when neither `overrides` nor
16384
- * `resolve` produces a color so imported/discovered authors the host did not
16385
- * configure ahead of time still get a stable, distinct color.
16386
- */
16387
- var FALLBACK_PALETTE = [
16388
- "#1f6feb",
16389
- "#d1242f",
16390
- "#8250df",
16391
- "#bf3989",
16392
- "#1a7f37",
16393
- "#9a6700",
16394
- "#bc4c00",
16395
- "#0969da",
16396
- "#cf222e",
16397
- "#6639ba",
16398
- "#116329",
16399
- "#7d4e00"
16400
- ];
16401
- /** Stable identity string for an author (used for hashing + dedupe). */
16402
- var authorIdentityKey = (author) => {
16403
- if (!author) return "";
16404
- return `${typeof author.name === "string" ? author.name : ""} ${typeof author.email === "string" ? author.email : ""}`;
16405
- };
16406
- /** Deterministic 32-bit FNV-1a hash of a string. */
16407
- var hashString = (value) => {
16408
- let hash = 2166136261;
16409
- for (let i = 0; i < value.length; i += 1) {
16410
- hash ^= value.charCodeAt(i);
16411
- hash = Math.imul(hash, 16777619);
16412
- }
16413
- return hash >>> 0;
16414
- };
16415
- /**
16416
- * Deterministic fallback color derived from the author identity. The same
16417
- * identity always maps to the same palette entry, so colors stay stable across
16418
- * reloads and across the paint / snapshot surfaces.
16419
- */
16420
- var fallbackAuthorColor = (author) => {
16421
- return FALLBACK_PALETTE[hashString(authorIdentityKey(author)) % FALLBACK_PALETTE.length];
16422
- };
16423
- var isNonEmptyString = (value) => typeof value === "string" && value.length > 0;
16424
- /**
16425
- * Composes the host `authorColors` config into a single resolver.
16426
- *
16427
- * Resolution order per author:
16428
- * 1. `overrides` by identity — `email` first, then `name` (exact match).
16429
- * 2. `resolve(author)`.
16430
- * 3. A deterministic fallback color from the author identity.
16431
- *
16432
- * Returns `undefined` when colors are disabled (`enabled === false`) or no
16433
- * config was provided, so callers can leave the existing default palette in
16434
- * place by simply not threading a resolver.
16435
- */
16436
- var composeAuthorColorResolver = (config) => {
16437
- if (!config || config.enabled === false) return void 0;
16438
- const overrides = config.overrides && typeof config.overrides === "object" ? config.overrides : void 0;
16439
- const resolve = typeof config.resolve === "function" ? config.resolve : void 0;
16440
- return (author) => {
16441
- const safeAuthor = author ?? {};
16442
- if (overrides) {
16443
- if (isNonEmptyString(safeAuthor.email) && isNonEmptyString(overrides[safeAuthor.email])) return overrides[safeAuthor.email];
16444
- if (isNonEmptyString(safeAuthor.name) && isNonEmptyString(overrides[safeAuthor.name])) return overrides[safeAuthor.name];
16445
- }
16446
- if (resolve) try {
16447
- const resolved = resolve(safeAuthor);
16448
- if (isNonEmptyString(resolved)) return resolved;
16449
- } catch {}
16450
- return fallbackAuthorColor(safeAuthor);
16451
- };
16452
- };
16453
- //#endregion
16454
16454
  //#region src/components/HtmlViewer/HtmlViewer.vue
16455
16455
  var _hoisted_1$20 = { class: "superdoc-html-viewer" };
16456
16456
  var _hoisted_2$16 = { class: "superdoc-html-viewer__document" };
@@ -22131,7 +22131,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
22131
22131
  executeAsync: (commandId, payload) => {
22132
22132
  const ui = getSuperDocUI();
22133
22133
  if (!ui) return Promise.resolve(UNAVAILABLE_COMMAND_RESULT);
22134
- return ui.commands.executeAsync(commandId, payload);
22134
+ return executeFirstPartyCommandAsync(ui, commandId, payload);
22135
22135
  },
22136
22136
  getState: (commandId) => getSuperDocUI()?.commands.get(commandId).getState() ?? UNAVAILABLE_COMMAND_STATE
22137
22137
  },
@@ -22332,7 +22332,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
22332
22332
  if (!payload) return;
22333
22333
  clearV2EditorFailure(payload.documentId ?? null);
22334
22334
  clearV2MutationRejectionNotifications(payload.documentId ?? null);
22335
- const { host, mount, documentId, capabilities, editCommands, bindEditShortcuts, bindSessionShortcuts, commentsAdapter, trackedChangesAdapter, contextMenu, documentApi, documentMutationReadiness, documentApiUnavailableReason, pageMetrics, pageLayout, pageFurniture, presence, lock, collaborationProvider, fonts, replaceFile, upgradeToCollaboration, documentOpenToken } = payload;
22335
+ const { host, firstPartyCommandMutation, mount, documentId, capabilities, editCommands, bindEditShortcuts, bindSessionShortcuts, commentsAdapter, trackedChangesAdapter, contextMenu, documentApi, documentMutationReadiness, documentApiUnavailableReason, pageMetrics, pageLayout, pageFurniture, presence, lock, connection, collaborationProvider, fonts, replaceFile, upgradeToCollaboration, documentOpenToken } = payload;
22336
22336
  documentOpenTelemetry?.trackDocumentOpen(documentOpenToken ?? null, documentId ?? null);
22337
22337
  const saveV2Bytes = createV2SaveQueue(async (saveOptions = {}) => {
22338
22338
  if (!host || typeof host.save !== "function") throw new Error("v2-editor: save unavailable");
@@ -22609,6 +22609,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
22609
22609
  pageFurniture: pageFurniture ?? null,
22610
22610
  presence: presence ?? null,
22611
22611
  lock: lock ?? null,
22612
+ connection: connection ?? null,
22612
22613
  provider: collaborationProvider ?? null,
22613
22614
  reviewWindow: {
22614
22615
  getDiagnostics: () => v2ReviewWindowController.getDiagnostics(),
@@ -22647,6 +22648,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
22647
22648
  if (doc) doc.provider = provider;
22648
22649
  }
22649
22650
  proxy.$superdoc.broadcastEditorCreate(facade);
22651
+ if (host && firstPartyCommandMutation) registerFirstPartyCommandMutation(proxy.$superdoc, host, firstPartyCommandMutation);
22650
22652
  installV2CommandShortcutBinding({
22651
22653
  documentId,
22652
22654
  bindEditShortcuts
@@ -23207,7 +23209,8 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
23207
23209
  case "collaboration-room-corrupt": return "SuperDoc v2 found a structurally invalid collaboration room. No changes were made.";
23208
23210
  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.";
23209
23211
  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.";
23210
- case "collaboration-v2-room-initializing": return "The SuperDoc v2 collaboration room is still initializing. Join mode did not modify it.";
23212
+ case "collaboration-v2-room-initializing": return "The SuperDoc v2 collaboration room has not completed initialization. This open attempt did not modify it.";
23213
+ 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.";
23211
23214
  case "collaboration-open-intent-invalid": return "SuperDoc v2 received an invalid collaboration room mode. No provider was connected.";
23212
23215
  case "collaboration-config-invalid": return "SuperDoc v2 received an invalid collaboration configuration. No provider was connected.";
23213
23216
  default: return "SuperDoc could not load the document editor.";
@@ -23234,7 +23237,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
23234
23237
  ...workerFailure ? { workerFailure } : {},
23235
23238
  reason
23236
23239
  };
23237
- 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-open-intent-invalid" || reason === "collaboration-config-invalid") console.warn(`[SuperDoc] ${message}`, logContext);
23240
+ 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);
23238
23241
  else console.error(`[SuperDoc] ${message}`, logContext);
23239
23242
  proxy.$superdoc.emit("exception", collaborationException ?? {
23240
23243
  error: new Error(message),
@@ -24454,7 +24457,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
24454
24457
  ], 38);
24455
24458
  };
24456
24459
  }
24457
- }, [["__scopeId", "data-v-4efb0cab"]]);
24460
+ }, [["__scopeId", "data-v-77e0209a"]]);
24458
24461
  //#endregion
24459
24462
  //#region src/core/create-app.js
24460
24463
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
@@ -44298,7 +44301,7 @@ function executeCommand(ui, commandId, argument, options = {}) {
44298
44301
  if (typeof ui.commands.has === "function" && !ui.commands.has(commandId)) return false;
44299
44302
  const execute = typeof ui.commands.executeAsync === "function" ? ui.commands.executeAsync : ui.commands.execute;
44300
44303
  if (typeof execute !== "function") return false;
44301
- const result = argument === void 0 ? execute.call(ui.commands, commandId) : execute.call(ui.commands, commandId, argument);
44304
+ const result = options.firstParty ? executeFirstPartyCommandAsync(ui, commandId, argument) : argument === void 0 ? execute.call(ui.commands, commandId) : execute.call(ui.commands, commandId, argument);
44302
44305
  const reportFailure = (settled) => {
44303
44306
  if (!options.reportUnhandled || settled !== false) return;
44304
44307
  options.onError?.(/* @__PURE__ */ new Error(`[superdoc toolbar] Command "${commandId}" did not run. Check that it is enabled and has the required input.`));
@@ -44946,6 +44949,7 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
44946
44949
  const name = item?.name?.value;
44947
44950
  if (!name) return false;
44948
44951
  const callbacks = {
44952
+ firstParty: true,
44949
44953
  onSettled: () => this.updateToolbarState(),
44950
44954
  onError: (error) => this.#emitCommandException(error, name)
44951
44955
  };
@@ -44974,6 +44978,7 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
44974
44978
  onPick: async (file) => {
44975
44979
  const src = await resolveImageSrc(file, this.superdoc?.config?.handleImageUpload);
44976
44980
  executeCommand(this.ui, "image", { src }, {
44981
+ firstParty: true,
44977
44982
  onSettled: () => this.updateToolbarState(),
44978
44983
  onError: (error) => this.#emitCommandException(error, "image")
44979
44984
  });
@@ -45301,6 +45306,14 @@ function contentControlSelectionKey(selection) {
45301
45306
  return null;
45302
45307
  }
45303
45308
  }
45309
+ function deriveCollaborationConnectionKind(snapshot, hasSyncedBefore) {
45310
+ switch (snapshot.state) {
45311
+ case "failed": return "failed";
45312
+ case "degraded": return snapshot.detail === "reconnecting" ? "reconnecting" : "lost";
45313
+ case "synced": return hasSyncedBefore ? "recovered" : "initial";
45314
+ default: return hasSyncedBefore ? "reconnecting" : "initial";
45315
+ }
45316
+ }
45304
45317
  function isV2ActiveEditorFacade(editor) {
45305
45318
  return Boolean(editor && typeof editor === "object" && editor.editorVersion === 2);
45306
45319
  }
@@ -45357,6 +45370,7 @@ var SuperDoc = class extends import_eventemitter3.default {
45357
45370
  #destroyed;
45358
45371
  #diagnostics;
45359
45372
  #isUpgrading;
45373
+ #pendingUpgrade;
45360
45374
  /** Aborts an in-flight upgrade (sync wait or ready wait). */
45361
45375
  #abortUpgrade;
45362
45376
  /**
@@ -45368,6 +45382,12 @@ var SuperDoc = class extends import_eventemitter3.default {
45368
45382
  #v2AwarenessUnsub;
45369
45383
  /** Unsubscribe handle for the v2 root-doc lock observer. */
45370
45384
  #v2LockUnsub;
45385
+ /** Unsubscribe handle for the v2 collaboration connection observer. */
45386
+ #v2ConnectionUnsub;
45387
+ /** Latest `collaboration-connection-change` payload, for late readers. */
45388
+ #v2ConnectionState;
45389
+ /** Whether the current collaboration room has completed a sync at least once. */
45390
+ #v2ConnectionHasSynced;
45371
45391
  /** Lock state captured before a local → v2 collaboration promotion remount. */
45372
45392
  #pendingV2LockSeed;
45373
45393
  #mountWrapper;
@@ -45456,9 +45476,13 @@ var SuperDoc = class extends import_eventemitter3.default {
45456
45476
  super();
45457
45477
  this.#destroyed = false;
45458
45478
  this.#isUpgrading = false;
45479
+ this.#pendingUpgrade = null;
45459
45480
  this.#abortUpgrade = null;
45460
45481
  this.#v2AwarenessUnsub = null;
45461
45482
  this.#v2LockUnsub = null;
45483
+ this.#v2ConnectionUnsub = null;
45484
+ this.#v2ConnectionState = null;
45485
+ this.#v2ConnectionHasSynced = false;
45462
45486
  this.#pendingV2LockSeed = null;
45463
45487
  this.#mountWrapper = null;
45464
45488
  this.#contentControlClickRoot = null;
@@ -45657,7 +45681,7 @@ var SuperDoc = class extends import_eventemitter3.default {
45657
45681
  this.config.colors = shuffleArray(this.config.colors);
45658
45682
  this.userColorMap = /* @__PURE__ */ new Map();
45659
45683
  this.colorIndex = 0;
45660
- this.version = "2.18.0-next.8";
45684
+ this.version = "2.18.0";
45661
45685
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
45662
45686
  this.superdocId = config.superdocId || v4();
45663
45687
  this.colors = this.config.colors ?? [];
@@ -45759,7 +45783,7 @@ var SuperDoc = class extends import_eventemitter3.default {
45759
45783
  return this.#diagnostics;
45760
45784
  }
45761
45785
  emit(event, ...args) {
45762
- if (event === "exception" || event === "document-replaced" || event === "ready" || event === "active-editor-change" || event === "collaboration-ready" || event === "locked") recordInteraction(this, event, () => args[0]);
45786
+ if (event === "exception" || event === "document-replaced" || event === "ready" || event === "active-editor-change" || event === "collaboration-ready" || event === "collaboration-connection-change" || event === "locked") recordInteraction(this, event, () => args[0]);
45763
45787
  return super.emit(event, ...args);
45764
45788
  }
45765
45789
  on(event, fn, context) {
@@ -46001,6 +46025,7 @@ var SuperDoc = class extends import_eventemitter3.default {
46001
46025
  this.#onConfig("pdf:document-ready", this.config.onPdfDocumentReady);
46002
46026
  this.#onConfig("sidebar-toggle", this.config.onSidebarToggle);
46003
46027
  this.#onConfig("collaboration-ready", this.config.onCollaborationReady);
46028
+ this.#onConfig("collaboration-connection-change", this.config.onCollaborationConnectionChange);
46004
46029
  this.on("collaboration-ready", (payload) => this.#startV2CollaborationEventBridge(payload?.editor ?? null));
46005
46030
  this.#onConfig("editor-update", this.config.onEditorUpdate);
46006
46031
  this.#onConfig("tracked-changes:bulk-decision", this.config.onTrackedChangesBulkDecision);
@@ -46091,12 +46116,59 @@ var SuperDoc = class extends import_eventemitter3.default {
46091
46116
  } catch {}
46092
46117
  this.#v2LockUnsub = null;
46093
46118
  }
46119
+ if (this.#v2ConnectionUnsub) {
46120
+ try {
46121
+ this.#v2ConnectionUnsub();
46122
+ } catch {}
46123
+ this.#v2ConnectionUnsub = null;
46124
+ }
46094
46125
  }
46095
46126
  #startV2CollaborationEventBridge(editor) {
46096
46127
  this.#stopV2CollaborationEventBridge();
46097
46128
  if (!isV2ActiveEditorFacade(editor)) return;
46098
46129
  this.#startV2AwarenessBridge(editor);
46099
46130
  this.#startV2LockBridge(editor);
46131
+ this.#startV2ConnectionBridge(editor);
46132
+ }
46133
+ #startV2ConnectionBridge(editor) {
46134
+ const connection = editor.connection;
46135
+ if (!connection || typeof connection.subscribe !== "function") return;
46136
+ const { documentId } = connection;
46137
+ if (this.#v2ConnectionState?.documentId !== documentId) {
46138
+ this.#v2ConnectionState = null;
46139
+ this.#v2ConnectionHasSynced = false;
46140
+ }
46141
+ const publish = (snapshot) => {
46142
+ if (this.#destroyed) return;
46143
+ const previous = this.#v2ConnectionState;
46144
+ if (previous && previous.state === snapshot.state && previous.detail === (snapshot.detail ?? null)) return;
46145
+ const payload = {
46146
+ documentId,
46147
+ state: snapshot.state,
46148
+ previousState: previous?.state ?? null,
46149
+ kind: deriveCollaborationConnectionKind(snapshot, this.#v2ConnectionHasSynced),
46150
+ detail: snapshot.detail ?? null,
46151
+ superdoc: this
46152
+ };
46153
+ this.#v2ConnectionState = payload;
46154
+ if (snapshot.state === "synced") this.#v2ConnectionHasSynced = true;
46155
+ this.emit("collaboration-connection-change", payload);
46156
+ };
46157
+ try {
46158
+ const initialSnapshot = connection.getSnapshot?.();
46159
+ if (initialSnapshot) publish(initialSnapshot);
46160
+ this.#v2ConnectionUnsub = connection.subscribe(publish);
46161
+ } catch (err) {
46162
+ console.warn("[SuperDoc] v2 connection bridge failed to subscribe", err);
46163
+ }
46164
+ }
46165
+ /**
46166
+ * Latest v2 collaboration connection change, or `null` when no v2
46167
+ * collaboration session has reported one. Same payload as the
46168
+ * `collaboration-connection-change` event.
46169
+ */
46170
+ getCollaborationConnectionState() {
46171
+ return this.#v2ConnectionState;
46100
46172
  }
46101
46173
  #startV2AwarenessBridge(editor) {
46102
46174
  const presence = editor.presence;
@@ -46184,29 +46256,78 @@ var SuperDoc = class extends import_eventemitter3.default {
46184
46256
  * - A supported v2 single-doc `v2Collaboration` target
46185
46257
  * - Create-and-upgrade only (no merge semantics)
46186
46258
  *
46187
- * @returns Resolves once the collaborative runtime is ready
46259
+ * @returns The attached room and document once the collaborative runtime is ready
46188
46260
  */
46189
46261
  async upgradeToCollaboration(options) {
46262
+ if (this.#pendingUpgrade) {
46263
+ const { target } = this.#resolveCollaborationUpgradeTarget(options);
46264
+ if (this.#sameUpgradeTarget(target, this.#pendingUpgrade.target)) return this.#pendingUpgrade.promise;
46265
+ throw Object.assign(/* @__PURE__ */ new Error("SuperDoc: a different collaboration target is already upgrading"), { code: "collaboration-upgrade-target-conflict" });
46266
+ }
46190
46267
  const { target } = this.#validateUpgradePrerequisites(options);
46191
46268
  this.#isUpgrading = true;
46192
- try {
46193
- const localSource = await this.#captureCurrentDocxSourceForUpgrade();
46194
- const rollback = this.#snapshotV2UpgradeState();
46195
- const lockSeed = this.#snapshotV2LockSeed();
46196
- this.#assertNotDestroyed();
46269
+ const operation = (async () => {
46197
46270
  try {
46198
- this.#pendingV2LockSeed = lockSeed;
46199
- const promotion = await this.#promoteSingleDocumentToV2Collaboration(target, localSource);
46200
- this.#finalizeSingleDocumentV2Collaboration(promotion, target);
46201
- this.#pendingV2LockSeed = null;
46202
- } catch (err) {
46203
- this.#rollbackV2UpgradeState(rollback);
46204
- throw err;
46205
- }
46206
- } finally {
46207
- this.#abortUpgrade = null;
46208
- this.#isUpgrading = false;
46209
- }
46271
+ const localSource = await this.#captureCurrentDocxSourceForUpgrade();
46272
+ const rollback = this.#snapshotV2UpgradeState();
46273
+ const lockSeed = this.#snapshotV2LockSeed();
46274
+ this.#assertNotDestroyed();
46275
+ try {
46276
+ this.#pendingV2LockSeed = lockSeed;
46277
+ const promotion = await this.#promoteSingleDocumentToV2Collaboration(target, localSource);
46278
+ this.#finalizeSingleDocumentV2Collaboration(promotion, target);
46279
+ this.#pendingV2LockSeed = null;
46280
+ const attachedDocumentId = this.activeEditor?.options?.documentId;
46281
+ const attachedRoomId = promotion.configDoc.v2Collaboration?.documentId;
46282
+ if (typeof attachedDocumentId !== "string" || attachedDocumentId !== promotion.configDoc.id || typeof attachedRoomId !== "string" || attachedRoomId !== target.documentId) throw new Error("SuperDoc: collaboration upgrade did not attach the expected room and document");
46283
+ return {
46284
+ roomId: attachedRoomId,
46285
+ documentId: attachedDocumentId
46286
+ };
46287
+ } catch (err) {
46288
+ this.#rollbackV2UpgradeState(rollback);
46289
+ throw err;
46290
+ }
46291
+ } finally {
46292
+ this.#abortUpgrade = null;
46293
+ this.#isUpgrading = false;
46294
+ this.#pendingUpgrade = null;
46295
+ }
46296
+ })();
46297
+ this.#pendingUpgrade = {
46298
+ target,
46299
+ promise: operation
46300
+ };
46301
+ return operation;
46302
+ }
46303
+ #sameUpgradeTarget(left, right) {
46304
+ if (left.token !== right.token) return false;
46305
+ const seen = /* @__PURE__ */ new WeakMap();
46306
+ const sameValue = (first, second) => {
46307
+ if (Object.is(first, second)) return true;
46308
+ if (!first || !second || typeof first !== "object" || typeof second !== "object") return false;
46309
+ if (Object.getPrototypeOf(first) !== Object.getPrototypeOf(second)) return false;
46310
+ if (seen.has(first)) return seen.get(first) === second;
46311
+ seen.set(first, second);
46312
+ if (first instanceof Date && second instanceof Date) return first.getTime() === second.getTime();
46313
+ if (first instanceof RegExp && second instanceof RegExp) return first.source === second.source && first.flags === second.flags;
46314
+ if (first instanceof ArrayBuffer && second instanceof ArrayBuffer) return sameValue(Array.from(new Uint8Array(first)), Array.from(new Uint8Array(second)));
46315
+ if (ArrayBuffer.isView(first) && ArrayBuffer.isView(second)) return first.constructor === second.constructor && sameValue(Array.from(new Uint8Array(first.buffer, first.byteOffset, first.byteLength)), Array.from(new Uint8Array(second.buffer, second.byteOffset, second.byteLength)));
46316
+ if (first instanceof Map && second instanceof Map) return first.size === second.size && sameValue(Array.from(first.entries()), Array.from(second.entries()));
46317
+ if (first instanceof Set && second instanceof Set) return first.size === second.size && sameValue(Array.from(first.values()), Array.from(second.values()));
46318
+ if (Array.isArray(first) && Array.isArray(second)) return first.length === second.length && first.every((value, index) => sameValue(value, second[index]));
46319
+ if (Object.getPrototypeOf(first) !== Object.prototype && Object.getPrototypeOf(first) !== null) return false;
46320
+ const keys = Object.keys(first).sort();
46321
+ const otherKeys = Object.keys(second).sort();
46322
+ return keys.length === otherKeys.length && keys.every((key, index) => key === otherKeys[index] && sameValue(first[key], second[key]));
46323
+ };
46324
+ return sameValue({
46325
+ ...left,
46326
+ token: void 0
46327
+ }, {
46328
+ ...right,
46329
+ token: void 0
46330
+ });
46210
46331
  }
46211
46332
  #snapshotV2LockSeed() {
46212
46333
  return {
@@ -46533,9 +46654,13 @@ var SuperDoc = class extends import_eventemitter3.default {
46533
46654
  if (docxDocs.length > 1) throw new Error("SuperDoc: upgradeToCollaboration() only supports a single DOCX document");
46534
46655
  if (cfg.documents.length !== docxDocs.length) throw new Error("SuperDoc: upgradeToCollaboration() only supports single-DOCX instances");
46535
46656
  if (!isV2ActiveEditorFacade(this.#resolveSourceEditor())) throw new Error("SuperDoc: upgradeToCollaboration() requires a ready v2 DOCX editor");
46536
- const configDoc = docxDocs[0];
46537
- const mountedDocumentId = configDoc.id;
46657
+ const mountedDocumentId = docxDocs[0].id;
46538
46658
  if (typeof mountedDocumentId !== "string" || mountedDocumentId.length === 0) throw new Error("SuperDoc: source document is missing its mounted document identity");
46659
+ return this.#resolveCollaborationUpgradeTarget(options);
46660
+ }
46661
+ #resolveCollaborationUpgradeTarget(options) {
46662
+ const cfg = this.config;
46663
+ const configDoc = cfg.documents.find((doc) => doc.type === DOCX);
46539
46664
  const collaborationModule = cfg.modules?.collaboration;
46540
46665
  const legacyCollaboration = options.ydoc || options.provider ? {
46541
46666
  ydoc: options.ydoc,
@@ -46551,7 +46676,10 @@ var SuperDoc = class extends import_eventemitter3.default {
46551
46676
  ...typeof window !== "undefined" ? { authEndpointBaseUrl: window.location.href } : {}
46552
46677
  });
46553
46678
  if (!resolution.ok) throw new Error(`SuperDoc: upgradeToCollaboration() ${resolution.reason}: ${resolution.message}`);
46554
- return { target: resolution.target };
46679
+ return { target: {
46680
+ ...resolution.target,
46681
+ roomMode: "create"
46682
+ } };
46555
46683
  }
46556
46684
  #unwrapMaybeRef(value) {
46557
46685
  if (value && typeof value === "object" && "value" in value) return value.value;