@stll/folio-core 0.15.13 → 0.16.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.
|
@@ -8,13 +8,15 @@ import { attemptSelectiveSave } from "../docx/selectiveSave.js";
|
|
|
8
8
|
import { acceptAIEditRevision, acceptAllChanges, rejectAIEditRevision, rejectAllChanges } from "../prosemirror/commands/comments.js";
|
|
9
9
|
import { proseDocToBlocks, updateDocumentContent } from "../prosemirror/conversion/fromProseDoc.js";
|
|
10
10
|
import { footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
|
|
11
|
-
import {
|
|
11
|
+
import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
|
|
12
|
+
import { getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
|
|
12
13
|
import { schema, singletonManager } from "../prosemirror/schema/index.js";
|
|
13
14
|
import { deterministicHexId } from "../utils/hexId.js";
|
|
14
15
|
import { buildAnnotatedBlockText } from "./clean-text.js";
|
|
15
16
|
import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
|
|
16
17
|
import { createFolioAIEditSnapshot, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
17
18
|
import { TaggedError } from "better-result";
|
|
19
|
+
import { Fragment } from "prosemirror-model";
|
|
18
20
|
import { EditorState } from "prosemirror-state";
|
|
19
21
|
//#region src/ai-edits/headless.ts
|
|
20
22
|
/**
|
|
@@ -80,39 +82,48 @@ const createReviewerComment = (text, author) => ({
|
|
|
80
82
|
*
|
|
81
83
|
* The shared `ParaIdAllocatorExtension` mints RANDOM ids (correct for freshly
|
|
82
84
|
* typed paragraphs in the live editor); this load-time pass is deterministic so
|
|
83
|
-
* a paraId-less corpus document anchors reproducibly.
|
|
84
|
-
*
|
|
85
|
+
* a paraId-less corpus document anchors reproducibly.
|
|
86
|
+
*
|
|
87
|
+
* Rebuilding only the changed branches, like {@link ensureParaIdsInDoc}, keeps
|
|
88
|
+
* the pass linear in paragraph count. Seeding through a transaction instead
|
|
89
|
+
* costs one `setNodeMarkup` step per paragraph, and both halves of that are
|
|
90
|
+
* quadratic: every step rebuilds the containing fragment, and the plugin
|
|
91
|
+
* `appendTransaction` chain rescans the accumulated step maps. Because the pass
|
|
92
|
+
* runs before the state exists, no history, mapping, or change-tracking
|
|
93
|
+
* semantics depend on it.
|
|
85
94
|
*/
|
|
86
|
-
const
|
|
95
|
+
const ensureDeterministicParaIdsInDoc = (doc) => {
|
|
87
96
|
const seen = /* @__PURE__ */ new Set();
|
|
88
|
-
const updates = [];
|
|
89
97
|
let ordinal = 0;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
const rewrite = (parent) => {
|
|
99
|
+
let changed = false;
|
|
100
|
+
const children = [];
|
|
101
|
+
parent.forEach((child) => {
|
|
102
|
+
let next = child;
|
|
103
|
+
if (child.type.name === "paragraph") {
|
|
104
|
+
ordinal += 1;
|
|
105
|
+
const existing = child.attrs["paraId"];
|
|
106
|
+
if (typeof existing === "string" && existing.length > 0 && !seen.has(existing)) seen.add(existing);
|
|
107
|
+
else {
|
|
108
|
+
let paraId = deterministicHexId(`${child.textContent}:${ordinal}`);
|
|
109
|
+
for (let salt = 1; seen.has(paraId); salt++) paraId = deterministicHexId(`${child.textContent}:${ordinal}:${salt}`);
|
|
110
|
+
seen.add(paraId);
|
|
111
|
+
next = child.type.create({
|
|
112
|
+
...child.attrs,
|
|
113
|
+
paraId
|
|
114
|
+
}, child.content, child.marks);
|
|
115
|
+
}
|
|
116
|
+
} else if (child.childCount > 0) {
|
|
117
|
+
const content = rewrite(child);
|
|
118
|
+
if (content !== child.content) next = child.copy(content);
|
|
106
119
|
}
|
|
120
|
+
if (next !== child) changed = true;
|
|
121
|
+
children.push(next);
|
|
107
122
|
});
|
|
108
|
-
return
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
for (const update of updates) tr.setNodeMarkup(update.pos, void 0, update.attrs);
|
|
113
|
-
ignoreTrackedChanges(tr);
|
|
114
|
-
tr.setMeta("addToHistory", false);
|
|
115
|
-
return state.apply(tr);
|
|
123
|
+
return changed ? Fragment.fromArray(children) : parent.content;
|
|
124
|
+
};
|
|
125
|
+
const content = rewrite(doc);
|
|
126
|
+
return content === doc.content ? doc : doc.copy(content);
|
|
116
127
|
};
|
|
117
128
|
const FOLIO_REVIEWED_VIEWS = Object.freeze([
|
|
118
129
|
"original",
|
|
@@ -225,9 +236,9 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
225
236
|
password: options.password
|
|
226
237
|
});
|
|
227
238
|
const plugins = singletonManager.getPlugins();
|
|
228
|
-
const state =
|
|
239
|
+
const state = ensureBaseDirectionInState(EditorState.create({
|
|
229
240
|
schema,
|
|
230
|
-
doc: toProseDoc(baseDocument),
|
|
241
|
+
doc: ensureDeterministicParaIdsInDoc(toProseDoc(baseDocument)),
|
|
231
242
|
plugins
|
|
232
243
|
}));
|
|
233
244
|
return new FolioDocxReviewer({
|
|
@@ -711,9 +722,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
711
722
|
...this.baseDocument.package.styles !== void 0 && { styles: this.baseDocument.package.styles },
|
|
712
723
|
...this.baseDocument.package.theme !== void 0 && { theme: this.baseDocument.package.theme }
|
|
713
724
|
};
|
|
714
|
-
const
|
|
725
|
+
const storyDoc = story.type === "header" || story.type === "footer" ? headerFooterToProseDoc(source.content, conversionOptions) : footnoteToProseDoc(source.content, conversionOptions);
|
|
726
|
+
const state = ensureBaseDirectionInState(EditorState.create({
|
|
715
727
|
schema,
|
|
716
|
-
doc:
|
|
728
|
+
doc: ensureDeterministicParaIdsInDoc(storyDoc),
|
|
717
729
|
plugins: singletonManager.getPlugins()
|
|
718
730
|
}));
|
|
719
731
|
this.secondaryStoryStates.set(key, {
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
//#region src/docx/server/extractDocxText.d.ts
|
|
2
2
|
/** Document part containing an extracted paragraph. */
|
|
3
3
|
type DocxParagraphSource = "header" | "body" | "footer";
|
|
4
|
+
/**
|
|
5
|
+
* Role of an emitted markdown table row.
|
|
6
|
+
*
|
|
7
|
+
* - `cells` — a `w:tr` rendered as a pipe row, including the first row when the
|
|
8
|
+
* table declares it as its header.
|
|
9
|
+
* - `syntheticHeader` — the empty header row emitted for a table that declares
|
|
10
|
+
* no header row; GFM has no headerless table.
|
|
11
|
+
* - `delimiter` — the `| --- |` line GFM requires under the header.
|
|
12
|
+
*/
|
|
13
|
+
type DocxTableRowKind = "cells" | "syntheticHeader" | "delimiter";
|
|
14
|
+
/** Table membership of a paragraph whose `text` is a markdown table row. */
|
|
15
|
+
type DocxTableRowPosition = {
|
|
16
|
+
/** 0-based index of the source `w:tbl`, in extraction order across all parts. */
|
|
17
|
+
table: number;
|
|
18
|
+
kind: DocxTableRowKind;
|
|
19
|
+
};
|
|
4
20
|
/** Paragraph text and lightweight formatting metadata from a DOCX archive. */
|
|
5
21
|
type ExtractedDocxParagraph = {
|
|
6
22
|
index: number;
|
|
@@ -10,6 +26,13 @@ type ExtractedDocxParagraph = {
|
|
|
10
26
|
bold?: boolean;
|
|
11
27
|
fontSize?: number;
|
|
12
28
|
alignment?: "left" | "center" | "right" | "both";
|
|
29
|
+
/**
|
|
30
|
+
* Present only when `text` is a markdown table row rendered from a `w:tbl`,
|
|
31
|
+
* absent for ordinary prose paragraphs. Consumers that join `text` across
|
|
32
|
+
* paragraphs need no change; consumers that want to regroup a table's rows,
|
|
33
|
+
* or drop the rows GFM forced into existence, can key off this.
|
|
34
|
+
*/
|
|
35
|
+
tableRow?: DocxTableRowPosition;
|
|
13
36
|
};
|
|
14
37
|
/** Accepted-revision paragraph text extracted in deterministic part order. */
|
|
15
38
|
type ExtractedDocxText = {
|
|
@@ -20,4 +43,4 @@ type ExtractedDocxText = {
|
|
|
20
43
|
/** Extract paragraph text and formatting metadata from a DOCX archive. */
|
|
21
44
|
declare const extractDocxText: (bytes: ArrayBuffer | Uint8Array) => Promise<ExtractedDocxText>;
|
|
22
45
|
//#endregion
|
|
23
|
-
export { DocxParagraphSource, ExtractedDocxParagraph, ExtractedDocxText, extractDocxText };
|
|
46
|
+
export { DocxParagraphSource, DocxTableRowKind, DocxTableRowPosition, ExtractedDocxParagraph, ExtractedDocxText, extractDocxText };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { escapeTableCell } from "../../markdown/escape.js";
|
|
1
2
|
import { RELATIONSHIP_TYPES, parseRelationships } from "../relsParser.js";
|
|
2
3
|
import { findAllDeep, findChild, findDeep, getAttribute, getAttributeAnyPrefix, getLocalName, getTextContent, parseXml } from "../xmlParser.js";
|
|
3
4
|
import { loadDocxArchive } from "./boundedArchive.js";
|
|
@@ -59,13 +60,159 @@ const readRunMetrics = (paragraph) => {
|
|
|
59
60
|
}
|
|
60
61
|
return metrics;
|
|
61
62
|
};
|
|
62
|
-
const
|
|
63
|
+
const TABLE_DELIMITER_CELL = "---";
|
|
64
|
+
/** GFM cannot nest tables; an inner table joins its cells inside the outer cell. */
|
|
65
|
+
const NESTED_TABLE_CELL_SEPARATOR = " / ";
|
|
66
|
+
/** Word supports 63 table columns; cap well above that so a hostile `w:gridSpan` cannot balloon a row. */
|
|
67
|
+
const MAX_TABLE_COLUMNS = 256;
|
|
68
|
+
/** Bound the mutual recursion between a cell and the tables nested inside it. */
|
|
69
|
+
const MAX_NESTED_TABLE_DEPTH = 8;
|
|
70
|
+
/**
|
|
71
|
+
* Collect a table's `w:tr`, or a row's `w:tc`, seeing through the wrappers Word
|
|
72
|
+
* puts around them (`w:sdt` / `w:sdtContent` content controls, `w:customXml`).
|
|
73
|
+
* The walk stops at `w:tbl` and `w:p` so a nested table's rows and cells never
|
|
74
|
+
* leak into the grid of the table that contains them.
|
|
75
|
+
*/
|
|
76
|
+
const collectTableParts = (parent, localName) => {
|
|
77
|
+
const parts = [];
|
|
78
|
+
const walk = (node) => {
|
|
79
|
+
for (const child of childElements(node)) {
|
|
80
|
+
const childName = getLocalName(child.name ?? "");
|
|
81
|
+
if (childName === localName) {
|
|
82
|
+
parts.push(child);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (childName === "tbl" || childName === "p") continue;
|
|
86
|
+
walk(child);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
walk(parent);
|
|
90
|
+
return parts;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Raw (unescaped) text of one cell: its paragraphs in order, one per line.
|
|
94
|
+
* Blank paragraphs are dropped so a cell padded with empty paragraphs does not
|
|
95
|
+
* render as a run of `<br>`. A nested table contributes one line per inner row.
|
|
96
|
+
*/
|
|
97
|
+
const readCellText = (cell, depth) => {
|
|
98
|
+
const lines = [];
|
|
99
|
+
const walk = (node) => {
|
|
100
|
+
for (const child of childElements(node)) {
|
|
101
|
+
const childName = getLocalName(child.name ?? "");
|
|
102
|
+
if (childName === "p") {
|
|
103
|
+
const text = collectText(child);
|
|
104
|
+
if (text.length > 0) lines.push(text);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (childName === "tbl") {
|
|
108
|
+
if (depth >= MAX_NESTED_TABLE_DEPTH) continue;
|
|
109
|
+
for (const line of flattenNestedTable(child, depth + 1)) lines.push(line);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
walk(child);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
walk(cell);
|
|
116
|
+
return lines.join("\n");
|
|
117
|
+
};
|
|
118
|
+
const flattenNestedTable = (table, depth) => {
|
|
119
|
+
const lines = [];
|
|
120
|
+
for (const row of collectTableParts(table, "tr")) {
|
|
121
|
+
const cells = collectTableParts(row, "tc").map((cell) => readCellText(cell, depth));
|
|
122
|
+
if (cells.some((text) => text.length > 0)) lines.push(cells.join(NESTED_TABLE_CELL_SEPARATOR));
|
|
123
|
+
}
|
|
124
|
+
return lines;
|
|
125
|
+
};
|
|
126
|
+
const readTableCell = (cell, depth) => {
|
|
127
|
+
const properties = findChild(cell, "w", "tcPr");
|
|
128
|
+
const gridSpanValue = getAttributeAnyPrefix(findChild(properties, "w", "gridSpan"), "val");
|
|
129
|
+
const parsedGridSpan = gridSpanValue === null ? 1 : Number.parseInt(gridSpanValue, 10);
|
|
130
|
+
const gridSpan = Number.isFinite(parsedGridSpan) && parsedGridSpan > 1 ? parsedGridSpan : 1;
|
|
131
|
+
const vMerge = findChild(properties, "w", "vMerge");
|
|
132
|
+
if (vMerge !== null && getAttributeAnyPrefix(vMerge, "val") !== "restart") return {
|
|
133
|
+
text: "",
|
|
134
|
+
gridSpan
|
|
135
|
+
};
|
|
136
|
+
return {
|
|
137
|
+
text: readCellText(cell, depth),
|
|
138
|
+
gridSpan
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Does the row declare itself a header? `w:tblHeader` is the only OOXML signal
|
|
143
|
+
* that says so: it marks the row Word repeats at the top of each page. The
|
|
144
|
+
* neighbouring `w:tblLook/@w:firstRow` is conditional *formatting* that Word
|
|
145
|
+
* writes on essentially every table (its default `w:val="04A0"`), so keying off
|
|
146
|
+
* it would promote the first data row of almost every document.
|
|
147
|
+
*
|
|
148
|
+
* Without the flag the table is headerless and GFM gets a synthetic empty header
|
|
149
|
+
* row: a table's first row is data until the document says otherwise, and column
|
|
150
|
+
* names invented here would be read back as facts about the document.
|
|
151
|
+
*/
|
|
152
|
+
const declaresHeaderRow = (row) => {
|
|
153
|
+
const header = findChild(findChild(row, "w", "trPr"), "w", "tblHeader");
|
|
154
|
+
if (header === null) return false;
|
|
155
|
+
const value = getAttributeAnyPrefix(header, "val");
|
|
156
|
+
return value !== "0" && value !== "false";
|
|
157
|
+
};
|
|
158
|
+
const readTableGrid = (table) => {
|
|
159
|
+
const rows = [];
|
|
160
|
+
let columnCount = 0;
|
|
161
|
+
let firstRowIsHeader = false;
|
|
162
|
+
for (const [rowIndex, row] of collectTableParts(table, "tr").entries()) {
|
|
163
|
+
if (rowIndex === 0) firstRowIsHeader = declaresHeaderRow(row);
|
|
164
|
+
const columns = [];
|
|
165
|
+
for (const cell of collectTableParts(row, "tc")) {
|
|
166
|
+
if (columns.length >= MAX_TABLE_COLUMNS) break;
|
|
167
|
+
const { text, gridSpan } = readTableCell(cell, 0);
|
|
168
|
+
columns.push(text);
|
|
169
|
+
const padding = Math.min(gridSpan - 1, MAX_TABLE_COLUMNS - columns.length);
|
|
170
|
+
for (let index = 0; index < padding; index++) columns.push("");
|
|
171
|
+
}
|
|
172
|
+
if (columns.length > columnCount) columnCount = columns.length;
|
|
173
|
+
rows.push(columns);
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
rows,
|
|
177
|
+
columnCount,
|
|
178
|
+
firstRowIsHeader
|
|
179
|
+
};
|
|
180
|
+
};
|
|
181
|
+
/** Pad a row to the table's column count and escape each cell into a pipe row. */
|
|
182
|
+
const toRowLine = (columns, columnCount) => {
|
|
183
|
+
const cells = [];
|
|
184
|
+
for (let column = 0; column < columnCount; column++) cells.push(escapeTableCell(columns[column] ?? ""));
|
|
185
|
+
return `| ${cells.join(" | ")} |`;
|
|
186
|
+
};
|
|
187
|
+
/** Render a `w:tbl` as GFM rows. A table with no cell at all renders nothing. */
|
|
188
|
+
const renderTableRows = (table, tableIndex) => {
|
|
189
|
+
const { rows, columnCount, firstRowIsHeader } = readTableGrid(table);
|
|
190
|
+
const [firstRow, ...remainingRows] = rows;
|
|
191
|
+
if (columnCount === 0 || firstRow === void 0) return [];
|
|
192
|
+
const rendered = [];
|
|
193
|
+
const push = (text, kind) => {
|
|
194
|
+
rendered.push({
|
|
195
|
+
text,
|
|
196
|
+
position: {
|
|
197
|
+
table: tableIndex,
|
|
198
|
+
kind
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
if (firstRowIsHeader) push(toRowLine(firstRow, columnCount), "cells");
|
|
203
|
+
else push(toRowLine([], columnCount), "syntheticHeader");
|
|
204
|
+
push(toRowLine(Array.from({ length: columnCount }, () => TABLE_DELIMITER_CELL), columnCount), "delimiter");
|
|
205
|
+
for (const row of firstRowIsHeader ? remainingRows : rows) push(toRowLine(row, columnCount), "cells");
|
|
206
|
+
return rendered;
|
|
207
|
+
};
|
|
208
|
+
const extractContainer = ({ container, source, startIndex, startTableIndex }) => {
|
|
63
209
|
const paragraphs = [];
|
|
64
210
|
let charCount = 0;
|
|
65
|
-
|
|
211
|
+
let tableCount = 0;
|
|
212
|
+
const pushProse = (paragraph) => {
|
|
66
213
|
const text = collectText(paragraph);
|
|
67
214
|
const entry = {
|
|
68
|
-
index: startIndex +
|
|
215
|
+
index: startIndex + paragraphs.length,
|
|
69
216
|
text,
|
|
70
217
|
source
|
|
71
218
|
};
|
|
@@ -81,15 +228,45 @@ const extractContainer = ({ container, source, startIndex }) => {
|
|
|
81
228
|
}
|
|
82
229
|
paragraphs.push(entry);
|
|
83
230
|
charCount += text.length;
|
|
84
|
-
}
|
|
231
|
+
};
|
|
232
|
+
const pushTableRow = ({ text, position }) => {
|
|
233
|
+
paragraphs.push({
|
|
234
|
+
index: startIndex + paragraphs.length,
|
|
235
|
+
text,
|
|
236
|
+
source,
|
|
237
|
+
tableRow: position
|
|
238
|
+
});
|
|
239
|
+
charCount += text.length;
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* Walk block content in document order. Descent mirrors the previous
|
|
243
|
+
* `findAllDeep(container, "w", "p")` — every wrapper (`w:sdt`, textboxes) is
|
|
244
|
+
* still entered — except that a `w:tbl` is consumed as a table instead of
|
|
245
|
+
* having its cell paragraphs emitted individually.
|
|
246
|
+
*/
|
|
247
|
+
const walkBlocks = (node) => {
|
|
248
|
+
for (const child of childElements(node)) {
|
|
249
|
+
const childName = getLocalName(child.name ?? "");
|
|
250
|
+
if (childName === "tbl") {
|
|
251
|
+
for (const row of renderTableRows(child, startTableIndex + tableCount)) pushTableRow(row);
|
|
252
|
+
tableCount += 1;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (childName === "p") pushProse(child);
|
|
256
|
+
walkBlocks(child);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
walkBlocks(container);
|
|
85
260
|
return {
|
|
86
261
|
paragraphs,
|
|
87
|
-
charCount
|
|
262
|
+
charCount,
|
|
263
|
+
tableCount
|
|
88
264
|
};
|
|
89
265
|
};
|
|
90
|
-
const extractParts = async ({ archive, source, rootName, startIndex, paths }) => {
|
|
266
|
+
const extractParts = async ({ archive, source, rootName, startIndex, startTableIndex, paths }) => {
|
|
91
267
|
const paragraphs = [];
|
|
92
268
|
let charCount = 0;
|
|
269
|
+
let tableCount = 0;
|
|
93
270
|
let nextIndex = startIndex;
|
|
94
271
|
for (const path of paths) {
|
|
95
272
|
const xml = await archive.readEntryString(path);
|
|
@@ -99,15 +276,18 @@ const extractParts = async ({ archive, source, rootName, startIndex, paths }) =>
|
|
|
99
276
|
const result = extractContainer({
|
|
100
277
|
container,
|
|
101
278
|
source,
|
|
102
|
-
startIndex: nextIndex
|
|
279
|
+
startIndex: nextIndex,
|
|
280
|
+
startTableIndex: startTableIndex + tableCount
|
|
103
281
|
});
|
|
104
282
|
paragraphs.push(...result.paragraphs);
|
|
105
283
|
charCount += result.charCount;
|
|
284
|
+
tableCount += result.tableCount;
|
|
106
285
|
nextIndex += result.paragraphs.length;
|
|
107
286
|
}
|
|
108
287
|
return {
|
|
109
288
|
paragraphs,
|
|
110
|
-
charCount
|
|
289
|
+
charCount,
|
|
290
|
+
tableCount
|
|
111
291
|
};
|
|
112
292
|
};
|
|
113
293
|
/** A `word/_rels/document.xml.rels` `Target` is relative to `word/`; resolve it to a full archive-entry path. */
|
|
@@ -173,18 +353,21 @@ const extractDocxText = async (bytes) => {
|
|
|
173
353
|
source: "header",
|
|
174
354
|
rootName: "hdr",
|
|
175
355
|
startIndex: 0,
|
|
356
|
+
startTableIndex: 0,
|
|
176
357
|
paths: referencedParts.headers
|
|
177
358
|
});
|
|
178
359
|
const bodyResult = extractContainer({
|
|
179
360
|
container: body,
|
|
180
361
|
source: "body",
|
|
181
|
-
startIndex: headers.paragraphs.length
|
|
362
|
+
startIndex: headers.paragraphs.length,
|
|
363
|
+
startTableIndex: headers.tableCount
|
|
182
364
|
});
|
|
183
365
|
const footers = await extractParts({
|
|
184
366
|
archive,
|
|
185
367
|
source: "footer",
|
|
186
368
|
rootName: "ftr",
|
|
187
369
|
startIndex: headers.paragraphs.length + bodyResult.paragraphs.length,
|
|
370
|
+
startTableIndex: headers.tableCount + bodyResult.tableCount,
|
|
188
371
|
paths: referencedParts.footers
|
|
189
372
|
});
|
|
190
373
|
return {
|
package/dist/server.d.ts
CHANGED
|
@@ -18,8 +18,8 @@ import { EvaluateDocxXmlPatchProposalArgs, FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULT
|
|
|
18
18
|
import { FOLIO_DOCX_CONFORMANCE_CHECKS, FOLIO_DOCX_CONFORMANCE_ISSUE_CODES, FOLIO_DOCX_CONFORMANCE_PROFILE, FOLIO_DOCX_CONFORMANCE_REPORT_VERSION, FolioDocxConformanceCheck, FolioDocxConformanceCheckId, FolioDocxConformanceCheckStatus, FolioDocxConformanceIssue, FolioDocxConformanceIssueCode, FolioDocxConformanceReport, FolioDocxConformanceStatus, ValidateDocxConformanceOptions, validateDocxConformance } from "./docx/server/validateDocxConformance.js";
|
|
19
19
|
import { ApplyDocxXmlPatchProposalArgs, FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE, FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION, FolioDocxXmlPatchApplication, FolioDocxXmlPatchApplicationError, FolioDocxXmlPatchApplicationReceipt, UnsupportedFolioDocxXmlPatchApplicationProfileError, applyDocxXmlPatchProposal } from "./docx/server/applyDocxXmlPatchProposal.js";
|
|
20
20
|
import { docxToMarkdown } from "./docx/server/docxToMarkdown.js";
|
|
21
|
-
import { DocxParagraphSource, ExtractedDocxParagraph, ExtractedDocxText, extractDocxText } from "./docx/server/extractDocxText.js";
|
|
21
|
+
import { DocxParagraphSource, DocxTableRowKind, DocxTableRowPosition, ExtractedDocxParagraph, ExtractedDocxText, extractDocxText } from "./docx/server/extractDocxText.js";
|
|
22
22
|
import { FOLIO_DOCX_PACKAGE_INSPECTION_DEFAULTS, FOLIO_DOCX_PACKAGE_INSPECTION_ERROR_CODES, FOLIO_DOCX_PACKAGE_INSPECTION_VERSION, FolioDocxInspectedXmlPart, FolioDocxPackageInspection, FolioDocxPackageInspectionError, FolioDocxPackageInspectionErrorCode, FolioDocxPackageInspectionLimits, FolioDocxPackagePart, FolioDocxPackagePartKind, InspectDocxPackageOptions, inspectDocxPackage } from "./docx/server/inspectDocxPackage.js";
|
|
23
23
|
import { GenerateRedlineDocxOptions, GenerateRedlineDocxResult, GenerateRedlineUnprocessedStory, InvalidGenerateRedlineDocxOptionsError, generateRedlineDocx } from "./redline.js";
|
|
24
24
|
import { FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, FolioBlockDiff, FolioCompareDocxVersionsOptions, FolioDocumentMetadataValue, FolioFormatProperty, FolioMetadataDiff, FolioStoryDiff, FolioVersionBlockHandle, FolioVersionComparisonPrivacyTransform, FolioVersionComparisonScope, FolioVersionDiff, FolioVersionDiffPrivacyOptions, FolioVersionDiffPrivacyReport, FolioVersionDiffSegment, FolioVersionDiffSummaryCounts, InvalidFolioVersionComparisonOptionsError, applyFolioVersionDiffPrivacy, compareDocxVersions, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope } from "./version-comparison.js";
|
|
25
|
-
export { type ApplyDocxXmlPatchProposalArgs, type ApplyFolioAIEditsToBufferOptions, type ApplyFolioAIEditsToBufferResult, type CreateCommentReplyInput, type CreateEmptyDocumentOptions, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, type DeriveBlockIdInput, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, DocxArchiveError, type DocxArchiveOptions, type DocxParagraphSource, EnsureParaIdsError, type EnsureParaIdsOptions, type EnsureParaIdsResult, type EvaluateDocxXmlPatchProposalArgs, type ExtractDocumentStyleSetOptions, type ExtractedDocxParagraph, type ExtractedDocxText, FOLIO_DOCUMENT_METADATA_PROPERTIES, 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, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FOLIO_DOCX_CONFORMANCE_CHECKS, FOLIO_DOCX_CONFORMANCE_ISSUE_CODES, FOLIO_DOCX_CONFORMANCE_PROFILE, FOLIO_DOCX_CONFORMANCE_REPORT_VERSION, FOLIO_DOCX_PACKAGE_INSPECTION_DEFAULTS, FOLIO_DOCX_PACKAGE_INSPECTION_ERROR_CODES, FOLIO_DOCX_PACKAGE_INSPECTION_VERSION, FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE, FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION, FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS, FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES, FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, 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 FolioApplyDocumentOperationsToStoryOptions, type FolioApplyOperationsOptions, type FolioBlockDiff, type FolioBlockId, type FolioCompareDocxVersionsOptions, type FolioDocumentMetadataProperty, type FolioDocumentMetadataValue, type FolioDocumentNavigationTarget, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentOutline, type FolioDocumentOutlineEntry, FolioDocumentPrivacyArchiveError, type FolioDocumentPrivacyOptions, type FolioDocumentPrivacyReport, type FolioDocumentPrivacyTransform, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, type FolioDocxConformanceCheck, type FolioDocxConformanceCheckId, type FolioDocxConformanceCheckStatus, type FolioDocxConformanceIssue, type FolioDocxConformanceIssueCode, type FolioDocxConformanceReport, type FolioDocxConformanceStatus, type FolioDocxInspectedXmlPart, type FolioDocxPackageInspection, FolioDocxPackageInspectionError, type FolioDocxPackageInspectionErrorCode, type FolioDocxPackageInspectionLimits, type FolioDocxPackagePart, type FolioDocxPackagePartKind, type FolioDocxPreparedXmlReplacement, FolioDocxReviewer, type FolioDocxReviewerOptions, type FolioDocxXmlPatchApplication, FolioDocxXmlPatchApplicationError, type FolioDocxXmlPatchApplicationReceipt, type FolioDocxXmlPatchProposal, type FolioDocxXmlPatchProposalEvaluation, type FolioDocxXmlPatchProposalIssue, type FolioDocxXmlPatchProposalIssueCode, type FolioDocxXmlPatchProposalLimits, type FolioDocxXmlReplacement, type FolioEditableDocumentStoryHandle, type FolioFormatProperty, type FolioMetadataDiff, type FolioReadReviewedStoryOptions, type FolioResolveReviewedStoryOptions, type FolioResolvedReviewedView, type FolioReviewChange, type FolioReviewChangeFilter, type FolioReviewChangeKind, type FolioReviewComment, type FolioReviewCommentFilter, type FolioReviewCommentReply, type FolioReviewReplyInput, type FolioReviewedStory, type FolioReviewedView, type FolioStoryDiff, type FolioVersionBlockHandle, type FolioVersionComparisonPrivacyTransform, type FolioVersionComparisonScope, type FolioVersionDiff, type FolioVersionDiffPrivacyOptions, type FolioVersionDiffPrivacyReport, type FolioVersionDiffSegment, type FolioVersionDiffSummaryCounts, type GenerateRedlineDocxOptions, type GenerateRedlineDocxResult, type GenerateRedlineUnprocessedStory, type InspectDocxPackageOptions, InvalidFolioDocumentOperationBatchError, InvalidFolioDocumentPrivacyOptionsError, InvalidFolioDocxXmlPatchProposalError, InvalidFolioDocxXmlPatchProposalOptionsError, InvalidFolioVersionComparisonOptionsError, InvalidGenerateRedlineDocxOptionsError, type RewriteDocxMetadataPrivacyResult, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioDocxXmlPatchApplicationProfileError, UnsupportedFolioReviewedViewError, type ValidateDocxConformanceOptions, applyDocxXmlPatchProposal, applyFolioAIEditsToBuffer, applyFolioVersionDiffPrivacy, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, docxToMarkdown, ensureParaIds, evaluateDocxXmlPatchProposal, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxPackage, isFolioBlockId, isFolioDocumentOperationModeSupported, isFolioDocumentPrivacyTransform, isFolioResolvedReviewedView, isFolioReviewedView, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, parseFolioDocxXmlPatchProposal, readFolioDocumentSection, replyToComment, rewriteDocxMetadataPrivacy, validateDocxConformance };
|
|
25
|
+
export { type ApplyDocxXmlPatchProposalArgs, type ApplyFolioAIEditsToBufferOptions, type ApplyFolioAIEditsToBufferResult, type CreateCommentReplyInput, type CreateEmptyDocumentOptions, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, type DeriveBlockIdInput, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, DocxArchiveError, type DocxArchiveOptions, type DocxParagraphSource, type DocxTableRowKind, type DocxTableRowPosition, EnsureParaIdsError, type EnsureParaIdsOptions, type EnsureParaIdsResult, type EvaluateDocxXmlPatchProposalArgs, type ExtractDocumentStyleSetOptions, type ExtractedDocxParagraph, type ExtractedDocxText, FOLIO_DOCUMENT_METADATA_PROPERTIES, 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, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FOLIO_DOCX_CONFORMANCE_CHECKS, FOLIO_DOCX_CONFORMANCE_ISSUE_CODES, FOLIO_DOCX_CONFORMANCE_PROFILE, FOLIO_DOCX_CONFORMANCE_REPORT_VERSION, FOLIO_DOCX_PACKAGE_INSPECTION_DEFAULTS, FOLIO_DOCX_PACKAGE_INSPECTION_ERROR_CODES, FOLIO_DOCX_PACKAGE_INSPECTION_VERSION, FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE, FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION, FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS, FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES, FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, 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 FolioApplyDocumentOperationsToStoryOptions, type FolioApplyOperationsOptions, type FolioBlockDiff, type FolioBlockId, type FolioCompareDocxVersionsOptions, type FolioDocumentMetadataProperty, type FolioDocumentMetadataValue, type FolioDocumentNavigationTarget, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentOutline, type FolioDocumentOutlineEntry, FolioDocumentPrivacyArchiveError, type FolioDocumentPrivacyOptions, type FolioDocumentPrivacyReport, type FolioDocumentPrivacyTransform, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, type FolioDocxConformanceCheck, type FolioDocxConformanceCheckId, type FolioDocxConformanceCheckStatus, type FolioDocxConformanceIssue, type FolioDocxConformanceIssueCode, type FolioDocxConformanceReport, type FolioDocxConformanceStatus, type FolioDocxInspectedXmlPart, type FolioDocxPackageInspection, FolioDocxPackageInspectionError, type FolioDocxPackageInspectionErrorCode, type FolioDocxPackageInspectionLimits, type FolioDocxPackagePart, type FolioDocxPackagePartKind, type FolioDocxPreparedXmlReplacement, FolioDocxReviewer, type FolioDocxReviewerOptions, type FolioDocxXmlPatchApplication, FolioDocxXmlPatchApplicationError, type FolioDocxXmlPatchApplicationReceipt, type FolioDocxXmlPatchProposal, type FolioDocxXmlPatchProposalEvaluation, type FolioDocxXmlPatchProposalIssue, type FolioDocxXmlPatchProposalIssueCode, type FolioDocxXmlPatchProposalLimits, type FolioDocxXmlReplacement, type FolioEditableDocumentStoryHandle, type FolioFormatProperty, type FolioMetadataDiff, type FolioReadReviewedStoryOptions, type FolioResolveReviewedStoryOptions, type FolioResolvedReviewedView, type FolioReviewChange, type FolioReviewChangeFilter, type FolioReviewChangeKind, type FolioReviewComment, type FolioReviewCommentFilter, type FolioReviewCommentReply, type FolioReviewReplyInput, type FolioReviewedStory, type FolioReviewedView, type FolioStoryDiff, type FolioVersionBlockHandle, type FolioVersionComparisonPrivacyTransform, type FolioVersionComparisonScope, type FolioVersionDiff, type FolioVersionDiffPrivacyOptions, type FolioVersionDiffPrivacyReport, type FolioVersionDiffSegment, type FolioVersionDiffSummaryCounts, type GenerateRedlineDocxOptions, type GenerateRedlineDocxResult, type GenerateRedlineUnprocessedStory, type InspectDocxPackageOptions, InvalidFolioDocumentOperationBatchError, InvalidFolioDocumentPrivacyOptionsError, InvalidFolioDocxXmlPatchProposalError, InvalidFolioDocxXmlPatchProposalOptionsError, InvalidFolioVersionComparisonOptionsError, InvalidGenerateRedlineDocxOptionsError, type RewriteDocxMetadataPrivacyResult, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioDocxXmlPatchApplicationProfileError, UnsupportedFolioReviewedViewError, type ValidateDocxConformanceOptions, applyDocxXmlPatchProposal, applyFolioAIEditsToBuffer, applyFolioVersionDiffPrivacy, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, docxToMarkdown, ensureParaIds, evaluateDocxXmlPatchProposal, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxPackage, isFolioBlockId, isFolioDocumentOperationModeSupported, isFolioDocumentPrivacyTransform, isFolioResolvedReviewedView, isFolioReviewedView, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, parseFolioDocxXmlPatchProposal, readFolioDocumentSection, replyToComment, rewriteDocxMetadataPrivacy, validateDocxConformance };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/folio-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
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",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"@stll/template-conditions": "^0.1.0",
|
|
113
113
|
"better-result": "2.10.0",
|
|
114
114
|
"csstype": "^3.1.3",
|
|
115
|
-
"dompurify": "^3.4.
|
|
115
|
+
"dompurify": "^3.4.13",
|
|
116
116
|
"fast-xml-parser": "^5.9.3",
|
|
117
117
|
"hyphen": "1.14.1",
|
|
118
118
|
"jszip": "3.10.1",
|