superdoc 2.22.0-next.8 → 2.22.0

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 (43) hide show
  1. package/dist/chunks/{create-super-doc-ui-Dos1KQcL.es.js → create-super-doc-ui-DmGX1w-i.es.js} +165 -48
  2. package/dist/chunks/{create-super-doc-ui-DTLJMJIl.cjs → create-super-doc-ui-MSiU8Kg0.cjs} +165 -48
  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/content-controls/content-controls.types.d.ts +2 -0
  6. package/dist/document-api/src/contract/operation-definitions.d.ts +2 -2
  7. package/dist/layout-engine/contracts/src/index.d.ts +17 -7
  8. package/dist/layout-engine/contracts/src/justify-utils.d.ts +5 -2
  9. package/dist/layout-engine/layout-bridge/src/incrementalLayout.d.ts +30 -4
  10. package/dist/layout-engine/layout-bridge/src/index.d.ts +3 -3
  11. package/dist/layout-engine/layout-bridge/src/layoutHeaderFooter.d.ts +4 -0
  12. package/dist/layout-engine/layout-engine/src/anchors.d.ts +4 -4
  13. package/dist/layout-engine/layout-engine/src/column-balancing.d.ts +2 -2
  14. package/dist/layout-engine/layout-engine/src/execution.d.ts +1 -1
  15. package/dist/layout-engine/layout-engine/src/index.d.ts +28 -1
  16. package/dist/layout-engine/layout-engine/src/inline-drawing-alignment.d.ts +4 -1
  17. package/dist/layout-engine/layout-engine/src/layout-paragraph.d.ts +9 -1
  18. package/dist/layout-engine/layout-engine/src/layout-utils.d.ts +5 -1
  19. package/dist/layout-engine/layout-engine/src/paragraph-continuation.d.ts +6 -0
  20. package/dist/layout-engine/layout-engine/src/section-props.d.ts +1 -1
  21. package/dist/layout-engine/layout-engine/src/source-preparation.d.ts +52 -0
  22. package/dist/layout-engine/painters/dom/src/page-content.d.ts +6 -0
  23. package/dist/layout-engine/painters/dom/src/paragraph/renderParagraphContent.d.ts +3 -1
  24. package/dist/layout-engine/painters/dom/src/paragraph/renderParagraphFragment.d.ts +3 -1
  25. package/dist/layout-engine/painters/dom/src/persistent-page-surface.d.ts +5 -4
  26. package/dist/layout-engine/painters/dom/src/pm-position-validation.d.ts +1 -0
  27. package/dist/layout-engine/painters/dom/src/render-work.d.ts +3 -0
  28. package/dist/layout-engine/painters/dom/src/renderer.d.ts +28 -5
  29. package/dist/layout-engine/painters/dom/src/runs/render-line.d.ts +3 -1
  30. package/dist/layout-engine/painters/dom/src/runs/tab-run.d.ts +1 -1
  31. package/dist/layout-engine/painters/dom/src/table/renderTableCell.d.ts +3 -1
  32. package/dist/layout-engine/painters/dom/src/table/renderTableFragment.d.ts +3 -1
  33. package/dist/layout-engine/painters/dom/src/table/renderTableRow.d.ts +3 -1
  34. package/dist/public/ui-react.cjs +1 -1
  35. package/dist/public/ui-react.es.js +1 -1
  36. package/dist/public/ui-vue.cjs +1 -1
  37. package/dist/public/ui-vue.es.js +1 -1
  38. package/dist/public/ui.cjs +1 -1
  39. package/dist/public/ui.es.js +1 -1
  40. package/dist/superdoc.cjs +3 -3
  41. package/dist/superdoc.es.js +3 -3
  42. package/dist-cdn/superdoc.min.js +40 -40
  43. package/package.json +2 -2
@@ -3993,6 +3993,65 @@ var INCREMENTAL_DEPENDENCY_CERTIFICATE_CLASSES = Object.freeze([
3993
3993
  ]);
3994
3994
  new Set(INCREMENTAL_DEPENDENCY_CERTIFICATE_CLASSES);
3995
3995
  //#endregion
3996
+ //#region ../document-api/src/types/story.types.ts
3997
+ /** All recognized story types. */
3998
+ var STORY_TYPES = [
3999
+ "body",
4000
+ "headerFooterSlot",
4001
+ "headerFooterPart",
4002
+ "footnote",
4003
+ "endnote",
4004
+ "textbox"
4005
+ ];
4006
+ /** Valid header/footer story kinds. */
4007
+ var STORY_HEADER_FOOTER_KINDS = ["header", "footer"];
4008
+ /** Valid header/footer slot variants. */
4009
+ var STORY_HEADER_FOOTER_VARIANTS = [
4010
+ "default",
4011
+ "first",
4012
+ "even"
4013
+ ];
4014
+ /** Valid header/footer slot resolution modes. */
4015
+ var STORY_HEADER_FOOTER_RESOLUTIONS = ["effective", "explicit"];
4016
+ /** Valid header/footer slot write modes. */
4017
+ var STORY_HEADER_FOOTER_ON_WRITE_VALUES = [
4018
+ "materializeIfInherited",
4019
+ "editResolvedPart",
4020
+ "error"
4021
+ ];
4022
+ /**
4023
+ * Type guard: returns `true` if `value` is a valid {@link StoryLocator}.
4024
+ *
4025
+ * Checks the full discriminated-union shape so malformed partial locators do
4026
+ * not leak through validation and fail later with raw property-access errors.
4027
+ */
4028
+ function isStoryLocator(value) {
4029
+ if (!isObjectRecord(value) || value.kind !== "story" || !isStringEnumMember(value.storyType, STORY_TYPES)) return false;
4030
+ switch (value.storyType) {
4031
+ case "body": return true;
4032
+ case "headerFooterSlot": return isSectionAddress(value.section) && isStringEnumMember(value.headerFooterKind, STORY_HEADER_FOOTER_KINDS) && isStringEnumMember(value.variant, STORY_HEADER_FOOTER_VARIANTS) && isOptionalStringEnumMember(value.resolution, STORY_HEADER_FOOTER_RESOLUTIONS) && isOptionalStringEnumMember(value.onWrite, STORY_HEADER_FOOTER_ON_WRITE_VALUES);
4033
+ case "headerFooterPart": return isNonEmptyString(value.refId);
4034
+ case "footnote":
4035
+ case "endnote": return isNonEmptyString(value.noteId);
4036
+ case "textbox": return isNonEmptyString(value.textboxId);
4037
+ }
4038
+ }
4039
+ function isObjectRecord(value) {
4040
+ return typeof value === "object" && value !== null;
4041
+ }
4042
+ function isNonEmptyString(value) {
4043
+ return typeof value === "string" && value.length > 0;
4044
+ }
4045
+ function isStringEnumMember(value, allowed) {
4046
+ return typeof value === "string" && allowed.includes(value);
4047
+ }
4048
+ function isOptionalStringEnumMember(value, allowed) {
4049
+ return value === void 0 || isStringEnumMember(value, allowed);
4050
+ }
4051
+ function isSectionAddress(value) {
4052
+ return isObjectRecord(value) && value.kind === "section" && typeof value.sectionId === "string" && value.sectionId.length > 0;
4053
+ }
4054
+ //#endregion
3996
4055
  //#region ../document-api/src/format/inline-run-patch.ts
3997
4056
  var schemaBooleanOrNull = () => ({ oneOf: [{ type: "boolean" }, { type: "null" }] });
3998
4057
  var schemaStringOrNull = () => ({ oneOf: [{
@@ -5703,6 +5762,13 @@ function selectionTargetFromTextTarget(target) {
5703
5762
  ...story ? { story } : {}
5704
5763
  };
5705
5764
  }
5765
+ function capturedTableCellTarget(value) {
5766
+ if (!value || typeof value !== "object") return null;
5767
+ const target = value;
5768
+ if (target.kind !== "selection" || target.coordinateSpace !== void 0 && target.coordinateSpace !== "visible" && target.coordinateSpace !== "tracked" || target.story !== void 0 && !isStoryLocator(target.story)) return null;
5769
+ for (const point of [target.start, target.end]) if (!point || point.kind !== "text" || typeof point.blockId !== "string" || point.blockId.length === 0 || !Number.isInteger(point.offset) || point.offset < 0 || point.story !== void 0 && !isStoryLocator(point.story)) return null;
5770
+ return value;
5771
+ }
5706
5772
  function selectionTargetForRestore(capture) {
5707
5773
  if (capture.selectionTarget) return capture.selectionTarget;
5708
5774
  return selectionTargetFromTextTarget(capture.target);
@@ -7042,11 +7108,11 @@ function createSuperDocUI(options) {
7042
7108
  };
7043
7109
  const selectionScopedAsyncReadKeys = (selection) => {
7044
7110
  const signature = selectionSignature(selection);
7045
- const effectiveUniformitySignature = selectionEffectiveUniformitySignature(selection);
7111
+ const effectiveUniformityKey = effectiveInlineUniformityCacheKey(selection);
7046
7112
  return /* @__PURE__ */ new Set([
7047
7113
  `contentControls:inRange:${signature}`,
7048
7114
  `query:${signature}`,
7049
- ...effectiveUniformitySignature ? [`effInline:${effectiveUniformitySignature}`] : []
7115
+ ...effectiveUniformityKey ? [effectiveUniformityKey] : []
7050
7116
  ]);
7051
7117
  };
7052
7118
  const pruneSelectionScopedAsyncReads = (selection) => {
@@ -7940,7 +8006,7 @@ function createSuperDocUI(options) {
7940
8006
  if (!sawWholeRun) return {};
7941
8007
  return collapseMarkSets(wholeSets);
7942
8008
  };
7943
- const completeProjectedInlineValues = (selection, direct) => {
8009
+ const completeProjectedInlineValues = (selection, direct, effectiveInput = effectiveInlineUniformityInput(selection)) => {
7944
8010
  if (direct.fontFamily !== void 0 && direct.fontSize !== void 0 && direct.color !== void 0 && direct.highlight !== void 0) return {
7945
8011
  values: direct,
7946
8012
  effectiveUniformityStatus: "ready"
@@ -7954,7 +8020,7 @@ function createSuperDocUI(options) {
7954
8020
  ...direct
7955
8021
  };
7956
8022
  if (!selection.empty && (combined.fontFamily === void 0 || combined.fontSize === void 0)) {
7957
- const uniformity = resolveEffectiveInlineUniformityValues(selection);
8023
+ const uniformity = resolveEffectiveInlineUniformityValues(selection, effectiveInput);
7958
8024
  if (uniformity.values) {
7959
8025
  if (combined.fontFamily === void 0 && uniformity.values.fontFamily !== void 0) combined.fontFamily = uniformity.values.fontFamily;
7960
8026
  if (combined.fontSize === void 0 && uniformity.values.fontSize !== void 0) combined.fontSize = uniformity.values.fontSize;
@@ -7984,9 +8050,23 @@ function createSuperDocUI(options) {
7984
8050
  "underline",
7985
8051
  "strikethrough"
7986
8052
  ];
7987
- const readEffectiveInlineUniformityCached = (selection) => {
7988
- const target = selection.selectionTarget;
7989
- if (!target) return {
8053
+ function effectiveInlineUniformityInput(selection) {
8054
+ const cellTarget = currentCellSelectionTarget();
8055
+ const target = cellTarget ?? selection.selectionTarget;
8056
+ if (!target) return null;
8057
+ const tableCellSelection = cellTarget ? tableCellSelectionForTarget(cellTarget) : null;
8058
+ return {
8059
+ target,
8060
+ offsetSpace: "selection",
8061
+ ...tableCellSelection ? { tableCellSelection } : {}
8062
+ };
8063
+ }
8064
+ function effectiveInlineUniformityCacheKey(selection, input = effectiveInlineUniformityInput(selection)) {
8065
+ if (!input) return null;
8066
+ return `effInline:${input.tableCellSelection ? `${selectionKey({ selectionTarget: input.target })}|cells:${JSON.stringify(input.tableCellSelection)}` : selectionEffectiveUniformitySignature(selection) ?? selectionSignature(selection)}`;
8067
+ }
8068
+ const readEffectiveInlineUniformityCached = (selection, input = effectiveInlineUniformityInput(selection)) => {
8069
+ if (!input) return {
7990
8070
  value: null,
7991
8071
  status: "ready"
7992
8072
  };
@@ -7995,15 +8075,13 @@ function createSuperDocUI(options) {
7995
8075
  value: null,
7996
8076
  status: "ready"
7997
8077
  };
7998
- const signature = selectionEffectiveUniformitySignature(selection) ?? selectionSignature(selection);
7999
- return readAsync(`effInline:${signature}`, contentToken(), () => op({
8000
- target,
8001
- offsetSpace: "selection",
8078
+ return readAsync(effectiveInlineUniformityCacheKey(selection, input), contentToken(), () => op({
8079
+ ...input,
8002
8080
  keys: EFFECTIVE_INLINE_UNIFORMITY_KEYS
8003
8081
  }), (raw) => raw && typeof raw === "object" ? raw : null);
8004
8082
  };
8005
- const resolveEffectiveInlineUniformityValues = (selection) => {
8006
- const { value, status } = readEffectiveInlineUniformityCached(selection);
8083
+ const resolveEffectiveInlineUniformityValues = (selection, input = effectiveInlineUniformityInput(selection)) => {
8084
+ const { value, status } = readEffectiveInlineUniformityCached(selection, input);
8007
8085
  if (!value || value.success !== true) return {
8008
8086
  values: null,
8009
8087
  status
@@ -8057,16 +8135,12 @@ function createSuperDocUI(options) {
8057
8135
  const fromWorker = values?.[key];
8058
8136
  return fromWorker !== void 0 ? fromWorker : null;
8059
8137
  };
8060
- const readEffectiveInlineUniformityNow = async (selection) => {
8061
- const target = selection.selectionTarget;
8062
- if (!target) return null;
8138
+ const readEffectiveInlineUniformityNow = async (input) => {
8139
+ if (!input) return null;
8063
8140
  const op = resolveDocOperation(getDoc(), "format.readEffectiveInlineUniformity");
8064
8141
  if (!op) return null;
8065
8142
  try {
8066
- const raw = await Promise.resolve(op({
8067
- target,
8068
- offsetSpace: "selection"
8069
- }));
8143
+ const raw = await Promise.resolve(op(input));
8070
8144
  return raw && typeof raw === "object" && raw.success === true ? raw : null;
8071
8145
  } catch {
8072
8146
  return null;
@@ -8081,8 +8155,8 @@ function createSuperDocUI(options) {
8081
8155
  else delete projected[key];
8082
8156
  }
8083
8157
  };
8084
- const projectSelectionInlineValuesWithStatus = (selection) => completeProjectedInlineValues(selection, projectInlineValuesFromQueryItem(resolveSelectionTextQueryItem(selection), selection));
8085
- const projectSelectionInlineValues = (selection) => projectSelectionInlineValuesWithStatus(selection).values;
8158
+ const projectSelectionInlineValuesWithStatus = (selection, effectiveInput = effectiveInlineUniformityInput(selection)) => completeProjectedInlineValues(selection, projectInlineValuesFromQueryItem(resolveSelectionTextQueryItem(selection), selection), effectiveInput);
8159
+ const projectSelectionInlineValues = (selection, effectiveInput = effectiveInlineUniformityInput(selection)) => projectSelectionInlineValuesWithStatus(selection, effectiveInput).values;
8086
8160
  /**
8087
8161
  * Read command-critical inline values for format-painter capture. Reactive
8088
8162
  * command state intentionally serves stale values while its worker read is
@@ -8091,20 +8165,21 @@ function createSuperDocUI(options) {
8091
8165
  * without changing the non-blocking toolbar snapshot policy.
8092
8166
  */
8093
8167
  const readFormatPainterInlineValues = async (selection) => {
8168
+ const effectiveInput = effectiveInlineUniformityInput(selection);
8094
8169
  const query = getDoc()?.query;
8095
8170
  const request = selectionTextQueryRequest(selection);
8096
- if (typeof query?.match !== "function" || !request) return projectSelectionInlineValues(selection);
8171
+ if (typeof query?.match !== "function" || !request) return projectSelectionInlineValues(selection, effectiveInput);
8097
8172
  try {
8098
8173
  const raw = await Promise.resolve(query.match(request));
8099
8174
  const direct = projectInlineValuesFromQueryItem(pickSelectionTextQueryItem(raw && typeof raw === "object" ? raw : null, selection), selection);
8100
- const completed = completeProjectedInlineValues(selection, direct);
8175
+ const completed = completeProjectedInlineValues(selection, direct, effectiveInput);
8101
8176
  if (completed.effectiveUniformityStatus !== "ready" && (direct.fontFamily === void 0 || direct.fontSize === void 0)) {
8102
- const effective = await readEffectiveInlineUniformityNow(selection);
8177
+ const effective = await readEffectiveInlineUniformityNow(effectiveInput);
8103
8178
  if (effective) applyEffectiveInlineUniformity(completed.values, direct, effective);
8104
8179
  }
8105
8180
  return completed.values;
8106
8181
  } catch {
8107
- return projectSelectionInlineValues(selection);
8182
+ return projectSelectionInlineValues(selection, effectiveInput);
8108
8183
  }
8109
8184
  };
8110
8185
  const readBlockNode = async (address) => {
@@ -8347,7 +8422,7 @@ function createSuperDocUI(options) {
8347
8422
  supported: true,
8348
8423
  reason: lockReason
8349
8424
  }, "builtin");
8350
- const enabled = selectionBlockIds(selection).length > 0 || resolveInlineSelectionTarget(selection) != null;
8425
+ const enabled = selectionBlockIds(selection).length > 0 || resolveInlineCommandTarget(selection) != null;
8351
8426
  return normalizeCommandState({
8352
8427
  enabled,
8353
8428
  active: false,
@@ -8401,7 +8476,7 @@ function createSuperDocUI(options) {
8401
8476
  value,
8402
8477
  reason: SUPERDOC_UI_REASONS.documentReadonly
8403
8478
  }, "builtin");
8404
- if (descriptor.inline && !resolveInlineSelectionTarget(selection)) {
8479
+ if (descriptor.inline && !resolveInlineCommandTarget(selection)) {
8405
8480
  const lockReason = contentControlLockReason(lockModesById);
8406
8481
  if (lockReason) return normalizeCommandState({
8407
8482
  enabled: false,
@@ -10356,9 +10431,32 @@ function createSuperDocUI(options) {
10356
10431
  * option is not part of public `MutationOptions`; this is the same cast
10357
10432
  * pattern the delete/replace callers use.
10358
10433
  */
10359
- const callInlineFormatMutation = (route, op, input, selection = state.selection) => {
10434
+ function currentCellSelectionTarget() {
10435
+ const handles = safeCall(() => getHost()?.getHandles?.(), null);
10436
+ if (!(handles?.selection?.getSnapshot?.())?.tableCells) return null;
10437
+ const resolved = handles?.selection?.toSelectionTarget?.();
10438
+ return resolved?.kind === "ok" && resolved.mode === "range" ? capturedTableCellTarget(resolved.target) : null;
10439
+ }
10440
+ function resolveInlineCommandTarget(selection) {
10441
+ return currentCellSelectionTarget() ?? resolveInlineSelectionTarget(selection);
10442
+ }
10443
+ function tableCellSelectionForTarget(target) {
10444
+ const cellTarget = currentCellSelectionTarget();
10445
+ const candidate = target;
10446
+ if (!cellTarget || candidate?.start?.blockId !== cellTarget.start.blockId || candidate?.end?.blockId !== cellTarget.end.blockId || candidate?.start?.offset !== cellTarget.start.offset || candidate?.end?.offset !== cellTarget.end.offset) return null;
10447
+ return {
10448
+ anchorBlockId: cellTarget.start.blockId,
10449
+ focusBlockId: cellTarget.end.blockId
10450
+ };
10451
+ }
10452
+ const callInlineFormatMutation = (route, op, input, selection = state.selection, tableCellSelection = tableCellSelectionForTarget(input?.target)) => {
10360
10453
  const options = editorMutationOptionsForRoute(route);
10361
10454
  if (options && options.success === false) return options;
10455
+ if (tableCellSelection) return op(input, {
10456
+ ...options,
10457
+ offsetSpace: "selection",
10458
+ tableCellSelection
10459
+ });
10362
10460
  if (!selection.selectionTarget) return options ? op(input, options) : op(input);
10363
10461
  return op(input, {
10364
10462
  ...options,
@@ -10464,7 +10562,7 @@ function createSuperDocUI(options) {
10464
10562
  }
10465
10563
  };
10466
10564
  const defaultStyleId = getStyleCatalog().cache?.full.defaults.paragraphStyleId ?? DEFAULT_PARAGRAPH_STYLE_ID;
10467
- const rangeTarget = resolveInlineSelectionTarget(state.selection);
10565
+ const rangeTarget = resolveInlineCommandTarget(state.selection);
10468
10566
  if (rangeTarget) {
10469
10567
  const inlineOp = resolveDocOperation(doc, "format.apply");
10470
10568
  if (inlineOp) try {
@@ -11061,8 +11159,8 @@ function createSuperDocUI(options) {
11061
11159
  if (state.selection.status !== "ready") return false;
11062
11160
  if (trackDecisionCommand(id)?.scope === "id") return true;
11063
11161
  if (!descriptor || descriptor.disposition !== "routed") return true;
11064
- if (descriptor.inline?.kind === "clear") return selectionBlockIds(state.selection).length > 0 || resolveInlineSelectionTarget(state.selection) != null;
11065
- if (descriptor.inline) return resolveInlineSelectionTarget(state.selection) != null || canStorePendingInlineFormat(state.selection);
11162
+ if (descriptor.inline?.kind === "clear") return selectionBlockIds(state.selection).length > 0 || resolveInlineCommandTarget(state.selection) != null;
11163
+ if (descriptor.inline) return resolveInlineCommandTarget(state.selection) != null || canStorePendingInlineFormat(state.selection);
11066
11164
  if (descriptor.blockParagraph || descriptor.list) return selectionBlockIds(state.selection).length > 0;
11067
11165
  if (descriptor.link) return linkPayloadHasExplicitTarget(payload) || selectionTextAddresses(state.selection).length > 0 || collapsedTextAddressFromSelection(state.selection) != null || commandActiveState(descriptor, getDoc(), state.selection);
11068
11166
  return true;
@@ -11126,7 +11224,7 @@ function createSuperDocUI(options) {
11126
11224
  if (descriptor.inline) {
11127
11225
  const { lockModesById } = computeActiveContentControlLockModes(doc?.contentControls, state.selection);
11128
11226
  if (contentControlLockReason(lockModesById)) return false;
11129
- const target = resolveInlineSelectionTarget(state.selection);
11227
+ const target = resolveInlineCommandTarget(state.selection);
11130
11228
  if (!target) {
11131
11229
  if (!canStorePendingInlineFormat(state.selection)) return settleCommandExecution(false);
11132
11230
  return settleCommandExecution(storePendingInlineFormat(descriptor, normalized));
@@ -11134,6 +11232,8 @@ function createSuperDocUI(options) {
11134
11232
  const active = commandActiveState(descriptor, doc, state.selection);
11135
11233
  const input = buildInlineFormatInput(descriptor.inline, target, normalized, active);
11136
11234
  if (!input) return false;
11235
+ const commandSelection = state.selection;
11236
+ const tableCellSelection = tableCellSelectionForTarget(target);
11137
11237
  let releaseScheduledMutation = null;
11138
11238
  try {
11139
11239
  const commandEditor = getEditor();
@@ -11141,7 +11241,7 @@ function createSuperDocUI(options) {
11141
11241
  const commandMode = readDocumentMode();
11142
11242
  const mutate = () => {
11143
11243
  if (getEditor() !== commandEditor || commandEditor?.doc !== commandDocSource || readDocumentMode() !== commandMode) return false;
11144
- return callInlineFormatMutation(route, op, input);
11244
+ return callInlineFormatMutation(route, op, input, commandSelection, tableCellSelection);
11145
11245
  };
11146
11246
  const selectionSignature = selectionInlineValueSignature(state.selection) ?? selectionKey(state.selection);
11147
11247
  const scheduledMutation = descriptor.inline.kind === "toggle" ? scheduleInlineToggleMutation(`${descriptor.id}:${selectionSignature}`, mutate) : null;
@@ -11452,34 +11552,42 @@ function createSuperDocUI(options) {
11452
11552
  if (painter.pointerSelecting || painter.keyboardSelecting) return;
11453
11553
  const selectionApi = getDoc()?.selection;
11454
11554
  const readSelection = async () => {
11555
+ const cellTarget = currentCellSelectionTarget();
11556
+ const tableCellSelection = tableCellSelectionForTarget(cellTarget);
11455
11557
  const rawSelection = typeof selectionApi?.["current"] === "function" ? await Promise.resolve(selectionApi["current"]({ includeText: true })) : readSelectionInfoLive().value;
11456
- return normalizeSelectionInfo(rawSelection);
11558
+ return {
11559
+ selection: normalizeSelectionInfo(rawSelection),
11560
+ cellTarget,
11561
+ tableCellSelection
11562
+ };
11457
11563
  };
11458
11564
  const waitForSelectionSettle = () => new Promise((resolve) => setTimeout(resolve, 16));
11459
11565
  const MAX_SELECTION_ATTEMPTS = 8;
11460
- let sel = null;
11566
+ let selectionRead = null;
11461
11567
  let key = "";
11462
11568
  for (let attempt = 0; attempt < MAX_SELECTION_ATTEMPTS; attempt += 1) {
11463
11569
  if (attempt > 0) await waitForSelectionSettle();
11464
11570
  if (painter.mode === "idle" || painter.pointerSelecting || painter.keyboardSelecting) return;
11465
- sel = await readSelection();
11571
+ selectionRead = await readSelection();
11572
+ const sel = selectionRead.selection;
11466
11573
  if (!sel || sel["empty"]) continue;
11467
11574
  key = selectionKey(sel);
11468
11575
  if (key === painter.sourceSelectionKey) continue;
11469
11576
  break;
11470
11577
  }
11471
- if (sel && !sel["empty"] && key !== painter.sourceSelectionKey) {
11472
- await applyFormatPainter(sel);
11578
+ if (selectionRead?.selection && !selectionRead.selection["empty"] && key !== painter.sourceSelectionKey) {
11579
+ await applyFormatPainter(selectionRead.selection, selectionRead.cellTarget, selectionRead.tableCellSelection);
11473
11580
  return;
11474
11581
  }
11475
11582
  if (painter.snapshot?.paragraph) {
11476
- const caretSel = await readSelection();
11583
+ const caretRead = await readSelection();
11584
+ const caretSel = caretRead.selection;
11477
11585
  if (!caretSel) return;
11478
11586
  if (selectionKey(caretSel) === painter.sourceSelectionKey) return;
11479
- await applyFormatPainter(caretSel);
11587
+ await applyFormatPainter(caretSel, caretRead.cellTarget, caretRead.tableCellSelection);
11480
11588
  }
11481
11589
  };
11482
- const applyFormatPainter = async (selection) => {
11590
+ const applyFormatPainter = async (selection, cellTarget, tableCellSelection) => {
11483
11591
  const snap = painter.snapshot;
11484
11592
  if (!snap) {
11485
11593
  if (painter.mode === "armed") exitFormatPainter();
@@ -11494,14 +11602,14 @@ function createSuperDocUI(options) {
11494
11602
  return;
11495
11603
  }
11496
11604
  const selTarget = selection["target"];
11497
- const selectionTarget = selection["selectionTarget"];
11605
+ const selectionTarget = cellTarget ?? selection.selectionTarget;
11498
11606
  if (!(selection["empty"] === true) && Object.keys(snap.inline).length > 0) {
11499
11607
  const inlineOp = resolveDocOperation(doc, "format.apply");
11500
11608
  if (inlineOp && selectionTarget) try {
11501
11609
  if (!commandResultSucceeded(commandResultFromOperationResult(await Promise.resolve(callInlineFormatMutation("format.apply", inlineOp, {
11502
11610
  target: selectionTarget,
11503
11611
  inline: snap.inline
11504
- }, selection))))) {
11612
+ }, selection, tableCellSelection))))) {
11505
11613
  recompute();
11506
11614
  return;
11507
11615
  }
@@ -11690,10 +11798,13 @@ function createSuperDocUI(options) {
11690
11798
  current: () => readSelectionInfoLive().value,
11691
11799
  capture: () => {
11692
11800
  const snapshot = selectionSub.get();
11693
- if (snapshot.empty || !snapshot.target && !snapshot.selectionTarget) return null;
11801
+ if (snapshot.empty) return null;
11802
+ const cells = currentCellSelectionTarget();
11803
+ if (!snapshot.target && !snapshot.selectionTarget && !cells) return null;
11694
11804
  return {
11695
11805
  ...snapshot,
11696
- capturedAt: Date.now()
11806
+ capturedAt: Date.now(),
11807
+ ...cells ? { _tableCellSelectionTarget: cells } : {}
11697
11808
  };
11698
11809
  },
11699
11810
  restore: (capture) => {
@@ -11712,7 +11823,13 @@ function createSuperDocUI(options) {
11712
11823
  ok: false,
11713
11824
  reason: SUPERDOC_UI_REASONS.notReady
11714
11825
  });
11715
- const target = selectionTargetForRestore(capture);
11826
+ const hasCellTarget = "_tableCellSelectionTarget" in capture;
11827
+ const cellTarget = hasCellTarget ? capturedTableCellTarget(capture._tableCellSelectionTarget) : null;
11828
+ if (hasCellTarget && !cellTarget) return done({
11829
+ ok: false,
11830
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
11831
+ });
11832
+ const target = cellTarget ?? selectionTargetForRestore(capture);
11716
11833
  if (!target) return done({
11717
11834
  ok: false,
11718
11835
  reason: SUPERDOC_UI_REASONS.targetUnresolved
@@ -11723,7 +11840,7 @@ function createSuperDocUI(options) {
11723
11840
  reason: resolved.reason
11724
11841
  });
11725
11842
  try {
11726
- return done(normalizeHostSelectionApplyResult(resolved.helper.apply(target)));
11843
+ return done(normalizeHostSelectionApplyResult(cellTarget ? resolved.helper.apply(target, { tableCells: true }) : resolved.helper.apply(target)));
11727
11844
  } catch {
11728
11845
  return done({
11729
11846
  ok: false,
@@ -19,7 +19,7 @@ var COLLABORATION_UPGRADE_ENGINE_MINIMUM_NODE_MAJOR = 20;
19
19
  var PRIVATE_ENGINE_INFO = (0, _superdoc_docx_engine_collaboration_upgrade_engine.getCollaborationUpgradeEngineInfo)();
20
20
  var ENGINE_INFO = Object.freeze({
21
21
  ...PRIVATE_ENGINE_INFO,
22
- superdocVersion: "2.22.0-next.8",
22
+ superdocVersion: "2.22.0",
23
23
  roomSchemaVersion: Object.freeze({ ...PRIVATE_ENGINE_INFO.roomSchemaVersion }),
24
24
  supportedBundleVersions: SUPPORTED_COLLABORATION_UPGRADE_BUNDLE_VERSIONS,
25
25
  supportedV1ReaderContractVersions: SUPPORTED_V1_READER_CONTRACT_VERSIONS
@@ -18,7 +18,7 @@ var COLLABORATION_UPGRADE_ENGINE_MINIMUM_NODE_MAJOR = 20;
18
18
  var PRIVATE_ENGINE_INFO = getCollaborationUpgradeEngineInfo$1();
19
19
  var ENGINE_INFO = Object.freeze({
20
20
  ...PRIVATE_ENGINE_INFO,
21
- superdocVersion: "2.22.0-next.8",
21
+ superdocVersion: "2.22.0",
22
22
  roomSchemaVersion: Object.freeze({ ...PRIVATE_ENGINE_INFO.roomSchemaVersion }),
23
23
  supportedBundleVersions: SUPPORTED_COLLABORATION_UPGRADE_BUNDLE_VERSIONS,
24
24
  supportedV1ReaderContractVersions: SUPPORTED_V1_READER_CONTRACT_VERSIONS
@@ -247,6 +247,8 @@ export interface ContentControlsUnwrapInput {
247
247
  }
248
248
  export interface ContentControlsDeleteInput {
249
249
  target: ContentControlTarget;
250
+ /** Override only this target's deletion lock; document and ancestor protection still apply. */
251
+ overrideDeletionLock?: boolean;
250
252
  }
251
253
  export interface ContentControlsCopyInput {
252
254
  target: ContentControlTarget;
@@ -2283,8 +2283,8 @@ export declare const OPERATION_DEFINITIONS: {
2283
2283
  };
2284
2284
  readonly 'contentControls.delete': {
2285
2285
  readonly memberPath: "contentControls.delete";
2286
- readonly description: "Delete a content control and its content from the document.";
2287
- readonly expectedResult: "Returns a ContentControlMutationResult; reports NO_OP if already removed.";
2286
+ readonly description: "Delete a content control and its content. Set overrideDeletionLock to remove a deletion-locked target without first unlocking it.";
2287
+ readonly expectedResult: "Returns a ContentControlMutationResult; reports NO_OP if already removed. An explicit override deletes in one history entry; Undo restores the original control and lock.";
2288
2288
  readonly requiresDocumentContext: true;
2289
2289
  readonly metadata: CommandStaticMetadata;
2290
2290
  readonly referenceDocPath: "content-controls/delete.mdx";
@@ -840,6 +840,8 @@ export declare const isExplicitNoneBorder: (border?: unknown) => boolean;
840
840
  */
841
841
  export declare const resolveBorderConflict: (first?: BorderSpec, second?: BorderSpec) => BorderSpec | undefined;
842
842
  export type TableCellAttrs = {
843
+ /** Resolved ordinary Latin text rotation from w:tcPr/w:textDirection. */
844
+ textDirection?: 'btLr' | 'tbRl';
843
845
  borders?: CellBorders;
844
846
  padding?: BoxSpacing;
845
847
  verticalAlign?: 'top' | 'middle' | 'center' | 'bottom';
@@ -891,6 +893,8 @@ export type TableCell = {
891
893
  attrs?: TableCellAttrs;
892
894
  sourceAnchor?: SourceAnchor;
893
895
  };
896
+ /** Read the resolved cell axis, with compatibility for older raw carriers. */
897
+ export declare function getTableCellTextDirection(attrs: TableCellAttrs | undefined): 'btLr' | 'tbRl' | undefined;
894
898
  export type TableRowProperties = {
895
899
  repeatHeader?: boolean;
896
900
  cantSplit?: boolean;
@@ -1671,13 +1675,9 @@ export declare function allExplicitColumnWidthsEqual(widths: readonly number[]):
1671
1675
  /**
1672
1676
  * Shared "genuinely unequal explicit column widths" predicate (SD-2324).
1673
1677
  *
1674
- * The layout engine skips end-of-section column balancing exactly when this
1675
- * is true: Word fills genuinely-unequal explicit columns column-by-column
1676
- * rather than rebalancing them. Host retained-layout dependency scanning
1677
- * (SD-3772) consumes the same predicate so checkpoint admission can never
1678
- * drift from the engine's balancing semantics. Explicit widths that are all
1679
- * EQUAL (`equalWidth="0"` with every `<w:col w:w>` equal) still balance like
1680
- * implicit equal columns and are NOT genuinely unequal.
1678
+ * Unequal geometry requires width-aware paragraph reflow during balancing;
1679
+ * equal explicit widths can use the same fixed-line balancing as implicit
1680
+ * equal columns. This predicate describes geometry, not balancing eligibility.
1681
1681
  */
1682
1682
  export declare function hasGenuinelyUnequalExplicitColumnWidths(columns: {
1683
1683
  equalWidth?: boolean | undefined;
@@ -2067,6 +2067,8 @@ export type ParagraphAttrs = {
2067
2067
  * See `@superdoc/contracts/direction-context` for axis semantics.
2068
2068
  */
2069
2069
  directionContext?: ParagraphDirectionContext;
2070
+ /** Authored paragraph writing-mode override; kept distinct from inherited cell rotation. */
2071
+ textDirection?: string;
2070
2072
  isTocEntry?: boolean;
2071
2073
  tocInstruction?: string;
2072
2074
  /** Stable id shared by every paragraph in the same TOC (docPartObj uniqueId or parent sdBlockId). */
@@ -2258,6 +2260,8 @@ export type Line = {
2258
2260
  justificationPlan?: LineJustificationPlan;
2259
2261
  /** True when this line used author-defined OOXML tab stops, not synthesized default stops. */
2260
2262
  hasExplicitTabStops?: boolean;
2263
+ /** A single first-line start tab ends at the hanging indent, leaving body text justifiable. */
2264
+ justifyAfterHangingTab?: boolean;
2261
2265
  segments?: LineSegment[];
2262
2266
  leaders?: LeaderDecoration[];
2263
2267
  bars?: BarDecoration[];
@@ -2411,6 +2415,12 @@ export type TableCellMeasure = {
2411
2415
  paragraph?: ParagraphMeasure;
2412
2416
  width: number;
2413
2417
  height: number;
2418
+ /** Resolved whole-cell rotated flow; dimensions exclude physical padding/borders. */
2419
+ verticalText?: {
2420
+ direction: 'btLr' | 'tbRl';
2421
+ inlineSize: number;
2422
+ blockSize: number;
2423
+ };
2414
2424
  /** Starting grid column index (0-based) */
2415
2425
  gridColumnStart?: number;
2416
2426
  /** Number of grid columns this cell spans */
@@ -20,6 +20,8 @@ export type ShouldApplyJustifyParams = {
20
20
  hasExplicitPositioning?: boolean;
21
21
  /** Whether the line used author-defined OOXML tab stops. */
22
22
  hasExplicitTabStops?: boolean;
23
+ /** Whether a single start tab only separates a hanging prefix from the body. */
24
+ justifyAfterHangingTab?: boolean;
23
25
  /** Whether this is the last line of the paragraph. */
24
26
  isLastLineOfParagraph: boolean;
25
27
  /** Whether the paragraph ends with a soft break (Shift+Enter / LineBreak run). */
@@ -33,13 +35,14 @@ export type ShouldApplyJustifyParams = {
33
35
  * Justify is applied when ALL of the following are true:
34
36
  * - Alignment is 'justify'
35
37
  * - No explicit skip override
36
- * - Line doesn't have author-defined tab stops
38
+ * - Line has no column-aligned tab stops
37
39
  * - Line is NOT the last line, OR paragraph ends with a soft break
38
40
  *
39
41
  * This matches Microsoft Word's behavior:
40
42
  * - All lines are justified except the true last line
41
43
  * - Soft breaks (Shift+Enter) do NOT count as "last line"
42
- * - Explicit tab-aligned text is never justified
44
+ * - Explicit column-aligned text is never justified
45
+ * - A single start tab at the hanging indent leaves body text justifiable
43
46
  * - Default/manual tab-aligned text can still be justified
44
47
  *
45
48
  * @param params - Parameters for justify decision
@@ -1,6 +1,6 @@
1
1
  import { NonFlowingPageRelativeAnchorDependencyProof, PageCheckpointDependencyClass, FlowBlock, Layout, LayoutBlockResumeCheckpoint, Measure, Page, HeaderFooterLayout, ColumnLayout } from '../../contracts/src/index.js';
2
2
  import { FontMeasureCapabilities, FontMeasureContext } from '../../../shared/font-system/src/index.js';
3
- import { LayoutOptions, HeaderFooterConstraints, LayoutExecutionCheckpoint } from '../../layout-engine/src/index.js';
3
+ import { LayoutContinuationResume, LayoutOptions, HeaderFooterConstraints, LayoutExecutionCheckpoint } from '../../layout-engine/src/index.js';
4
4
  import { computeDirtyRegions } from './diff.js';
5
5
  import { MeasureCache } from './cache.js';
6
6
  import { HeaderFooterBatch } from './layoutHeaderFooter.js';
@@ -127,8 +127,28 @@ export type IncrementalLayoutResult = {
127
127
  * and `pageTokenTotalMs` includes token remeasure/relayout details.
128
128
  */
129
129
  bridgeTiming: IncrementalLayoutBridgeTiming;
130
+ progressive?: {
131
+ status: 'pending';
132
+ completedPageRange: {
133
+ startPageIndex: number;
134
+ endPageIndexExclusive: number;
135
+ };
136
+ completedRanges: readonly {
137
+ startOrdinal: number;
138
+ endOrdinalExclusive: number;
139
+ }[];
140
+ resume: LayoutContinuationResume;
141
+ continuation: IncrementalLayoutContinuation;
142
+ pageCountFieldsExact: false;
143
+ };
144
+ progressiveFallbackReason?: string;
145
+ };
146
+ export type IncrementalLayoutContinuation = {
147
+ advance(execution?: IncrementalLayoutExecutionControl): Promise<IncrementalLayoutResult>;
148
+ dispose(): void;
130
149
  };
131
150
  export type IncrementalLayoutBridgeTiming = {
151
+ paginationPreparationRejection?: string;
132
152
  /** Content-free reason a document used the conservative note fallback. */
133
153
  footnoteCoupledRejection?: string;
134
154
  totalMs: number;
@@ -184,6 +204,7 @@ export type IncrementalLayoutBridgeTiming = {
184
204
  pagesPaginated: number | null;
185
205
  pagesSplicedByReuse: number;
186
206
  paginationPasses: number;
207
+ paginationPreparationReuses?: number;
187
208
  pageTokenRelayouts: number;
188
209
  headerFooterPreLayoutReuses: number;
189
210
  headerFooterPreLayoutBodyBlocksEnumerated: number;
@@ -351,8 +372,7 @@ export type IncrementalPaginationProof = IncrementalPaginationProofBase & ({
351
372
  predecessorBlockId: string | null;
352
373
  };
353
374
  /**
354
- * SD-3772 D1: true means the host proved (via the shared
355
- * `hasGenuinelyUnequalExplicitColumnWidths` predicate) that no
375
+ * SD-3772 D1: true means the host proved that no
356
376
  * potentially balanceable multi-column section exists anywhere in the
357
377
  * retained document. Balancing is a post-pagination finalizer that a
358
378
  * mid-section checkpoint cannot seed. False is admissible only
@@ -513,8 +533,14 @@ export interface IncrementalLayoutExecutionControl {
513
533
  yieldEveryBlocks?: number;
514
534
  /** Time-aware mounted probe. Null is the allocation-free under-budget path. */
515
535
  checkpointIfDue?: (checkpoint?: LayoutExecutionCheckpoint) => Promise<void> | null;
536
+ progressive?: {
537
+ firstBatchPageCount?: number;
538
+ restartBeforeBlockIndex?: number;
539
+ restartBeforePageIndex?: number;
540
+ };
516
541
  }
517
- export declare function incrementalLayout(previousBlocks: FlowBlock[], _previousLayout: Layout | null, nextBlocks: FlowBlock[], options: LayoutOptions, measureBlock: (block: FlowBlock, constraints: {
542
+ export declare function incrementalLayout(...args: Parameters<typeof incrementalLayoutComplete>): Promise<IncrementalLayoutResult>;
543
+ declare function incrementalLayoutComplete(previousBlocks: FlowBlock[], _previousLayout: Layout | null, nextBlocks: FlowBlock[], options: LayoutOptions, measureBlock: (block: FlowBlock, constraints: {
518
544
  maxWidth: number;
519
545
  maxHeight: number;
520
546
  }) => Promise<Measure>, headerFooter?: {
@@ -4,9 +4,9 @@ export type { HeaderFooterType } from '../../contracts/src/index.js';
4
4
  export { selectHeaderFooterVariantForPage, resolveEffectiveHeaderFooterRef, type HeaderFooterKind, type HeaderFooterVariant, type HeaderFooterSectionRefs, type HeaderFooterResolutionSection, type HeaderFooterVariantSelectionInput, type HeaderFooterEffectiveRefInput, type HeaderFooterEffectiveRefResult, } from '../../contracts/src/index.js';
5
5
  export { extractIdentifierFromConverter, getHeaderFooterType, defaultHeaderFooterIdentifier, resolveHeaderFooterForPage, buildMultiSectionIdentifier, defaultMultiSectionIdentifier, getHeaderFooterTypeForSection, getHeaderFooterIdForPage, resolveHeaderFooterForPageAndSection, } from './headerFooterUtils.js';
6
6
  export type { HeaderFooterIdentifier, MultiSectionHeaderFooterIdentifier, SectionHeaderFooterIds, } from './headerFooterUtils.js';
7
- export { layoutHeaderFooterWithCache, invalidateHeaderFooterMeasureCache, type HeaderFooterBatchResult, getBucketForPageNumber, getBucketRepresentative, } from './layoutHeaderFooter.js';
7
+ export { layoutHeaderFooterWithCache, createRetainedHeaderFooterPageReader, invalidateHeaderFooterMeasureCache, type HeaderFooterBatchResult, getBucketForPageNumber, getBucketRepresentative, } from './layoutHeaderFooter.js';
8
8
  export type { HeaderFooterBatch, DigitBucket } from './layoutHeaderFooter.js';
9
- export { resolveHeaderFooterTokens, PROVISIONAL_PAGE_COUNT_PLACEHOLDER, type ResolveHeaderFooterTokensOptions, } from './resolveHeaderFooterTokens.js';
9
+ export { resolveHeaderFooterTokens, cloneHeaderFooterBlocks, PROVISIONAL_PAGE_COUNT_PLACEHOLDER, type ResolveHeaderFooterTokensOptions, } from './resolveHeaderFooterTokens.js';
10
10
  export { buildPageStyleReferenceResolver, flowBlocksContainCrossReferenceMetadata, type PageStyleReferenceResolver, } from './resolveLiveCrossReferences.js';
11
11
  export { findWordBoundaries, findParagraphBoundaries } from './text-boundaries.js';
12
12
  export type { BoundaryRange } from './text-boundaries.js';
@@ -14,7 +14,7 @@ export { buildSectionAwareHeaderFooterLayoutKey, buildSectionContentWidth, build
14
14
  export type { HeaderFooterSectionKind, HeaderFooterRefs, SectionAwareHeaderFooterMeasurementGroup, } from './sectionAwareHeaderFooter.js';
15
15
  export { clearIncrementalModuleState, incrementalLayout, measureCache, normalizeMargin } from './incrementalLayout.js';
16
16
  export { hashMeasureContent } from './cache.js';
17
- export type { HeaderFooterLayoutResult, HeaderFooterGeometrySeed, IncrementalLayoutBridgeTiming, IncrementalLayoutResult, IncrementalLayoutExecutionControl, IncrementalMeasureReuseProof, IncrementalLayoutReuseOptions, IncrementalLayoutReuseSummary, IncrementalSectionPageNumberTransform, IncrementalLayoutTailAdoption, IncrementalLayoutTailDisposition, IncrementalPaginationProof, LayoutPositionTransform, FootnoteReserveSeed, } from './incrementalLayout.js';
17
+ export type { HeaderFooterLayoutResult, HeaderFooterGeometrySeed, IncrementalLayoutBridgeTiming, IncrementalLayoutResult, IncrementalLayoutExecutionControl, IncrementalLayoutContinuation, IncrementalMeasureReuseProof, IncrementalLayoutReuseOptions, IncrementalLayoutReuseSummary, IncrementalSectionPageNumberTransform, IncrementalLayoutTailAdoption, IncrementalLayoutTailDisposition, IncrementalPaginationProof, LayoutPositionTransform, FootnoteReserveSeed, } from './incrementalLayout.js';
18
18
  export { collectFootnoteLedgers, getPreferredReserveCandidates, getPreferredReserveTrialTargets, isMandatoryOnlyFootnotePage, scoreFootnoteWindow, summarizeFootnoteWindow, } from './footnote-scorer.js';
19
19
  export type { FootnotePreferredReserveCandidate, FootnoteWindowScoreInput, FootnoteWindowScoreReason, FootnoteWindowScoreResult, FootnoteWindowStats, } from './footnote-scorer.js';
20
20
  export { computeDisplayPageNumber } from '../../layout-engine/src/index.js';