@stll/folio-core 0.3.1 → 0.5.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.
- package/dist/ai-edits/apply.d.ts +3 -9
- package/dist/ai-edits/apply.js +94 -14
- package/dist/ai-edits/blockRange.d.ts +33 -0
- package/dist/ai-edits/blockRange.js +58 -0
- package/dist/ai-edits/headless.d.ts +35 -5
- package/dist/ai-edits/headless.js +92 -22
- package/dist/ai-edits/index.d.ts +5 -3
- package/dist/ai-edits/index.js +3 -2
- package/dist/ai-edits/snapshot.d.ts +14 -2
- package/dist/ai-edits/snapshot.js +12 -1
- package/dist/ai-edits/types.d.ts +42 -3
- package/dist/compat/eigenpal.d.ts +3 -2
- package/dist/compat/eigenpal.js +6 -5
- package/dist/controller/layoutPipeline.d.ts +1 -0
- package/dist/controller/layoutPipeline.js +35 -115
- package/dist/controller/layoutScheduler.d.ts +2 -1
- package/dist/controller/layoutScheduler.js +6 -0
- package/dist/document-operations.d.ts +122 -0
- package/dist/document-operations.js +575 -0
- package/dist/docx/blockContentParser.js +17 -7
- package/dist/docx/encryption/agileDecryption.d.ts +6 -0
- package/dist/docx/encryption/agileDecryption.js +209 -0
- package/dist/docx/encryption/compoundFile.d.ts +20 -0
- package/dist/docx/encryption/compoundFile.js +251 -0
- package/dist/docx/encryption/containerFormat.d.ts +15 -0
- package/dist/docx/encryption/containerFormat.js +40 -0
- package/dist/docx/encryption/cryptoBytes.d.ts +11 -0
- package/dist/docx/encryption/cryptoBytes.js +55 -0
- package/dist/docx/encryption/encryptionInfo.d.ts +32 -0
- package/dist/docx/encryption/encryptionInfo.js +128 -0
- package/dist/docx/encryption/errors.d.ts +18 -0
- package/dist/docx/encryption/errors.js +13 -0
- package/dist/docx/encryption/index.d.ts +4 -0
- package/dist/docx/encryption/index.js +4 -0
- package/dist/docx/encryption/openEncryptedDocx.d.ts +28 -0
- package/dist/docx/encryption/openEncryptedDocx.js +65 -0
- package/dist/docx/footnoteParser.d.ts +1 -17
- package/dist/docx/footnoteParser.js +1 -27
- package/dist/docx/groupDrawingParser.d.ts +8 -0
- package/dist/docx/groupDrawingParser.js +131 -0
- package/dist/docx/index.d.ts +3 -1
- package/dist/docx/index.js +3 -1
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +8 -2
- package/dist/docx/paragraphParser.js +9 -3
- package/dist/docx/parser.d.ts +4 -5
- package/dist/docx/parser.js +8 -2
- package/dist/docx/runParser.js +8 -1
- package/dist/docx/selectiveXmlPatch.js +4 -10
- package/dist/docx/serializer/numberingSerializer.js +3 -1
- package/dist/docx/settingsParser.d.ts +6 -3
- package/dist/docx/settingsParser.js +2 -0
- package/dist/docx/unzip.d.ts +7 -5
- package/dist/docx/unzip.js +8 -2
- package/dist/docx/xmlParser.js +11 -4
- package/dist/index.d.ts +3 -2
- package/dist/index.js +6 -5
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +37 -1
- package/dist/layout-bridge/convert/footnoteLayout.js +138 -14
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +6 -8
- package/dist/layout-bridge/convert/headerFooterLayout.js +21 -13
- package/dist/layout-bridge/convert/toFlowBlocks.js +58 -19
- package/dist/layout-engine/index.d.ts +6 -5
- package/dist/layout-engine/index.js +116 -27
- package/dist/layout-engine/keep-together.d.ts +13 -8
- package/dist/layout-engine/keep-together.js +40 -25
- package/dist/layout-engine/layoutInstrumentation.d.ts +11 -1
- package/dist/layout-engine/layoutInstrumentation.js +10 -1
- package/dist/layout-engine/measure/measureBlocks.js +43 -19
- package/dist/layout-engine/measure/measureParagraph.js +91 -14
- package/dist/layout-engine/paginator.d.ts +2 -1
- package/dist/layout-engine/paginator.js +9 -3
- package/dist/layout-engine/textBoxGroup.d.ts +7 -0
- package/dist/layout-engine/textBoxGroup.js +8 -0
- package/dist/layout-engine/types.d.ts +33 -9
- package/dist/layout-engine/types.js +20 -2
- package/dist/layout-painter/renderPage.d.ts +15 -1
- package/dist/layout-painter/renderPage.js +29 -12
- package/dist/layout-painter/renderParagraph.js +44 -9
- package/dist/layout-painter/renderTable.js +27 -7
- package/dist/managers/DocumentLoaderManager.d.ts +3 -1
- package/dist/managers/DocumentLoaderManager.js +13 -5
- package/dist/managers/TableSelectionManager.js +1 -1
- package/dist/model.js +1 -1
- package/dist/paged-layout/pageText.d.ts +14 -0
- package/dist/paged-layout/pageText.js +21 -0
- package/dist/prosemirror/attrs/index.js +3 -0
- package/dist/prosemirror/commands/comments.js +75 -5
- package/dist/prosemirror/commands/hyperlink.js +4 -2
- package/dist/prosemirror/commands/image.js +2 -1
- package/dist/prosemirror/commands/index.js +1 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +68 -0
- package/dist/prosemirror/commands/propertyChangeScope.js +242 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +21 -5
- package/dist/prosemirror/conversion/toProseDoc.js +43 -15
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +3 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +6 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.js +12 -11
- package/dist/prosemirror/findReplaceSelection.js +2 -1
- package/dist/prosemirror/index.js +1 -1
- package/dist/prosemirror/insertOperations.js +1 -1
- package/dist/prosemirror/schema/nodes.d.ts +14 -7
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/utils/tabCalculator.js +4 -2
- package/dist/redline.d.ts +20 -0
- package/dist/redline.js +127 -0
- package/dist/server.d.ts +7 -3
- package/dist/server.js +6 -2
- package/dist/symbols.d.ts +35 -0
- package/dist/symbols.js +512 -0
- package/dist/utils/clipboard.js +3 -3
- package/dist/utils/fontLoader.js +3 -0
- package/dist/utils/fontResolver.js +65 -1
- package/dist/utils/zoom.d.ts +21 -0
- package/dist/utils/zoom.js +21 -0
- package/dist/version-comparison.d.ts +92 -0
- package/dist/version-comparison.js +436 -0
- package/package.json +7 -3
- package/dist/paged-layout/headerFooterMargins.d.ts +0 -73
- package/dist/paged-layout/headerFooterMargins.js +0 -120
|
@@ -152,7 +152,8 @@ const tableSpec = {
|
|
|
152
152
|
cellMargins: { default: null },
|
|
153
153
|
look: { default: null },
|
|
154
154
|
borders: { default: null },
|
|
155
|
-
_originalFormatting: { default: null }
|
|
155
|
+
_originalFormatting: { default: null },
|
|
156
|
+
tblPrChange: { default: null }
|
|
156
157
|
},
|
|
157
158
|
parseDOM: [{
|
|
158
159
|
tag: "table",
|
|
@@ -171,17 +172,12 @@ const tableSpec = {
|
|
|
171
172
|
const domAttrs = { class: "docx-table" };
|
|
172
173
|
if (attrs.styleId) domAttrs["data-style-id"] = attrs.styleId;
|
|
173
174
|
const styles = ["border-collapse: collapse"];
|
|
174
|
-
if (attrs.width !== void 0 && attrs.widthType === "pct") {
|
|
175
|
-
|
|
176
|
-
styles.push("table-layout: fixed");
|
|
177
|
-
} else if (attrs.width !== void 0 && attrs.widthType === "dxa") {
|
|
175
|
+
if (attrs.width !== void 0 && attrs.widthType === "pct") styles.push(`width: ${attrs.width / 50}%`);
|
|
176
|
+
else if (attrs.width !== void 0 && attrs.widthType === "dxa") {
|
|
178
177
|
const widthPx = Math.round(attrs.width / 20 * 1.333);
|
|
179
178
|
styles.push(`width: ${widthPx}px`);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
styles.push("width: 100%");
|
|
183
|
-
styles.push("table-layout: fixed");
|
|
184
|
-
}
|
|
179
|
+
} else styles.push("width: 100%");
|
|
180
|
+
styles.push("table-layout: fixed");
|
|
185
181
|
if (attrs.justification === "center") styles.push("margin-left: auto", "margin-right: auto");
|
|
186
182
|
else if (attrs.justification === "right") styles.push("margin-left: auto");
|
|
187
183
|
domAttrs["style"] = styles.join("; ");
|
|
@@ -199,13 +195,16 @@ const tableRowSpec = {
|
|
|
199
195
|
height: { default: null },
|
|
200
196
|
heightRule: { default: null },
|
|
201
197
|
isHeader: { default: false },
|
|
202
|
-
|
|
198
|
+
hidden: { default: false },
|
|
199
|
+
_originalFormatting: { default: null },
|
|
200
|
+
trPrChange: { default: null }
|
|
203
201
|
},
|
|
204
202
|
parseDOM: [{ tag: "tr" }],
|
|
205
203
|
toDOM(node) {
|
|
206
204
|
const attrs = expectTableRowAttrs(node);
|
|
207
205
|
const domAttrs = {};
|
|
208
206
|
if (typeof attrs.height === "number") domAttrs["style"] = `height: ${Math.round(attrs.height / 20 * 1.333)}px`;
|
|
207
|
+
if (attrs.hidden) domAttrs["style"] = `${domAttrs["style"] ? `${domAttrs["style"]}; ` : ""}display: none`;
|
|
209
208
|
return [
|
|
210
209
|
"tr",
|
|
211
210
|
domAttrs,
|
|
@@ -315,6 +314,7 @@ const tableCellSpec = {
|
|
|
315
314
|
textDirection: { default: null },
|
|
316
315
|
noWrap: { default: false },
|
|
317
316
|
_originalFormatting: { default: null },
|
|
317
|
+
tcPrChange: { default: null },
|
|
318
318
|
_preserveVMergeRestart: { default: null },
|
|
319
319
|
_docxVMergeContinuationCells: { default: null }
|
|
320
320
|
},
|
|
@@ -367,6 +367,7 @@ const tableHeaderSpec = {
|
|
|
367
367
|
textDirection: { default: null },
|
|
368
368
|
noWrap: { default: false },
|
|
369
369
|
_originalFormatting: { default: null },
|
|
370
|
+
tcPrChange: { default: null },
|
|
370
371
|
_preserveVMergeRestart: { default: null },
|
|
371
372
|
_docxVMergeContinuationCells: { default: null }
|
|
372
373
|
},
|
|
@@ -44,7 +44,8 @@ function resolveFindMatchRange(doc, match) {
|
|
|
44
44
|
cellOffset += cell.nodeSize;
|
|
45
45
|
continue;
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
const cellPos = rowPos + 1 + cellOffset;
|
|
48
|
+
if (!walkBlocks(cell, cellPos + 1)) return false;
|
|
48
49
|
cellOffset += cell.nodeSize;
|
|
49
50
|
}
|
|
50
51
|
rowOffset += row.nodeSize;
|
|
@@ -11,10 +11,10 @@ import { createSelectionTrackerPlugin, extractSelectionContext, getSelectionCont
|
|
|
11
11
|
import { getHyperlinkAttrs, getSelectedText, isHyperlinkActive } from "./extensions/marks/HyperlinkExtension.js";
|
|
12
12
|
import { schema, singletonManager } from "./schema/index.js";
|
|
13
13
|
import { createEmptyDoc, toProseDoc } from "./conversion/toProseDoc.js";
|
|
14
|
-
import { addColumnLeft, addColumnRight, addRowAbove, addRowBelow, applyTableStyle, autoFitContents, deleteColumn, deleteRow, deleteTable, distributeColumns, insertTable, mergeCells, removeTableBorders, selectColumn, selectRow, selectTable, setAllTableBorders, setCellBorder, setCellFillColor, setCellMargins, setCellTextDirection, setCellVerticalAlign, setInsideTableBorders, setOutsideTableBorders, setRowHeight, setTableBorderColor, setTableBorderPreset, setTableBorderWidth, setTableBorders, setTableProperties, splitCell, toggleHeaderRow, toggleNoWrap } from "./commands/table.js";
|
|
15
14
|
import { extractSelectionState } from "./selectionState.js";
|
|
16
15
|
import { clearFontFamily, clearFontSize, clearHighlight, clearTextColor, insertHyperlink, removeHyperlink, setFontFamily, setFontSize, setHighlight, setHyperlink, setTextColor, toggleBold, toggleItalic, toggleStrike, toggleSubscript, toggleSuperscript, toggleUnderline } from "./commands/formatting.js";
|
|
17
16
|
import { addTabStop, alignCenter, alignJustify, alignLeft, alignRight, applyStyle, clearStyle, decreaseIndent, decreaseListLevel, generateTOC, increaseIndent, increaseListLevel, removeList, removeTabStop, setAlignment, setIndentFirstLine, setIndentLeft, setIndentRight, setLineSpacing, setLtr, setRtl, toggleBidi, toggleBulletList, toggleNumberedList } from "./commands/paragraph.js";
|
|
17
|
+
import { addColumnLeft, addColumnRight, addRowAbove, addRowBelow, applyTableStyle, autoFitContents, deleteColumn, deleteRow, deleteTable, distributeColumns, insertTable, mergeCells, removeTableBorders, selectColumn, selectRow, selectTable, setAllTableBorders, setCellBorder, setCellFillColor, setCellMargins, setCellTextDirection, setCellVerticalAlign, setInsideTableBorders, setOutsideTableBorders, setRowHeight, setTableBorderColor, setTableBorderPreset, setTableBorderWidth, setTableBorders, setTableProperties, splitCell, toggleHeaderRow, toggleNoWrap } from "./commands/table.js";
|
|
18
18
|
import { PAINTABLE_MARK_NAMES, applyFormatMarks, captureFormatMarks } from "./commands/formatPainter.js";
|
|
19
19
|
import { insertPageBreak } from "./commands/pageBreak.js";
|
|
20
20
|
import { insertImageFromFile } from "./commands/image.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { insertTable } from "./commands/table.js";
|
|
2
1
|
import { generateTOC } from "./commands/paragraph.js";
|
|
2
|
+
import { insertTable } from "./commands/table.js";
|
|
3
3
|
import { insertPageBreak } from "./commands/pageBreak.js";
|
|
4
4
|
import { insertImageFromFile } from "./commands/image.js";
|
|
5
5
|
//#region src/prosemirror/insertOperations.ts
|
|
@@ -18,7 +18,8 @@ type ParagraphAttrs = {
|
|
|
18
18
|
spaceAfter?: number;
|
|
19
19
|
lineSpacing?: number;
|
|
20
20
|
lineSpacingRule?: document_d_exports.LineSpacingRule;
|
|
21
|
-
spacingExplicit?: SpacingExplicit;
|
|
21
|
+
spacingExplicit?: SpacingExplicit; /** Layout provenance: document defaults survive on empty paragraphs. */
|
|
22
|
+
spacingFromDocDefaults?: SpacingExplicit;
|
|
22
23
|
indentLeft?: number;
|
|
23
24
|
indentRight?: number;
|
|
24
25
|
indentFirstLine?: number;
|
|
@@ -65,7 +66,8 @@ type ParagraphAttrs = {
|
|
|
65
66
|
* inline LISTNUM "(a)" with the deeper level's marker column.
|
|
66
67
|
*/
|
|
67
68
|
listMarkerSecondSlotOffsetTwips?: number; /** Number format for each level used by multi-level marker templates. */
|
|
68
|
-
listLevelNumFmts?: document_d_exports.NumberFormat[]; /**
|
|
69
|
+
listLevelNumFmts?: document_d_exports.NumberFormat[]; /** Initial counter for each level used by multi-level marker templates. */
|
|
70
|
+
listLevelStarts?: number[]; /** Abstract numbering ID shared by numbering instances. */
|
|
69
71
|
listAbstractNumId?: number; /** Numbering start override for this numId/level. */
|
|
70
72
|
listStartOverride?: number;
|
|
71
73
|
styleId?: string;
|
|
@@ -82,7 +84,8 @@ type ParagraphAttrs = {
|
|
|
82
84
|
pageBreakBefore?: boolean; /** Word's cached rendered-page-break marker; preserved for round-trip only. */
|
|
83
85
|
renderedPageBreakBefore?: boolean;
|
|
84
86
|
keepNext?: boolean;
|
|
85
|
-
keepLines?: boolean;
|
|
87
|
+
keepLines?: boolean;
|
|
88
|
+
widowControl?: boolean; /** Contextual spacing — suppress space between same-style paragraphs */
|
|
86
89
|
contextualSpacing?: boolean;
|
|
87
90
|
defaultTextFormatting?: document_d_exports.TextFormatting;
|
|
88
91
|
sectionBreakType?: "nextPage" | "continuous" | "oddPage" | "evenPage";
|
|
@@ -345,7 +348,8 @@ type TableAttrs = {
|
|
|
345
348
|
}; /** Table look flags for conditional formatting (w:tblLook) */
|
|
346
349
|
look?: document_d_exports.TableLook; /** Table-level borders (w:tblBorders) — full BorderSpec per side */
|
|
347
350
|
borders?: document_d_exports.TableBorders; /** Original table formatting from DOCX for lossless round-trip serialization */
|
|
348
|
-
_originalFormatting?: document_d_exports.TableFormatting;
|
|
351
|
+
_originalFormatting?: document_d_exports.TableFormatting; /** Tracked table property changes (w:tblPrChange) for round-trip + accept/reject */
|
|
352
|
+
tblPrChange?: document_d_exports.TablePropertyChange[];
|
|
349
353
|
};
|
|
350
354
|
/**
|
|
351
355
|
* Table row attributes
|
|
@@ -353,8 +357,10 @@ type TableAttrs = {
|
|
|
353
357
|
type TableRowAttrs = {
|
|
354
358
|
/** Row height (in twips) */height?: number; /** Height rule ('auto', 'exact', 'atLeast') */
|
|
355
359
|
heightRule?: NonNullable<document_d_exports.TableRowFormatting["heightRule"]>; /** Is header row */
|
|
356
|
-
isHeader?: boolean; /**
|
|
357
|
-
|
|
360
|
+
isHeader?: boolean; /** Whether the row is hidden (`w:hidden`) */
|
|
361
|
+
hidden?: boolean; /** Original row formatting from DOCX for lossless round-trip serialization */
|
|
362
|
+
_originalFormatting?: document_d_exports.TableRowFormatting; /** Tracked row property changes (w:trPrChange) for round-trip + accept/reject */
|
|
363
|
+
trPrChange?: document_d_exports.TableRowPropertyChange[];
|
|
358
364
|
};
|
|
359
365
|
/**
|
|
360
366
|
* Table cell attributes
|
|
@@ -381,7 +387,8 @@ type TableCellAttrs = {
|
|
|
381
387
|
left?: number;
|
|
382
388
|
right?: number;
|
|
383
389
|
}; /** Original cell formatting from DOCX for lossless round-trip serialization */
|
|
384
|
-
_originalFormatting?: document_d_exports.TableCellFormatting; /**
|
|
390
|
+
_originalFormatting?: document_d_exports.TableCellFormatting; /** Tracked cell property changes (w:tcPrChange) for round-trip + accept/reject */
|
|
391
|
+
tcPrChange?: document_d_exports.TableCellPropertyChange[]; /** Preserve a DOCX vMerge restart even when PM cannot model it as a rowspan. */
|
|
385
392
|
_preserveVMergeRestart?: boolean; /** Original DOCX vMerge continuation cells skipped into this PM rowspan. */
|
|
386
393
|
_docxVMergeContinuationCells?: document_d_exports.TableCell[];
|
|
387
394
|
};
|
|
@@ -91,6 +91,7 @@ function listAttrsFromResolvedStyle(resolved, numbering) {
|
|
|
91
91
|
listImplicitChildLevelAdvances: null,
|
|
92
92
|
listMarkerSecondSlotOffsetTwips: null,
|
|
93
93
|
listLevelNumFmts: rendering?.levelNumFmts ?? null,
|
|
94
|
+
listLevelStarts: rendering?.levelStarts ?? null,
|
|
94
95
|
listAbstractNumId: rendering?.abstractNumId ?? null,
|
|
95
96
|
listStartOverride: rendering?.startOverride ?? null
|
|
96
97
|
};
|
|
@@ -5,6 +5,8 @@ const DEFAULT_TAB_INTERVAL_TWIPS = 720;
|
|
|
5
5
|
const TWIPS_PER_INCH = 1440;
|
|
6
6
|
/** Pixels per inch (96 dpi standard for CSS) */
|
|
7
7
|
const PIXELS_PER_INCH = 96;
|
|
8
|
+
/** Ignore sub-twip round trips when the cursor already occupies a tab stop. */
|
|
9
|
+
const TAB_POSITION_EPSILON_TWIPS = .01;
|
|
8
10
|
/**
|
|
9
11
|
* Convert twips to pixels
|
|
10
12
|
* @param twips - Value in twips (1/1440 inch)
|
|
@@ -44,7 +46,7 @@ function computeTabStops(context) {
|
|
|
44
46
|
leader: "none"
|
|
45
47
|
});
|
|
46
48
|
}
|
|
47
|
-
let pos =
|
|
49
|
+
let pos = Math.floor(Math.max(maxExplicit, leftIndent) / defaultTabInterval) * defaultTabInterval;
|
|
48
50
|
const limitPos = leftIndent + 14400;
|
|
49
51
|
while (pos < limitPos) {
|
|
50
52
|
pos += defaultTabInterval;
|
|
@@ -84,7 +86,7 @@ function computeTabStops(context) {
|
|
|
84
86
|
function calculateTabWidth(currentXPx, context, following = {}) {
|
|
85
87
|
const { defaultTabInterval = 720 } = context;
|
|
86
88
|
const currentXTwips = pixelsToTwips(currentXPx);
|
|
87
|
-
const nextStop = computeTabStops(context).find((stop) => stop.pos > currentXTwips);
|
|
89
|
+
const nextStop = computeTabStops(context).find((stop) => stop.pos > currentXTwips + TAB_POSITION_EPSILON_TWIPS);
|
|
88
90
|
if (!nextStop) {
|
|
89
91
|
const defaultTabPx = twipsToPixels(defaultTabInterval);
|
|
90
92
|
let tabWidth = defaultTabPx - currentXPx % defaultTabPx;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FolioAIEditApplyResult } from "./ai-edits/types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/redline.d.ts
|
|
4
|
+
/** Options for {@link generateRedlineDocx}. */
|
|
5
|
+
type GenerateRedlineDocxOptions = {
|
|
6
|
+
/** Author recorded on the generated tracked changes. (default: `"folio compare"`) */author?: string;
|
|
7
|
+
};
|
|
8
|
+
/** Result of {@link generateRedlineDocx}. */
|
|
9
|
+
type GenerateRedlineDocxResult = FolioAIEditApplyResult & {
|
|
10
|
+
/** The redline `.docx`: the base document with base → revised tracked changes. */buffer: ArrayBuffer;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Compare `base` and `revised` and return the base document with every
|
|
14
|
+
* difference recorded as a tracked change. See the module doc comment for
|
|
15
|
+
* semantics and limitations; `skipped` reports any block the generator could
|
|
16
|
+
* not redline (e.g. additions with no anchor in an empty base document).
|
|
17
|
+
*/
|
|
18
|
+
declare const generateRedlineDocx: (base: ArrayBuffer, revised: ArrayBuffer, options?: GenerateRedlineDocxOptions) => Promise<GenerateRedlineDocxResult>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { GenerateRedlineDocxOptions, GenerateRedlineDocxResult, generateRedlineDocx };
|
package/dist/redline.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { FolioDocxReviewer } from "./ai-edits/headless.js";
|
|
2
|
+
import { alignFolioBlocks } from "./version-comparison.js";
|
|
3
|
+
//#region src/redline.ts
|
|
4
|
+
/**
|
|
5
|
+
* Redline generator: compare two `.docx` buffers and produce a THIRD `.docx`
|
|
6
|
+
* whose differences are recorded as real Word tracked changes (`w:ins` /
|
|
7
|
+
* `w:del`), ready for review in Word or the folio editor — the
|
|
8
|
+
* document-producing counterpart to {@link compareDocxVersions}'s structured
|
|
9
|
+
* diff.
|
|
10
|
+
*
|
|
11
|
+
* The pipeline is a composition of existing machinery, not a new engine:
|
|
12
|
+
* the base buffer is opened in a headless {@link FolioDocxReviewer}, the two
|
|
13
|
+
* snapshots are aligned with {@link alignFolioBlocks} (the same three-pass
|
|
14
|
+
* alignment the comparer uses), each alignment event maps onto a
|
|
15
|
+
* tracked-changes {@link FolioAIEditOperation}, and the reviewer's shared
|
|
16
|
+
* apply path records them as redlines. Word-level minimality comes free:
|
|
17
|
+
* the apply engine word-diffs a `replaceBlock` and only marks the divergent
|
|
18
|
+
* tokens.
|
|
19
|
+
*
|
|
20
|
+
* ## Semantics and limitations
|
|
21
|
+
*
|
|
22
|
+
* - **As-accepted inputs.** Pending tracked changes in either input count as
|
|
23
|
+
* applied before comparison (mirroring {@link compareDocxVersions}): the
|
|
24
|
+
* base's own pending redlines are accepted in the output, so the tracked
|
|
25
|
+
* changes it carries are exactly base → revised. This is intentionally
|
|
26
|
+
* lossy about the inputs' own revision history and authorship.
|
|
27
|
+
* - **Text redlines only.** Format-only changes and relocated blocks are
|
|
28
|
+
* redlined as plain edits (a move becomes a delete + insert, like Word
|
|
29
|
+
* compare with move detection off); block-level formatting is carried via
|
|
30
|
+
* the revised block's `styleId` on inserted blocks.
|
|
31
|
+
* - **Anchoring.** Insertions anchor `before` the next surviving base block
|
|
32
|
+
* so consecutive additions keep their order; additions past the last base
|
|
33
|
+
* block anchor `after` it. A base document with no content blocks offers
|
|
34
|
+
* no anchor at all — such additions surface in `skipped` rather than
|
|
35
|
+
* silently vanishing.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* For each event index, the base block id the next `revisedOnly` event
|
|
39
|
+
* should anchor before: the base side of the nearest later `pair` or
|
|
40
|
+
* `baseOnly` event, or `null` when only additions remain until the end of
|
|
41
|
+
* the document. One backward pass, so a long run of trailing additions
|
|
42
|
+
* (huge revised document vs. a small base) stays linear instead of
|
|
43
|
+
* re-scanning the tail per added block.
|
|
44
|
+
*/
|
|
45
|
+
const nextBaseBlockIdByIndex = (events) => {
|
|
46
|
+
const nextIds = Array.from({ length: events.length });
|
|
47
|
+
let nextId = null;
|
|
48
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
49
|
+
nextIds[i] = nextId;
|
|
50
|
+
const event = events[i];
|
|
51
|
+
if (event?.type === "pair") nextId = event.baseBlock.id;
|
|
52
|
+
else if (event?.type === "baseOnly") nextId = event.block.id;
|
|
53
|
+
}
|
|
54
|
+
return nextIds;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Compare `base` and `revised` and return the base document with every
|
|
58
|
+
* difference recorded as a tracked change. See the module doc comment for
|
|
59
|
+
* semantics and limitations; `skipped` reports any block the generator could
|
|
60
|
+
* not redline (e.g. additions with no anchor in an empty base document).
|
|
61
|
+
*/
|
|
62
|
+
const generateRedlineDocx = async (base, revised, options = {}) => {
|
|
63
|
+
const [baseReviewer, revisedReviewer] = await Promise.all([FolioDocxReviewer.fromBuffer(base, { author: options.author ?? "folio compare" }), FolioDocxReviewer.fromBuffer(revised)]);
|
|
64
|
+
baseReviewer.acceptAll();
|
|
65
|
+
const baseSnapshot = baseReviewer.snapshot();
|
|
66
|
+
const revisedBlocks = revisedReviewer.snapshot().blocks;
|
|
67
|
+
const events = alignFolioBlocks(baseSnapshot.blocks, revisedBlocks);
|
|
68
|
+
const anchorIds = nextBaseBlockIdByIndex(events);
|
|
69
|
+
const operations = [];
|
|
70
|
+
let operationSeq = 0;
|
|
71
|
+
const nextOperationId = () => `redline-${++operationSeq}`;
|
|
72
|
+
/** Additions past the last base block, inserted after it in reverse so the final order matches the revised document. */
|
|
73
|
+
const trailingAdditions = [];
|
|
74
|
+
const lastBaseBlockId = baseSnapshot.blocks.at(-1)?.id ?? null;
|
|
75
|
+
events.forEach((event, eventIndex) => {
|
|
76
|
+
if (event.type === "pair") {
|
|
77
|
+
if (event.baseBlock.text !== event.revisedBlock.text) operations.push({
|
|
78
|
+
id: nextOperationId(),
|
|
79
|
+
type: "replaceBlock",
|
|
80
|
+
blockId: event.baseBlock.id,
|
|
81
|
+
text: event.revisedBlock.text
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (event.type === "baseOnly") {
|
|
86
|
+
operations.push({
|
|
87
|
+
id: nextOperationId(),
|
|
88
|
+
type: "deleteBlock",
|
|
89
|
+
blockId: event.block.id
|
|
90
|
+
});
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const anchorId = anchorIds[eventIndex] ?? null;
|
|
94
|
+
if (anchorId === null) {
|
|
95
|
+
trailingAdditions.push({
|
|
96
|
+
text: event.block.text,
|
|
97
|
+
...event.block.styleId !== void 0 && { styleId: event.block.styleId }
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
operations.push({
|
|
102
|
+
id: nextOperationId(),
|
|
103
|
+
type: "insertBeforeBlock",
|
|
104
|
+
blockId: anchorId,
|
|
105
|
+
text: event.block.text,
|
|
106
|
+
...event.block.styleId !== void 0 && { styleId: event.block.styleId }
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
for (const addition of trailingAdditions.toReversed()) operations.push({
|
|
110
|
+
id: nextOperationId(),
|
|
111
|
+
type: "insertAfterBlock",
|
|
112
|
+
blockId: lastBaseBlockId ?? "redline-unanchored",
|
|
113
|
+
text: addition.text,
|
|
114
|
+
...addition.styleId !== void 0 && { styleId: addition.styleId }
|
|
115
|
+
});
|
|
116
|
+
const { applied, skipped } = baseReviewer.applyOperations(operations, {
|
|
117
|
+
mode: "tracked-changes",
|
|
118
|
+
snapshot: baseSnapshot
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
buffer: await baseReviewer.toBuffer(),
|
|
122
|
+
applied,
|
|
123
|
+
skipped
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
//#endregion
|
|
127
|
+
export { generateRedlineDocx };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIComment, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditSnapshot } from "./ai-edits/types.js";
|
|
1
|
+
import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIComment, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditSnapshot, FolioAIInlineFormatting, FolioAITextRangeHandle } from "./ai-edits/types.js";
|
|
2
|
+
import { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationStatus, FolioDocumentOperationType, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "./document-operations.js";
|
|
2
3
|
import { FolioReviewChange, FolioReviewChangeKind } from "./ai-edits/read.js";
|
|
3
|
-
import { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FolioApplyOperationsOptions, FolioDocxReviewer, FolioDocxReviewerOptions, FolioReviewChangeFilter, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, applyFolioAIEditsToBuffer } from "./ai-edits/headless.js";
|
|
4
|
+
import { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FolioApplyDocumentOperationsOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocxReviewer, FolioDocxReviewerOptions, FolioReviewChangeFilter, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, applyFolioAIEditsToBuffer } from "./ai-edits/headless.js";
|
|
5
|
+
import { createFolioAITextRangeHandle } from "./ai-edits/snapshot.js";
|
|
4
6
|
import { DeriveBlockIdInput, FolioBlockId, deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
|
|
5
7
|
import { CreateEmptyDocumentOptions, createEmptyDocument } from "./utils/createDocument.js";
|
|
6
8
|
import { createDocx } from "./docx/rezip.js";
|
|
7
9
|
import { CreateCommentReplyInput, replyToComment } from "./docx/replyToComment.js";
|
|
8
|
-
|
|
10
|
+
import { GenerateRedlineDocxOptions, GenerateRedlineDocxResult, generateRedlineDocx } from "./redline.js";
|
|
11
|
+
import { FolioBlockDiff, FolioFormatProperty, FolioVersionDiff, FolioVersionDiffSegment, compareDocxVersions } from "./version-comparison.js";
|
|
12
|
+
export { type ApplyFolioAIEditsToBufferOptions, type ApplyFolioAIEditsToBufferResult, type CreateCommentReplyInput, type CreateEmptyDocumentOptions, type DeriveBlockIdInput, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditSnapshot, type FolioAIInlineFormatting, type FolioAITextRangeHandle, type FolioApplyDocumentOperationsOptions, type FolioApplyOperationsOptions, type FolioBlockDiff, type FolioBlockId, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationType, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocxReviewer, type FolioDocxReviewerOptions, type FolioFormatProperty, type FolioReviewChange, type FolioReviewChangeFilter, type FolioReviewChangeKind, type FolioReviewComment, type FolioReviewCommentFilter, type FolioReviewCommentReply, type FolioReviewReplyInput, type FolioVersionDiff, type FolioVersionDiffSegment, type GenerateRedlineDocxOptions, type GenerateRedlineDocxResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioAIEditsToBuffer, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, deriveBlockId, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch, replyToComment };
|
package/dist/server.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
|
|
2
|
+
import { createFolioAITextRangeHandle } from "./ai-edits/snapshot.js";
|
|
3
|
+
import { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "./document-operations.js";
|
|
1
4
|
import { createEmptyDocument } from "./utils/createDocument.js";
|
|
2
5
|
import { createDocx } from "./docx/rezip.js";
|
|
3
|
-
import { deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
|
|
4
6
|
import { replyToComment } from "./docx/replyToComment.js";
|
|
5
7
|
import { FolioDocxReviewer, applyFolioAIEditsToBuffer } from "./ai-edits/headless.js";
|
|
6
|
-
|
|
8
|
+
import { compareDocxVersions } from "./version-comparison.js";
|
|
9
|
+
import { generateRedlineDocx } from "./redline.js";
|
|
10
|
+
export { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocxReviewer, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioAIEditsToBuffer, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, deriveBlockId, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch, replyToComment };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/symbols.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Symbol catalog for the "Insert Symbol" dialog.
|
|
4
|
+
*
|
|
5
|
+
* Framework-neutral data + filtering shared by every folio adapter (React,
|
|
6
|
+
* Vue, ...), so the two never drift on which symbols they offer. Category
|
|
7
|
+
* display names are i18n keys (`dialogs.insertSymbol.categories.*`) resolved by
|
|
8
|
+
* each adapter's own translator; the `char`/`name` pairs are the raw catalog.
|
|
9
|
+
*/
|
|
10
|
+
type SymbolEntry = {
|
|
11
|
+
/** The character to insert. */char: string; /** English descriptive name, used for search matching. */
|
|
12
|
+
name: string;
|
|
13
|
+
};
|
|
14
|
+
/** i18n message keys for the symbol-category display labels. */
|
|
15
|
+
type SymbolCategoryNameKey = "dialogs.insertSymbol.categories.common" | "dialogs.insertSymbol.categories.arrows" | "dialogs.insertSymbol.categories.math" | "dialogs.insertSymbol.categories.greek" | "dialogs.insertSymbol.categories.currency" | "dialogs.insertSymbol.categories.shapes";
|
|
16
|
+
type SymbolCategory = {
|
|
17
|
+
/** Stable category id (also the default active tab). */name: string; /** i18n key for the category's display label. */
|
|
18
|
+
nameKey: SymbolCategoryNameKey;
|
|
19
|
+
symbols: SymbolEntry[];
|
|
20
|
+
};
|
|
21
|
+
/** A catalog entry tagged with the category it belongs to (search results). */
|
|
22
|
+
type SymbolSearchEntry = SymbolEntry & {
|
|
23
|
+
category: string;
|
|
24
|
+
};
|
|
25
|
+
declare const SYMBOL_CATEGORIES: SymbolCategory[];
|
|
26
|
+
/** Every catalog symbol, flattened and tagged with its category. */
|
|
27
|
+
declare const ALL_SYMBOLS: SymbolSearchEntry[];
|
|
28
|
+
/**
|
|
29
|
+
* Symbols whose descriptive name contains `query` (case-insensitive), or whose
|
|
30
|
+
* character is exactly `query`. An empty/whitespace query returns `[]` so
|
|
31
|
+
* callers fall back to the active category.
|
|
32
|
+
*/
|
|
33
|
+
declare function filterSymbols(query: string): SymbolSearchEntry[];
|
|
34
|
+
//#endregion
|
|
35
|
+
export { ALL_SYMBOLS, SYMBOL_CATEGORIES, SymbolCategory, SymbolCategoryNameKey, SymbolEntry, SymbolSearchEntry, filterSymbols };
|