superdoc 2.25.0-next.16 → 2.25.0-next.18

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