superdoc 2.24.0-next.1 → 2.24.0-next.11

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 (68) hide show
  1. package/dist/chunks/{create-super-doc-ui-DFNXiKvQ.es.js → create-super-doc-ui-8UTu16Lw.es.js} +229 -82
  2. package/dist/chunks/{create-super-doc-ui-D_FbXpOQ.cjs → create-super-doc-ui-bTFGKVRb.cjs} +229 -82
  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/authorities/authorities.types.d.ts +2 -0
  6. package/dist/document-api/src/bookmarks/bookmarks.types.d.ts +1 -0
  7. package/dist/document-api/src/capabilities/html-markdown-support.d.ts +2 -4
  8. package/dist/document-api/src/captions/captions.types.d.ts +1 -0
  9. package/dist/document-api/src/citations/citations.types.d.ts +2 -0
  10. package/dist/document-api/src/content-controls/content-controls.types.d.ts +1 -0
  11. package/dist/document-api/src/contract/command-catalog.d.ts +1 -1
  12. package/dist/document-api/src/contract/operation-definitions.d.ts +34 -6
  13. package/dist/document-api/src/cross-refs/cross-refs.types.d.ts +1 -0
  14. package/dist/document-api/src/fields/fields.types.d.ts +1 -0
  15. package/dist/document-api/src/footnotes/footnotes.types.d.ts +2 -0
  16. package/dist/document-api/src/get/get.d.ts +2 -1
  17. package/dist/document-api/src/hyperlinks/hyperlinks.types.d.ts +1 -0
  18. package/dist/document-api/src/images/images.types.d.ts +2 -0
  19. package/dist/document-api/src/index/index.types.d.ts +2 -0
  20. package/dist/document-api/src/index.d.ts +15 -12
  21. package/dist/document-api/src/inline-semantics/token-sets.d.ts +11 -0
  22. package/dist/document-api/src/insert/insert.d.ts +7 -1
  23. package/dist/document-api/src/lists/lists.types.d.ts +10 -0
  24. package/dist/document-api/src/paragraphs/paragraphs.types.d.ts +3 -0
  25. package/dist/document-api/src/receipt-bridge.d.ts +1 -0
  26. package/dist/document-api/src/replace/replace.d.ts +31 -4
  27. package/dist/document-api/src/sections/sections.types.d.ts +4 -1
  28. package/dist/document-api/src/selection/selection.d.ts +9 -2
  29. package/dist/document-api/src/selection/selection.types.d.ts +57 -8
  30. package/dist/document-api/src/styles/apply.d.ts +5 -0
  31. package/dist/document-api/src/styles/catalog.d.ts +40 -19
  32. package/dist/document-api/src/styles/create.d.ts +160 -0
  33. package/dist/document-api/src/styles/index.d.ts +8 -4
  34. package/dist/document-api/src/styles/registry.d.ts +34 -2
  35. package/dist/document-api/src/styles/schema.d.ts +24 -5
  36. package/dist/document-api/src/styles/theme.d.ts +32 -0
  37. package/dist/document-api/src/styles/validation.d.ts +11 -2
  38. package/dist/document-api/src/templates/apply.d.ts +13 -15
  39. package/dist/document-api/src/toc/toc.types.d.ts +3 -0
  40. package/dist/document-api/src/types/blocks.types.d.ts +6 -1
  41. package/dist/document-api/src/types/create.types.d.ts +3 -1
  42. package/dist/document-api/src/types/fragment.d.ts +3 -0
  43. package/dist/document-api/src/types/mutation-plan.types.d.ts +4 -0
  44. package/dist/document-api/src/types/query-match.types.d.ts +43 -5
  45. package/dist/document-api/src/types/receipt.d.ts +8 -0
  46. package/dist/document-api/src/types/sd-contract.d.ts +2 -0
  47. package/dist/document-api/src/types/sd-props.d.ts +54 -14
  48. package/dist/document-api/src/types/sd-styles.d.ts +58 -3
  49. package/dist/document-api/src/types/table-operations.types.d.ts +2 -0
  50. package/dist/document-api/src/write/write.d.ts +4 -2
  51. package/dist/public/ui-react.cjs +1 -1
  52. package/dist/public/ui-react.es.js +1 -1
  53. package/dist/public/ui-vue.cjs +1 -1
  54. package/dist/public/ui-vue.es.js +1 -1
  55. package/dist/public/ui.cjs +1 -1
  56. package/dist/public/ui.es.js +1 -1
  57. package/dist/style.css +87 -87
  58. package/dist/style.layered.css +87 -87
  59. package/dist/superdoc/src/core/editor-runtime/v2/v2-session-shortcut-routes.d.ts +2 -0
  60. package/dist/superdoc/src/public/ui/types.d.ts +22 -0
  61. package/dist/superdoc/src/public/ui.d.cts +4 -0
  62. package/dist/superdoc/src/public/ui.d.ts +1 -1
  63. package/dist/superdoc.cjs +110 -49
  64. package/dist/superdoc.es.js +110 -49
  65. package/dist-cdn/style.layered.css +1 -1
  66. package/dist-cdn/superdoc.min.css +1 -1
  67. package/dist-cdn/superdoc.min.js +42 -42
  68. package/package.json +2 -2
@@ -5938,6 +5938,7 @@ function buildTableCommandInput(action, context) {
5938
5938
  };
5939
5939
  case "delete-table": return { nodeId };
5940
5940
  case "merge-cells": {
5941
+ if (!context.canMerge) return null;
5941
5942
  const range = mergeRange ?? {
5942
5943
  start: {
5943
5944
  rowIndex,
@@ -6073,6 +6074,18 @@ function createSuperDocUI(options) {
6073
6074
  const host = getEditor()?.host;
6074
6075
  return host && typeof host === "object" ? host : null;
6075
6076
  };
6077
+ /**
6078
+ * The host selection snapshot a selection read, slice, or capture was
6079
+ * resolved from. The host replaces its snapshot object on every selection
6080
+ * change, so an identical object means the selection has not moved since.
6081
+ */
6082
+ const selectionHostSnapshots = /* @__PURE__ */ new WeakMap();
6083
+ const readHostSelectionSnapshot = () => {
6084
+ const selectionHandle = (safeCall(() => getHost()?.getHandles?.() ?? null, null)?.editing)?.selection;
6085
+ if (typeof selectionHandle?.getSnapshot !== "function") return null;
6086
+ const snapshot = safeCall(() => selectionHandle.getSnapshot(), null);
6087
+ return snapshot && typeof snapshot === "object" ? snapshot : null;
6088
+ };
6076
6089
  const createFirstPartyCommandDoc = () => {
6077
6090
  const editor = getEditor();
6078
6091
  const doc = editor?.doc;
@@ -6244,7 +6257,7 @@ function createSuperDocUI(options) {
6244
6257
  };
6245
6258
  const resolveTableContext = () => {
6246
6259
  const snapshot = readHostTableContext();
6247
- if (!snapshot || snapshot.inTable !== true) return null;
6260
+ if (!snapshot || snapshot.inTable !== true || snapshot.status && snapshot.status !== "ready") return null;
6248
6261
  const table = snapshot.table;
6249
6262
  const tableNodeId = typeof table?.nodeId === "string" && table.nodeId.length > 0 ? table.nodeId : null;
6250
6263
  if (!tableNodeId) return null;
@@ -6256,12 +6269,15 @@ function createSuperDocUI(options) {
6256
6269
  if (rowIndex == null || columnIndex == null) return null;
6257
6270
  const cellNodeId = typeof cell?.nodeId === "string" && cell.nodeId.length > 0 ? cell.nodeId : null;
6258
6271
  const range = snapshot.cellRange;
6272
+ const mergeRange = readMergeRange(range);
6273
+ const metadata = snapshot.tableSelection;
6259
6274
  return {
6260
6275
  tableNodeId,
6261
6276
  rowIndex,
6262
6277
  columnIndex,
6263
6278
  cellNodeId,
6264
- mergeRange: readMergeRange(range)
6279
+ mergeRange,
6280
+ canMerge: metadata == null || metadata.selectionMode === "cells" && metadata.isRectangular === true
6265
6281
  };
6266
6282
  };
6267
6283
  const customCommands = /* @__PURE__ */ new Map();
@@ -7277,12 +7293,21 @@ function createSuperDocUI(options) {
7277
7293
  ...EMPTY_SELECTION,
7278
7294
  status
7279
7295
  };
7296
+ const slice = buildSelectionSlice(info, status);
7297
+ const hostSelection = selectionHostSnapshots.get(info);
7298
+ if (hostSelection) selectionHostSnapshots.set(slice, hostSelection);
7299
+ return slice;
7300
+ };
7301
+ const buildSelectionSlice = (info, status) => {
7280
7302
  const target = info.target ?? null;
7281
7303
  const quotedText = typeof info.text === "string" ? info.text : "";
7282
7304
  return {
7283
7305
  status,
7284
7306
  empty: info.empty === true,
7285
7307
  target,
7308
+ trackedTarget: info.trackedTarget ?? null,
7309
+ trackedChangeRangeDecisionSupported: info.trackedChangeRangeDecisionSupported,
7310
+ trackedChangesWhollySelected: info.trackedChangesWhollySelected,
7286
7311
  selectionTarget: info.selectionTarget ?? null,
7287
7312
  tableSelection: info.tableSelection ?? null,
7288
7313
  activeMarks: Array.isArray(info.activeMarks) ? info.activeMarks : [],
@@ -7291,18 +7316,22 @@ function createSuperDocUI(options) {
7291
7316
  quotedText
7292
7317
  };
7293
7318
  };
7294
- const readSelectionInfoLive = () => {
7295
- const selectionApi = getDoc()?.selection;
7296
- return readAsync("selection", selectionReadToken(), () => selectionApi?.current ? selectionApi.current({ includeText: true }) : void 0, normalizeSelectionInfo);
7297
- };
7319
+ const readSelectionInfoLive = () => readAsync("selection", selectionReadToken(), selectionCurrentRun, normalizeSelectionInfo);
7298
7320
  const computeSelection = () => {
7299
7321
  const { value: info, status } = readSelectionInfoLive();
7300
7322
  return selectionSliceFromInfo(info, status);
7301
7323
  };
7302
- const selectionCurrentRun = () => {
7324
+ function selectionCurrentRun() {
7303
7325
  const selectionApi = getDoc()?.selection;
7304
- return selectionApi?.current ? selectionApi.current({ includeText: true }) : void 0;
7305
- };
7326
+ if (!selectionApi?.current) return void 0;
7327
+ const hostSelection = readHostSelectionSnapshot();
7328
+ const tag = (info) => {
7329
+ if (hostSelection && info && typeof info === "object") selectionHostSnapshots.set(info, hostSelection);
7330
+ return info;
7331
+ };
7332
+ const result = selectionApi.current({ includeText: true });
7333
+ return isPromiseLike(result) ? Promise.resolve(result).then(tag) : tag(result);
7334
+ }
7306
7335
  /** True when a host sync snapshot is a resolved collapsed caret (not a range). */
7307
7336
  const isCollapsedCaretSnapshot = (info) => {
7308
7337
  if (info.empty !== true) return false;
@@ -8282,6 +8311,10 @@ function createSuperDocUI(options) {
8282
8311
  if (!supported) return command.scope === "all" ? SUPERDOC_UI_REASONS.bulkDecisionsDisabled : unavailableRouteReason(doc);
8283
8312
  if (disabledReason) return disabledReason;
8284
8313
  if (command.scope !== "all" && selection.activeChangeIds.length === 0) return SUPERDOC_UI_REASONS.selectionRequired;
8314
+ if (command.scope !== "all") {
8315
+ const plan = planSelectionTrackDecision(selection, true);
8316
+ if (plan?.kind === "blocked") return plan.reason;
8317
+ }
8285
8318
  };
8286
8319
  const findTrackChangeForPermission = (id) => {
8287
8320
  syncCoordinatorEditor();
@@ -8301,20 +8334,22 @@ function createSuperDocUI(options) {
8301
8334
  if (typeof canPerform !== "function" || ids.length === 0) return void 0;
8302
8335
  const currentUser = superdoc.config && typeof superdoc.config === "object" ? superdoc.config.user : null;
8303
8336
  for (const id of ids) {
8304
- const trackedChange = findTrackChangeForPermission(id) ?? { id };
8305
- const isOwn = trackedChangeOwnedByCurrentUser(trackedChange, currentUser);
8306
- const permission = kind === "accept" ? isOwn ? "RESOLVE_OWN" : "RESOLVE_OTHER" : isOwn ? "REJECT_OWN" : "REJECT_OTHER";
8307
- let allowed;
8308
- try {
8309
- allowed = canPerform.call(superdoc, {
8310
- permission,
8311
- trackedChange,
8312
- comment: null
8313
- });
8314
- } catch {
8315
- return;
8337
+ const trackedChange = findTrackChangeForPermission(id);
8338
+ const isOwn = trackedChange ? trackedChangeOwnedByCurrentUser(trackedChange, currentUser) : false;
8339
+ const permissions = trackedChange ? [kind === "accept" ? isOwn ? "RESOLVE_OWN" : "RESOLVE_OTHER" : isOwn ? "REJECT_OWN" : "REJECT_OTHER"] : kind === "accept" ? ["RESOLVE_OWN", "RESOLVE_OTHER"] : ["REJECT_OWN", "REJECT_OTHER"];
8340
+ for (const candidate of permissions) {
8341
+ let allowed;
8342
+ try {
8343
+ allowed = canPerform.call(superdoc, {
8344
+ permission: candidate,
8345
+ trackedChange: trackedChange ?? { id },
8346
+ comment: null
8347
+ });
8348
+ } catch {
8349
+ return trackedChange ? void 0 : SUPERDOC_UI_REASONS.permissionDenied;
8350
+ }
8351
+ if (allowed === false || !trackedChange && allowed !== true) return SUPERDOC_UI_REASONS.permissionDenied;
8316
8352
  }
8317
- if (allowed === false) return SUPERDOC_UI_REASONS.permissionDenied;
8318
8353
  }
8319
8354
  };
8320
8355
  const hostCommandSupport = (command) => {
@@ -9155,7 +9190,7 @@ function createSuperDocUI(options) {
9155
9190
  reason: SUPERDOC_UI_REASONS.documentReadonly
9156
9191
  }, "builtin");
9157
9192
  const context = resolveTableContext();
9158
- if (!(context != null && (!descriptor.table.requiresCell || context.cellNodeId != null))) return normalizeCommandState({
9193
+ if (!(context != null && (!descriptor.table.requiresCell || context.cellNodeId != null) && (descriptor.table.action !== "merge-cells" || context.canMerge))) return normalizeCommandState({
9159
9194
  enabled: false,
9160
9195
  active: false,
9161
9196
  supported: true,
@@ -9994,14 +10029,22 @@ function createSuperDocUI(options) {
9994
10029
  * ordinary/insertion selections are visible-space, while a selection that
9995
10030
  * reaches painted deleted text is projected in tracked space by the host.
9996
10031
  *
9997
- * A cross-block or malformed selection fails closed. Its intermediate block
9998
- * coverage cannot be reconstructed from two endpoints without guessing.
10032
+ * Cross-block coverage must come from the model-backed tracked target;
10033
+ * intermediate block lengths cannot be inferred from the endpoints.
9999
10034
  */
10000
10035
  function trackDecisionRangeFromSelection(selection) {
10001
10036
  if (selection.empty) return null;
10002
10037
  const explicit = selection.selectionTarget;
10003
10038
  const start = explicit?.start;
10004
10039
  const end = explicit?.end;
10040
+ if (start?.kind === "text" && end?.kind === "text" && start.blockId !== end.blockId) {
10041
+ const range = selection.trackedTarget;
10042
+ return range ? {
10043
+ kind: "range",
10044
+ coordinateSpace: "tracked",
10045
+ range
10046
+ } : null;
10047
+ }
10005
10048
  if (explicit?.kind !== "selection" || start?.kind !== "text" || end?.kind !== "text" || typeof start.blockId !== "string" || start.blockId !== end.blockId || !Number.isInteger(start.offset) || !Number.isInteger(end.offset) || start.offset < 0 || end.offset < 0 || start.offset === end.offset) return null;
10006
10049
  const rangeStart = Math.min(start.offset, end.offset);
10007
10050
  const rangeEnd = Math.max(start.offset, end.offset);
@@ -10046,32 +10089,20 @@ function createSuperDocUI(options) {
10046
10089
  function normalizeTrackDecisionText(value) {
10047
10090
  return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
10048
10091
  }
10049
- function activeChangePartialDecisionRoute() {
10050
- const activeId = state.selection.activeChangeIds[0];
10092
+ function activeChangePartialDecisionRoute(selection = state.selection) {
10093
+ const activeId = selection.activeChangeIds[0];
10051
10094
  if (!activeId) return "unavailable";
10052
- const story = selectionStoryLocator(state.selection);
10053
- const item = (state.trackChanges.status === "ready" ? state.trackChanges.items.find((candidate) => entityRowMatchesRequest(candidate, activeId, story)) : void 0) ?? readBodyTrackChangeForDecision(activeId, story) ?? readAllStoryTrackChanges()?.find((candidate) => entityRowMatchesRequest(candidate, activeId, story));
10095
+ const story = selectionStoryLocator(selection);
10096
+ const trackChanges = state?.trackChanges;
10097
+ const item = (trackChanges?.status === "ready" ? trackChanges.items.find((candidate) => entityRowMatchesRequest(candidate, activeId, story)) : void 0) ?? readBodyTrackChangeForDecision(activeId, story) ?? readAllStoryTrackChanges()?.find((candidate) => entityRowMatchesRequest(candidate, activeId, story));
10054
10098
  if (!item) return "unavailable";
10055
10099
  const change = trackChangesItemPayload(item);
10056
10100
  const type = change.type;
10057
10101
  const grouping = change.grouping;
10058
- if (selectedTextCoversTrackChangeText(state.selection, item)) return "id";
10102
+ if (selectedTextCoversTrackChangeText(selection, item)) return "id";
10059
10103
  if (type === "replacement" && grouping === "replacement-pair") return "range";
10060
10104
  return (type === "insertion" || type === "deletion" || type === "insert" || type === "delete") && grouping === "standalone" ? "range" : "id";
10061
10105
  }
10062
- function executeTrackDecisionRangeWithIdFallback(kind, rangeTarget, changeId, story) {
10063
- const fallback = () => callTrackDecisionTarget(kind, trackDecisionIdTarget(changeId, story), changeId, story);
10064
- try {
10065
- const result = callTrackDecisionTarget(kind, rangeTarget, changeId, story);
10066
- if (isPromiseLike(result)) return settleCommandExecution(Promise.resolve(result).then((settled) => {
10067
- return commandResultSucceeded(commandResultFromOperationResult(settled)) ? settled : fallback();
10068
- }));
10069
- const commandResult = commandResultFromOperationResult(result);
10070
- return settleCommandExecution(commandResultSucceeded(commandResult) ? result : fallback());
10071
- } catch {
10072
- return false;
10073
- }
10074
- }
10075
10106
  /** A decision target carried by the payload itself, independent of the selection. */
10076
10107
  function explicitTrackDecisionTarget(payload) {
10077
10108
  if (typeof payload === "string" && payload.length > 0) return {
@@ -10115,29 +10146,88 @@ function createSuperDocUI(options) {
10115
10146
  story
10116
10147
  } : null;
10117
10148
  }
10149
+ /**
10150
+ * The selection-derived decision for a review command. The command gate and
10151
+ * execution share these rules so an enabled command always has a target.
10152
+ * `null` means the selection does not drive the decision (no active change
10153
+ * or a collapsed caret) and the caller falls back to the active change id.
10154
+ * `gateOnly` stops after the blocking rules and returns only a block or null.
10155
+ */
10156
+ function planSelectionTrackDecision(selection, gateOnly = false) {
10157
+ if (selection.tableSelection?.kind === "tableCells" && (selection.tableSelection.type === "cells" || selection.tableSelection.type === "table")) return {
10158
+ kind: "blocked",
10159
+ reason: SUPERDOC_UI_REASONS.operationUnavailable
10160
+ };
10161
+ const ids = selection.activeChangeIds;
10162
+ if (ids.length > 1) {
10163
+ if (selection.empty || selection.trackedChangesWhollySelected === true) return {
10164
+ kind: "ids",
10165
+ ids
10166
+ };
10167
+ if (selection.trackedChangeRangeDecisionSupported === false) return {
10168
+ kind: "blocked",
10169
+ reason: SUPERDOC_UI_REASONS.operationUnavailable
10170
+ };
10171
+ const target = trackDecisionRangeFromSelection(selection);
10172
+ return target ? {
10173
+ kind: "range",
10174
+ target,
10175
+ changeId: null,
10176
+ permissionIds: ids
10177
+ } : {
10178
+ kind: "blocked",
10179
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
10180
+ };
10181
+ }
10182
+ if (ids.length !== 1 || selection.empty) return null;
10183
+ const activeId = ids[0];
10184
+ let routeValue;
10185
+ const route = () => routeValue ??= selection.trackedChangesWhollySelected === true ? "id" : activeChangePartialDecisionRoute(selection);
10186
+ if (selection.trackedChangeRangeDecisionSupported === false && route() !== "id") return {
10187
+ kind: "blocked",
10188
+ reason: SUPERDOC_UI_REASONS.operationUnavailable
10189
+ };
10190
+ if (!selection.selectionTarget) return null;
10191
+ const target = trackDecisionRangeFromSelection(selection);
10192
+ if (!target && route() !== "id") return {
10193
+ kind: "blocked",
10194
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
10195
+ };
10196
+ if (gateOnly) return null;
10197
+ if (route() === "id") {
10198
+ const selectedStory = selectionStoryLocator(selection);
10199
+ return {
10200
+ kind: "id",
10201
+ id: activeId,
10202
+ story: selectedStory && storyLocatorSignature(selectedStory) !== storyLocatorSignature(null) ? selectedStory : void 0
10203
+ };
10204
+ }
10205
+ if (route() === "unavailable" && !selection.trackedTarget) return { kind: "unavailable" };
10206
+ return target ? {
10207
+ kind: "range",
10208
+ target,
10209
+ changeId: activeId,
10210
+ story: nonBodySelectionStoryLocator(selection)
10211
+ } : {
10212
+ kind: "blocked",
10213
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
10214
+ };
10215
+ }
10118
10216
  function executeTrackDecisionCommand(command, payload) {
10119
10217
  if (command.scope === "id" && payload == null) {
10120
- if (state.selection.activeChangeIds.length > 1) return executeTrackDecisionTarget(command.kind, {
10121
- kind: "ids",
10122
- ids: state.selection.activeChangeIds
10123
- }, null);
10124
- if (state.selection.activeChangeIds.length === 1 && !state.selection.empty && state.selection.selectionTarget) {
10125
- const route = activeChangePartialDecisionRoute();
10126
- if (route === "unavailable") return false;
10127
- if (route === "range") {
10128
- const target = trackDecisionRangeFromSelection(state.selection);
10129
- const story = nonBodySelectionStoryLocator(state.selection);
10130
- return target ? executeTrackDecisionRangeWithIdFallback(command.kind, target, state.selection.activeChangeIds[0], story) : false;
10131
- }
10132
- if (route === "id") {
10133
- const activeId = state.selection.activeChangeIds[0];
10134
- const selectedStory = selectionStoryLocator(state.selection);
10135
- const story = selectedStory && storyLocatorSignature(selectedStory) !== storyLocatorSignature(null) ? selectedStory : void 0;
10136
- return executeTrackDecision(command.kind, story ? {
10137
- id: activeId,
10138
- story
10139
- } : activeId);
10140
- }
10218
+ const plan = planSelectionTrackDecision(state.selection);
10219
+ switch (plan?.kind) {
10220
+ case "blocked":
10221
+ case "unavailable": return false;
10222
+ case "ids": return executeTrackDecisionTarget(command.kind, {
10223
+ kind: "ids",
10224
+ ids: plan.ids
10225
+ }, null, void 0, plan.ids);
10226
+ case "range": return executeTrackDecisionTarget(command.kind, plan.target, plan.changeId, plan.story, plan.permissionIds);
10227
+ case "id": return executeTrackDecision(command.kind, plan.story ? {
10228
+ id: plan.id,
10229
+ story: plan.story
10230
+ } : plan.id);
10141
10231
  }
10142
10232
  }
10143
10233
  const resolved = resolveTrackDecisionTarget(command, payload);
@@ -11806,11 +11896,14 @@ function createSuperDocUI(options) {
11806
11896
  if (snapshot.empty) return null;
11807
11897
  const cells = currentCellSelectionTarget();
11808
11898
  if (!snapshot.target && !snapshot.selectionTarget && !cells) return null;
11809
- return {
11899
+ const capture = {
11810
11900
  ...snapshot,
11811
11901
  capturedAt: Date.now(),
11812
11902
  ...cells ? { _tableCellSelectionTarget: cells } : {}
11813
11903
  };
11904
+ const hostSelection = selectionHostSnapshots.get(snapshot);
11905
+ if (hostSelection) selectionHostSnapshots.set(capture, hostSelection);
11906
+ return capture;
11814
11907
  },
11815
11908
  restore: (capture) => {
11816
11909
  const done = (result) => {
@@ -11839,6 +11932,8 @@ function createSuperDocUI(options) {
11839
11932
  ok: false,
11840
11933
  reason: SUPERDOC_UI_REASONS.targetUnresolved
11841
11934
  });
11935
+ const heldHostSelection = selectionHostSnapshots.get(capture);
11936
+ if (!hasCellTarget && target.coordinateSpace === "tracked" && heldHostSelection !== void 0 && readHostSelectionSnapshot() === heldHostSelection) return done({ ok: true });
11842
11937
  const resolved = getSelectionApplyHelper();
11843
11938
  if (!("helper" in resolved)) return done({
11844
11939
  ok: false,
@@ -12675,20 +12770,20 @@ function createSuperDocUI(options) {
12675
12770
  if (run() === false) return Promise.resolve(false);
12676
12771
  return lastCommandSettlement;
12677
12772
  };
12678
- const executeTrackDecisionTarget = (kind, target, changeId, story) => {
12773
+ const executeTrackDecisionTarget = (kind, target, changeId, story, selectionChangeIds) => {
12679
12774
  try {
12680
- return settleCommandExecution(callTrackDecisionTarget(kind, target, changeId, story));
12775
+ return settleCommandExecution(callTrackDecisionTarget(kind, target, changeId, story, selectionChangeIds));
12681
12776
  } catch {
12682
12777
  return false;
12683
12778
  }
12684
12779
  };
12685
- const callTrackDecisionTarget = (kind, target, changeId, story) => {
12780
+ const callTrackDecisionTarget = (kind, target, changeId, story, selectionChangeIds) => {
12686
12781
  if (trackedChangeDecisionsAreDisabled()) return false;
12687
12782
  const tcApi = getDoc()?.trackChanges;
12688
12783
  const isAllTarget = target.kind === "all" || target.scope === "all";
12689
12784
  const isRangeTarget = target.kind === "range";
12690
12785
  const isMultiIdTarget = target.kind === "ids";
12691
- const permissionIds = isAllTarget ? [] : isMultiIdTarget && Array.isArray(target.ids) ? target.ids.filter((id) => typeof id === "string" && id.length > 0) : changeId ? [changeId] : [];
12786
+ const permissionIds = isAllTarget ? [] : selectionChangeIds ?? (isMultiIdTarget && Array.isArray(target.ids) ? target.ids.filter((id) => typeof id === "string" && id.length > 0) : changeId ? [changeId] : []);
12692
12787
  if (permissionIds.length > 0 && trackedChangeDecisionPermissionReason(kind, permissionIds)) return false;
12693
12788
  if (bulkTrackDecisionBlockedReason({
12694
12789
  kind,
@@ -13060,7 +13155,13 @@ function createSuperDocUI(options) {
13060
13155
  return {
13061
13156
  point,
13062
13157
  entities: validInput ? entityAt(point) : [],
13063
- selection: state.selection,
13158
+ selection: (() => {
13159
+ const current = computeSelection();
13160
+ return current.status === "ready" ? current : {
13161
+ ...current,
13162
+ tableSelection: null
13163
+ };
13164
+ })(),
13064
13165
  position: null,
13065
13166
  insideSelection: false
13066
13167
  };
@@ -13277,21 +13378,67 @@ function createSuperDocUI(options) {
13277
13378
  columnIndex: null,
13278
13379
  cellNodeId: null,
13279
13380
  rows: null,
13280
- columns: null
13381
+ columns: null,
13382
+ status: "ready",
13383
+ cellRange: null,
13384
+ tableSelection: null
13385
+ };
13386
+ const publicTableContext = (snapshot) => {
13387
+ const status = snapshot?.status === "pending" || snapshot?.status === "stale" ? snapshot.status : "ready";
13388
+ if (!snapshot || snapshot.inTable !== true || status !== "ready") return {
13389
+ ...EMPTY_TABLE_CONTEXT,
13390
+ status
13391
+ };
13392
+ const table = snapshot.table;
13393
+ const row = snapshot.row;
13394
+ const column = snapshot.column;
13395
+ const cell = snapshot.cell;
13396
+ if (typeof table?.nodeId !== "string") return {
13397
+ ...EMPTY_TABLE_CONTEXT,
13398
+ status
13399
+ };
13400
+ return {
13401
+ inTable: true,
13402
+ status,
13403
+ tableNodeId: table.nodeId,
13404
+ rowIndex: typeof row?.index === "number" ? row.index : null,
13405
+ columnIndex: typeof column?.index === "number" ? column.index : null,
13406
+ cellNodeId: typeof cell?.nodeId === "string" ? cell.nodeId : null,
13407
+ rows: typeof table.rows === "number" ? table.rows : null,
13408
+ columns: typeof table.columns === "number" ? table.columns : null,
13409
+ cellRange: readMergeRange(snapshot.cellRange),
13410
+ tableSelection: snapshot.tableSelection ?? null
13411
+ };
13281
13412
  };
13282
13413
  const tables = {
13283
- getContext: () => {
13284
- const context = resolveTableContext();
13285
- if (!context) return { ...EMPTY_TABLE_CONTEXT };
13286
- const table = readHostTableContext()?.table;
13414
+ getContext: () => publicTableContext(readHostTableContext()),
13415
+ getContextAsync: async () => {
13416
+ for (let attempt = 0; attempt < 2; attempt += 1) {
13417
+ const token = selectionReadToken();
13418
+ const host = getHost();
13419
+ const read = host?.getTableContextAsync;
13420
+ if (typeof read !== "function") return publicTableContext(readHostTableContext());
13421
+ let snapshot = null;
13422
+ try {
13423
+ snapshot = await read.call(host);
13424
+ } catch {
13425
+ return {
13426
+ ...EMPTY_TABLE_CONTEXT,
13427
+ status: "pending"
13428
+ };
13429
+ }
13430
+ if (disposed) return {
13431
+ ...EMPTY_TABLE_CONTEXT,
13432
+ status: "pending"
13433
+ };
13434
+ if (token !== selectionReadToken() || host !== getHost()) continue;
13435
+ const context = publicTableContext(snapshot);
13436
+ if (context.status === "stale") continue;
13437
+ return context;
13438
+ }
13287
13439
  return {
13288
- inTable: true,
13289
- tableNodeId: context.tableNodeId,
13290
- rowIndex: context.rowIndex,
13291
- columnIndex: context.columnIndex,
13292
- cellNodeId: context.cellNodeId,
13293
- rows: typeof table?.rows === "number" ? table.rows : null,
13294
- columns: typeof table?.columns === "number" ? table.columns : null
13440
+ ...EMPTY_TABLE_CONTEXT,
13441
+ status: "stale"
13295
13442
  };
13296
13443
  },
13297
13444
  isInTable: () => resolveTableContext() != null