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.
@@ -187,6 +187,17 @@ export class BuiltInToolbar extends EventEmitter<string | symbol, any> {
187
187
  _detachFormatPainterModeChange: any;
188
188
  /** Registered custom-button command unregister handles. */
189
189
  _customCommandRegs: Map<any, any>;
190
+ /**
191
+ * Custom entries already reported as unbuildable.
192
+ *
193
+ * `#makeToolbarItems` runs on every throttled resize and on font or
194
+ * active-editor changes, not only at construction, so a single static
195
+ * configuration error would otherwise fire `exception` once per rebuild --
196
+ * unbounded, into consumer telemetry, for a mistake that cannot change
197
+ * without a reconfigure. Keyed by name plus message so a different fault
198
+ * on the same entry still reports once.
199
+ */
200
+ _reportedInvalidCustomItems: Set<any>;
190
201
  /** Signature of the last-built font options, to skip redundant rebuilds. */
191
202
  _lastFontOptionsSignature: string;
192
203
  app: import('vue').App<Element> | undefined;
@@ -99,6 +99,7 @@ import type { SuperDocExceptionEditorPayload as __Cjs_SuperDocExceptionEditorPay
99
99
  import type { SuperDocExceptionPayload as __Cjs_SuperDocExceptionPayload } from './index.js' with { "resolution-mode": "import" };
100
100
  import type { SuperDocExceptionRestorePayload as __Cjs_SuperDocExceptionRestorePayload } from './index.js' with { "resolution-mode": "import" };
101
101
  import type { SuperDocExceptionStorePayload as __Cjs_SuperDocExceptionStorePayload } from './index.js' with { "resolution-mode": "import" };
102
+ import type { SuperDocExceptionToolbarPayload as __Cjs_SuperDocExceptionToolbarPayload } from './index.js' with { "resolution-mode": "import" };
102
103
  import type { SuperDocExtension as __Cjs_SuperDocExtension } from './index.js' with { "resolution-mode": "import" };
103
104
  import type { SuperDocExtensionActivateReturn as __Cjs_SuperDocExtensionActivateReturn } from './index.js' with { "resolution-mode": "import" };
104
105
  import type { SuperDocExtensionCapabilities as __Cjs_SuperDocExtensionCapabilities } from './index.js' with { "resolution-mode": "import" };
@@ -166,6 +167,12 @@ import type { SurfacesModuleConfig as __Cjs_SurfacesModuleConfig } from './index
166
167
  import type { TextAddress as __Cjs_TextAddress } from './index.js' with { "resolution-mode": "import" };
167
168
  import type { TextSegment as __Cjs_TextSegment } from './index.js' with { "resolution-mode": "import" };
168
169
  import type { TextTarget as __Cjs_TextTarget } from './index.js' with { "resolution-mode": "import" };
170
+ import type { ToolbarCustomButton as __Cjs_ToolbarCustomButton } from './index.js' with { "resolution-mode": "import" };
171
+ import type { ToolbarCustomButtonContext as __Cjs_ToolbarCustomButtonContext } from './index.js' with { "resolution-mode": "import" };
172
+ import type { ToolbarCustomButtonItem as __Cjs_ToolbarCustomButtonItem } from './index.js' with { "resolution-mode": "import" };
173
+ import type { ToolbarCustomDropdownItem as __Cjs_ToolbarCustomDropdownItem } from './index.js' with { "resolution-mode": "import" };
174
+ import type { ToolbarCustomDropdownOption as __Cjs_ToolbarCustomDropdownOption } from './index.js' with { "resolution-mode": "import" };
175
+ import type { ToolbarCustomSeparatorItem as __Cjs_ToolbarCustomSeparatorItem } from './index.js' with { "resolution-mode": "import" };
169
176
  import type { TrackChangeAuthor as __Cjs_TrackChangeAuthor } from './index.js' with { "resolution-mode": "import" };
170
177
  import type { TrackChangeHighlightColors as __Cjs_TrackChangeHighlightColors } from './index.js' with { "resolution-mode": "import" };
171
178
  import type { TrackChangesAuthorColorsConfig as __Cjs_TrackChangesAuthorColorsConfig } from './index.js' with { "resolution-mode": "import" };
@@ -290,6 +297,7 @@ export type { __Cjs_SuperDocExceptionEditorPayload as SuperDocExceptionEditorPay
290
297
  export type { __Cjs_SuperDocExceptionPayload as SuperDocExceptionPayload };
291
298
  export type { __Cjs_SuperDocExceptionRestorePayload as SuperDocExceptionRestorePayload };
292
299
  export type { __Cjs_SuperDocExceptionStorePayload as SuperDocExceptionStorePayload };
300
+ export type { __Cjs_SuperDocExceptionToolbarPayload as SuperDocExceptionToolbarPayload };
293
301
  export type { __Cjs_SuperDocExtension as SuperDocExtension };
294
302
  export type { __Cjs_SuperDocExtensionActivateReturn as SuperDocExtensionActivateReturn };
295
303
  export type { __Cjs_SuperDocExtensionCapabilities as SuperDocExtensionCapabilities };
@@ -357,6 +365,12 @@ export type { __Cjs_SurfacesModuleConfig as SurfacesModuleConfig };
357
365
  export type { __Cjs_TextAddress as TextAddress };
358
366
  export type { __Cjs_TextSegment as TextSegment };
359
367
  export type { __Cjs_TextTarget as TextTarget };
368
+ export type { __Cjs_ToolbarCustomButton as ToolbarCustomButton };
369
+ export type { __Cjs_ToolbarCustomButtonContext as ToolbarCustomButtonContext };
370
+ export type { __Cjs_ToolbarCustomButtonItem as ToolbarCustomButtonItem };
371
+ export type { __Cjs_ToolbarCustomDropdownItem as ToolbarCustomDropdownItem };
372
+ export type { __Cjs_ToolbarCustomDropdownOption as ToolbarCustomDropdownOption };
373
+ export type { __Cjs_ToolbarCustomSeparatorItem as ToolbarCustomSeparatorItem };
360
374
  export type { __Cjs_TrackChangeAuthor as TrackChangeAuthor };
361
375
  export type { __Cjs_TrackChangeHighlightColors as TrackChangeHighlightColors };
362
376
  export type { __Cjs_TrackChangesAuthorColorsConfig as TrackChangesAuthorColorsConfig };
@@ -62,6 +62,7 @@ export type { SuperDocAwarenessUpdatePayload } from '../core/types/index.js';
62
62
  export type { SuperDocCommentsUpdatePayload } from '../core/types/index.js';
63
63
  export type { SuperDocEditorPayload } from '../core/types/index.js';
64
64
  export type { SuperDocExceptionEditorPayload } from '../core/types/index.js';
65
+ export type { SuperDocExceptionToolbarPayload } from '../core/types/index.js';
65
66
  export type { SuperDocExceptionPayload } from '../core/types/index.js';
66
67
  export type { SuperDocExceptionRestorePayload } from '../core/types/index.js';
67
68
  export type { SuperDocExceptionStorePayload } from '../core/types/index.js';
@@ -75,6 +76,12 @@ export type { LayoutMode } from '../../../layout-engine/painters/dom/src/index.j
75
76
  export type { CommentsConfig } from '../core/types/index.js';
76
77
  export type { TrackChangeHighlightColors } from '../core/types/index.js';
77
78
  export type { ContentControlsConfig } from '../core/types/index.js';
79
+ export type { ToolbarCustomButton } from '../core/types/index.js';
80
+ export type { ToolbarCustomButtonItem } from '../core/types/index.js';
81
+ export type { ToolbarCustomDropdownItem } from '../core/types/index.js';
82
+ export type { ToolbarCustomDropdownOption } from '../core/types/index.js';
83
+ export type { ToolbarCustomSeparatorItem } from '../core/types/index.js';
84
+ export type { ToolbarCustomButtonContext } from '../core/types/index.js';
78
85
  export type { LinkPopoverConfig } from '../core/types/index.js';
79
86
  export type { LinkPopoverContext } from '../core/types/index.js';
80
87
  export type { LinkPopoverResolution } from '../core/types/index.js';
package/dist/superdoc.cjs CHANGED
@@ -4,7 +4,7 @@ const require_blank_docx = require("./chunks/blank-docx-BuFAbRjs.cjs");
4
4
  const require_eventemitter3 = require("./chunks/eventemitter3-C_TAnXOl.cjs");
5
5
  const require_uuid = require("./chunks/uuid-BhG0ngwk.cjs");
6
6
  const require_jszip = require("./chunks/jszip-D8mAFF-r.cjs");
7
- const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-BWYOinLD.cjs");
7
+ const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-CTe12b19.cjs");
8
8
  const require__plugin_vue_export_helper = require("./chunks/_plugin-vue_export-helper-SDR04tiH.cjs");
9
9
  const require_constants = require("./chunks/constants-DpXuDx_g.cjs");
10
10
  let vue = require("vue");
@@ -9908,16 +9908,38 @@ var useCommentsStore = defineStore("comments", () => {
9908
9908
  if (!fromId || !toId || fromId === toId) continue;
9909
9909
  const comment = commentsList.value.find((row) => matchesRemapSource(row, fromId));
9910
9910
  if (!comment) continue;
9911
+ const previousCommentId = normalizeCommentId(comment.commentId);
9912
+ const previousCanonicalId = normalizeCommentId(comment.trackedChangeCanonicalId);
9913
+ const previousPositionEntry = resolveCommentPositionEntry(comment).entry;
9914
+ const advancesRemapLineage = ![
9915
+ comment.commentId,
9916
+ comment.importedId,
9917
+ comment.trackedChangeCanonicalId
9918
+ ].map((id) => normalizeCommentId(id)).includes(fromId) && previousCommentId != null && previousCanonicalId != null && previousCommentId === previousCanonicalId;
9911
9919
  const commentIdentityUpdates = {};
9912
9920
  for (const field of ["commentId", "importedId"]) if (rewriteIfMatch(comment, field, fromId, toId)) commentIdentityUpdates[field] = toId;
9913
9921
  if (rewriteIfMatch(comment, "trackedChangeCanonicalId", fromId, toId)) commentIdentityUpdates.trackedChangeCanonicalId = toId;
9914
- const fromAnchor = buildBodyTrackedChangeAnchorKey(fromId);
9915
- const storyKey = parseTrackedChangeAnchorKey(comment.trackedChangeAnchorKey)?.storyKey ?? normalizeTrackedChangeStoryKey(getTrackedChangeStoryKey(comment));
9916
- const currentStoryFromAnchor = buildTrackedChangeAnchorKeyForStory(storyKey, fromId);
9922
+ if (advancesRemapLineage) {
9923
+ comment.commentId = toId;
9924
+ comment.trackedChangeCanonicalId = toId;
9925
+ commentIdentityUpdates.commentId = toId;
9926
+ commentIdentityUpdates.trackedChangeCanonicalId = toId;
9927
+ }
9928
+ const currentAnchorParts = parseTrackedChangeAnchorKey(comment.trackedChangeAnchorKey);
9929
+ const storyKey = currentAnchorParts?.storyKey ?? normalizeTrackedChangeStoryKey(getTrackedChangeStoryKey(comment));
9917
9930
  const toAnchor = buildTrackedChangeAnchorKeyForStory(storyKey, toId);
9918
- if (normalizeCommentId(comment.trackedChangeAnchorKey) === fromAnchor || comment.trackedChangeAnchorKey == null) comment.trackedChangeAnchorKey = toAnchor;
9919
- else if (normalizeCommentId(comment.trackedChangeAnchorKey) === currentStoryFromAnchor) comment.trackedChangeAnchorKey = toAnchor;
9920
- if (Array.isArray(comment.trackedChangePositionAliases)) comment.trackedChangePositionAliases = comment.trackedChangePositionAliases.map((alias) => normalizeCommentId(alias) === fromId ? toId : alias);
9931
+ const anchorSourceIds = /* @__PURE__ */ new Set([fromId]);
9932
+ if (advancesRemapLineage) {
9933
+ if (previousCommentId) anchorSourceIds.add(previousCommentId);
9934
+ if (previousCanonicalId) anchorSourceIds.add(previousCanonicalId);
9935
+ }
9936
+ if (comment.trackedChangeAnchorKey == null || anchorSourceIds.has(currentAnchorParts?.rawId)) comment.trackedChangeAnchorKey = toAnchor;
9937
+ comment.trackedChangePositionAliases = normalizeTrackedChangePositionAliases([...Array.isArray(comment.trackedChangePositionAliases) ? comment.trackedChangePositionAliases : [], fromId]);
9938
+ const identitySourceIds = /* @__PURE__ */ new Set([fromId]);
9939
+ if (advancesRemapLineage) {
9940
+ if (previousCommentId) identitySourceIds.add(previousCommentId);
9941
+ if (previousCanonicalId) identitySourceIds.add(previousCanonicalId);
9942
+ }
9921
9943
  commentsList.value.forEach((row) => {
9922
9944
  if (row === comment) return;
9923
9945
  if (!belongsToTrackedChangeSyncDocument(row, normalizedDocumentId)) return;
@@ -9927,20 +9949,23 @@ var useCommentsStore = defineStore("comments", () => {
9927
9949
  "trackedChangeParentId",
9928
9950
  "threadingParentCommentId",
9929
9951
  "parentCommentId"
9930
- ]) if (rewriteIfMatch(row, field, fromId, toId)) rowIdentityUpdates[field] = toId;
9952
+ ]) {
9953
+ if (!identitySourceIds.has(normalizeCommentId(row?.[field]))) continue;
9954
+ row[field] = toId;
9955
+ rowIdentityUpdates[field] = toId;
9956
+ }
9931
9957
  if (Object.keys(rowIdentityUpdates).length && typeof row.updateIdentityValues === "function") row.updateIdentityValues(rowIdentityUpdates);
9932
9958
  });
9933
9959
  if (Object.keys(commentIdentityUpdates).length && typeof comment.updateIdentityValues === "function") comment.updateIdentityValues(commentIdentityUpdates);
9934
- const fallbackPositionEntries = [positions[fromId], positions[fromAnchor]];
9935
- const previousEntry = positions[currentStoryFromAnchor] ?? fallbackPositionEntries.find((entry) => entry && positionEntryMatchesTrackedChangeStory(entry, comment));
9960
+ const previousEntry = previousPositionEntry ?? [positions[fromId], positions[buildBodyTrackedChangeAnchorKey(fromId)]].find((entry) => entry && positionEntryMatchesTrackedChangeStory(entry, comment));
9936
9961
  if (previousEntry) {
9937
9962
  for (const key of [toId, toAnchor]) if (positions[key] === void 0) {
9938
9963
  positions[key] = previousEntry;
9939
9964
  positionsChanged = true;
9940
9965
  }
9941
9966
  }
9942
- if (normalizeCommentId(activeComment.value) === fromId) activeComment.value = toId;
9943
- if (normalizeCommentId(activeFloatingCommentInstanceId.value) === fromId) activeFloatingCommentInstanceId.value = toId;
9967
+ if (identitySourceIds.has(normalizeCommentId(activeComment.value))) activeComment.value = toId;
9968
+ if (identitySourceIds.has(normalizeCommentId(activeFloatingCommentInstanceId.value))) activeFloatingCommentInstanceId.value = toId;
9944
9969
  }
9945
9970
  if (positionsChanged) editorCommentPositions.value = { ...positions };
9946
9971
  };
@@ -15838,6 +15863,33 @@ function createV2RemoteReviewHydrationScheduler({ hydrate, getActiveDocumentId,
15838
15863
  };
15839
15864
  }
15840
15865
  //#endregion
15866
+ //#region src/helpers/v2-typing-review-hydration.js
15867
+ /**
15868
+ * Coalesce automatic review-catalog reconciliation behind a genuine typing
15869
+ * quiet period. Every typing signal restarts the trailing timer; there is no
15870
+ * maximum-wait escape hatch because that would deliberately run the expensive
15871
+ * catalog reads in the middle of a sustained input burst.
15872
+ */
15873
+ function createV2TypingReviewHydrationScheduler({ hydrate, idleMs = 6500, setTimer = (callback, delay) => setTimeout(callback, delay), clearTimer = (timer) => clearTimeout(timer) }) {
15874
+ let timer = null;
15875
+ const clear = () => {
15876
+ if (timer != null) clearTimer(timer);
15877
+ timer = null;
15878
+ };
15879
+ const schedule = () => {
15880
+ clear();
15881
+ timer = setTimer(() => {
15882
+ timer = null;
15883
+ hydrate();
15884
+ }, idleMs);
15885
+ };
15886
+ return {
15887
+ schedule,
15888
+ clear,
15889
+ isPending: () => timer != null
15890
+ };
15891
+ }
15892
+ //#endregion
15841
15893
  //#region src/helpers/v2-author-required-rejection.js
15842
15894
  var NO_AUTHOR_SIGNAL = "no-author-configured";
15843
15895
  /** Stable, non-terminal exception code surfaced to consumers. */
@@ -20302,8 +20354,6 @@ var _hoisted_4$7 = { class: "floating-comments" };
20302
20354
  var UNAVAILABLE_COMMAND_RESULT = false;
20303
20355
  var V2_SELECTION_TOOLBAR_SYNC_RETRY_FRAMES = 3;
20304
20356
  var V2_TYPING_REVIEW_HYDRATION_IDLE_MS = 6500;
20305
- var V2_TYPING_REVIEW_HYDRATION_CEILING_MS = 8e3;
20306
- var V2_TYPING_REVIEW_VISIBLE_PRIME_MS = 250;
20307
20357
  var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default({
20308
20358
  __name: "SuperDoc",
20309
20359
  emits: ["selection-update"],
@@ -20424,7 +20474,6 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
20424
20474
  let v2GeometryRafHandle = 0;
20425
20475
  let v2TrackedChangeRestampRetainPaints = 0;
20426
20476
  let v2TrackedChangeRestampRetentionReason = null;
20427
- let v2TrackedChangeRestampRetentionIds = null;
20428
20477
  const activeEditorRef = (0, vue.computed)(() => proxy.$superdoc.activeEditor);
20429
20478
  const getSuperDocUI = () => proxy.$superdoc?.ui ?? null;
20430
20479
  const UNAVAILABLE_COMMAND_STATE = Object.freeze({
@@ -21334,49 +21383,29 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21334
21383
  }
21335
21384
  },
21336
21385
  onCommittedPagePaint: (commit) => {
21386
+ if (commit?.routeLane === "canonical.typing-mutation" || v2TypingReviewHydrationScheduler.isPending()) return;
21337
21387
  v2ReviewHydrationController.onCommittedPagePaint?.({
21338
21388
  ...commit,
21339
21389
  documentId: commit?.payload?.documentId ?? null
21340
21390
  });
21341
21391
  }
21342
21392
  });
21343
- const collectV2TrackedChangeRestampIds = (impactOrIds) => {
21344
- if (impactOrIds instanceof Set) return new Set([...impactOrIds].filter(Boolean).map(String));
21345
- if (Array.isArray(impactOrIds)) return new Set(impactOrIds.filter(Boolean).map(String));
21346
- const ids = /* @__PURE__ */ new Set();
21347
- for (const id of impactOrIds?.upsertIds ?? []) ids.add(String(id));
21348
- for (const id of impactOrIds?.removedIds ?? []) ids.add(String(id));
21349
- for (const pair of impactOrIds?.remappedPairs ?? []) {
21350
- if (pair?.from) ids.add(String(pair.from));
21351
- if (pair?.to) ids.add(String(pair.to));
21352
- }
21353
- return ids;
21354
- };
21355
- const armV2TrackedChangeRestampGeometryRetention = (reason = "tracked-change-restamp", impactOrIds = null) => {
21393
+ const armV2TrackedChangeRestampGeometryRetention = (reason = "tracked-change-restamp") => {
21356
21394
  v2TrackedChangeRestampRetainPaints = Math.max(v2TrackedChangeRestampRetainPaints, 3);
21357
21395
  v2TrackedChangeRestampRetentionReason = reason;
21358
- const retainedIds = collectV2TrackedChangeRestampIds(impactOrIds);
21359
- if (retainedIds.size > 0) v2TrackedChangeRestampRetentionIds = /* @__PURE__ */ new Set([...v2TrackedChangeRestampRetentionIds ?? [], ...retainedIds]);
21360
21396
  };
21361
21397
  const takeV2TrackedChangeRestampGeometryRetention = () => {
21362
- if (v2TrackedChangeRestampRetainPaints <= 0) return void 0;
21363
- const reason = v2TrackedChangeRestampRetentionReason ?? "tracked-change-restamp";
21364
- const retainedTrackedChangeIds = v2TrackedChangeRestampRetentionIds?.size ? Array.from(v2TrackedChangeRestampRetentionIds) : null;
21365
- v2TrackedChangeRestampRetainPaints -= 1;
21366
- if (v2TrackedChangeRestampRetainPaints <= 0) {
21367
- v2TrackedChangeRestampRetentionReason = null;
21368
- v2TrackedChangeRestampRetentionIds = null;
21369
- }
21398
+ const reason = v2TrackedChangeRestampRetainPaints > 0 ? v2TrackedChangeRestampRetentionReason ?? "tracked-change-restamp" : "render-epoch-handoff";
21399
+ if (v2TrackedChangeRestampRetainPaints > 0) v2TrackedChangeRestampRetainPaints -= 1;
21400
+ if (v2TrackedChangeRestampRetainPaints <= 0) v2TrackedChangeRestampRetentionReason = null;
21370
21401
  return {
21371
21402
  retainMissingTrackedChangeGeometry: true,
21372
- ...retainedTrackedChangeIds ? { retainedTrackedChangeIds } : {},
21373
21403
  reason
21374
21404
  };
21375
21405
  };
21376
21406
  const clearV2TrackedChangeRestampGeometryRetention = () => {
21377
21407
  v2TrackedChangeRestampRetainPaints = 0;
21378
21408
  v2TrackedChangeRestampRetentionReason = null;
21379
- v2TrackedChangeRestampRetentionIds = null;
21380
21409
  };
21381
21410
  const resolveV2GeometryPublishOptions = (options) => typeof options === "function" ? options() : options;
21382
21411
  const scheduleV2GeometryPublish = (payload, options = void 0) => {
@@ -21399,6 +21428,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21399
21428
  const onV2RenderReadiness = (payload) => {
21400
21429
  const snapshot = payload?.snapshot ?? payload ?? null;
21401
21430
  if (!snapshot) return;
21431
+ if (v2TypingReviewHydrationScheduler.isPending()) return;
21402
21432
  v2ReviewHydrationController.onRenderReadiness(snapshot);
21403
21433
  };
21404
21434
  const v2PageMetricsSnapshot = (0, vue.shallowRef)(null);
@@ -21458,8 +21488,6 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21458
21488
  v2RulerHostStyle.value = {};
21459
21489
  v2RulerReady.value = false;
21460
21490
  };
21461
- let v2TypingReviewHydrationTimer = 0;
21462
- let v2TypingReviewVisiblePrimeTimer = 0;
21463
21491
  const hydrateV2CommentRowsFromHost = () => {
21464
21492
  const commentsAdapter = proxy.$superdoc?.activeEditor?.v2Comments ?? null;
21465
21493
  if (!commentsAdapter) return;
@@ -21480,19 +21508,6 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21480
21508
  ...options
21481
21509
  });
21482
21510
  };
21483
- const hydrateVisibleV2TrackedChangeRowsFromHost = (options = {}) => {
21484
- const trackedChangesAdapter = proxy.$superdoc?.activeEditor?.v2TrackedChanges ?? null;
21485
- if (!trackedChangesAdapter) return;
21486
- commentsStore.hydrateTrackedChangesFromV2?.({
21487
- superdoc: proxy.$superdoc,
21488
- adapter: trackedChangesAdapter,
21489
- documentId: proxy.$superdoc?.activeEditor?.documentId ?? null,
21490
- trackedChangesListMode: "visible-window",
21491
- refreshReason: "typing-visible-prime",
21492
- blocking: false,
21493
- ...options
21494
- });
21495
- };
21496
21511
  const v2ReviewMutationReconciler = require_create_super_doc_ui.createV2ReviewMutationReconciler({
21497
21512
  getContext: () => {
21498
21513
  const activeEditor = proxy.$superdoc?.activeEditor ?? null;
@@ -21522,46 +21537,21 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21522
21537
  reason: "review-reconcile"
21523
21538
  })
21524
21539
  });
21525
- let v2LastTypingMutationAtMs = 0;
21526
- let v2TypingReviewHydrationHeldSinceMs = 0;
21540
+ const v2TypingReviewHydrationScheduler = createV2TypingReviewHydrationScheduler({
21541
+ idleMs: V2_TYPING_REVIEW_HYDRATION_IDLE_MS,
21542
+ hydrate: () => v2ReviewHydrationController.reconcileInBackground?.("typing-idle")
21543
+ });
21527
21544
  const clearV2TypingReviewHydrationTimer = () => {
21528
- if (v2TypingReviewHydrationTimer && typeof clearTimeout === "function") clearTimeout(v2TypingReviewHydrationTimer);
21529
- if (v2TypingReviewVisiblePrimeTimer && typeof clearTimeout === "function") clearTimeout(v2TypingReviewVisiblePrimeTimer);
21530
- v2TypingReviewHydrationTimer = 0;
21531
- v2TypingReviewVisiblePrimeTimer = 0;
21532
- v2LastTypingMutationAtMs = 0;
21533
- v2TypingReviewHydrationHeldSinceMs = 0;
21534
- };
21535
- const scheduleV2TypingReviewVisiblePrime = (options = {}) => {
21536
- if (v2TypingReviewVisiblePrimeTimer) return;
21537
- v2TypingReviewVisiblePrimeTimer = setTimeout(() => {
21538
- v2TypingReviewVisiblePrimeTimer = 0;
21539
- hydrateVisibleV2TrackedChangeRowsFromHost(options);
21540
- }, V2_TYPING_REVIEW_VISIBLE_PRIME_MS);
21541
- };
21542
- const scheduleV2TypingReviewHydration = (options = {}) => {
21543
- v2LastTypingMutationAtMs = Date.now();
21544
- scheduleV2TypingReviewVisiblePrime(options.primeOptions);
21545
- if (!v2TypingReviewHydrationHeldSinceMs) v2TypingReviewHydrationHeldSinceMs = v2LastTypingMutationAtMs;
21546
- if (v2TypingReviewHydrationTimer && typeof clearTimeout === "function") clearTimeout(v2TypingReviewHydrationTimer);
21547
- const attempt = () => {
21548
- v2TypingReviewHydrationTimer = 0;
21549
- const idleForMs = Date.now() - v2LastTypingMutationAtMs;
21550
- const heldForMs = Date.now() - v2TypingReviewHydrationHeldSinceMs;
21551
- if (idleForMs < V2_TYPING_REVIEW_HYDRATION_IDLE_MS && heldForMs < V2_TYPING_REVIEW_HYDRATION_CEILING_MS) {
21552
- const idleRemainingMs = V2_TYPING_REVIEW_HYDRATION_IDLE_MS - idleForMs;
21553
- const ceilingRemainingMs = V2_TYPING_REVIEW_HYDRATION_CEILING_MS - heldForMs;
21554
- v2TypingReviewHydrationTimer = setTimeout(attempt, Math.min(idleRemainingMs, ceilingRemainingMs));
21555
- return;
21556
- }
21557
- v2TypingReviewHydrationHeldSinceMs = 0;
21558
- hydrateV2ReviewRowsFromHost();
21559
- };
21560
- const ceilingRemainingMs = Math.max(0, V2_TYPING_REVIEW_HYDRATION_CEILING_MS - (Date.now() - v2TypingReviewHydrationHeldSinceMs));
21561
- v2TypingReviewHydrationTimer = setTimeout(attempt, Math.min(V2_TYPING_REVIEW_HYDRATION_IDLE_MS, ceilingRemainingMs));
21545
+ v2TypingReviewHydrationScheduler.clear();
21546
+ };
21547
+ const scheduleV2TypingReviewHydration = () => {
21548
+ v2TypingReviewHydrationScheduler.schedule();
21562
21549
  };
21563
21550
  const v2RemoteReviewHydrationScheduler = createV2RemoteReviewHydrationScheduler({
21564
- hydrate: () => v2ReviewHydrationController.reconcileInBackground?.("remote-review-change"),
21551
+ hydrate: () => {
21552
+ if (v2TypingReviewHydrationScheduler.isPending()) return false;
21553
+ return v2ReviewHydrationController.reconcileInBackground?.("remote-review-change");
21554
+ },
21565
21555
  getActiveDocumentId: () => {
21566
21556
  const activeEditor = proxy.$superdoc?.activeEditor ?? null;
21567
21557
  return activeEditor?.documentId ?? activeEditor?.options?.documentId ?? null;
@@ -21571,6 +21561,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21571
21561
  if (!event) return;
21572
21562
  const documentId = document?.id ?? null;
21573
21563
  if (event.type === "review-mutation:started") {
21564
+ clearV2TypingReviewHydrationTimer();
21574
21565
  const activeEditor = proxy.$superdoc?.activeEditor ?? null;
21575
21566
  commentsStore.supersedeV2ReviewHydration?.({
21576
21567
  commentsAdapter: activeEditor?.v2Comments ?? null,
@@ -21621,7 +21612,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21621
21612
  v2ReviewHydrationController.invalidate("mutation-committed");
21622
21613
  const reviewImpact = require_create_super_doc_ui.getV2TrackedChangeMutationImpact(event);
21623
21614
  if (Array.isArray(reviewImpact?.remappedPairs) && reviewImpact.remappedPairs.length > 0) commentsStore.remapTrackedChangeIdentities?.(reviewImpact.remappedPairs, { documentId });
21624
- if (reviewImpact) armV2TrackedChangeRestampGeometryRetention("tracked-change-mutation", reviewImpact);
21615
+ if (reviewImpact) armV2TrackedChangeRestampGeometryRetention("tracked-change-mutation");
21625
21616
  const readiness = (proxy.$superdoc?.activeEditor ?? null)?.documentMutationReadiness ?? null;
21626
21617
  const receipt = event.origin === "history" ? null : event.receipt;
21627
21618
  const canWaitForExactPaint = reviewImpact && typeof receipt?.txId === "string" && receipt.txId.length > 0 && typeof readiness?.whenPainted === "function";
@@ -21641,11 +21632,13 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21641
21632
  Promise.resolve(reconciliation).then(settleReviewMutation, () => settleReviewMutation(false));
21642
21633
  return;
21643
21634
  }
21635
+ const isTypingMutation = require_create_super_doc_ui.isV2EditableTextMutationEvent(event);
21636
+ if (!isTypingMutation) clearV2TypingReviewHydrationTimer();
21644
21637
  if (reviewImpact?.reconcileMode === "authoritative" && reviewImpact.upsertIds.size > 0) {
21645
21638
  hydrateV2CommentRowsFromHost();
21646
21639
  return;
21647
21640
  }
21648
- if (require_create_super_doc_ui.isV2EditableTextMutationEvent(event)) {
21641
+ if (isTypingMutation) {
21649
21642
  scheduleV2TypingReviewHydration();
21650
21643
  return;
21651
21644
  }
@@ -21671,6 +21664,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21671
21664
  const handleV2DocumentModeChange = () => {
21672
21665
  if (!isV2Mode.value) return;
21673
21666
  try {
21667
+ clearV2TypingReviewHydrationTimer();
21674
21668
  v2ReviewHydrationController.hydrateNow("document-mode-change");
21675
21669
  } catch (err) {
21676
21670
  console.warn("[SuperDoc][v2] document-mode-change rehydrate failed", err);
@@ -21689,6 +21683,22 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21689
21683
  else republishGeometry();
21690
21684
  });
21691
21685
  };
21686
+ let v2BuiltInReviewSurfaceOpen = false;
21687
+ let v2ExternalReviewSurfaceOpen = false;
21688
+ const syncV2ReviewCatalogDemand = () => {
21689
+ if (!isV2Mode.value) return;
21690
+ if (!v2BuiltInReviewSurfaceOpen && !v2ExternalReviewSurfaceOpen) {
21691
+ v2ReviewHydrationController.releaseCatalogDemand();
21692
+ return;
21693
+ }
21694
+ clearV2TypingReviewHydrationTimer();
21695
+ v2ReviewHydrationController.demandCatalog("review-sidebar-visible");
21696
+ };
21697
+ const handleV2CommentsListChange = ({ isRendered } = {}) => {
21698
+ v2ExternalReviewSurfaceOpen = isRendered === true;
21699
+ syncV2ReviewCatalogDemand();
21700
+ };
21701
+ proxy.$superdoc?.on?.("comments-list-change", handleV2CommentsListChange);
21692
21702
  const getV2EditorFailureMessage = (reason) => {
21693
21703
  switch (reason) {
21694
21704
  case "editing-mount-required": return "SuperDoc could not load the document editor because the page did not provide a mount container.";
@@ -21816,6 +21826,11 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21816
21826
  onFontsResolved: onFontsResolvedFn,
21817
21827
  onPageCountKnown: proxy.$superdoc.config.onPageCountKnown ?? null,
21818
21828
  onReviewWindowPlanned: (payload) => {
21829
+ if (payload?.routeLane === "canonical.typing-mutation") {
21830
+ v2TypingReviewHydrationScheduler.schedule();
21831
+ return;
21832
+ }
21833
+ if (v2TypingReviewHydrationScheduler.isPending()) return;
21819
21834
  v2ReviewHydrationController.onReviewWindowPlanned?.({
21820
21835
  ...payload,
21821
21836
  documentId: doc.id
@@ -22148,6 +22163,8 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
22148
22163
  });
22149
22164
  (0, vue.watch)(showCommentsSidebar, (value) => {
22150
22165
  proxy.$superdoc.broadcastSidebarToggle(value);
22166
+ v2BuiltInReviewSurfaceOpen = value === true;
22167
+ syncV2ReviewCatalogDemand();
22151
22168
  });
22152
22169
  useViewportFit({
22153
22170
  getSuperdoc: () => proxy.$superdoc,
@@ -22313,6 +22330,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
22313
22330
  document.removeEventListener("mouseup", handleDocumentSelectionChange, true);
22314
22331
  document.removeEventListener("selectionchange", handleDocumentSelectionChange);
22315
22332
  proxy.$superdoc?.off?.("document-mode-change", handleV2DocumentModeChange);
22333
+ proxy.$superdoc?.off?.("comments-list-change", handleV2CommentsListChange);
22316
22334
  });
22317
22335
  const selectionLayer = (0, vue.ref)(null);
22318
22336
  const isDragging = (0, vue.ref)(false);
@@ -22808,7 +22826,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
22808
22826
  ], 38);
22809
22827
  };
22810
22828
  }
22811
- }, [["__scopeId", "data-v-62293b70"]]);
22829
+ }, [["__scopeId", "data-v-25570fd2"]]);
22812
22830
  //#endregion
22813
22831
  //#region src/core/create-app.js
22814
22832
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
@@ -39686,7 +39704,7 @@ var useToolbarItem = (options) => {
39686
39704
  "dropdown",
39687
39705
  "overflow"
39688
39706
  ].includes(options.type)) throw new Error("Invalid toolbar item type - " + options.type);
39689
- if (options.type === "button" && !options.defaultLabel && !options.icon) throw new Error("Toolbar button item needs either icon or label - " + options.name);
39707
+ 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".`);
39690
39708
  if (!options.name) throw new Error("Invalid toolbar item name - " + options.name);
39691
39709
  const id = (0, vue.ref)(require_uuid.v4());
39692
39710
  const type = options.type;
@@ -39731,6 +39749,12 @@ var useToolbarItem = (options) => {
39731
39749
  const nestedOptions = (0, vue.ref)([]);
39732
39750
  if (options.options) {
39733
39751
  if (!Array.isArray(options.options)) throw new Error("Invalid toolbar item options - " + options.options);
39752
+ let unusable = 0;
39753
+ for (let index = 0; index < options.options.length; index += 1) {
39754
+ const option = options.options[index];
39755
+ if (!option || typeof option !== "object") unusable += 1;
39756
+ }
39757
+ if (unusable) throw new Error(`Toolbar item options must all be objects - ${options.name}. ${unusable} row(s) are not, so this item is skipped.`);
39734
39758
  nestedOptions.value?.push(...options.options);
39735
39759
  }
39736
39760
  const activate = (attrs = {}, ...args) => {
@@ -42257,6 +42281,17 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
42257
42281
  this._detachFormatPainterModeChange = null;
42258
42282
  /** Registered custom-button command unregister handles. */
42259
42283
  this._customCommandRegs = /* @__PURE__ */ new Map();
42284
+ /**
42285
+ * Custom entries already reported as unbuildable.
42286
+ *
42287
+ * `#makeToolbarItems` runs on every throttled resize and on font or
42288
+ * active-editor changes, not only at construction, so a single static
42289
+ * configuration error would otherwise fire `exception` once per rebuild --
42290
+ * unbounded, into consumer telemetry, for a mistake that cannot change
42291
+ * without a reconfigure. Keyed by name plus message so a different fault
42292
+ * on the same entry still reports once.
42293
+ */
42294
+ this._reportedInvalidCustomItems = /* @__PURE__ */ new Set();
42260
42295
  /** Signature of the last-built font options, to skip redundant rebuilds. */
42261
42296
  this._lastFontOptionsSignature = "";
42262
42297
  if (!this.config.selector && this.config.element) this.config.selector = this.config.element;
@@ -42363,8 +42398,23 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
42363
42398
  isDev: this.isDev
42364
42399
  });
42365
42400
  const customItems = this.config.customButtons || [];
42366
- const customToolbarItems = customItems.map((item) => this.#prepareCustomButton(item));
42367
- if (customItems.length) defaultItems.push(...customToolbarItems);
42401
+ const takenNames = new Set([...defaultItems, ...overflowItems].map((item) => item.name?.value).filter(Boolean));
42402
+ const customToolbarItems = customItems.map((item, index) => {
42403
+ try {
42404
+ 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.`);
42405
+ const prepared = this.#prepareCustomButton(item);
42406
+ takenNames.add(prepared.name?.value);
42407
+ return prepared;
42408
+ } catch (error) {
42409
+ const signature = `${item?.name ?? `<unnamed@${index}>`}::${error?.message ?? String(error)}`;
42410
+ if (!this._reportedInvalidCustomItems.has(signature)) {
42411
+ this._reportedInvalidCustomItems.add(signature);
42412
+ this.#emitCommandException(error, item?.name ?? null);
42413
+ }
42414
+ return null;
42415
+ }
42416
+ }).filter(Boolean);
42417
+ if (customToolbarItems.length) defaultItems.push(...customToolbarItems);
42368
42418
  let allConfigItems = [...defaultItems.map((item) => item.name.value), ...overflowItems.map((item) => item.name.value)];
42369
42419
  if (hasExplicitGroupComposition) {
42370
42420
  const groupedItems = Object.values(this.config.groups).flatMap((item) => item);
@@ -42777,13 +42827,15 @@ var BuiltInToolbar = class extends require_eventemitter3.import_eventemitter3.de
42777
42827
  }
42778
42828
  }
42779
42829
  #emitCommandException(originalError, name) {
42780
- const error = originalError instanceof Error ? originalError : new Error(String(originalError));
42781
- this.emit("exception", {
42782
- error,
42830
+ const payload = {
42831
+ error: originalError instanceof Error ? originalError : new Error(String(originalError)),
42783
42832
  originalError,
42784
42833
  itemName: name ?? null,
42785
42834
  editor: this.superdoc?.activeEditor ?? this.activeEditor ?? null
42786
- });
42835
+ };
42836
+ const hostPayload = { ...payload };
42837
+ this.emit("exception", payload);
42838
+ this.superdoc?.emit?.("exception", hostPayload);
42787
42839
  }
42788
42840
  /**
42789
42841
  * Main toolbar command entry point used by the rendered components.
@@ -43278,7 +43330,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
43278
43330
  this.config.colors = shuffleArray(this.config.colors);
43279
43331
  this.userColorMap = /* @__PURE__ */ new Map();
43280
43332
  this.colorIndex = 0;
43281
- this.version = "2.4.0-next.12";
43333
+ this.version = "2.4.0-next.13";
43282
43334
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
43283
43335
  this.superdocId = config.superdocId || require_uuid.v4();
43284
43336
  this.colors = this.config.colors ?? [];
@@ -44700,6 +44752,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44700
44752
  if (element) this.config.modules.comments.element = element;
44701
44753
  this.commentsList = new SuperComments(this.config.modules?.comments, this);
44702
44754
  if (this.config.onCommentsListChange) this.config.onCommentsListChange({ isRendered: true });
44755
+ this.emit("comments-list-change", { isRendered: true });
44703
44756
  }
44704
44757
  /**
44705
44758
  * Remove the comments list from the superdoc
@@ -44709,6 +44762,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
44709
44762
  this.commentsList.close();
44710
44763
  this.commentsList = null;
44711
44764
  if (this.config.onCommentsListChange) this.config.onCommentsListChange({ isRendered: false });
44765
+ this.emit("comments-list-change", { isRendered: false });
44712
44766
  }
44713
44767
  }
44714
44768
  /**