superdoc 2.4.0-next.12 → 2.4.0-next.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import { t as blank_default } from "./chunks/blank-docx-DzQccOlW.es.js";
3
3
  import { t as import_eventemitter3 } from "./chunks/eventemitter3-DEIiXiH2.es.js";
4
4
  import { t as v4 } from "./chunks/uuid-H0Xcmmhy.es.js";
5
5
  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";
6
- import { a as createV2ReviewMutationReconciler, i as isV2EditableTextMutationEvent, o as getV2TrackedChangeMutationImpact, s as DOM_CLASS_NAMES, t as createSuperDocUI } from "./chunks/create-super-doc-ui-D0mNmAHJ.es.js";
6
+ import { a as createV2ReviewMutationReconciler, i as isV2EditableTextMutationEvent, o as getV2TrackedChangeMutationImpact, s as DOM_CLASS_NAMES, t as createSuperDocUI } from "./chunks/create-super-doc-ui-BRBwfV-s.es.js";
7
7
  import { t as _plugin_vue_export_helper_default } from "./chunks/_plugin-vue_export-helper-BOaGB7Aw.es.js";
8
8
  import { t as PDF_TO_CSS_UNITS } from "./chunks/constants-B6VBlmKp.es.js";
9
9
  import * as Vue from "vue";
@@ -9907,16 +9907,38 @@ var useCommentsStore = defineStore("comments", () => {
9907
9907
  if (!fromId || !toId || fromId === toId) continue;
9908
9908
  const comment = commentsList.value.find((row) => matchesRemapSource(row, fromId));
9909
9909
  if (!comment) continue;
9910
+ const previousCommentId = normalizeCommentId(comment.commentId);
9911
+ const previousCanonicalId = normalizeCommentId(comment.trackedChangeCanonicalId);
9912
+ const previousPositionEntry = resolveCommentPositionEntry(comment).entry;
9913
+ const advancesRemapLineage = ![
9914
+ comment.commentId,
9915
+ comment.importedId,
9916
+ comment.trackedChangeCanonicalId
9917
+ ].map((id) => normalizeCommentId(id)).includes(fromId) && previousCommentId != null && previousCanonicalId != null && previousCommentId === previousCanonicalId;
9910
9918
  const commentIdentityUpdates = {};
9911
9919
  for (const field of ["commentId", "importedId"]) if (rewriteIfMatch(comment, field, fromId, toId)) commentIdentityUpdates[field] = toId;
9912
9920
  if (rewriteIfMatch(comment, "trackedChangeCanonicalId", fromId, toId)) commentIdentityUpdates.trackedChangeCanonicalId = toId;
9913
- const fromAnchor = buildBodyTrackedChangeAnchorKey(fromId);
9914
- const storyKey = parseTrackedChangeAnchorKey(comment.trackedChangeAnchorKey)?.storyKey ?? normalizeTrackedChangeStoryKey(getTrackedChangeStoryKey(comment));
9915
- const currentStoryFromAnchor = buildTrackedChangeAnchorKeyForStory(storyKey, fromId);
9921
+ if (advancesRemapLineage) {
9922
+ comment.commentId = toId;
9923
+ comment.trackedChangeCanonicalId = toId;
9924
+ commentIdentityUpdates.commentId = toId;
9925
+ commentIdentityUpdates.trackedChangeCanonicalId = toId;
9926
+ }
9927
+ const currentAnchorParts = parseTrackedChangeAnchorKey(comment.trackedChangeAnchorKey);
9928
+ const storyKey = currentAnchorParts?.storyKey ?? normalizeTrackedChangeStoryKey(getTrackedChangeStoryKey(comment));
9916
9929
  const toAnchor = buildTrackedChangeAnchorKeyForStory(storyKey, toId);
9917
- if (normalizeCommentId(comment.trackedChangeAnchorKey) === fromAnchor || comment.trackedChangeAnchorKey == null) comment.trackedChangeAnchorKey = toAnchor;
9918
- else if (normalizeCommentId(comment.trackedChangeAnchorKey) === currentStoryFromAnchor) comment.trackedChangeAnchorKey = toAnchor;
9919
- if (Array.isArray(comment.trackedChangePositionAliases)) comment.trackedChangePositionAliases = comment.trackedChangePositionAliases.map((alias) => normalizeCommentId(alias) === fromId ? toId : alias);
9930
+ const anchorSourceIds = /* @__PURE__ */ new Set([fromId]);
9931
+ if (advancesRemapLineage) {
9932
+ if (previousCommentId) anchorSourceIds.add(previousCommentId);
9933
+ if (previousCanonicalId) anchorSourceIds.add(previousCanonicalId);
9934
+ }
9935
+ if (comment.trackedChangeAnchorKey == null || anchorSourceIds.has(currentAnchorParts?.rawId)) comment.trackedChangeAnchorKey = toAnchor;
9936
+ comment.trackedChangePositionAliases = normalizeTrackedChangePositionAliases([...Array.isArray(comment.trackedChangePositionAliases) ? comment.trackedChangePositionAliases : [], fromId]);
9937
+ const identitySourceIds = /* @__PURE__ */ new Set([fromId]);
9938
+ if (advancesRemapLineage) {
9939
+ if (previousCommentId) identitySourceIds.add(previousCommentId);
9940
+ if (previousCanonicalId) identitySourceIds.add(previousCanonicalId);
9941
+ }
9920
9942
  commentsList.value.forEach((row) => {
9921
9943
  if (row === comment) return;
9922
9944
  if (!belongsToTrackedChangeSyncDocument(row, normalizedDocumentId)) return;
@@ -9926,20 +9948,23 @@ var useCommentsStore = defineStore("comments", () => {
9926
9948
  "trackedChangeParentId",
9927
9949
  "threadingParentCommentId",
9928
9950
  "parentCommentId"
9929
- ]) if (rewriteIfMatch(row, field, fromId, toId)) rowIdentityUpdates[field] = toId;
9951
+ ]) {
9952
+ if (!identitySourceIds.has(normalizeCommentId(row?.[field]))) continue;
9953
+ row[field] = toId;
9954
+ rowIdentityUpdates[field] = toId;
9955
+ }
9930
9956
  if (Object.keys(rowIdentityUpdates).length && typeof row.updateIdentityValues === "function") row.updateIdentityValues(rowIdentityUpdates);
9931
9957
  });
9932
9958
  if (Object.keys(commentIdentityUpdates).length && typeof comment.updateIdentityValues === "function") comment.updateIdentityValues(commentIdentityUpdates);
9933
- const fallbackPositionEntries = [positions[fromId], positions[fromAnchor]];
9934
- const previousEntry = positions[currentStoryFromAnchor] ?? fallbackPositionEntries.find((entry) => entry && positionEntryMatchesTrackedChangeStory(entry, comment));
9959
+ const previousEntry = previousPositionEntry ?? [positions[fromId], positions[buildBodyTrackedChangeAnchorKey(fromId)]].find((entry) => entry && positionEntryMatchesTrackedChangeStory(entry, comment));
9935
9960
  if (previousEntry) {
9936
9961
  for (const key of [toId, toAnchor]) if (positions[key] === void 0) {
9937
9962
  positions[key] = previousEntry;
9938
9963
  positionsChanged = true;
9939
9964
  }
9940
9965
  }
9941
- if (normalizeCommentId(activeComment.value) === fromId) activeComment.value = toId;
9942
- if (normalizeCommentId(activeFloatingCommentInstanceId.value) === fromId) activeFloatingCommentInstanceId.value = toId;
9966
+ if (identitySourceIds.has(normalizeCommentId(activeComment.value))) activeComment.value = toId;
9967
+ if (identitySourceIds.has(normalizeCommentId(activeFloatingCommentInstanceId.value))) activeFloatingCommentInstanceId.value = toId;
9943
9968
  }
9944
9969
  if (positionsChanged) editorCommentPositions.value = { ...positions };
9945
9970
  };
@@ -15824,6 +15849,33 @@ function createV2RemoteReviewHydrationScheduler({ hydrate, getActiveDocumentId,
15824
15849
  };
15825
15850
  }
15826
15851
  //#endregion
15852
+ //#region src/helpers/v2-typing-review-hydration.js
15853
+ /**
15854
+ * Coalesce automatic review-catalog reconciliation behind a genuine typing
15855
+ * quiet period. Every typing signal restarts the trailing timer; there is no
15856
+ * maximum-wait escape hatch because that would deliberately run the expensive
15857
+ * catalog reads in the middle of a sustained input burst.
15858
+ */
15859
+ function createV2TypingReviewHydrationScheduler({ hydrate, idleMs = 6500, setTimer = (callback, delay) => setTimeout(callback, delay), clearTimer = (timer) => clearTimeout(timer) }) {
15860
+ let timer = null;
15861
+ const clear = () => {
15862
+ if (timer != null) clearTimer(timer);
15863
+ timer = null;
15864
+ };
15865
+ const schedule = () => {
15866
+ clear();
15867
+ timer = setTimer(() => {
15868
+ timer = null;
15869
+ hydrate();
15870
+ }, idleMs);
15871
+ };
15872
+ return {
15873
+ schedule,
15874
+ clear,
15875
+ isPending: () => timer != null
15876
+ };
15877
+ }
15878
+ //#endregion
15827
15879
  //#region src/helpers/v2-author-required-rejection.js
15828
15880
  var NO_AUTHOR_SIGNAL = "no-author-configured";
15829
15881
  /** Stable, non-terminal exception code surfaced to consumers. */
@@ -20263,8 +20315,6 @@ var _hoisted_4$7 = { class: "floating-comments" };
20263
20315
  var UNAVAILABLE_COMMAND_RESULT = false;
20264
20316
  var V2_SELECTION_TOOLBAR_SYNC_RETRY_FRAMES = 3;
20265
20317
  var V2_TYPING_REVIEW_HYDRATION_IDLE_MS = 6500;
20266
- var V2_TYPING_REVIEW_HYDRATION_CEILING_MS = 8e3;
20267
- var V2_TYPING_REVIEW_VISIBLE_PRIME_MS = 250;
20268
20318
  var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
20269
20319
  __name: "SuperDoc",
20270
20320
  emits: ["selection-update"],
@@ -20385,7 +20435,6 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
20385
20435
  let v2GeometryRafHandle = 0;
20386
20436
  let v2TrackedChangeRestampRetainPaints = 0;
20387
20437
  let v2TrackedChangeRestampRetentionReason = null;
20388
- let v2TrackedChangeRestampRetentionIds = null;
20389
20438
  const activeEditorRef = computed(() => proxy.$superdoc.activeEditor);
20390
20439
  const getSuperDocUI = () => proxy.$superdoc?.ui ?? null;
20391
20440
  const UNAVAILABLE_COMMAND_STATE = Object.freeze({
@@ -21295,49 +21344,29 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21295
21344
  }
21296
21345
  },
21297
21346
  onCommittedPagePaint: (commit) => {
21347
+ if (commit?.routeLane === "canonical.typing-mutation" || v2TypingReviewHydrationScheduler.isPending()) return;
21298
21348
  v2ReviewHydrationController.onCommittedPagePaint?.({
21299
21349
  ...commit,
21300
21350
  documentId: commit?.payload?.documentId ?? null
21301
21351
  });
21302
21352
  }
21303
21353
  });
21304
- const collectV2TrackedChangeRestampIds = (impactOrIds) => {
21305
- if (impactOrIds instanceof Set) return new Set([...impactOrIds].filter(Boolean).map(String));
21306
- if (Array.isArray(impactOrIds)) return new Set(impactOrIds.filter(Boolean).map(String));
21307
- const ids = /* @__PURE__ */ new Set();
21308
- for (const id of impactOrIds?.upsertIds ?? []) ids.add(String(id));
21309
- for (const id of impactOrIds?.removedIds ?? []) ids.add(String(id));
21310
- for (const pair of impactOrIds?.remappedPairs ?? []) {
21311
- if (pair?.from) ids.add(String(pair.from));
21312
- if (pair?.to) ids.add(String(pair.to));
21313
- }
21314
- return ids;
21315
- };
21316
- const armV2TrackedChangeRestampGeometryRetention = (reason = "tracked-change-restamp", impactOrIds = null) => {
21354
+ const armV2TrackedChangeRestampGeometryRetention = (reason = "tracked-change-restamp") => {
21317
21355
  v2TrackedChangeRestampRetainPaints = Math.max(v2TrackedChangeRestampRetainPaints, 3);
21318
21356
  v2TrackedChangeRestampRetentionReason = reason;
21319
- const retainedIds = collectV2TrackedChangeRestampIds(impactOrIds);
21320
- if (retainedIds.size > 0) v2TrackedChangeRestampRetentionIds = /* @__PURE__ */ new Set([...v2TrackedChangeRestampRetentionIds ?? [], ...retainedIds]);
21321
21357
  };
21322
21358
  const takeV2TrackedChangeRestampGeometryRetention = () => {
21323
- if (v2TrackedChangeRestampRetainPaints <= 0) return void 0;
21324
- const reason = v2TrackedChangeRestampRetentionReason ?? "tracked-change-restamp";
21325
- const retainedTrackedChangeIds = v2TrackedChangeRestampRetentionIds?.size ? Array.from(v2TrackedChangeRestampRetentionIds) : null;
21326
- v2TrackedChangeRestampRetainPaints -= 1;
21327
- if (v2TrackedChangeRestampRetainPaints <= 0) {
21328
- v2TrackedChangeRestampRetentionReason = null;
21329
- v2TrackedChangeRestampRetentionIds = null;
21330
- }
21359
+ const reason = v2TrackedChangeRestampRetainPaints > 0 ? v2TrackedChangeRestampRetentionReason ?? "tracked-change-restamp" : "render-epoch-handoff";
21360
+ if (v2TrackedChangeRestampRetainPaints > 0) v2TrackedChangeRestampRetainPaints -= 1;
21361
+ if (v2TrackedChangeRestampRetainPaints <= 0) v2TrackedChangeRestampRetentionReason = null;
21331
21362
  return {
21332
21363
  retainMissingTrackedChangeGeometry: true,
21333
- ...retainedTrackedChangeIds ? { retainedTrackedChangeIds } : {},
21334
21364
  reason
21335
21365
  };
21336
21366
  };
21337
21367
  const clearV2TrackedChangeRestampGeometryRetention = () => {
21338
21368
  v2TrackedChangeRestampRetainPaints = 0;
21339
21369
  v2TrackedChangeRestampRetentionReason = null;
21340
- v2TrackedChangeRestampRetentionIds = null;
21341
21370
  };
21342
21371
  const resolveV2GeometryPublishOptions = (options) => typeof options === "function" ? options() : options;
21343
21372
  const scheduleV2GeometryPublish = (payload, options = void 0) => {
@@ -21360,6 +21389,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21360
21389
  const onV2RenderReadiness = (payload) => {
21361
21390
  const snapshot = payload?.snapshot ?? payload ?? null;
21362
21391
  if (!snapshot) return;
21392
+ if (v2TypingReviewHydrationScheduler.isPending()) return;
21363
21393
  v2ReviewHydrationController.onRenderReadiness(snapshot);
21364
21394
  };
21365
21395
  const v2PageMetricsSnapshot = shallowRef(null);
@@ -21419,8 +21449,6 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21419
21449
  v2RulerHostStyle.value = {};
21420
21450
  v2RulerReady.value = false;
21421
21451
  };
21422
- let v2TypingReviewHydrationTimer = 0;
21423
- let v2TypingReviewVisiblePrimeTimer = 0;
21424
21452
  const hydrateV2CommentRowsFromHost = () => {
21425
21453
  const commentsAdapter = proxy.$superdoc?.activeEditor?.v2Comments ?? null;
21426
21454
  if (!commentsAdapter) return;
@@ -21441,19 +21469,6 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21441
21469
  ...options
21442
21470
  });
21443
21471
  };
21444
- const hydrateVisibleV2TrackedChangeRowsFromHost = (options = {}) => {
21445
- const trackedChangesAdapter = proxy.$superdoc?.activeEditor?.v2TrackedChanges ?? null;
21446
- if (!trackedChangesAdapter) return;
21447
- commentsStore.hydrateTrackedChangesFromV2?.({
21448
- superdoc: proxy.$superdoc,
21449
- adapter: trackedChangesAdapter,
21450
- documentId: proxy.$superdoc?.activeEditor?.documentId ?? null,
21451
- trackedChangesListMode: "visible-window",
21452
- refreshReason: "typing-visible-prime",
21453
- blocking: false,
21454
- ...options
21455
- });
21456
- };
21457
21472
  const v2ReviewMutationReconciler = createV2ReviewMutationReconciler({
21458
21473
  getContext: () => {
21459
21474
  const activeEditor = proxy.$superdoc?.activeEditor ?? null;
@@ -21483,46 +21498,21 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21483
21498
  reason: "review-reconcile"
21484
21499
  })
21485
21500
  });
21486
- let v2LastTypingMutationAtMs = 0;
21487
- let v2TypingReviewHydrationHeldSinceMs = 0;
21501
+ const v2TypingReviewHydrationScheduler = createV2TypingReviewHydrationScheduler({
21502
+ idleMs: V2_TYPING_REVIEW_HYDRATION_IDLE_MS,
21503
+ hydrate: () => v2ReviewHydrationController.reconcileInBackground?.("typing-idle")
21504
+ });
21488
21505
  const clearV2TypingReviewHydrationTimer = () => {
21489
- if (v2TypingReviewHydrationTimer && typeof clearTimeout === "function") clearTimeout(v2TypingReviewHydrationTimer);
21490
- if (v2TypingReviewVisiblePrimeTimer && typeof clearTimeout === "function") clearTimeout(v2TypingReviewVisiblePrimeTimer);
21491
- v2TypingReviewHydrationTimer = 0;
21492
- v2TypingReviewVisiblePrimeTimer = 0;
21493
- v2LastTypingMutationAtMs = 0;
21494
- v2TypingReviewHydrationHeldSinceMs = 0;
21495
- };
21496
- const scheduleV2TypingReviewVisiblePrime = (options = {}) => {
21497
- if (v2TypingReviewVisiblePrimeTimer) return;
21498
- v2TypingReviewVisiblePrimeTimer = setTimeout(() => {
21499
- v2TypingReviewVisiblePrimeTimer = 0;
21500
- hydrateVisibleV2TrackedChangeRowsFromHost(options);
21501
- }, V2_TYPING_REVIEW_VISIBLE_PRIME_MS);
21502
- };
21503
- const scheduleV2TypingReviewHydration = (options = {}) => {
21504
- v2LastTypingMutationAtMs = Date.now();
21505
- scheduleV2TypingReviewVisiblePrime(options.primeOptions);
21506
- if (!v2TypingReviewHydrationHeldSinceMs) v2TypingReviewHydrationHeldSinceMs = v2LastTypingMutationAtMs;
21507
- if (v2TypingReviewHydrationTimer && typeof clearTimeout === "function") clearTimeout(v2TypingReviewHydrationTimer);
21508
- const attempt = () => {
21509
- v2TypingReviewHydrationTimer = 0;
21510
- const idleForMs = Date.now() - v2LastTypingMutationAtMs;
21511
- const heldForMs = Date.now() - v2TypingReviewHydrationHeldSinceMs;
21512
- if (idleForMs < V2_TYPING_REVIEW_HYDRATION_IDLE_MS && heldForMs < V2_TYPING_REVIEW_HYDRATION_CEILING_MS) {
21513
- const idleRemainingMs = V2_TYPING_REVIEW_HYDRATION_IDLE_MS - idleForMs;
21514
- const ceilingRemainingMs = V2_TYPING_REVIEW_HYDRATION_CEILING_MS - heldForMs;
21515
- v2TypingReviewHydrationTimer = setTimeout(attempt, Math.min(idleRemainingMs, ceilingRemainingMs));
21516
- return;
21517
- }
21518
- v2TypingReviewHydrationHeldSinceMs = 0;
21519
- hydrateV2ReviewRowsFromHost();
21520
- };
21521
- const ceilingRemainingMs = Math.max(0, V2_TYPING_REVIEW_HYDRATION_CEILING_MS - (Date.now() - v2TypingReviewHydrationHeldSinceMs));
21522
- v2TypingReviewHydrationTimer = setTimeout(attempt, Math.min(V2_TYPING_REVIEW_HYDRATION_IDLE_MS, ceilingRemainingMs));
21506
+ v2TypingReviewHydrationScheduler.clear();
21507
+ };
21508
+ const scheduleV2TypingReviewHydration = () => {
21509
+ v2TypingReviewHydrationScheduler.schedule();
21523
21510
  };
21524
21511
  const v2RemoteReviewHydrationScheduler = createV2RemoteReviewHydrationScheduler({
21525
- hydrate: () => v2ReviewHydrationController.reconcileInBackground?.("remote-review-change"),
21512
+ hydrate: () => {
21513
+ if (v2TypingReviewHydrationScheduler.isPending()) return false;
21514
+ return v2ReviewHydrationController.reconcileInBackground?.("remote-review-change");
21515
+ },
21526
21516
  getActiveDocumentId: () => {
21527
21517
  const activeEditor = proxy.$superdoc?.activeEditor ?? null;
21528
21518
  return activeEditor?.documentId ?? activeEditor?.options?.documentId ?? null;
@@ -21532,6 +21522,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21532
21522
  if (!event) return;
21533
21523
  const documentId = document?.id ?? null;
21534
21524
  if (event.type === "review-mutation:started") {
21525
+ clearV2TypingReviewHydrationTimer();
21535
21526
  const activeEditor = proxy.$superdoc?.activeEditor ?? null;
21536
21527
  commentsStore.supersedeV2ReviewHydration?.({
21537
21528
  commentsAdapter: activeEditor?.v2Comments ?? null,
@@ -21582,7 +21573,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21582
21573
  v2ReviewHydrationController.invalidate("mutation-committed");
21583
21574
  const reviewImpact = getV2TrackedChangeMutationImpact(event);
21584
21575
  if (Array.isArray(reviewImpact?.remappedPairs) && reviewImpact.remappedPairs.length > 0) commentsStore.remapTrackedChangeIdentities?.(reviewImpact.remappedPairs, { documentId });
21585
- if (reviewImpact) armV2TrackedChangeRestampGeometryRetention("tracked-change-mutation", reviewImpact);
21576
+ if (reviewImpact) armV2TrackedChangeRestampGeometryRetention("tracked-change-mutation");
21586
21577
  const readiness = (proxy.$superdoc?.activeEditor ?? null)?.documentMutationReadiness ?? null;
21587
21578
  const receipt = event.origin === "history" ? null : event.receipt;
21588
21579
  const canWaitForExactPaint = reviewImpact && typeof receipt?.txId === "string" && receipt.txId.length > 0 && typeof readiness?.whenPainted === "function";
@@ -21602,11 +21593,13 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21602
21593
  Promise.resolve(reconciliation).then(settleReviewMutation, () => settleReviewMutation(false));
21603
21594
  return;
21604
21595
  }
21596
+ const isTypingMutation = isV2EditableTextMutationEvent(event);
21597
+ if (!isTypingMutation) clearV2TypingReviewHydrationTimer();
21605
21598
  if (reviewImpact?.reconcileMode === "authoritative" && reviewImpact.upsertIds.size > 0) {
21606
21599
  hydrateV2CommentRowsFromHost();
21607
21600
  return;
21608
21601
  }
21609
- if (isV2EditableTextMutationEvent(event)) {
21602
+ if (isTypingMutation) {
21610
21603
  scheduleV2TypingReviewHydration();
21611
21604
  return;
21612
21605
  }
@@ -21632,6 +21625,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21632
21625
  const handleV2DocumentModeChange = () => {
21633
21626
  if (!isV2Mode.value) return;
21634
21627
  try {
21628
+ clearV2TypingReviewHydrationTimer();
21635
21629
  v2ReviewHydrationController.hydrateNow("document-mode-change");
21636
21630
  } catch (err) {
21637
21631
  console.warn("[SuperDoc][v2] document-mode-change rehydrate failed", err);
@@ -21650,6 +21644,22 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21650
21644
  else republishGeometry();
21651
21645
  });
21652
21646
  };
21647
+ let v2BuiltInReviewSurfaceOpen = false;
21648
+ let v2ExternalReviewSurfaceOpen = false;
21649
+ const syncV2ReviewCatalogDemand = () => {
21650
+ if (!isV2Mode.value) return;
21651
+ if (!v2BuiltInReviewSurfaceOpen && !v2ExternalReviewSurfaceOpen) {
21652
+ v2ReviewHydrationController.releaseCatalogDemand();
21653
+ return;
21654
+ }
21655
+ clearV2TypingReviewHydrationTimer();
21656
+ v2ReviewHydrationController.demandCatalog("review-sidebar-visible");
21657
+ };
21658
+ const handleV2CommentsListChange = ({ isRendered } = {}) => {
21659
+ v2ExternalReviewSurfaceOpen = isRendered === true;
21660
+ syncV2ReviewCatalogDemand();
21661
+ };
21662
+ proxy.$superdoc?.on?.("comments-list-change", handleV2CommentsListChange);
21653
21663
  const getV2EditorFailureMessage = (reason) => {
21654
21664
  switch (reason) {
21655
21665
  case "editing-mount-required": return "SuperDoc could not load the document editor because the page did not provide a mount container.";
@@ -21777,6 +21787,11 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21777
21787
  onFontsResolved: onFontsResolvedFn,
21778
21788
  onPageCountKnown: proxy.$superdoc.config.onPageCountKnown ?? null,
21779
21789
  onReviewWindowPlanned: (payload) => {
21790
+ if (payload?.routeLane === "canonical.typing-mutation") {
21791
+ v2TypingReviewHydrationScheduler.schedule();
21792
+ return;
21793
+ }
21794
+ if (v2TypingReviewHydrationScheduler.isPending()) return;
21780
21795
  v2ReviewHydrationController.onReviewWindowPlanned?.({
21781
21796
  ...payload,
21782
21797
  documentId: doc.id
@@ -22109,6 +22124,8 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
22109
22124
  });
22110
22125
  watch(showCommentsSidebar, (value) => {
22111
22126
  proxy.$superdoc.broadcastSidebarToggle(value);
22127
+ v2BuiltInReviewSurfaceOpen = value === true;
22128
+ syncV2ReviewCatalogDemand();
22112
22129
  });
22113
22130
  useViewportFit({
22114
22131
  getSuperdoc: () => proxy.$superdoc,
@@ -22274,6 +22291,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
22274
22291
  document.removeEventListener("mouseup", handleDocumentSelectionChange, true);
22275
22292
  document.removeEventListener("selectionchange", handleDocumentSelectionChange);
22276
22293
  proxy.$superdoc?.off?.("document-mode-change", handleV2DocumentModeChange);
22294
+ proxy.$superdoc?.off?.("comments-list-change", handleV2CommentsListChange);
22277
22295
  });
22278
22296
  const selectionLayer = ref(null);
22279
22297
  const isDragging = ref(false);
@@ -22769,7 +22787,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
22769
22787
  ], 38);
22770
22788
  };
22771
22789
  }
22772
- }, [["__scopeId", "data-v-62293b70"]]);
22790
+ }, [["__scopeId", "data-v-25570fd2"]]);
22773
22791
  //#endregion
22774
22792
  //#region src/core/create-app.js
22775
22793
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
@@ -39639,7 +39657,7 @@ var useToolbarItem = (options) => {
39639
39657
  "dropdown",
39640
39658
  "overflow"
39641
39659
  ].includes(options.type)) throw new Error("Invalid toolbar item type - " + options.type);
39642
- if (options.type === "button" && !options.defaultLabel && !options.icon) throw new Error("Toolbar button item needs either icon or label - " + options.name);
39660
+ if (options.type === "button" && !options.defaultLabel && !options.icon) throw new Error(`Toolbar button item needs either "icon" or "defaultLabel" - ${options.name}. "label" is the live label and is not a substitute for "defaultLabel".`);
39643
39661
  if (!options.name) throw new Error("Invalid toolbar item name - " + options.name);
39644
39662
  const id = ref(v4());
39645
39663
  const type = options.type;
@@ -39684,6 +39702,12 @@ var useToolbarItem = (options) => {
39684
39702
  const nestedOptions = ref([]);
39685
39703
  if (options.options) {
39686
39704
  if (!Array.isArray(options.options)) throw new Error("Invalid toolbar item options - " + options.options);
39705
+ let unusable = 0;
39706
+ for (let index = 0; index < options.options.length; index += 1) {
39707
+ const option = options.options[index];
39708
+ if (!option || typeof option !== "object") unusable += 1;
39709
+ }
39710
+ if (unusable) throw new Error(`Toolbar item options must all be objects - ${options.name}. ${unusable} row(s) are not, so this item is skipped.`);
39687
39711
  nestedOptions.value?.push(...options.options);
39688
39712
  }
39689
39713
  const activate = (attrs = {}, ...args) => {
@@ -42191,6 +42215,17 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
42191
42215
  this._detachFormatPainterModeChange = null;
42192
42216
  /** Registered custom-button command unregister handles. */
42193
42217
  this._customCommandRegs = /* @__PURE__ */ new Map();
42218
+ /**
42219
+ * Custom entries already reported as unbuildable.
42220
+ *
42221
+ * `#makeToolbarItems` runs on every throttled resize and on font or
42222
+ * active-editor changes, not only at construction, so a single static
42223
+ * configuration error would otherwise fire `exception` once per rebuild --
42224
+ * unbounded, into consumer telemetry, for a mistake that cannot change
42225
+ * without a reconfigure. Keyed by name plus message so a different fault
42226
+ * on the same entry still reports once.
42227
+ */
42228
+ this._reportedInvalidCustomItems = /* @__PURE__ */ new Set();
42194
42229
  /** Signature of the last-built font options, to skip redundant rebuilds. */
42195
42230
  this._lastFontOptionsSignature = "";
42196
42231
  if (!this.config.selector && this.config.element) this.config.selector = this.config.element;
@@ -42297,8 +42332,23 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
42297
42332
  isDev: this.isDev
42298
42333
  });
42299
42334
  const customItems = this.config.customButtons || [];
42300
- const customToolbarItems = customItems.map((item) => this.#prepareCustomButton(item));
42301
- if (customItems.length) defaultItems.push(...customToolbarItems);
42335
+ const takenNames = new Set([...defaultItems, ...overflowItems].map((item) => item.name?.value).filter(Boolean));
42336
+ const customToolbarItems = customItems.map((item, index) => {
42337
+ try {
42338
+ if (item?.name && takenNames.has(item.name)) throw new Error(`Toolbar item name "${item.name}" is already taken by another item, so this entry is skipped.`);
42339
+ const prepared = this.#prepareCustomButton(item);
42340
+ takenNames.add(prepared.name?.value);
42341
+ return prepared;
42342
+ } catch (error) {
42343
+ const signature = `${item?.name ?? `<unnamed@${index}>`}::${error?.message ?? String(error)}`;
42344
+ if (!this._reportedInvalidCustomItems.has(signature)) {
42345
+ this._reportedInvalidCustomItems.add(signature);
42346
+ this.#emitCommandException(error, item?.name ?? null);
42347
+ }
42348
+ return null;
42349
+ }
42350
+ }).filter(Boolean);
42351
+ if (customToolbarItems.length) defaultItems.push(...customToolbarItems);
42302
42352
  let allConfigItems = [...defaultItems.map((item) => item.name.value), ...overflowItems.map((item) => item.name.value)];
42303
42353
  if (hasExplicitGroupComposition) {
42304
42354
  const groupedItems = Object.values(this.config.groups).flatMap((item) => item);
@@ -42711,13 +42761,15 @@ var BuiltInToolbar = class extends import_eventemitter3.default {
42711
42761
  }
42712
42762
  }
42713
42763
  #emitCommandException(originalError, name) {
42714
- const error = originalError instanceof Error ? originalError : new Error(String(originalError));
42715
- this.emit("exception", {
42716
- error,
42764
+ const payload = {
42765
+ error: originalError instanceof Error ? originalError : new Error(String(originalError)),
42717
42766
  originalError,
42718
42767
  itemName: name ?? null,
42719
42768
  editor: this.superdoc?.activeEditor ?? this.activeEditor ?? null
42720
- });
42769
+ };
42770
+ const hostPayload = { ...payload };
42771
+ this.emit("exception", payload);
42772
+ this.superdoc?.emit?.("exception", hostPayload);
42721
42773
  }
42722
42774
  /**
42723
42775
  * Main toolbar command entry point used by the rendered components.
@@ -43212,7 +43264,7 @@ var SuperDoc = class extends import_eventemitter3.default {
43212
43264
  this.config.colors = shuffleArray(this.config.colors);
43213
43265
  this.userColorMap = /* @__PURE__ */ new Map();
43214
43266
  this.colorIndex = 0;
43215
- this.version = "2.4.0-next.12";
43267
+ this.version = "2.4.0-next.13";
43216
43268
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
43217
43269
  this.superdocId = config.superdocId || v4();
43218
43270
  this.colors = this.config.colors ?? [];
@@ -44634,6 +44686,7 @@ var SuperDoc = class extends import_eventemitter3.default {
44634
44686
  if (element) this.config.modules.comments.element = element;
44635
44687
  this.commentsList = new SuperComments(this.config.modules?.comments, this);
44636
44688
  if (this.config.onCommentsListChange) this.config.onCommentsListChange({ isRendered: true });
44689
+ this.emit("comments-list-change", { isRendered: true });
44637
44690
  }
44638
44691
  /**
44639
44692
  * Remove the comments list from the superdoc
@@ -44643,6 +44696,7 @@ var SuperDoc = class extends import_eventemitter3.default {
44643
44696
  this.commentsList.close();
44644
44697
  this.commentsList = null;
44645
44698
  if (this.config.onCommentsListChange) this.config.onCommentsListChange({ isRendered: false });
44699
+ this.emit("comments-list-change", { isRendered: false });
44646
44700
  }
44647
44701
  }
44648
44702
  /**