superdoc 2.20.0 → 2.20.1-next.10

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.
@@ -4219,6 +4219,14 @@ function readCollapsedHostTrackChangeId(snapshot) {
4219
4219
  const focusId = focusVisual?.trackChangeId;
4220
4220
  return typeof anchorId === "string" && anchorId.length > 0 && anchorId === focusId ? anchorId : null;
4221
4221
  }
4222
+ function isNonCollapsedHostSelection(snapshot) {
4223
+ if (!snapshot || typeof snapshot !== "object") return false;
4224
+ const record = snapshot;
4225
+ const anchor = record.anchor;
4226
+ const focus = record.focus;
4227
+ if (typeof anchor?.blockId !== "string" || !anchor.blockId || typeof focus?.blockId !== "string" || !focus.blockId || !Number.isFinite(anchor.blockOffset) || !Number.isFinite(focus.blockOffset)) return false;
4228
+ return anchor.blockId !== focus.blockId || anchor.blockOffset !== focus.blockOffset || Number.isFinite(anchor.trackedBlockOffset) && Number.isFinite(focus.trackedBlockOffset) && anchor.trackedBlockOffset !== focus.trackedBlockOffset;
4229
+ }
4222
4230
  var trackedChangeIdContextCache = /* @__PURE__ */ new WeakMap();
4223
4231
  /**
4224
4232
  * Collect the raw source-provenance ids a tracked-change list item exposes,
@@ -5675,6 +5683,7 @@ function createSuperDocUI(options) {
5675
5683
  let asyncReadFailureRetryAtMs = 0;
5676
5684
  let foregroundAsyncRetryTimer = null;
5677
5685
  let pendingSelectionSeedValidationToken = null;
5686
+ let typedCaretValidationHeld = false;
5678
5687
  let typingContentInvalidationTimer = null;
5679
5688
  const pendingAsyncReadSettlements = [];
5680
5689
  const scheduleAsyncRefresh = () => {
@@ -6112,6 +6121,16 @@ function createSuperDocUI(options) {
6112
6121
  scheduleForegroundAsyncRetry();
6113
6122
  return;
6114
6123
  }
6124
+ if (typedCaretValidationHeld && Date.now() - lastTypingMutationAtMs < HEAVY_READ_IDLE_MS) {
6125
+ const token = selectionReadToken();
6126
+ if (asyncReads.get("selection")?.token !== token) {
6127
+ refreshSelectionFromHost(void 0, true);
6128
+ if (asyncReads.get("selection")?.token === token) recompute();
6129
+ }
6130
+ scheduleForegroundAsyncRetry(HEAVY_READ_IDLE_POLL_MS);
6131
+ return;
6132
+ }
6133
+ typedCaretValidationHeld = false;
6115
6134
  const validationToken = pendingSelectionSeedValidationToken;
6116
6135
  pendingSelectionSeedValidationToken = null;
6117
6136
  if (validationToken === selectionReadToken()) issueAsyncRead("selection", validationToken, selectionCurrentRun, normalizeSelectionInfo);
@@ -6250,7 +6269,7 @@ function createSuperDocUI(options) {
6250
6269
  }
6251
6270
  if (pendingPostPaintSelectionRefresh) continue;
6252
6271
  selectionEpoch += 1;
6253
- seedCaretSelectionFromHost();
6272
+ refreshSelectionFromHost(void 0, true);
6254
6273
  recompute(paintCompleted ? "post-paint-selection" : "post-paint-selection-failed");
6255
6274
  }
6256
6275
  };
@@ -6261,7 +6280,11 @@ function createSuperDocUI(options) {
6261
6280
  if (typingContentInvalidationTimer) clearTimeout(typingContentInvalidationTimer);
6262
6281
  typingContentInvalidationTimer = setTimeout(() => {
6263
6282
  typingContentInvalidationTimer = null;
6264
- if (!disposed) invalidateDocumentContentAndRecompute();
6283
+ if (!disposed) {
6284
+ invalidateDocumentContent();
6285
+ if (typedCaretValidationHeld && !foregroundMutationActive()) refreshSelectionFromHost(void 0, true);
6286
+ recompute();
6287
+ }
6265
6288
  }, 500);
6266
6289
  };
6267
6290
  const issueAsyncRead = (key, token, run, normalize) => {
@@ -6355,6 +6378,13 @@ function createSuperDocUI(options) {
6355
6378
  value: entry.value,
6356
6379
  status: "ready"
6357
6380
  };
6381
+ if (key === "selection" && typedCaretValidationHeld && Date.now() - lastTypingMutationAtMs < HEAVY_READ_IDLE_MS) {
6382
+ scheduleForegroundAsyncRetry(HEAVY_READ_IDLE_POLL_MS);
6383
+ return {
6384
+ value: entry?.hasSettled ? entry.value : null,
6385
+ status: entry?.hasSettled ? "stale" : "pending"
6386
+ };
6387
+ }
6358
6388
  if (isHeavyDocReadKey(key) && !heavyReadDemandActive(key, token)) {
6359
6389
  if (hostSourceLoadPhase() === "loading") {
6360
6390
  heavyReadsHeldUntilIdle = true;
@@ -6476,6 +6506,7 @@ function createSuperDocUI(options) {
6476
6506
  heldSettledInlineValues = null;
6477
6507
  optimisticParagraphAlignment = null;
6478
6508
  pendingSelectionSeedValidationToken = null;
6509
+ typedCaretValidationHeld = false;
6479
6510
  coldAsyncReadDeferrals.clear();
6480
6511
  demandedHeavyReads.clear();
6481
6512
  incompleteTrackChangesDirectoryReadTokens.clear();
@@ -6647,8 +6678,9 @@ function createSuperDocUI(options) {
6647
6678
  * effective font resolved from it) on the next recompute, instead of serving
6648
6679
  * the previous selection until the worker `selection.current` round-trip lands
6649
6680
  * (SD-3652). Only a resolvable COLLAPSED CARET is seeded - a range's snapshot
6650
- * cannot be resolved synchronously in worker mode, so ranges defer to the async
6651
- * read (seeding an empty value would wrongly disable range-only commands). The
6681
+ * cannot be resolved synchronously in worker mode, so a new range starts an
6682
+ * authoritative async read without waiting for the input-idle gate (SD-5394).
6683
+ * Seeding an empty value would wrongly disable range-only commands. The
6652
6684
  * authoritative async read still overwrites marks/text/review overlap, but
6653
6685
  * foreground typing defers and coalesces that worker hop through the shared
6654
6686
  * retry gate. Previous marks are carried forward for toolbar continuity. A
@@ -6656,15 +6688,27 @@ function createSuperDocUI(options) {
6656
6688
  * same carrier, or while an active typing dispatch advances the same caret by
6657
6689
  * one code point. Pending work alone never carries review command context.
6658
6690
  */
6659
- const seedCaretSelectionFromHost = (hostSelectionSnapshot) => {
6691
+ const refreshSelectionFromHost = (hostSelectionSnapshot, afterTypingPaint = false) => {
6692
+ const foreground = foregroundMutationState();
6693
+ if (isNonCollapsedHostSelection(hostSelectionSnapshot) && !(foreground && foreground.active > 0)) {
6694
+ typedCaretValidationHeld = false;
6695
+ pendingSelectionSeedValidationToken = null;
6696
+ issueAsyncRead("selection", selectionReadToken(), selectionCurrentRun, normalizeSelectionInfo);
6697
+ return;
6698
+ }
6660
6699
  const host = getHost();
6661
6700
  const read = host?.readLiveSelectionSyncSnapshot;
6662
6701
  if (typeof read !== "function") return;
6663
6702
  const seed = normalizeSelectionInfo(safeCall(() => read.call(host), null));
6664
6703
  if (!seed || !isCollapsedCaretSnapshot(seed)) return;
6704
+ const previousCaret = collapsedTextAddressFromSelection(state.selection);
6705
+ const nextCaret = collapsedTextAddressFromTarget(seed.selectionTarget) ?? collapsedTextAddressFromTarget(seed.target);
6706
+ const previousRange = previousCaret?.range;
6707
+ const nextRange = nextCaret?.range;
6708
+ const sameSeedCaret = Boolean(previousCaret && nextCaret && previousCaret.blockId === nextCaret.blockId && storyLocatorSignature(previousCaret.story) === storyLocatorSignature(nextCaret.story) && previousRange?.start === nextRange?.start);
6709
+ typedCaretValidationHeld = Boolean(foreground && foreground.active > 0) || typedCaretValidationHeld && (afterTypingPaint || sameSeedCaret);
6665
6710
  const token = selectionReadToken();
6666
- const foreground = foregroundMutationState();
6667
- const deferAuthoritativeRead = Boolean(foreground && (foreground.active > 0 || foreground.pending > 0));
6711
+ const deferAuthoritativeRead = Boolean(foreground && (foreground.active > 0 || foreground.pending > 0)) || typedCaretValidationHeld && Date.now() - lastTypingMutationAtMs < HEAVY_READ_IDLE_MS;
6668
6712
  if (!deferAuthoritativeRead) {
6669
6713
  pendingSelectionSeedValidationToken = null;
6670
6714
  if (issueAsyncRead("selection", token, selectionCurrentRun, normalizeSelectionInfo)) return;
@@ -6676,10 +6720,6 @@ function createSuperDocUI(options) {
6676
6720
  const seedMarks = Array.isArray(seed.activeMarks) && seed.activeMarks.length > 0 ? seed.activeMarks : Array.isArray(state.selection.activeMarks) ? state.selection.activeMarks : [];
6677
6721
  const seedCommentIds = Array.isArray(seed.activeCommentIds) ? seed.activeCommentIds : [];
6678
6722
  const paintedTrackChangeId = readCollapsedHostTrackChangeId(hostSelectionSnapshot);
6679
- const previousCaret = collapsedTextAddressFromSelection(state.selection);
6680
- const nextCaret = collapsedTextAddressFromTarget(seed.selectionTarget) ?? collapsedTextAddressFromTarget(seed.target);
6681
- const previousRange = previousCaret?.range;
6682
- const nextRange = nextCaret?.range;
6683
6723
  const sameActiveTypingCaret = Boolean(foreground && foreground.active > 0 && previousCaret && nextCaret && previousCaret.blockId === nextCaret.blockId && storyLocatorSignature(previousCaret.story) === storyLocatorSignature(nextCaret.story) && typeof previousRange?.start === "number" && typeof nextRange?.start === "number" && Math.abs(nextRange.start - previousRange.start) <= 2);
6684
6724
  const carriedChangeIds = paintedTrackChangeId != null || sameActiveTypingCaret ? state.selection.activeChangeIds.filter((id) => paintedTrackChangeId == null || buildTrackedChangeIdContext(state.trackChanges.items).aliasesFor(id).includes(paintedTrackChangeId)) : [];
6685
6725
  const seedChangeIds = Array.isArray(seed.activeChangeIds) && seed.activeChangeIds.length > 0 ? seed.activeChangeIds : carriedChangeIds;
@@ -7646,6 +7686,7 @@ function createSuperDocUI(options) {
7646
7686
  if (support && (support.status === "supported" || support.enabled === true)) return void 0;
7647
7687
  return SUPERDOC_UI_REASONS.bulkDecisionsDisabled;
7648
7688
  };
7689
+ const canStorePendingInlineFormat = (selection) => selection.empty && collapsedTextAddressFromSelection(selection) != null && typeof getHost()?.setPendingInlineFormat === "function";
7649
7690
  const computeCommandState = (id, doc, selection, projectedInlineValues, lockModesById = /* @__PURE__ */ new Map()) => {
7650
7691
  if (customCommands.has(id)) return normalizeCommandState({
7651
7692
  supported: true,
@@ -7800,7 +7841,7 @@ function createSuperDocUI(options) {
7800
7841
  value,
7801
7842
  reason: lockReason
7802
7843
  }, "builtin");
7803
- return normalizeCommandState(selectionBlockIds(selection).length > 0 && typeof getHost()?.setPendingInlineFormat === "function" ? {
7844
+ return normalizeCommandState(canStorePendingInlineFormat(selection) ? {
7804
7845
  enabled: true,
7805
7846
  active,
7806
7847
  supported: true,
@@ -8895,7 +8936,7 @@ function createSuperDocUI(options) {
8895
8936
  const unsubscribe = next.subscribe((snapshot) => {
8896
8937
  if (foregroundMutationActive() && caretSelectionNeedsProjectionPaint() && schedulePostPaintSelectionRefresh()) return;
8897
8938
  selectionEpoch += 1;
8898
- seedCaretSelectionFromHost(snapshot);
8939
+ refreshSelectionFromHost(snapshot);
8899
8940
  recompute("host-selection");
8900
8941
  });
8901
8942
  if (typeof unsubscribe === "function") detachHostSelection = unsubscribe;
@@ -10452,7 +10493,7 @@ function createSuperDocUI(options) {
10452
10493
  if (trackDecisionCommand(id)?.scope === "id") return true;
10453
10494
  if (!descriptor || descriptor.disposition !== "routed") return true;
10454
10495
  if (descriptor.inline?.kind === "clear") return selectionBlockIds(state.selection).length > 0 || resolveInlineSelectionTarget(state.selection) != null;
10455
- if (descriptor.inline) return resolveInlineSelectionTarget(state.selection) != null || selectionBlockIds(state.selection).length > 0;
10496
+ if (descriptor.inline) return resolveInlineSelectionTarget(state.selection) != null || canStorePendingInlineFormat(state.selection);
10456
10497
  if (descriptor.blockParagraph || descriptor.list) return selectionBlockIds(state.selection).length > 0;
10457
10498
  if (descriptor.link) return linkPayloadHasExplicitTarget(payload) || selectionTextAddresses(state.selection).length > 0 || collapsedTextAddressFromSelection(state.selection) != null || commandActiveState(descriptor, getDoc(), state.selection);
10458
10499
  return true;
@@ -10518,7 +10559,7 @@ function createSuperDocUI(options) {
10518
10559
  if (contentControlLockReason(lockModesById)) return false;
10519
10560
  const target = resolveInlineSelectionTarget(state.selection);
10520
10561
  if (!target) {
10521
- if (state.selection.empty === false) return settleCommandExecution(false);
10562
+ if (!canStorePendingInlineFormat(state.selection)) return settleCommandExecution(false);
10522
10563
  return settleCommandExecution(storePendingInlineFormat(descriptor, normalized));
10523
10564
  }
10524
10565
  const active = commandActiveState(descriptor, doc, state.selection);
@@ -4220,6 +4220,14 @@ function readCollapsedHostTrackChangeId(snapshot) {
4220
4220
  const focusId = focusVisual?.trackChangeId;
4221
4221
  return typeof anchorId === "string" && anchorId.length > 0 && anchorId === focusId ? anchorId : null;
4222
4222
  }
4223
+ function isNonCollapsedHostSelection(snapshot) {
4224
+ if (!snapshot || typeof snapshot !== "object") return false;
4225
+ const record = snapshot;
4226
+ const anchor = record.anchor;
4227
+ const focus = record.focus;
4228
+ if (typeof anchor?.blockId !== "string" || !anchor.blockId || typeof focus?.blockId !== "string" || !focus.blockId || !Number.isFinite(anchor.blockOffset) || !Number.isFinite(focus.blockOffset)) return false;
4229
+ return anchor.blockId !== focus.blockId || anchor.blockOffset !== focus.blockOffset || Number.isFinite(anchor.trackedBlockOffset) && Number.isFinite(focus.trackedBlockOffset) && anchor.trackedBlockOffset !== focus.trackedBlockOffset;
4230
+ }
4223
4231
  var trackedChangeIdContextCache = /* @__PURE__ */ new WeakMap();
4224
4232
  /**
4225
4233
  * Collect the raw source-provenance ids a tracked-change list item exposes,
@@ -5676,6 +5684,7 @@ function createSuperDocUI(options) {
5676
5684
  let asyncReadFailureRetryAtMs = 0;
5677
5685
  let foregroundAsyncRetryTimer = null;
5678
5686
  let pendingSelectionSeedValidationToken = null;
5687
+ let typedCaretValidationHeld = false;
5679
5688
  let typingContentInvalidationTimer = null;
5680
5689
  const pendingAsyncReadSettlements = [];
5681
5690
  const scheduleAsyncRefresh = () => {
@@ -6113,6 +6122,16 @@ function createSuperDocUI(options) {
6113
6122
  scheduleForegroundAsyncRetry();
6114
6123
  return;
6115
6124
  }
6125
+ if (typedCaretValidationHeld && Date.now() - lastTypingMutationAtMs < HEAVY_READ_IDLE_MS) {
6126
+ const token = selectionReadToken();
6127
+ if (asyncReads.get("selection")?.token !== token) {
6128
+ refreshSelectionFromHost(void 0, true);
6129
+ if (asyncReads.get("selection")?.token === token) recompute();
6130
+ }
6131
+ scheduleForegroundAsyncRetry(HEAVY_READ_IDLE_POLL_MS);
6132
+ return;
6133
+ }
6134
+ typedCaretValidationHeld = false;
6116
6135
  const validationToken = pendingSelectionSeedValidationToken;
6117
6136
  pendingSelectionSeedValidationToken = null;
6118
6137
  if (validationToken === selectionReadToken()) issueAsyncRead("selection", validationToken, selectionCurrentRun, normalizeSelectionInfo);
@@ -6251,7 +6270,7 @@ function createSuperDocUI(options) {
6251
6270
  }
6252
6271
  if (pendingPostPaintSelectionRefresh) continue;
6253
6272
  selectionEpoch += 1;
6254
- seedCaretSelectionFromHost();
6273
+ refreshSelectionFromHost(void 0, true);
6255
6274
  recompute(paintCompleted ? "post-paint-selection" : "post-paint-selection-failed");
6256
6275
  }
6257
6276
  };
@@ -6262,7 +6281,11 @@ function createSuperDocUI(options) {
6262
6281
  if (typingContentInvalidationTimer) clearTimeout(typingContentInvalidationTimer);
6263
6282
  typingContentInvalidationTimer = setTimeout(() => {
6264
6283
  typingContentInvalidationTimer = null;
6265
- if (!disposed) invalidateDocumentContentAndRecompute();
6284
+ if (!disposed) {
6285
+ invalidateDocumentContent();
6286
+ if (typedCaretValidationHeld && !foregroundMutationActive()) refreshSelectionFromHost(void 0, true);
6287
+ recompute();
6288
+ }
6266
6289
  }, 500);
6267
6290
  };
6268
6291
  const issueAsyncRead = (key, token, run, normalize) => {
@@ -6356,6 +6379,13 @@ function createSuperDocUI(options) {
6356
6379
  value: entry.value,
6357
6380
  status: "ready"
6358
6381
  };
6382
+ if (key === "selection" && typedCaretValidationHeld && Date.now() - lastTypingMutationAtMs < HEAVY_READ_IDLE_MS) {
6383
+ scheduleForegroundAsyncRetry(HEAVY_READ_IDLE_POLL_MS);
6384
+ return {
6385
+ value: entry?.hasSettled ? entry.value : null,
6386
+ status: entry?.hasSettled ? "stale" : "pending"
6387
+ };
6388
+ }
6359
6389
  if (isHeavyDocReadKey(key) && !heavyReadDemandActive(key, token)) {
6360
6390
  if (hostSourceLoadPhase() === "loading") {
6361
6391
  heavyReadsHeldUntilIdle = true;
@@ -6477,6 +6507,7 @@ function createSuperDocUI(options) {
6477
6507
  heldSettledInlineValues = null;
6478
6508
  optimisticParagraphAlignment = null;
6479
6509
  pendingSelectionSeedValidationToken = null;
6510
+ typedCaretValidationHeld = false;
6480
6511
  coldAsyncReadDeferrals.clear();
6481
6512
  demandedHeavyReads.clear();
6482
6513
  incompleteTrackChangesDirectoryReadTokens.clear();
@@ -6648,8 +6679,9 @@ function createSuperDocUI(options) {
6648
6679
  * effective font resolved from it) on the next recompute, instead of serving
6649
6680
  * the previous selection until the worker `selection.current` round-trip lands
6650
6681
  * (SD-3652). Only a resolvable COLLAPSED CARET is seeded - a range's snapshot
6651
- * cannot be resolved synchronously in worker mode, so ranges defer to the async
6652
- * read (seeding an empty value would wrongly disable range-only commands). The
6682
+ * cannot be resolved synchronously in worker mode, so a new range starts an
6683
+ * authoritative async read without waiting for the input-idle gate (SD-5394).
6684
+ * Seeding an empty value would wrongly disable range-only commands. The
6653
6685
  * authoritative async read still overwrites marks/text/review overlap, but
6654
6686
  * foreground typing defers and coalesces that worker hop through the shared
6655
6687
  * retry gate. Previous marks are carried forward for toolbar continuity. A
@@ -6657,15 +6689,27 @@ function createSuperDocUI(options) {
6657
6689
  * same carrier, or while an active typing dispatch advances the same caret by
6658
6690
  * one code point. Pending work alone never carries review command context.
6659
6691
  */
6660
- const seedCaretSelectionFromHost = (hostSelectionSnapshot) => {
6692
+ const refreshSelectionFromHost = (hostSelectionSnapshot, afterTypingPaint = false) => {
6693
+ const foreground = foregroundMutationState();
6694
+ if (isNonCollapsedHostSelection(hostSelectionSnapshot) && !(foreground && foreground.active > 0)) {
6695
+ typedCaretValidationHeld = false;
6696
+ pendingSelectionSeedValidationToken = null;
6697
+ issueAsyncRead("selection", selectionReadToken(), selectionCurrentRun, normalizeSelectionInfo);
6698
+ return;
6699
+ }
6661
6700
  const host = getHost();
6662
6701
  const read = host?.readLiveSelectionSyncSnapshot;
6663
6702
  if (typeof read !== "function") return;
6664
6703
  const seed = normalizeSelectionInfo(safeCall(() => read.call(host), null));
6665
6704
  if (!seed || !isCollapsedCaretSnapshot(seed)) return;
6705
+ const previousCaret = collapsedTextAddressFromSelection(state.selection);
6706
+ const nextCaret = collapsedTextAddressFromTarget(seed.selectionTarget) ?? collapsedTextAddressFromTarget(seed.target);
6707
+ const previousRange = previousCaret?.range;
6708
+ const nextRange = nextCaret?.range;
6709
+ const sameSeedCaret = Boolean(previousCaret && nextCaret && previousCaret.blockId === nextCaret.blockId && storyLocatorSignature(previousCaret.story) === storyLocatorSignature(nextCaret.story) && previousRange?.start === nextRange?.start);
6710
+ typedCaretValidationHeld = Boolean(foreground && foreground.active > 0) || typedCaretValidationHeld && (afterTypingPaint || sameSeedCaret);
6666
6711
  const token = selectionReadToken();
6667
- const foreground = foregroundMutationState();
6668
- const deferAuthoritativeRead = Boolean(foreground && (foreground.active > 0 || foreground.pending > 0));
6712
+ const deferAuthoritativeRead = Boolean(foreground && (foreground.active > 0 || foreground.pending > 0)) || typedCaretValidationHeld && Date.now() - lastTypingMutationAtMs < HEAVY_READ_IDLE_MS;
6669
6713
  if (!deferAuthoritativeRead) {
6670
6714
  pendingSelectionSeedValidationToken = null;
6671
6715
  if (issueAsyncRead("selection", token, selectionCurrentRun, normalizeSelectionInfo)) return;
@@ -6677,10 +6721,6 @@ function createSuperDocUI(options) {
6677
6721
  const seedMarks = Array.isArray(seed.activeMarks) && seed.activeMarks.length > 0 ? seed.activeMarks : Array.isArray(state.selection.activeMarks) ? state.selection.activeMarks : [];
6678
6722
  const seedCommentIds = Array.isArray(seed.activeCommentIds) ? seed.activeCommentIds : [];
6679
6723
  const paintedTrackChangeId = readCollapsedHostTrackChangeId(hostSelectionSnapshot);
6680
- const previousCaret = collapsedTextAddressFromSelection(state.selection);
6681
- const nextCaret = collapsedTextAddressFromTarget(seed.selectionTarget) ?? collapsedTextAddressFromTarget(seed.target);
6682
- const previousRange = previousCaret?.range;
6683
- const nextRange = nextCaret?.range;
6684
6724
  const sameActiveTypingCaret = Boolean(foreground && foreground.active > 0 && previousCaret && nextCaret && previousCaret.blockId === nextCaret.blockId && storyLocatorSignature(previousCaret.story) === storyLocatorSignature(nextCaret.story) && typeof previousRange?.start === "number" && typeof nextRange?.start === "number" && Math.abs(nextRange.start - previousRange.start) <= 2);
6685
6725
  const carriedChangeIds = paintedTrackChangeId != null || sameActiveTypingCaret ? state.selection.activeChangeIds.filter((id) => paintedTrackChangeId == null || buildTrackedChangeIdContext(state.trackChanges.items).aliasesFor(id).includes(paintedTrackChangeId)) : [];
6686
6726
  const seedChangeIds = Array.isArray(seed.activeChangeIds) && seed.activeChangeIds.length > 0 ? seed.activeChangeIds : carriedChangeIds;
@@ -7647,6 +7687,7 @@ function createSuperDocUI(options) {
7647
7687
  if (support && (support.status === "supported" || support.enabled === true)) return void 0;
7648
7688
  return SUPERDOC_UI_REASONS.bulkDecisionsDisabled;
7649
7689
  };
7690
+ const canStorePendingInlineFormat = (selection) => selection.empty && collapsedTextAddressFromSelection(selection) != null && typeof getHost()?.setPendingInlineFormat === "function";
7650
7691
  const computeCommandState = (id, doc, selection, projectedInlineValues, lockModesById = /* @__PURE__ */ new Map()) => {
7651
7692
  if (customCommands.has(id)) return normalizeCommandState({
7652
7693
  supported: true,
@@ -7801,7 +7842,7 @@ function createSuperDocUI(options) {
7801
7842
  value,
7802
7843
  reason: lockReason
7803
7844
  }, "builtin");
7804
- return normalizeCommandState(selectionBlockIds(selection).length > 0 && typeof getHost()?.setPendingInlineFormat === "function" ? {
7845
+ return normalizeCommandState(canStorePendingInlineFormat(selection) ? {
7805
7846
  enabled: true,
7806
7847
  active,
7807
7848
  supported: true,
@@ -8896,7 +8937,7 @@ function createSuperDocUI(options) {
8896
8937
  const unsubscribe = next.subscribe((snapshot) => {
8897
8938
  if (foregroundMutationActive() && caretSelectionNeedsProjectionPaint() && schedulePostPaintSelectionRefresh()) return;
8898
8939
  selectionEpoch += 1;
8899
- seedCaretSelectionFromHost(snapshot);
8940
+ refreshSelectionFromHost(snapshot);
8900
8941
  recompute("host-selection");
8901
8942
  });
8902
8943
  if (typeof unsubscribe === "function") detachHostSelection = unsubscribe;
@@ -10453,7 +10494,7 @@ function createSuperDocUI(options) {
10453
10494
  if (trackDecisionCommand(id)?.scope === "id") return true;
10454
10495
  if (!descriptor || descriptor.disposition !== "routed") return true;
10455
10496
  if (descriptor.inline?.kind === "clear") return selectionBlockIds(state.selection).length > 0 || resolveInlineSelectionTarget(state.selection) != null;
10456
- if (descriptor.inline) return resolveInlineSelectionTarget(state.selection) != null || selectionBlockIds(state.selection).length > 0;
10497
+ if (descriptor.inline) return resolveInlineSelectionTarget(state.selection) != null || canStorePendingInlineFormat(state.selection);
10457
10498
  if (descriptor.blockParagraph || descriptor.list) return selectionBlockIds(state.selection).length > 0;
10458
10499
  if (descriptor.link) return linkPayloadHasExplicitTarget(payload) || selectionTextAddresses(state.selection).length > 0 || collapsedTextAddressFromSelection(state.selection) != null || commandActiveState(descriptor, getDoc(), state.selection);
10459
10500
  return true;
@@ -10519,7 +10560,7 @@ function createSuperDocUI(options) {
10519
10560
  if (contentControlLockReason(lockModesById)) return false;
10520
10561
  const target = resolveInlineSelectionTarget(state.selection);
10521
10562
  if (!target) {
10522
- if (state.selection.empty === false) return settleCommandExecution(false);
10563
+ if (!canStorePendingInlineFormat(state.selection)) return settleCommandExecution(false);
10523
10564
  return settleCommandExecution(storePendingInlineFormat(descriptor, normalized));
10524
10565
  }
10525
10566
  const active = commandActiveState(descriptor, doc, state.selection);
@@ -19,7 +19,7 @@ var COLLABORATION_UPGRADE_ENGINE_MINIMUM_NODE_MAJOR = 20;
19
19
  var PRIVATE_ENGINE_INFO = (0, _superdoc_docx_engine_collaboration_upgrade_engine.getCollaborationUpgradeEngineInfo)();
20
20
  var ENGINE_INFO = Object.freeze({
21
21
  ...PRIVATE_ENGINE_INFO,
22
- superdocVersion: "2.20.0",
22
+ superdocVersion: "2.20.1-next.10",
23
23
  roomSchemaVersion: Object.freeze({ ...PRIVATE_ENGINE_INFO.roomSchemaVersion }),
24
24
  supportedBundleVersions: SUPPORTED_COLLABORATION_UPGRADE_BUNDLE_VERSIONS,
25
25
  supportedV1ReaderContractVersions: SUPPORTED_V1_READER_CONTRACT_VERSIONS
@@ -18,7 +18,7 @@ var COLLABORATION_UPGRADE_ENGINE_MINIMUM_NODE_MAJOR = 20;
18
18
  var PRIVATE_ENGINE_INFO = getCollaborationUpgradeEngineInfo$1();
19
19
  var ENGINE_INFO = Object.freeze({
20
20
  ...PRIVATE_ENGINE_INFO,
21
- superdocVersion: "2.20.0",
21
+ superdocVersion: "2.20.1-next.10",
22
22
  roomSchemaVersion: Object.freeze({ ...PRIVATE_ENGINE_INFO.roomSchemaVersion }),
23
23
  supportedBundleVersions: SUPPORTED_COLLABORATION_UPGRADE_BUNDLE_VERSIONS,
24
24
  supportedV1ReaderContractVersions: SUPPORTED_V1_READER_CONTRACT_VERSIONS
@@ -262,7 +262,9 @@ export interface ContentControlsPatchInput {
262
262
  tag?: string | null;
263
263
  appearance?: ContentControlAppearance | null;
264
264
  color?: string | null;
265
+ /** V2 rejects the entire patch with CAPABILITY_UNAVAILABLE when this field is defined, including null. */
265
266
  placeholder?: string | null;
267
+ /** V2 rejects the entire patch with CAPABILITY_UNAVAILABLE when this field is defined, including false. */
266
268
  showingPlaceholder?: boolean;
267
269
  temporary?: boolean;
268
270
  tabIndex?: number | null;
@@ -2310,8 +2310,8 @@ export declare const OPERATION_DEFINITIONS: {
2310
2310
  };
2311
2311
  readonly 'contentControls.patch': {
2312
2312
  readonly memberPath: "contentControls.patch";
2313
- readonly description: "Patch metadata properties on a content control (tag, alias, appearance, color, etc.).";
2314
- readonly expectedResult: "Returns a ContentControlMutationResult; reports NO_OP if no fields changed.";
2313
+ readonly description: "Patch metadata properties on a content control (tag, alias, appearance, color, etc.). V2 does not support placeholder or showingPlaceholder; defining either field rejects the entire patch without changing properties or content.";
2314
+ readonly expectedResult: "Returns a ContentControlMutationResult; reports NO_OP if no fields changed, or CAPABILITY_UNAVAILABLE for unsupported V2 placeholder fields.";
2315
2315
  readonly requiresDocumentContext: true;
2316
2316
  readonly metadata: CommandStaticMetadata;
2317
2317
  readonly referenceDocPath: "content-controls/patch.mdx";
@@ -1,5 +1,5 @@
1
1
  import { RichContentMutationOptions, WriteAdapter } from '../write/write.js';
2
- import { SelectionTarget, TargetLocator } from '../types/address.js';
2
+ import { SelectionTarget } from '../types/address.js';
3
3
  import { SDMutationReceipt } from '../types/sd-contract.js';
4
4
  import { SDReplaceInput } from '../types/structural-input.js';
5
5
  import { BodyStoryLocator, StoryLocator } from '../types/story.types.js';
@@ -7,15 +7,24 @@ import { BlockNodeAddress } from '../types/base.js';
7
7
  import { NestingPolicy } from '../types/placement.js';
8
8
  import { SelectionMutationAdapter } from '../selection-mutation.js';
9
9
  /** Text replacement input: uses SelectionTarget / ref, or the complete main body. */
10
- export type TextReplaceInput = (TargetLocator & {
11
- target?: SelectionTarget;
12
- ref?: string;
10
+ export type TextReplaceInput = {
11
+ target: SelectionTarget;
12
+ ref?: never;
13
13
  text: string;
14
+ /** Require this exact selected text before replacing a single-paragraph text selection. */
15
+ expectedText?: string;
14
16
  /** Target a specific document story (body, header, footer, footnote, endnote). */
15
17
  in?: StoryLocator;
16
- }) | {
18
+ } | {
19
+ ref: string;
20
+ target?: never;
21
+ text: string;
22
+ expectedText?: never;
23
+ in?: StoryLocator;
24
+ } | {
17
25
  target: BodyStoryLocator;
18
26
  text: string;
27
+ expectedText?: never;
19
28
  ref?: never;
20
29
  in?: never;
21
30
  };
@@ -16,6 +16,8 @@ export type SelectionReplaceRequest = TargetLocator & {
16
16
  target?: SelectionTarget;
17
17
  ref?: string;
18
18
  text: string;
19
+ /** Exact selected-text precondition for an explicit single-paragraph selection. */
20
+ expectedText?: string;
19
21
  /** Story locator threaded from the operation input's `in` field. */
20
22
  in?: StoryLocator;
21
23
  };
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-BQBQsjoe.cjs");
2
+ const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-j8Qm5SYa.cjs");
3
3
  const require_slice_source = require("../chunks/slice-source-CFaqq0Vo.cjs");
4
4
  let react = require("react");
5
5
  //#region src/public/ui/react.ts
@@ -1,4 +1,4 @@
1
- import { t as createSuperDocUI } from "../chunks/create-super-doc-ui-D5VrP4Dx.es.js";
1
+ import { t as createSuperDocUI } from "../chunks/create-super-doc-ui-Cfwszc3b.es.js";
2
2
  import { t as toSliceSource } from "../chunks/slice-source-gfOhG2MW.es.js";
3
3
  import { createContext, createElement, useCallback, useContext, useEffect, useRef, useState } from "react";
4
4
  //#region src/public/ui/react.ts
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-BQBQsjoe.cjs");
2
+ const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-j8Qm5SYa.cjs");
3
3
  const require_slice_source = require("../chunks/slice-source-CFaqq0Vo.cjs");
4
4
  let superdoc_entry_vue = require("vue");
5
5
  //#region src/public/ui/vue.ts
@@ -1,4 +1,4 @@
1
- import { t as createSuperDocUI } from "../chunks/create-super-doc-ui-D5VrP4Dx.es.js";
1
+ import { t as createSuperDocUI } from "../chunks/create-super-doc-ui-Cfwszc3b.es.js";
2
2
  import { t as toSliceSource } from "../chunks/slice-source-gfOhG2MW.es.js";
3
3
  import { computed, inject, onScopeDispose, provide, shallowRef, toRaw, toValue, watch } from "vue";
4
4
  //#region src/public/ui/vue.ts
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-BQBQsjoe.cjs");
2
+ const require_create_super_doc_ui = require("../chunks/create-super-doc-ui-j8Qm5SYa.cjs");
3
3
  exports.BUILT_IN_COMMAND_IDS = require_create_super_doc_ui.BUILT_IN_COMMAND_IDS;
4
4
  exports.createSuperDocUI = require_create_super_doc_ui.createSuperDocUI;
5
5
  exports.shallowEqual = require_create_super_doc_ui.shallowEqual;
@@ -1,2 +1,2 @@
1
- import { a as BUILT_IN_COMMAND_IDS, t as createSuperDocUI, u as shallowEqual } from "../chunks/create-super-doc-ui-D5VrP4Dx.es.js";
1
+ import { a as BUILT_IN_COMMAND_IDS, t as createSuperDocUI, u as shallowEqual } from "../chunks/create-super-doc-ui-Cfwszc3b.es.js";
2
2
  export { BUILT_IN_COMMAND_IDS, createSuperDocUI, shallowEqual };