@stll/folio-core 0.47.0 → 0.47.1

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.
@@ -57,6 +57,8 @@ type ApplyFolioAIEditOperationsOptions = {
57
57
  * still describes a table by its cell texts.
58
58
  */
59
59
  tableTemplates?: FolioTableTemplates;
60
+ /** What a replacement does with a background the text it replaces carries. */
61
+ replacementBackground?: FolioReplacementBackground;
60
62
  };
61
63
  /**
62
64
  * How an apply batch cuts the redline for a replacement. Word-level by
@@ -68,6 +70,20 @@ type ApplyFolioAIEditOperationsOptions = {
68
70
  type FolioWordDiffOptions = {
69
71
  granularity?: WordDiffGranularity;
70
72
  };
73
+ /**
74
+ * What a replacement does with a highlight or `w:shd` the text it replaces
75
+ * carries.
76
+ *
77
+ * `clear` is the authoring default: text typed over a highlighted placeholder
78
+ * is new text, and keeping the marker that said "fill this in" would carry it
79
+ * into the finished document. `keep` is for a caller that states the
80
+ * replacement's own run properties rather than letting it inherit them — a
81
+ * comparison reproducing a revised document holds the properties that document
82
+ * has, and clearing them first records a run-property change it takes straight
83
+ * back, which reaches the reader as a revision whose before and after are the
84
+ * same.
85
+ */
86
+ type FolioReplacementBackground = "clear" | "keep";
71
87
  /**
72
88
  * An apply result plus where the batch left the revision-id counter.
73
89
  *
@@ -88,4 +104,4 @@ type FolioAIEditApplyOutcome = FolioAIEditApplyResult & {
88
104
  declare const applyFolioAIEditOperations: (options: ApplyFolioAIEditOperationsOptions) => FolioAIEditApplyOutcome;
89
105
  declare const previewFolioAIEditOperations: (options: ApplyFolioAIEditOperationsOptions) => FolioAIEditApplyOutcome;
90
106
  //#endregion
91
- export { FolioAIEditApplyOutcome, FolioAIEditView, FolioRevisionStamp, FolioWordDiffOptions, applyFolioAIEditOperations, previewFolioAIEditOperations };
107
+ export { FolioAIEditApplyOutcome, FolioAIEditView, FolioReplacementBackground, FolioRevisionStamp, FolioWordDiffOptions, applyFolioAIEditOperations, previewFolioAIEditOperations };
@@ -578,8 +578,8 @@ const formattingChangesForAuthoredFormatting = (authoredFormatting, formatting)
578
578
  }
579
579
  return changes;
580
580
  };
581
- const clearReplacementBackground = ({ tr, schema, from, to, mode, revisionIdSeed, author, date, initials, suggestionId = null, styleResolver }) => {
582
- if (![schema.marks["highlight"], schema.marks["runShading"]].some((markType) => markType !== void 0 && tr.doc.rangeHasMark(from, to, markType))) return {
581
+ const clearReplacementBackground = ({ tr, schema, from, to, mode, replacementBackground, revisionIdSeed, author, date, initials, suggestionId = null, styleResolver }) => {
582
+ if (!(replacementBackground === "clear" && [schema.marks["highlight"], schema.marks["runShading"]].some((markType) => markType !== void 0 && tr.doc.rangeHasMark(from, to, markType)))) return {
583
583
  type: "applied",
584
584
  transaction: tr,
585
585
  revisionIds: [],
@@ -1241,7 +1241,7 @@ const buildInsertedParagraphs = ({ item, schema, formattingFromStyle, mode, auth
1241
1241
  nextRevisionId
1242
1242
  };
1243
1243
  };
1244
- const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff, wordDiffMode = "bounded", tableTemplates }) => {
1244
+ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff, wordDiffMode = "bounded", tableTemplates, replacementBackground = "clear" }) => {
1245
1245
  const applied = [];
1246
1246
  const skipped = [];
1247
1247
  const normalizations = [];
@@ -1532,6 +1532,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
1532
1532
  from: item.from,
1533
1533
  to: item.to,
1534
1534
  mode,
1535
+ replacementBackground,
1535
1536
  revisionIdSeed: revisionIdBackgroundSeed,
1536
1537
  author,
1537
1538
  date,
@@ -1659,6 +1660,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
1659
1660
  from: item.from,
1660
1661
  to: item.to,
1661
1662
  mode,
1663
+ replacementBackground,
1662
1664
  revisionIdSeed: revisionIdBackgroundSeed,
1663
1665
  author,
1664
1666
  date,
@@ -1,7 +1,7 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { FolioTableTemplates } from "./table-template.js";
3
3
  import { FolioAIBlock, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditSnapshot } from "./types.js";
4
- import { FolioRevisionStamp, FolioWordDiffOptions } from "./apply.js";
4
+ import { FolioReplacementBackground, FolioRevisionStamp, FolioWordDiffOptions } from "./apply.js";
5
5
  import { ImportReferencedStyleDefinitionsResult } from "../compare/style-resources.js";
6
6
  import { InlineProvenanceTargetOptions } from "../compare/inline-provenance.js";
7
7
  import { MatchInlineAtomsOptions } from "../compare/inline-atoms.js";
@@ -124,6 +124,13 @@ type FolioApplyDocumentOperationsToStoryOptions = FolioApplyDocumentOperationsOp
124
124
  * every table, row and cell property on the way.
125
125
  */
126
126
  tableTemplates?: FolioTableTemplates;
127
+ /**
128
+ * What a replacement in the batch does with a highlight or `w:shd` the text
129
+ * it replaces carries. Clearing it is the authoring default; a caller that
130
+ * states the replacement's own run properties — `compareDocx` reproducing
131
+ * the target document — keeps them instead.
132
+ */
133
+ replacementBackground?: FolioReplacementBackground;
127
134
  };
128
135
  type FolioHeaderFooterStoryHandle = Extract<FolioEditableDocumentStoryHandle, {
129
136
  type: "header" | "footer";
@@ -382,7 +389,7 @@ declare class FolioDocxReviewer {
382
389
  */
383
390
  applyDocumentOperations(batch: FolioDocumentOperationBatch, options?: FolioApplyDocumentOperationsOptions): FolioDocumentOperationResult;
384
391
  /** Apply a versioned operation batch to one editable document story. */
385
- applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates }: FolioApplyDocumentOperationsToStoryOptions): FolioDocumentOperationResult;
392
+ applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates, replacementBackground }: FolioApplyDocumentOperationsToStoryOptions): FolioDocumentOperationResult;
386
393
  private applyDocumentOperationsInternal;
387
394
  /** Undo the latest unchanged document-operation batch and its created comments. */
388
395
  undoDocumentOperations(undoHandle: FolioDocumentOperationUndoHandle): FolioDocumentOperationUndoResult;
@@ -782,7 +782,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
782
782
  });
783
783
  }
784
784
  /** Apply a versioned operation batch to one editable document story. */
785
- applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates }) {
785
+ applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates, replacementBackground }) {
786
786
  return this.applyDocumentOperationsInternal({
787
787
  story,
788
788
  batch,
@@ -790,10 +790,11 @@ var FolioDocxReviewer = class FolioDocxReviewer {
790
790
  ...revisionStamp !== void 0 && { revisionStamp },
791
791
  ...wordDiff !== void 0 && { wordDiff },
792
792
  ...tableTemplates !== void 0 && { tableTemplates },
793
+ ...replacementBackground !== void 0 && { replacementBackground },
793
794
  createUndoEntry: true
794
795
  });
795
796
  }
796
- applyDocumentOperationsInternal({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates, createUndoEntry }) {
797
+ applyDocumentOperationsInternal({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates, replacementBackground, createUndoEntry }) {
797
798
  const beforeState = this.requireEditableStoryState(story);
798
799
  const createdCommentsLengthBefore = this.createdComments.length;
799
800
  const view = {
@@ -811,6 +812,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
811
812
  ...revisionStamp !== void 0 && { revisionStamp },
812
813
  ...wordDiff !== void 0 && { wordDiff },
813
814
  ...tableTemplates !== void 0 && { tableTemplates },
815
+ ...replacementBackground !== void 0 && { replacementBackground },
814
816
  createCommentId: (text) => {
815
817
  const comment = createReviewerComment(this.nextCommentId(), text, this.author);
816
818
  this.createdComments.push(comment);
@@ -573,7 +573,8 @@ const applyComparison = ({ reviewer, revisionStamp, granularity, revisionFormat,
573
573
  mode: "tracked-changes",
574
574
  operations: plan.operations
575
575
  },
576
- tableTemplates: resolveTableTemplates(targetTables, plan.tableTemplates)
576
+ tableTemplates: resolveTableTemplates(targetTables, plan.tableTemplates),
577
+ replacementBackground: "keep"
577
578
  });
578
579
  if (nextRevisionId === void 0) panic("The applier did not report where it left the revision-id counter", { story: pair.baseStory });
579
580
  idSeed = nextRevisionId;
@@ -482,6 +482,37 @@ const groupFolioContentTableRows = (blocks) => {
482
482
  }
483
483
  return [...rows.values()];
484
484
  };
485
+ /**
486
+ * Which uninterrupted run of its own cell each block belongs to.
487
+ *
488
+ * A cell holds paragraphs and nested tables, and the nested table splits the
489
+ * cell's paragraphs into runs: the ones before it and the ones after it are
490
+ * separate sequences, because nothing moves a block from one side of a table
491
+ * to the other. Aligned as one sequence they pair across it, and the plan that
492
+ * follows can neither move the base paragraph past the nested table nor delete
493
+ * the one it left behind when that paragraph ends the cell. So the runs are
494
+ * aligned separately, the way rows and cells already are.
495
+ *
496
+ * Document order is the whole input: a cell's blocks are contiguous until a
497
+ * block of another cell interrupts them, and inside one table segment the only
498
+ * cell that can interrupt another is one of a table nested in it.
499
+ */
500
+ const cellRunOrdinals = (blocks) => {
501
+ const ordinals = /* @__PURE__ */ new Map();
502
+ const runByCell = /* @__PURE__ */ new Map();
503
+ let previousKey = null;
504
+ for (const block of blocks) {
505
+ const { table } = block;
506
+ if (!table) continue;
507
+ const key = `${String(table.tableIndex)}:${String(table.rowIndex)}:${String(table.cellIndex)}`;
508
+ const seen = runByCell.get(key) ?? -1;
509
+ const run = key === previousKey ? seen : seen + 1;
510
+ runByCell.set(key, run);
511
+ ordinals.set(block, run);
512
+ previousKey = key;
513
+ }
514
+ return ordinals;
515
+ };
485
516
  const groupTables = (blocks) => {
486
517
  const tables = /* @__PURE__ */ new Map();
487
518
  for (const block of blocks) {
@@ -1013,7 +1044,7 @@ const alignProfiledContentSequence = ({ base, revised, workSession, canPair = ()
1013
1044
  return aligned;
1014
1045
  };
1015
1046
  const rowLocation = (row) => row.at(0)?.table ?? null;
1016
- const alignRowCells = ({ baseRow, revisedRow, workSession, moveScopeContext, stableIdMismatch, idStability, baseColumnKeys, revisedColumnKeys }) => {
1047
+ const alignRowCells = ({ baseRow, revisedRow, workSession, moveScopeContext, stableIdMismatch, idStability, baseColumnKeys, revisedColumnKeys, cellRuns }) => {
1017
1048
  const byCell = (row, columnKeys) => {
1018
1049
  const cells = /* @__PURE__ */ new Map();
1019
1050
  for (const block of row) {
@@ -1024,47 +1055,62 @@ const alignRowCells = ({ baseRow, revisedRow, workSession, moveScopeContext, sta
1024
1055
  if (alignedColumn === void 0) return panic("A paired table cell has no aligned grid column", { gridColumnIndex: table.gridColumnIndex });
1025
1056
  cellIndex = alignedColumn;
1026
1057
  }
1027
- const blocks = cells.get(cellIndex);
1058
+ const runs = cells.get(cellIndex) ?? /* @__PURE__ */ new Map();
1059
+ cells.set(cellIndex, runs);
1060
+ const run = cellRuns.get(block) ?? 0;
1061
+ const blocks = runs.get(run);
1028
1062
  if (blocks) blocks.push(block);
1029
- else cells.set(cellIndex, [block]);
1063
+ else runs.set(run, [block]);
1030
1064
  }
1031
1065
  return cells;
1032
1066
  };
1067
+ const ascending = (left, right) => left - right;
1033
1068
  const baseCells = byCell(baseRow, baseColumnKeys);
1034
1069
  const revisedCells = byCell(revisedRow, revisedColumnKeys);
1035
- const cellIndexes = [.../* @__PURE__ */ new Set([...baseCells.keys(), ...revisedCells.keys()])].toSorted((left, right) => left - right);
1070
+ const cellIndexes = [.../* @__PURE__ */ new Set([...baseCells.keys(), ...revisedCells.keys()])].toSorted(ascending);
1036
1071
  const steps = [];
1037
1072
  for (const cellIndex of cellIndexes) {
1038
- const baseBlocks = baseCells.get(cellIndex) ?? [];
1039
- const revisedBlocks = revisedCells.get(cellIndex) ?? [];
1040
- const aligned = alignFolioContentBlocksInScope(baseBlocks, revisedBlocks, {
1073
+ const baseRuns = baseCells.get(cellIndex) ?? /* @__PURE__ */ new Map();
1074
+ const revisedRuns = revisedCells.get(cellIndex) ?? /* @__PURE__ */ new Map();
1075
+ const runOrdinals = [.../* @__PURE__ */ new Set([...baseRuns.keys(), ...revisedRuns.keys()])].toSorted(ascending);
1076
+ for (const run of runOrdinals) steps.push(...alignCellRun({
1077
+ baseBlocks: baseRuns.get(run) ?? [],
1078
+ revisedBlocks: revisedRuns.get(run) ?? [],
1041
1079
  workSession,
1042
- idStability,
1080
+ moveScopeContext,
1043
1081
  stableIdMismatch,
1044
- structuralScope: "pairedTableCell"
1045
- }).flatMap((event) => {
1046
- if (event.type === "pair" && !contentBlocksShareContainerPath(event.baseBlock, event.revisedBlock)) return [{
1047
- type: "baseOnly",
1048
- block: event.baseBlock
1049
- }, {
1050
- type: "revisedOnly",
1051
- block: event.revisedBlock
1052
- }];
1053
- return [event];
1054
- });
1055
- const bucketByContainerPath = /* @__PURE__ */ new Map();
1056
- const bucketForBlock = (block) => {
1057
- const path = containerPathKeyOf(block);
1058
- const existing = bucketByContainerPath.get(path);
1059
- if (existing !== void 0) return existing;
1060
- const bucket = moveScopeContext.nextTableCellBucket++;
1061
- bucketByContainerPath.set(path, bucket);
1062
- return bucket;
1063
- };
1064
- steps.push(...scopedAlignmentSteps(aligned, moveScopeContext, bucketForBlock));
1082
+ idStability
1083
+ }));
1065
1084
  }
1066
1085
  return steps;
1067
1086
  };
1087
+ const alignCellRun = ({ baseBlocks, revisedBlocks, workSession, moveScopeContext, stableIdMismatch, idStability }) => {
1088
+ const aligned = alignFolioContentBlocksInScope(baseBlocks, revisedBlocks, {
1089
+ workSession,
1090
+ idStability,
1091
+ stableIdMismatch,
1092
+ structuralScope: "pairedTableCell"
1093
+ }).flatMap((event) => {
1094
+ if (event.type === "pair" && !contentBlocksShareContainerPath(event.baseBlock, event.revisedBlock)) return [{
1095
+ type: "baseOnly",
1096
+ block: event.baseBlock
1097
+ }, {
1098
+ type: "revisedOnly",
1099
+ block: event.revisedBlock
1100
+ }];
1101
+ return [event];
1102
+ });
1103
+ const bucketByContainerPath = /* @__PURE__ */ new Map();
1104
+ const bucketForBlock = (block) => {
1105
+ const path = containerPathKeyOf(block);
1106
+ const existing = bucketByContainerPath.get(path);
1107
+ if (existing !== void 0) return existing;
1108
+ const bucket = moveScopeContext.nextTableCellBucket++;
1109
+ bucketByContainerPath.set(path, bucket);
1110
+ return bucket;
1111
+ };
1112
+ return scopedAlignmentSteps(aligned, moveScopeContext, bucketForBlock);
1113
+ };
1068
1114
  const pairTableRows = ({ baseRows, revisedRows, workSession, idStability }) => {
1069
1115
  const profile = (row) => ({
1070
1116
  item: row,
@@ -1097,7 +1143,7 @@ const pairTableRows = ({ baseRows, revisedRows, workSession, idStability }) => {
1097
1143
  }
1098
1144
  });
1099
1145
  };
1100
- const alignTableRows = ({ rows, workSession, moveScopeContext, stableIdMismatch, idStability, baseColumnKeys, revisedColumnKeys }) => {
1146
+ const alignTableRows = ({ rows, workSession, moveScopeContext, stableIdMismatch, idStability, baseColumnKeys, revisedColumnKeys, cellRuns }) => {
1101
1147
  const steps = [];
1102
1148
  const pushRow = (row, side) => {
1103
1149
  const location = rowLocation(row);
@@ -1123,7 +1169,8 @@ const alignTableRows = ({ rows, workSession, moveScopeContext, stableIdMismatch,
1123
1169
  stableIdMismatch,
1124
1170
  idStability,
1125
1171
  baseColumnKeys,
1126
- revisedColumnKeys
1172
+ revisedColumnKeys,
1173
+ cellRuns
1127
1174
  }));
1128
1175
  break;
1129
1176
  default: panic("Unhandled table row alignment", { alignment });
@@ -1148,7 +1195,7 @@ const rowCellSpansEqual = (baseRow, revisedRow) => {
1148
1195
  });
1149
1196
  };
1150
1197
  const rowHasVerticalSpan = (row) => row.some(({ table }) => table !== void 0 && table.rowSpan > 1);
1151
- const buildTablePlan = ({ baseBlocks, revisedBlocks, workSession, moveScopeContext, stableIdMismatch, idStability }) => {
1198
+ const buildTablePlan = ({ baseBlocks, revisedBlocks, workSession, moveScopeContext, stableIdMismatch, idStability, cellRuns }) => {
1152
1199
  const resolveIdStability = idStability ?? folioContentIdStability;
1153
1200
  const columns = alignTableColumns(baseBlocks, revisedBlocks);
1154
1201
  const rows = pairTableRows({
@@ -1169,13 +1216,15 @@ const buildTablePlan = ({ baseBlocks, revisedBlocks, workSession, moveScopeConte
1169
1216
  stableIdMismatch,
1170
1217
  idStability: resolveIdStability,
1171
1218
  baseColumnKeys: columns?.baseColumnKeys,
1172
- revisedColumnKeys: columns?.revisedColumnKeys
1219
+ revisedColumnKeys: columns?.revisedColumnKeys,
1220
+ cellRuns
1173
1221
  })]
1174
1222
  };
1175
1223
  };
1176
1224
  const buildTableSegmentPlan = ({ baseBlocks, revisedBlocks, workSession, moveScopeContext, stableIdMismatch, idStability }) => {
1177
1225
  const baseTables = groupTables(baseBlocks);
1178
1226
  const revisedTables = groupTables(revisedBlocks);
1227
+ const cellRuns = new Map([...cellRunOrdinals(baseBlocks), ...cellRunOrdinals(revisedBlocks)]);
1179
1228
  const resolveIdStability = idStability ?? folioContentIdStability;
1180
1229
  const profile = (blocks) => ({
1181
1230
  item: blocks,
@@ -1197,7 +1246,8 @@ const buildTableSegmentPlan = ({ baseBlocks, revisedBlocks, workSession, moveSco
1197
1246
  workSession,
1198
1247
  moveScopeContext,
1199
1248
  stableIdMismatch,
1200
- idStability: resolveIdStability
1249
+ idStability: resolveIdStability,
1250
+ cellRuns
1201
1251
  });
1202
1252
  steps.push(...table.steps);
1203
1253
  representable &&= table.representable;
@@ -1,7 +1,7 @@
1
1
  import { document_d_exports } from "./types/document.js";
2
2
  import { FolioTableTemplates } from "./ai-edits/table-template.js";
3
3
  import { FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditNormalization, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAITextRangeHandle } from "./ai-edits/types.js";
4
- import { FolioAIEditView, FolioRevisionStamp, FolioWordDiffOptions } from "./ai-edits/apply.js";
4
+ import { FolioAIEditView, FolioReplacementBackground, FolioRevisionStamp, FolioWordDiffOptions } from "./ai-edits/apply.js";
5
5
  //#region src/document-operations.d.ts
6
6
  declare const FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION: 1;
7
7
  /** Direct paragraph-alignment values accepted by the operation contract. */
@@ -282,7 +282,14 @@ type ApplyFolioDocumentOperationsOptions = {
282
282
  * copying the target document's — hands the whole thing over instead.
283
283
  */
284
284
  tableTemplates?: FolioTableTemplates;
285
+ /**
286
+ * What a replacement in the batch does with a highlight or `w:shd` the text
287
+ * it replaces carries. Clearing it is the authoring default; a caller that
288
+ * states the replacement's own run properties — a comparison reproducing the
289
+ * revised document — keeps them instead.
290
+ */
291
+ replacementBackground?: FolioReplacementBackground;
285
292
  };
286
- declare const applyFolioDocumentOperations: ({ view, snapshot, batch, story, author, createCommentId, createUndoHandle, revisionStamp, wordDiff, tableTemplates }: ApplyFolioDocumentOperationsOptions) => FolioDocumentOperationResult;
293
+ declare const applyFolioDocumentOperations: ({ view, snapshot, batch, story, author, createCommentId, createUndoHandle, revisionStamp, wordDiff, tableTemplates, replacementBackground }: ApplyFolioDocumentOperationsOptions) => FolioDocumentOperationResult;
287
294
  //#endregion
288
295
  export { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PAGE_BREAK_CLEAR_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationBatchPrecondition, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationQueuedOperation, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
@@ -1183,7 +1183,7 @@ const getFolioDocumentOperationReceiptsForStory = ({ operations, applied, story
1183
1183
  });
1184
1184
  return receipts;
1185
1185
  };
1186
- const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", author, createCommentId, createUndoHandle, revisionStamp, wordDiff, tableTemplates }) => {
1186
+ const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", author, createCommentId, createUndoHandle, revisionStamp, wordDiff, tableTemplates, replacementBackground }) => {
1187
1187
  const parsedBatch = parseFolioDocumentOperationBatch(batch);
1188
1188
  const apply = ({ targetView, targetCreateCommentId = createCommentId, preview = false }) => {
1189
1189
  return (preview ? previewFolioAIEditOperations : applyFolioAIEditOperations)({
@@ -1195,7 +1195,8 @@ const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", a
1195
1195
  ...targetCreateCommentId !== void 0 && { createCommentId: targetCreateCommentId },
1196
1196
  ...revisionStamp !== void 0 && { revisionStamp },
1197
1197
  ...wordDiff !== void 0 && { wordDiff },
1198
- ...tableTemplates !== void 0 && { tableTemplates }
1198
+ ...tableTemplates !== void 0 && { tableTemplates },
1199
+ ...replacementBackground !== void 0 && { replacementBackground }
1199
1200
  });
1200
1201
  };
1201
1202
  const preview = () => apply({
@@ -833,7 +833,7 @@ function parseCellContent(tcElement, styles, theme, numbering, rels, media, opti
833
833
  if (preserved?.children) captured.push(...preserved.children);
834
834
  };
835
835
  dispatchCellChildren(tcElement, options);
836
- if (!modelled.some((block) => block.type === "paragraph" || block.type === "table")) modelled.push({
836
+ if (modelled.findLast((block) => block.type === "paragraph" || block.type === "table")?.type !== "paragraph") modelled.push({
837
837
  type: "paragraph",
838
838
  content: []
839
839
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-core",
3
- "version": "0.47.0",
3
+ "version": "0.47.1",
4
4
  "description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
5
5
  "keywords": [
6
6
  "document-model",