superdoc 2.25.0-next.16 → 2.25.0-next.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/chunks/{create-super-doc-ui-BbTRF-Z9.cjs → create-super-doc-ui-DZIB6Pd4.cjs} +317 -165
  2. package/dist/chunks/{create-super-doc-ui-Cr_04XnJ.es.js → create-super-doc-ui-DqCrc6Vw.es.js} +312 -166
  3. package/dist/collaboration-upgrade-engine.cjs +1 -1
  4. package/dist/collaboration-upgrade-engine.es.js +1 -1
  5. package/dist/document-api/src/contract/operation-definitions.d.ts +1 -1
  6. package/dist/document-api/src/customXml/customXml.types.d.ts +1 -1
  7. package/dist/document-api/src/lists/lists.types.d.ts +2 -0
  8. package/dist/document-api/src/metadata/anchored-metadata.types.d.ts +1 -1
  9. package/dist/document-api/src/types/mutation-plan.types.d.ts +4 -2
  10. package/dist/layout-engine/layout-bridge/src/index.d.ts +2 -0
  11. package/dist/layout-engine/layout-bridge/src/neutral-segment-geometry.d.ts +18 -1
  12. package/dist/layout-engine/layout-bridge/src/visual-bidi-geometry.d.ts +37 -0
  13. package/dist/public/ui-react.cjs +1 -1
  14. package/dist/public/ui-react.es.js +1 -1
  15. package/dist/public/ui-vue.cjs +1 -1
  16. package/dist/public/ui-vue.es.js +1 -1
  17. package/dist/public/ui.cjs +1 -1
  18. package/dist/public/ui.es.js +1 -1
  19. package/dist/style.css +1 -1
  20. package/dist/style.layered.css +1 -1
  21. package/dist/superdoc/src/public/ui/create-super-doc-ui.d.ts +7 -0
  22. package/dist/superdoc/src/stores/comments-store.d.ts +9 -3
  23. package/dist/superdoc/src/stores/superdoc-store.d.ts +27 -9
  24. package/dist/superdoc.cjs +98 -12
  25. package/dist/superdoc.es.js +98 -12
  26. package/dist-cdn/style.layered.css +1 -1
  27. package/dist-cdn/superdoc.min.css +1 -1
  28. package/dist-cdn/superdoc.min.js +42 -42
  29. package/package.json +2 -2
@@ -4340,6 +4340,9 @@ function readFirstPartyPageNumbers(ui) {
4340
4340
  function executeFirstPartyCommandAsync(ui, id, payload) {
4341
4341
  return firstPartyCommandExecutors.get(ui)?.(id, payload) ?? ui.commands.executeAsync(id, payload);
4342
4342
  }
4343
+ function executeFirstPartyShortcutAsync(ui, id, payload) {
4344
+ return firstPartyCommandExecutors.get(ui)?.(id, payload, true) ?? Promise.resolve(false);
4345
+ }
4343
4346
  function uiBenchNowMs() {
4344
4347
  return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
4345
4348
  }
@@ -6081,6 +6084,7 @@ function createSuperDocUI(options) {
6081
6084
  * change, so an identical object means the selection has not moved since.
6082
6085
  */
6083
6086
  const selectionHostSnapshots = /* @__PURE__ */ new WeakMap();
6087
+ let wholeTableParagraphSeed = null;
6084
6088
  const readHostSelectionSnapshot = () => {
6085
6089
  const selectionHandle = (safeCall(() => getHost()?.getHandles?.() ?? null, null)?.editing)?.selection;
6086
6090
  if (typeof selectionHandle?.getSnapshot !== "function") return null;
@@ -7375,6 +7379,21 @@ function createSuperDocUI(options) {
7375
7379
  if (wholeTableSelectionChanged) {
7376
7380
  typedCaretValidationHeld = false;
7377
7381
  pendingSelectionSeedValidationToken = null;
7382
+ wholeTableParagraphSeed = null;
7383
+ const editor = getEditor();
7384
+ const lifetime = safeCall(() => host?.getForegroundInteractionLifetime?.() ?? null, null);
7385
+ const mount = safeCall(() => host?.getMountedHandle?.() ?? null, null);
7386
+ const mode = readDocumentMode();
7387
+ if (host && editor && mount && lifetime && !lifetime.aborted && mode !== null && !seed.empty && seed.tableSelection?.kind === "tableCells" && seed.tableSelection.type === "table" && seed.target?.segments.length && seed.selectionTarget) wholeTableParagraphSeed = {
7388
+ editor,
7389
+ host,
7390
+ doc: editor.doc,
7391
+ mount,
7392
+ lifetime,
7393
+ sourceToken: contentToken(),
7394
+ mode,
7395
+ info: seed
7396
+ };
7378
7397
  const token = selectionReadToken();
7379
7398
  asyncReads.set("selection", {
7380
7399
  token,
@@ -7420,10 +7439,23 @@ function createSuperDocUI(options) {
7420
7439
  inflightToken: deferAuthoritativeRead ? null : prev?.inflightToken ?? null
7421
7440
  });
7422
7441
  };
7442
+ const selectionInfoMatchesHost = (info) => {
7443
+ const snapshot = readHostSelectionSnapshot();
7444
+ const target = info?.selectionTarget;
7445
+ if (!snapshot || target?.kind !== "selection") return false;
7446
+ const matches = (point, endpoint) => {
7447
+ const offset = target.coordinateSpace === "tracked" ? endpoint?.trackedBlockOffset : endpoint?.blockOffset;
7448
+ return point?.kind === "text" && typeof point.blockId === "string" && point.blockId === endpoint?.blockId && typeof point.offset === "number" && point.offset === offset && storyLocatorSignature(point.story ?? target.story) === storyLocatorSignature(endpoint?.story ?? snapshot.story);
7449
+ };
7450
+ return matches(target.start, snapshot.anchor) && matches(target.end, snapshot.focus) || matches(target.start, snapshot.focus) && matches(target.end, snapshot.anchor);
7451
+ };
7423
7452
  const readSelectionInfoFresh = async (attempt = 0) => {
7424
- const selectionApi = getDoc()?.selection;
7453
+ const doc = getDoc();
7454
+ const selectionApi = doc?.selection;
7425
7455
  const current = selectionApi?.current;
7426
7456
  const token = selectionReadToken();
7457
+ const sourceToken = contentToken();
7458
+ const mode = readDocumentMode();
7427
7459
  if (typeof current !== "function") {
7428
7460
  asyncReads.set("selection", {
7429
7461
  token,
@@ -7432,7 +7464,7 @@ function createSuperDocUI(options) {
7432
7464
  inflightToken: null
7433
7465
  });
7434
7466
  recompute();
7435
- return state.selection;
7467
+ return selectionSliceFromInfo(null, "pending");
7436
7468
  }
7437
7469
  let raw;
7438
7470
  try {
@@ -7442,15 +7474,17 @@ function createSuperDocUI(options) {
7442
7474
  raw = null;
7443
7475
  }
7444
7476
  if (disposed) return selectionSliceFromInfo(null, "pending");
7445
- if (selectionReadToken() !== token) return attempt < 1 ? readSelectionInfoFresh(attempt + 1) : state.selection;
7477
+ const info = normalizeSelectionInfo(raw);
7478
+ if (selectionReadToken() !== token && !(getDoc() === doc && contentToken() === sourceToken && readDocumentMode() === mode && selectionInfoMatchesHost(info))) return attempt < 1 ? readSelectionInfoFresh(attempt + 1) : selectionSliceFromInfo(null, "pending");
7446
7479
  asyncReads.set("selection", {
7447
- token,
7448
- value: normalizeSelectionInfo(raw),
7480
+ token: selectionReadToken(),
7481
+ value: info,
7449
7482
  hasSettled: true,
7450
7483
  inflightToken: null
7451
7484
  });
7485
+ const selection = selectionSliceFromInfo(info, info ? "ready" : "pending");
7452
7486
  recompute();
7453
- return state.selection;
7487
+ return selection;
7454
7488
  };
7455
7489
  const readCommentsDirectory = () => {
7456
7490
  const commentsApi = getDoc()?.comments;
@@ -8101,6 +8135,10 @@ function createSuperDocUI(options) {
8101
8135
  effectiveUniformityStatus: uniformity.status
8102
8136
  };
8103
8137
  }
8138
+ if (selection.empty && typeof getHost()?.readAppliedSelectionPresentation === "function" && !appliedSelectionPresentation(selection)) return {
8139
+ values: combined,
8140
+ effectiveUniformityStatus: "pending"
8141
+ };
8104
8142
  return {
8105
8143
  values: combined,
8106
8144
  effectiveUniformityStatus: "ready"
@@ -8296,9 +8334,9 @@ function createSuperDocUI(options) {
8296
8334
  const projectInlineValuesWithSettledHold = (selection) => {
8297
8335
  const projection = projectSelectionInlineValuesWithStatus(selection);
8298
8336
  const projected = projection.values;
8299
- if (selection.empty) return projection;
8300
8337
  const signature = selectionInlineValueSignature(selection);
8301
8338
  if (!signature) return projection;
8339
+ if (heldSettledInlineValues?.key !== signature) heldSettledInlineValues = null;
8302
8340
  if (selection.status === "ready" && selectionQueryStatus(selection) === "ready" && projection.effectiveUniformityStatus === "ready") {
8303
8341
  heldSettledInlineValues = {
8304
8342
  key: signature,
@@ -8627,6 +8665,7 @@ function createSuperDocUI(options) {
8627
8665
  }, "builtin");
8628
8666
  };
8629
8667
  function hyperlinkOverlapsSelection(link, selection) {
8668
+ if (storyLocatorSignature(readEntityStory(link)) !== storyLocatorSignature(selectionStoryLocator(selection))) return false;
8630
8669
  const anchor = link.address?.anchor;
8631
8670
  const start = anchor?.start;
8632
8671
  const end = anchor?.end;
@@ -8669,13 +8708,10 @@ function createSuperDocUI(options) {
8669
8708
  if (!linksApi || typeof linksApi.list !== "function") return null;
8670
8709
  const blockIds = selectionBlockIds(selection);
8671
8710
  if (!canProbeEverySelectedBlock(blockIds)) return null;
8711
+ const story = selectionStoryLocator(selection);
8672
8712
  for (const blockId of blockIds) {
8673
- const within = {
8674
- kind: "block",
8675
- nodeType: "paragraph",
8676
- nodeId: blockId
8677
- };
8678
- const { value: result } = readAsync(`hyperlinks:${blockId}`, contentToken(), () => linksApi.list({ within }), (raw) => raw && typeof raw === "object" ? raw : null);
8713
+ const within = paragraphTarget(blockId, story);
8714
+ const { value: result } = readAsync(story ? `hyperlinks:${storyLocatorSignature(story)}:${blockId}` : `hyperlinks:${blockId}`, contentToken(), () => linksApi.list({ within }), (raw) => raw && typeof raw === "object" ? raw : null);
8679
8715
  const hit = (result && Array.isArray(result.items) ? result.items : []).find((item) => hyperlinkOverlapsSelection(item, selection));
8680
8716
  if (hit) return hit;
8681
8717
  }
@@ -10319,7 +10355,15 @@ function createSuperDocUI(options) {
10319
10355
  if (!resolved) return false;
10320
10356
  return executeTrackDecisionTarget(command.kind, resolved.target, resolved.changeId, resolved.story);
10321
10357
  }
10358
+ const hasInactiveTextSelection = () => {
10359
+ const host = getHost();
10360
+ const read = host?.readLiveSelectionSyncSnapshot;
10361
+ if (typeof read !== "function") return false;
10362
+ const info = safeCall(() => read.call(host), null);
10363
+ return !info || info.empty === true && !info.target && !info.selectionTarget;
10364
+ };
10322
10365
  function executeListToggleCommand(kind, payload) {
10366
+ if (hasInactiveTextSelection()) return false;
10323
10367
  const lists = getEditCommands()?.lists;
10324
10368
  const apply = lists?.apply;
10325
10369
  const host = getHost();
@@ -10448,6 +10492,7 @@ function createSuperDocUI(options) {
10448
10492
  /** Clear-patch to send for the current document mode (see TRACKED_CLEAR_INLINE_PATCH). */
10449
10493
  const clearInlinePatchForMode = () => readDocumentMode() === "suggesting" ? TRACKED_CLEAR_INLINE_PATCH : CLEAR_INLINE_PATCH;
10450
10494
  const callEditorMutation = (route, op, input) => {
10495
+ if ((route.startsWith("format.paragraph.") || route.startsWith("lists.") || route.startsWith("styles.paragraph.")) && hasInactiveTextSelection()) return false;
10451
10496
  const options = editorMutationOptionsForRoute(route);
10452
10497
  if (options && options.success === false) return options;
10453
10498
  return options ? op(input, options) : op(input);
@@ -10698,7 +10743,7 @@ function createSuperDocUI(options) {
10698
10743
  * command drops all stored marks. The host applies the store to the created
10699
10744
  * span on the next insert; settleCommandExecution drives the recompute.
10700
10745
  */
10701
- const storePendingInlineFormat = (descriptor, payload) => {
10746
+ const storePendingInlineFormat = (descriptor, payload, selection = state.selection) => {
10702
10747
  const spec = descriptor.inline;
10703
10748
  const method = inlineFormatMethod(descriptor);
10704
10749
  const host = getHost();
@@ -10709,7 +10754,7 @@ function createSuperDocUI(options) {
10709
10754
  }
10710
10755
  if (!method) return false;
10711
10756
  let value;
10712
- if (spec.kind === "toggle") value = !commandActiveState(descriptor, getDoc(), state.selection);
10757
+ if (spec.kind === "toggle") value = !commandActiveState(descriptor, getDoc(), selection);
10713
10758
  else {
10714
10759
  const raw = payload && typeof payload === "object" && "value" in payload ? payload.value : payload;
10715
10760
  if (spec.kind === "value-number") {
@@ -10998,155 +11043,214 @@ function createSuperDocUI(options) {
10998
11043
  const currentText = typeof record.currentText === "string" ? record.currentText : void 0;
10999
11044
  const requestedHyperlinkTarget = isLooseObject(record.hyperlinkTarget) ? record.hyperlinkTarget : null;
11000
11045
  const requestedTextTarget = isLooseObject(record.textTarget) ? record.textTarget : null;
11001
- const existing = resolveCurrentHyperlink(doc, state.selection);
11002
11046
  const hasRequestedHyperlinkTarget = typeof requestedHyperlinkTarget?.storyId === "string" && typeof requestedHyperlinkTarget?.hyperlinkNodeId === "string";
11003
- if (hasRequestedHyperlinkTarget && href === null) {
11004
- const remove = linksApi.remove;
11005
- if (typeof remove !== "function") return false;
11006
- try {
11007
- return callEditorMutation("hyperlinks.remove", remove, {
11008
- storyId: requestedHyperlinkTarget.storyId,
11009
- hyperlinkNodeId: requestedHyperlinkTarget.hyperlinkNodeId,
11010
- keepText: true
11011
- });
11012
- } catch {
11013
- return false;
11014
- }
11015
- }
11016
- if (existing && href === null) {
11017
- const remove = trustedLinksApi?.remove;
11018
- if (typeof remove !== "function") return false;
11019
- try {
11020
- return callEditorMutation("hyperlinks.remove", remove, {
11021
- target: existing.address,
11022
- mode: "unwrap"
11023
- });
11024
- } catch {
11025
- return false;
11026
- }
11027
- }
11028
- if ((existing || requestedHyperlinkTarget) && typeof href === "string" && href.trim() !== "") {
11029
- const patch = trustedLinksApi?.patch;
11030
- const updateTarget = linksApi.updateTarget;
11031
- if (!(!hasRequestedHyperlinkTarget && existing && typeof patch === "function") && !(hasRequestedHyperlinkTarget && typeof updateTarget === "function")) return false;
11032
- const shouldReplaceText = typeof text === "string" && text !== (hasRequestedHyperlinkTarget ? currentText : existing?.text ?? currentText);
11033
- const replace = (doc?.text)?.replace;
11034
- const target = shouldReplaceText ? hasRequestedHyperlinkTarget ? requestedTextTarget : hyperlinkAddressToSelectionTarget(existing?.address) : null;
11035
- const canReplaceText = shouldReplaceText && Boolean(target) && typeof replace === "function";
11036
- if (shouldReplaceText && !canReplaceText) return false;
11037
- const results = [];
11038
- const settledResults = [];
11039
- let hyperlinkResult = null;
11040
- let committedHyperlinkResult = null;
11041
- let settledHyperlinkResult = null;
11042
- let hyperlinkWasDeferred = false;
11043
- try {
11044
- if (hasRequestedHyperlinkTarget) {
11045
- if (typeof updateTarget !== "function") return false;
11046
- const operationResult = callEditorMutation("hyperlinks.updateTarget", updateTarget, {
11047
+ const textTargets = textAddressesFromTarget(payloadTarget);
11048
+ const caretTarget = collapsedTextAddressFromTarget(payloadTarget);
11049
+ if (payloadTarget !== null && !textTargets.length && !caretTarget && !hasRequestedHyperlinkTarget) return false;
11050
+ const addresses = textTargets.length ? textTargets : caretTarget ? [caretTarget] : [];
11051
+ const linkSelection = payloadTarget !== null ? {
11052
+ ...EMPTY_SELECTION,
11053
+ empty: textTargets.length === 0,
11054
+ target: {
11055
+ kind: "text",
11056
+ segments: addresses.map((address) => ({
11057
+ blockId: address.blockId,
11058
+ range: address.range
11059
+ })),
11060
+ ...addresses[0]?.story ? { story: addresses[0].story } : {}
11061
+ }
11062
+ } : state.selection;
11063
+ const executeMutation = (existing) => {
11064
+ if (hasRequestedHyperlinkTarget && href === null) {
11065
+ const remove = linksApi.remove;
11066
+ if (typeof remove !== "function") return false;
11067
+ try {
11068
+ return callEditorMutation("hyperlinks.remove", remove, {
11047
11069
  storyId: requestedHyperlinkTarget.storyId,
11048
11070
  hyperlinkNodeId: requestedHyperlinkTarget.hyperlinkNodeId,
11049
- newTarget: hyperlinkTargetFromHref(href)
11071
+ keepText: true
11050
11072
  });
11051
- hyperlinkWasDeferred = isPromiseLike(operationResult);
11052
- const result = settleOperationResult(operationResult);
11053
- if (!commandResultSucceeded(result.immediate)) return false;
11054
- results.push(result.immediate);
11055
- settledResults.push(result.settled);
11056
- hyperlinkResult = result.immediate;
11057
- committedHyperlinkResult = result.committed;
11058
- settledHyperlinkResult = result.settled;
11059
- } else if (existing) {
11060
- if (typeof patch !== "function") return false;
11061
- const operationResult = callEditorMutation("hyperlinks.patch", patch, {
11073
+ } catch {
11074
+ return false;
11075
+ }
11076
+ }
11077
+ if (existing && href === null) {
11078
+ const remove = trustedLinksApi?.remove;
11079
+ if (typeof remove !== "function") return false;
11080
+ try {
11081
+ return callEditorMutation("hyperlinks.remove", remove, {
11062
11082
  target: existing.address,
11063
- patch: { href }
11083
+ mode: "unwrap"
11064
11084
  });
11065
- hyperlinkWasDeferred = isPromiseLike(operationResult);
11066
- const result = settleOperationResult(operationResult);
11067
- if (!commandResultSucceeded(result.immediate)) return false;
11068
- results.push(result.immediate);
11069
- settledResults.push(result.settled);
11070
- hyperlinkResult = result.immediate;
11071
- committedHyperlinkResult = result.committed;
11072
- settledHyperlinkResult = result.settled;
11085
+ } catch {
11086
+ return false;
11073
11087
  }
11074
- } catch {
11075
- return false;
11076
11088
  }
11077
- if (canReplaceText) {
11078
- const combineSettledLinkEdit = (finalHyperlinkResult, finalTextResult) => {
11079
- if (!commandResultSucceeded(finalTextResult)) return commandResultSucceeded(finalHyperlinkResult) ? partialLinkEditFailure(finalHyperlinkResult, finalTextResult) : finalTextResult;
11080
- return combineCommandResults([finalHyperlinkResult, finalTextResult]);
11081
- };
11082
- const replaceTextAfterHyperlinkCommit = (finalHyperlinkResult) => {
11083
- if (!commandResultSucceeded(finalHyperlinkResult)) return finalHyperlinkResult;
11089
+ if ((existing || requestedHyperlinkTarget) && typeof href === "string" && href.trim() !== "") {
11090
+ const patch = trustedLinksApi?.patch;
11091
+ const updateTarget = linksApi.updateTarget;
11092
+ if (!(!hasRequestedHyperlinkTarget && existing && typeof patch === "function") && !(hasRequestedHyperlinkTarget && typeof updateTarget === "function")) return false;
11093
+ const shouldReplaceText = typeof text === "string" && text !== (hasRequestedHyperlinkTarget ? currentText : existing?.text ?? currentText);
11094
+ const replace = (doc?.text)?.replace;
11095
+ const target = shouldReplaceText ? hasRequestedHyperlinkTarget ? requestedTextTarget : hyperlinkAddressToSelectionTarget(existing?.address) : null;
11096
+ const canReplaceText = shouldReplaceText && Boolean(target) && typeof replace === "function";
11097
+ if (shouldReplaceText && !canReplaceText) return false;
11098
+ const results = [];
11099
+ const settledResults = [];
11100
+ let hyperlinkResult = null;
11101
+ let committedHyperlinkResult = null;
11102
+ let settledHyperlinkResult = null;
11103
+ let hyperlinkWasDeferred = false;
11104
+ try {
11105
+ if (hasRequestedHyperlinkTarget) {
11106
+ if (typeof updateTarget !== "function") return false;
11107
+ const operationResult = callEditorMutation("hyperlinks.updateTarget", updateTarget, {
11108
+ storyId: requestedHyperlinkTarget.storyId,
11109
+ hyperlinkNodeId: requestedHyperlinkTarget.hyperlinkNodeId,
11110
+ newTarget: hyperlinkTargetFromHref(href)
11111
+ });
11112
+ hyperlinkWasDeferred = isPromiseLike(operationResult);
11113
+ const result = settleOperationResult(operationResult);
11114
+ if (!commandResultSucceeded(result.immediate)) return false;
11115
+ results.push(result.immediate);
11116
+ settledResults.push(result.settled);
11117
+ hyperlinkResult = result.immediate;
11118
+ committedHyperlinkResult = result.committed;
11119
+ settledHyperlinkResult = result.settled;
11120
+ } else if (existing) {
11121
+ if (typeof patch !== "function") return false;
11122
+ const operationResult = callEditorMutation("hyperlinks.patch", patch, {
11123
+ target: existing.address,
11124
+ patch: { href }
11125
+ });
11126
+ hyperlinkWasDeferred = isPromiseLike(operationResult);
11127
+ const result = settleOperationResult(operationResult);
11128
+ if (!commandResultSucceeded(result.immediate)) return false;
11129
+ results.push(result.immediate);
11130
+ settledResults.push(result.settled);
11131
+ hyperlinkResult = result.immediate;
11132
+ committedHyperlinkResult = result.committed;
11133
+ settledHyperlinkResult = result.settled;
11134
+ }
11135
+ } catch {
11136
+ return false;
11137
+ }
11138
+ if (canReplaceText) {
11139
+ const combineSettledLinkEdit = (finalHyperlinkResult, finalTextResult) => {
11140
+ if (!commandResultSucceeded(finalTextResult)) return commandResultSucceeded(finalHyperlinkResult) ? partialLinkEditFailure(finalHyperlinkResult, finalTextResult) : finalTextResult;
11141
+ return combineCommandResults([finalHyperlinkResult, finalTextResult]);
11142
+ };
11143
+ const replaceTextAfterHyperlinkCommit = (finalHyperlinkResult) => {
11144
+ if (!commandResultSucceeded(finalHyperlinkResult)) return finalHyperlinkResult;
11145
+ try {
11146
+ const result = settleOperationResult(callEditorMutation("text.replace", replace, {
11147
+ target,
11148
+ text
11149
+ }));
11150
+ if (!commandResultSucceeded(result.immediate)) return partialLinkEditFailure(finalHyperlinkResult, result.immediate);
11151
+ return Promise.all([settledHyperlinkResult ?? Promise.resolve(finalHyperlinkResult), result.settled]).then(([settledHyperlink, finalTextResult]) => combineSettledLinkEdit(settledHyperlink, finalTextResult));
11152
+ } catch {
11153
+ return partialLinkEditFailure(finalHyperlinkResult, false);
11154
+ }
11155
+ };
11156
+ if (hyperlinkWasDeferred) {
11157
+ pendingCommandSettlement = (committedHyperlinkResult ?? Promise.resolve(hyperlinkResult ?? false)).then(replaceTextAfterHyperlinkCommit);
11158
+ return combineCommandResults(results);
11159
+ }
11084
11160
  try {
11085
11161
  const result = settleOperationResult(callEditorMutation("text.replace", replace, {
11086
11162
  target,
11087
11163
  text
11088
11164
  }));
11089
- if (!commandResultSucceeded(result.immediate)) return partialLinkEditFailure(finalHyperlinkResult, result.immediate);
11090
- return Promise.all([settledHyperlinkResult ?? Promise.resolve(finalHyperlinkResult), result.settled]).then(([settledHyperlink, finalTextResult]) => combineSettledLinkEdit(settledHyperlink, finalTextResult));
11165
+ if (!commandResultSucceeded(result.immediate)) return hyperlinkResult ? partialLinkEditFailure(hyperlinkResult, result.immediate) : result.immediate;
11166
+ results.push(result.immediate);
11167
+ settledResults.push(result.settled);
11168
+ pendingCommandSettlement = Promise.all([settledHyperlinkResult ?? Promise.resolve(hyperlinkResult ?? false), result.settled]).then(([finalHyperlinkResult, finalTextResult]) => {
11169
+ return combineSettledLinkEdit(finalHyperlinkResult, finalTextResult);
11170
+ });
11091
11171
  } catch {
11092
- return partialLinkEditFailure(finalHyperlinkResult, false);
11172
+ return hyperlinkResult ? partialLinkEditFailure(hyperlinkResult, false) : false;
11093
11173
  }
11094
- };
11095
- if (hyperlinkWasDeferred) {
11096
- pendingCommandSettlement = (committedHyperlinkResult ?? Promise.resolve(hyperlinkResult ?? false)).then(replaceTextAfterHyperlinkCommit);
11097
- return combineCommandResults(results);
11098
11174
  }
11099
- try {
11100
- const result = settleOperationResult(callEditorMutation("text.replace", replace, {
11101
- target,
11102
- text
11103
- }));
11104
- if (!commandResultSucceeded(result.immediate)) return hyperlinkResult ? partialLinkEditFailure(hyperlinkResult, result.immediate) : result.immediate;
11105
- results.push(result.immediate);
11106
- settledResults.push(result.settled);
11107
- pendingCommandSettlement = Promise.all([settledHyperlinkResult ?? Promise.resolve(hyperlinkResult ?? false), result.settled]).then(([finalHyperlinkResult, finalTextResult]) => {
11108
- return combineSettledLinkEdit(finalHyperlinkResult, finalTextResult);
11109
- });
11110
- } catch {
11111
- return hyperlinkResult ? partialLinkEditFailure(hyperlinkResult, false) : false;
11175
+ if (!canReplaceText && settledResults.length > 0) pendingCommandSettlement = Promise.all(settledResults).then((settled) => combineCommandResults(settled));
11176
+ return combineCommandResults(results);
11177
+ }
11178
+ if (typeof href === "string" && href.trim() !== "") {
11179
+ const normalizedHref = href.trim();
11180
+ const wrap = trustedLinksApi?.wrap;
11181
+ const wrapTargets = payloadTarget !== null ? textTargets : selectionTextAddresses(state.selection);
11182
+ if (wrapTargets.length > 0) {
11183
+ if (typeof wrap !== "function") return false;
11184
+ try {
11185
+ return applyPerTextAddress("hyperlinks.wrap", wrap, wrapTargets, (target) => ({
11186
+ target,
11187
+ link: { destination: { href: normalizedHref } }
11188
+ }));
11189
+ } catch {
11190
+ return false;
11191
+ }
11112
11192
  }
11113
- }
11114
- if (!canReplaceText && settledResults.length > 0) pendingCommandSettlement = Promise.all(settledResults).then((settled) => combineCommandResults(settled));
11115
- return combineCommandResults(results);
11116
- }
11117
- if (typeof href === "string" && href.trim() !== "") {
11118
- const normalizedHref = href.trim();
11119
- const wrap = trustedLinksApi?.wrap;
11120
- const textTargets = textAddressesFromTarget(payloadTarget);
11121
- const wrapTargets = textTargets.length > 0 ? textTargets : selectionTextAddresses(state.selection);
11122
- if (wrapTargets.length > 0) {
11123
- if (typeof wrap !== "function") return false;
11193
+ if (requestedTextTarget && !collapsedTextAddressFromTarget(requestedTextTarget)) return false;
11194
+ const insert = trustedLinksApi?.insert;
11195
+ const text = readLinkPayloadText(payload, normalizedHref);
11196
+ if (typeof insert !== "function" || text.trim() === "") return false;
11197
+ const target = payloadTarget !== null ? caretTarget : collapsedTextAddressFromSelection(state.selection);
11198
+ if (!target) return false;
11124
11199
  try {
11125
- return applyPerTextAddress("hyperlinks.wrap", wrap, wrapTargets, (target) => ({
11200
+ return callEditorMutation("hyperlinks.insert", insert, {
11126
11201
  target,
11202
+ text,
11127
11203
  link: { destination: { href: normalizedHref } }
11128
- }));
11204
+ });
11129
11205
  } catch {
11130
11206
  return false;
11131
11207
  }
11132
11208
  }
11133
- if (requestedTextTarget && !collapsedTextAddressFromTarget(requestedTextTarget)) return false;
11134
- const insert = trustedLinksApi?.insert;
11135
- const text = readLinkPayloadText(payload, normalizedHref);
11136
- if (typeof insert !== "function" || text.trim() === "") return false;
11137
- const target = collapsedTextAddressFromTarget(payloadTarget) ?? collapsedTextAddressFromSelection(state.selection);
11138
- if (!target) return false;
11209
+ return false;
11210
+ };
11211
+ const executeResolved = (existing) => {
11212
+ const previousSettlement = pendingCommandSettlement;
11213
+ pendingCommandSettlement = null;
11139
11214
  try {
11140
- return callEditorMutation("hyperlinks.insert", insert, {
11141
- target,
11142
- text,
11143
- link: { destination: { href: normalizedHref } }
11144
- });
11145
- } catch {
11146
- return false;
11215
+ return {
11216
+ immediate: executeMutation(existing),
11217
+ settled: pendingCommandSettlement
11218
+ };
11219
+ } finally {
11220
+ pendingCommandSettlement = previousSettlement;
11147
11221
  }
11222
+ };
11223
+ const finish = (existing) => {
11224
+ const result = executeResolved(existing);
11225
+ pendingCommandSettlement = result.settled;
11226
+ return result.immediate;
11227
+ };
11228
+ if (requestedHyperlinkTarget) return finish(null);
11229
+ if (payloadTarget === null) return finish(resolveCurrentHyperlink(doc, linkSelection));
11230
+ const blockIds = selectionBlockIds(linkSelection);
11231
+ if (!canProbeEverySelectedBlock(blockIds) || typeof linksApi.list !== "function") return false;
11232
+ const commandEditor = getEditor(), commandDocSource = commandEditor?.doc;
11233
+ const commandMode = readDocumentMode(), commandContent = contentToken();
11234
+ const story = selectionStoryLocator(linkSelection);
11235
+ try {
11236
+ const reads = blockIds.map((blockId) => linksApi.list({ within: paragraphTarget(blockId, story) }));
11237
+ const validReports = (reports) => reports.every((report) => isLooseObject(report) && Array.isArray(report.items));
11238
+ const existingFrom = (reports) => {
11239
+ for (const report of reports) {
11240
+ const hit = (isLooseObject(report) && Array.isArray(report.items) ? report.items : []).find((item) => hyperlinkOverlapsSelection(item, linkSelection));
11241
+ if (hit) return hit;
11242
+ }
11243
+ return null;
11244
+ };
11245
+ if (!reads.some(isPromiseLike)) return validReports(reads) ? finish(existingFrom(reads)) : false;
11246
+ return Promise.all(reads).then(async (reports) => {
11247
+ if (disposed || getEditor() !== commandEditor || commandEditor?.doc !== commandDocSource || readDocumentMode() !== commandMode || contentToken() !== commandContent || !validReports(reports)) return false;
11248
+ const result = executeResolved(existingFrom(reports));
11249
+ return result.settled ? await result.settled : result.immediate;
11250
+ }, () => false);
11251
+ } catch {
11252
+ return false;
11148
11253
  }
11149
- return false;
11150
11254
  };
11151
11255
  /** Route a create command (`create.table` / `create.image` / `create.tableOfContents`). */
11152
11256
  const executeCreateCommand = (descriptor, op, payload) => {
@@ -11338,9 +11442,20 @@ function createSuperDocUI(options) {
11338
11442
  const record = readLinkPayloadRecord(payload);
11339
11443
  return Boolean(readLinkPayloadTarget(payload) || isLooseObject(record.hyperlinkTarget) || isLooseObject(record.textTarget));
11340
11444
  };
11445
+ const currentWholeTableParagraphSelection = () => {
11446
+ const owner = wholeTableParagraphSeed, host = getHost();
11447
+ if (!owner || disposed || owner.lifetime.aborted || owner.host !== host || owner.editor !== getEditor() || owner.editor.doc !== owner.doc || owner.sourceToken !== contentToken() || owner.mode !== readDocumentMode() || safeCall(() => host?.getMountedHandle?.(), null) !== owner.mount) return false;
11448
+ const tableId = owner.info.tableSelection?.tableId;
11449
+ const activeTable = safeCall(() => host?.getHandles?.().selection?.getWholeTableNodeId?.(), null);
11450
+ if (!tableId || activeTable !== tableId || state.selection.tableSelection?.type !== "table" || state.selection.tableSelection.tableId !== tableId || selectionSignature(state.selection) !== selectionSignature(selectionSliceFromInfo(owner.info, "ready"))) return false;
11451
+ const applied = appliedSelectionPresentation(state.selection);
11452
+ const info = normalizeSelectionInfo(applied?.selection);
11453
+ return !!info && selectionInfoMatchesHost(info) && JSON.stringify(info.tableSelection) === JSON.stringify(owner.info.tableSelection);
11454
+ };
11341
11455
  const commandSelectionIsReady = (id, descriptor, payload) => {
11342
11456
  const foreground = foregroundMutationState();
11343
11457
  if (foreground && (foreground.active > 0 || foreground.pending > 0)) return false;
11458
+ if (descriptor?.blockParagraph && currentWholeTableParagraphSelection()) return true;
11344
11459
  if (state.selection.status !== "ready") return false;
11345
11460
  if (trackDecisionCommand(id)?.scope === "id") return true;
11346
11461
  if (!descriptor || descriptor.disposition !== "routed") return true;
@@ -11354,14 +11469,11 @@ function createSuperDocUI(options) {
11354
11469
  const trackCommand = trackDecisionCommand(id);
11355
11470
  const descriptor = getCommandDescriptor(id);
11356
11471
  if (trackCommand?.scope === "id" && explicitTrackDecisionTarget(payload)) return false;
11472
+ if (descriptor?.link && linkPayloadHasExplicitTarget(payload)) return false;
11357
11473
  if (trackCommand?.scope !== "id" && !descriptorNeedsFreshSelection(descriptor)) return false;
11358
11474
  return !commandSelectionIsReady(id, descriptor, payload);
11359
11475
  };
11360
- const prepareCommandSelectionAsync = (id, payload) => {
11361
- if (!commandNeedsFreshSelection(id, payload)) return null;
11362
- return readSelectionInfoFresh().then(() => void 0);
11363
- };
11364
- const executeCommandInternal = (id, payload, context) => {
11476
+ const executeCommandInternal = (id, payload, context, preparedSelection) => {
11365
11477
  pendingCommandSettlement = null;
11366
11478
  lastCommandSettlement = Promise.resolve(false);
11367
11479
  if (disposed) return false;
@@ -11407,17 +11519,18 @@ function createSuperDocUI(options) {
11407
11519
  const op = resolveDocOperation(doc, route);
11408
11520
  if (!op) return false;
11409
11521
  if (descriptor.inline) {
11410
- const { lockModesById } = computeActiveContentControlLockModes(doc?.contentControls, state.selection);
11522
+ const selection = preparedSelection ?? state.selection;
11523
+ const { lockModesById } = computeActiveContentControlLockModes(doc?.contentControls, selection);
11411
11524
  if (contentControlLockReason(lockModesById)) return false;
11412
- const target = resolveInlineCommandTarget(state.selection);
11525
+ const target = resolveInlineCommandTarget(selection);
11413
11526
  if (!target) {
11414
- if (!canStorePendingInlineFormat(state.selection)) return settleCommandExecution(false);
11415
- return settleCommandExecution(storePendingInlineFormat(descriptor, normalized));
11527
+ if (!canStorePendingInlineFormat(selection)) return settleCommandExecution(false);
11528
+ return settleCommandExecution(storePendingInlineFormat(descriptor, normalized, selection));
11416
11529
  }
11417
- const active = commandActiveState(descriptor, doc, state.selection);
11530
+ const active = commandActiveState(descriptor, doc, selection);
11418
11531
  const input = buildInlineFormatInput(descriptor.inline, target, normalized, active);
11419
11532
  if (!input) return false;
11420
- const commandSelection = state.selection;
11533
+ const commandSelection = selection;
11421
11534
  const tableCellSelection = tableCellSelectionForTarget(target);
11422
11535
  let releaseScheduledMutation = null;
11423
11536
  try {
@@ -11428,12 +11541,12 @@ function createSuperDocUI(options) {
11428
11541
  if (getEditor() !== commandEditor || commandEditor?.doc !== commandDocSource || readDocumentMode() !== commandMode) return false;
11429
11542
  return callInlineFormatMutation(route, op, input, commandSelection, tableCellSelection);
11430
11543
  };
11431
- const selectionSignature = selectionInlineValueSignature(state.selection) ?? selectionKey(state.selection);
11544
+ const selectionSignature = selectionInlineValueSignature(selection) ?? selectionKey(selection);
11432
11545
  const scheduledMutation = descriptor.inline.kind === "toggle" ? scheduleInlineToggleMutation(`${descriptor.id}:${selectionSignature}`, mutate) : null;
11433
11546
  releaseScheduledMutation = scheduledMutation?.release ?? null;
11434
11547
  const mutationResult = scheduledMutation ? scheduledMutation.result : mutate();
11435
- const optimisticGeneration = captureOptimisticInlineToggle(descriptor, state.selection, input, mutationResult);
11436
- const result = decorateInlineCommandResult(descriptor, state.selection, input, mutationResult);
11548
+ const optimisticGeneration = captureOptimisticInlineToggle(descriptor, selection, input, mutationResult);
11549
+ const result = decorateInlineCommandResult(descriptor, selection, input, mutationResult);
11437
11550
  const immediate = settleCommandExecution(result, (settled) => {
11438
11551
  settleOptimisticInlineToggle(descriptor, optimisticGeneration, settled);
11439
11552
  releaseScheduledMutation?.();
@@ -11446,6 +11559,7 @@ function createSuperDocUI(options) {
11446
11559
  return false;
11447
11560
  }
11448
11561
  }
11562
+ if ((descriptor.blockParagraph || descriptor.list) && hasInactiveTextSelection()) return false;
11449
11563
  if (descriptor.blockParagraph) {
11450
11564
  const result = executeBlockParagraph(descriptor, op, normalized);
11451
11565
  const optimisticAlignmentGeneration = descriptor.blockParagraph.kind === "alignment" && commandResultSucceeded(result) ? armOptimisticParagraphAlignment(state.selection, normalized) : null;
@@ -11495,16 +11609,16 @@ function createSuperDocUI(options) {
11495
11609
  return false;
11496
11610
  }
11497
11611
  };
11498
- const runCommand = (id, payload, context, firstParty) => {
11612
+ const runCommand = (id, payload, context, firstParty, preparedSelection) => {
11499
11613
  const actionId = beginInteraction(superdoc, "command:started", () => ({
11500
11614
  commandId: id,
11501
- selection: state.selection
11615
+ selection: preparedSelection ?? state.selection
11502
11616
  }));
11503
11617
  let result;
11504
11618
  const previousCommandDoc = firstPartyCommandDoc;
11505
11619
  firstPartyCommandDoc = firstParty && !customCommands.has(id) ? createFirstPartyCommandDoc() : void 0;
11506
11620
  try {
11507
- result = executeCommandInternal(id, payload, context);
11621
+ result = executeCommandInternal(id, payload, context, preparedSelection);
11508
11622
  } catch (error) {
11509
11623
  recordInteraction(superdoc, "command:settled", () => ({
11510
11624
  actionId,
@@ -11883,20 +11997,45 @@ function createSuperDocUI(options) {
11883
11997
  if (painter.mode === "armed") exitFormatPainter();
11884
11998
  recompute();
11885
11999
  };
11886
- const runCommandAsync = (id, payload, context, firstParty) => {
11887
- const prepared = prepareCommandSelectionAsync(id, payload);
11888
- const settlement = prepared ? prepared.then(() => {
11889
- runCommand(id, payload, context, firstParty);
11890
- return lastCommandSettlement;
11891
- }) : (() => {
11892
- runCommand(id, payload, context, firstParty);
11893
- return lastCommandSettlement;
11894
- })();
12000
+ const runCommandAsync = (id, payload, context, firstParty, requireEnabled = false) => {
12001
+ const editor = getEditor();
12002
+ const doc = editor?.doc;
12003
+ const mode = readDocumentMode();
12004
+ const host = editor?.host;
12005
+ const reader = firstParty && getCommandDescriptor(id)?.inline?.kind === "toggle" && !customCommands.has(id) && host ? firstPartyCommandMutations.get(superdoc)?.get(host)?.selectionReader?.capture() : null;
12006
+ let rawSelection;
12007
+ const needsSelection = !!reader || commandNeedsFreshSelection(id, payload);
12008
+ const execute = () => {
12009
+ const prepared = reader ? reader.read().then((raw) => {
12010
+ rawSelection = raw;
12011
+ const info = reader.isCurrent(raw) ? normalizeSelectionInfo(raw) : null;
12012
+ return selectionSliceFromInfo(info, info ? "ready" : "pending");
12013
+ }, () => selectionSliceFromInfo(null, "pending")) : needsSelection ? readSelectionInfoFresh() : null;
12014
+ return prepared ? prepared.then((selection) => {
12015
+ if (disposed || getEditor() !== editor || editor?.doc !== doc || readDocumentMode() !== mode || selection.status !== "ready" || reader && !reader.isCurrent(rawSelection)) return false;
12016
+ if (requireEnabled) {
12017
+ const command = computeCommandState(id, getDoc(), selection, projectSelectionInlineValues(selection));
12018
+ if (!command.supported || !command.enabled) return false;
12019
+ }
12020
+ runCommand(id, payload, context, firstParty, selection);
12021
+ return lastCommandSettlement;
12022
+ }) : (() => {
12023
+ if (requireEnabled) {
12024
+ const command = computeCommandState(id, getDoc(), state.selection, projectSelectionInlineValues(state.selection));
12025
+ if (!command.supported || !command.enabled) return false;
12026
+ }
12027
+ runCommand(id, payload, context, firstParty);
12028
+ return lastCommandSettlement;
12029
+ })();
12030
+ };
12031
+ const runForeground = host?.runForegroundInteraction;
12032
+ const lifetime = safeCall(() => host?.getForegroundInteractionLifetime?.(), void 0);
12033
+ const settlement = needsSelection && typeof runForeground === "function" ? Promise.resolve(runForeground.call(host, execute, lifetime)) : Promise.resolve(execute());
11895
12034
  if (getCommandDescriptor(id)?.inline?.kind === "toggle") lastInlineToggleCommandSettlement = settlement;
11896
12035
  return settlement;
11897
12036
  };
11898
12037
  const executeCommandAsync = (id, payload, context) => runCommandAsync(id, payload, context, false);
11899
- const executeFirstPartyCommandAsync = (id, payload) => runCommandAsync(id, payload, void 0, true);
12038
+ const executeFirstPartyCommandAsync = (id, payload, requireEnabled = false) => runCommandAsync(id, payload, void 0, true, requireEnabled);
11900
12039
  const makeCommandHandle = (id) => ({
11901
12040
  id,
11902
12041
  getState: () => state.toolbar.commands[id] ?? computeCommandState(id, getDoc(), state.selection, projectSelectionInlineValues(state.selection)),
@@ -13522,6 +13661,7 @@ function createSuperDocUI(options) {
13522
13661
  getContextAsync: async () => {
13523
13662
  for (let attempt = 0; attempt < 2; attempt += 1) {
13524
13663
  const token = selectionReadToken();
13664
+ const editor = getEditor(), doc = editor?.doc, content = contentToken(), mode = readDocumentMode();
13525
13665
  const host = getHost();
13526
13666
  const read = host?.getTableContextAsync;
13527
13667
  if (typeof read !== "function") return publicTableContext(readHostTableContext());
@@ -13538,9 +13678,15 @@ function createSuperDocUI(options) {
13538
13678
  ...EMPTY_TABLE_CONTEXT,
13539
13679
  status: "pending"
13540
13680
  };
13541
- if (token !== selectionReadToken() || host !== getHost()) continue;
13681
+ if (host !== getHost() || editor !== getEditor() || doc !== getEditor()?.doc || content !== contentToken() || mode !== readDocumentMode()) continue;
13542
13682
  const context = publicTableContext(snapshot);
13543
13683
  if (context.status === "stale") continue;
13684
+ if (token !== selectionReadToken()) {
13685
+ const appliedSnapshot = readHostTableContext();
13686
+ if (appliedSnapshot?.status !== "ready") continue;
13687
+ const applied = publicTableContext(appliedSnapshot);
13688
+ if (context.status !== "ready" || applied.status !== "ready" || JSON.stringify(context) !== JSON.stringify(applied)) continue;
13689
+ }
13544
13690
  return context;
13545
13691
  }
13546
13692
  return {
@@ -14605,4 +14751,4 @@ function commandIsActive(descriptor, selection) {
14605
14751
  return descriptor.activeMark ? selection.activeMarks.includes(descriptor.activeMark) : false;
14606
14752
  }
14607
14753
  //#endregion
14608
- export { BUILT_IN_COMMAND_IDS as a, createImageFilePicker as c, DOM_CLASS_NAMES as d, closeInteractionHistory as f, recordInteraction as g, isWorkflowRecordingEnabled as h, registerFirstPartyCommandMutation as i, resolveImageSrc as l, getWorkflowRecording as m, executeFirstPartyCommandAsync as n, applyV2CommentInvalidationFromMutation as o, createInteractionHistory as p, readFirstPartyPageNumbers as r, getV2TrackedChangeMutationImpact as s, createSuperDocUI as t, shallowEqual as u };
14754
+ export { recordInteraction as _, registerFirstPartyCommandMutation as a, getV2TrackedChangeMutationImpact as c, shallowEqual as d, DOM_CLASS_NAMES as f, isWorkflowRecordingEnabled as g, getWorkflowRecording as h, readFirstPartyPageNumbers as i, createImageFilePicker as l, createInteractionHistory as m, executeFirstPartyCommandAsync as n, BUILT_IN_COMMAND_IDS as o, closeInteractionHistory as p, executeFirstPartyShortcutAsync as r, applyV2CommentInvalidationFromMutation as s, createSuperDocUI as t, resolveImageSrc as u };