@stll/folio-core 0.64.1 → 0.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/ai-edits/apply.js +31 -9
  2. package/dist/ai-edits/headless.js +3 -3
  3. package/dist/compare/reproducible-package.d.ts +5 -0
  4. package/dist/compare/reproducible-package.js +10 -23
  5. package/dist/docx/archiveInflation.d.ts +8 -103
  6. package/dist/docx/archiveInflation.js +8 -145
  7. package/dist/docx/attributeRemainder.js +1 -1
  8. package/dist/docx/canonicalStoryRepack.js +2 -2
  9. package/dist/docx/documentSectionFacts.js +1 -1
  10. package/dist/docx/ensureParaIds.js +9 -24
  11. package/dist/docx/importedIdCollisions.js +1 -1
  12. package/dist/docx/mathToMathml.js +1 -1
  13. package/dist/docx/metadataPrivacy.js +38 -48
  14. package/dist/docx/numericIdNormalization.js +2 -2
  15. package/dist/docx/packageParts.d.ts +4 -4
  16. package/dist/docx/packageParts.js +17 -24
  17. package/dist/docx/parser.js +5 -6
  18. package/dist/docx/removeHeaderFooterParts.d.ts +2 -2
  19. package/dist/docx/removeHeaderFooterParts.js +19 -36
  20. package/dist/docx/revisionIdNormalization.js +2 -2
  21. package/dist/docx/rezip.d.ts +7 -7
  22. package/dist/docx/rezip.js +144 -532
  23. package/dist/docx/sdtProperties.js +1 -2
  24. package/dist/docx/selectedXmlIdentityScan.d.ts +1 -1
  25. package/dist/docx/selectedXmlIdentityScan.js +1 -1
  26. package/dist/docx/selectiveSave.js +37 -41
  27. package/dist/docx/selectiveSaveFlags.d.ts +1 -1
  28. package/dist/docx/selectiveSaveTripwire.js +5 -11
  29. package/dist/docx/selectiveXmlPatch.js +2 -2
  30. package/dist/docx/server/applyDocxXmlPatchProposal.js +20 -41
  31. package/dist/docx/server/boundedArchive.d.ts +2 -3
  32. package/dist/docx/server/boundedArchive.js +78 -99
  33. package/dist/docx/server/inspectDocxPackage.d.ts +1 -1
  34. package/dist/docx/server/inspectDocxPackage.js +1 -1
  35. package/dist/docx/server/validateDocxConformance.js +1 -1
  36. package/dist/docx/shapeAlternateContent.js +1 -1
  37. package/dist/docx/storyBlockReplay.js +1 -1
  38. package/dist/docx/unzip.d.ts +2 -3
  39. package/dist/docx/unzip.js +127 -155
  40. package/dist/docx/verbatimCapture.js +1 -1
  41. package/dist/docx/xmlParser.d.ts +5 -36
  42. package/dist/docx/xmlParser.js +14 -78
  43. package/dist/docx/xmlSafety.js +5 -5
  44. package/dist/layout-painter/renderPage.js +4 -2
  45. package/dist/markdown/countParagraphList.d.ts +18 -0
  46. package/dist/markdown/countParagraphList.js +90 -0
  47. package/dist/markdown/renderParagraph.js +2 -62
  48. package/dist/markdown/renderRuns.js +4 -1
  49. package/dist/prosemirror/conversion/fromProseDoc.js +5 -3
  50. package/dist/prosemirror/conversion/toProseDoc.js +4 -1
  51. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +4 -1
  52. package/dist/prosemirror/replacedAnnotations.d.ts +6 -0
  53. package/dist/prosemirror/replacedAnnotations.js +30 -8
  54. package/dist/prosemirror/schema/nodes.d.ts +7 -0
  55. package/dist/prosemirror/textBoxAnchorAttrs.js +9 -1
  56. package/package.json +3 -5
  57. package/dist/docx/streamingXmlParser.d.ts +0 -60
  58. package/dist/docx/streamingXmlParser.js +0 -406
  59. package/dist/docx/xmlNamespaceContext.d.ts +0 -6
  60. package/dist/docx/xmlNamespaceContext.js +0 -53
  61. package/dist/docx/xmlResourceLimits.d.ts +0 -107
  62. package/dist/docx/xmlResourceLimits.js +0 -194
@@ -0,0 +1,18 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { RenderContext } from "./types.js";
3
+ export type CountedParagraphList = {
4
+ /**
5
+ * The list the paragraph shows: its own numbering's, or, while a pending
6
+ * property change removed it, the one that numbering showed.
7
+ */
8
+ visibleList: document_d_exports.ListRendering | undefined;
9
+ /** The label the counter gave the paragraph; absent when it shows none. */
10
+ label: string | undefined;
11
+ };
12
+ export declare function countParagraphList(ctx: RenderContext, pkg: document_d_exports.DocxPackage | undefined, para: document_d_exports.Paragraph): CountedParagraphList;
13
+ /**
14
+ * Count the paragraphs of a text box Markdown does not write, cells
15
+ * included, in document order. A text box holds no text box, so the walk
16
+ * stops at each paragraph.
17
+ */
18
+ export declare function countTextBoxLists(ctx: RenderContext, pkg: document_d_exports.DocxPackage | undefined, shape: document_d_exports.Shape): void;
@@ -0,0 +1,90 @@
1
+ import { getCachedNumberingMap } from "../docx/numberingParser.js";
2
+ import { BLOCK_TREE_DESCENT, visitBlockTreeRecords } from "../docx/paragraphTraversal.js";
3
+ import { listLabelAttrsFromRendering } from "../prosemirror/listLabels.js";
4
+ import { effectiveParagraphNumberingReference, paragraphNumberingAttr } from "../prosemirror/numberingAttr.js";
5
+ import { resolveParagraphList } from "../prosemirror/paragraphList.js";
6
+ // src/markdown/countParagraphList.ts
7
+ /**
8
+ * Advance the Markdown walk's list counter past one paragraph, as the page
9
+ * counts it. Every paragraph the page counts, Markdown counts, whatever it
10
+ * renders as and whether or not it writes it: the items after a numbered
11
+ * heading continue from its number, and the paragraphs of a text box count
12
+ * right after the paragraph the box is drawn in (where the page and the
13
+ * content reader count them), although Markdown writes no text box.
14
+ */
15
+ /** Explicit projection carried by canonical models that are not attached to a package. */
16
+ const standaloneListRendering = (para) => para.listRendering;
17
+ function countParagraphList(ctx, pkg, para) {
18
+ const inherited = ctx.paragraphNumbering(para.formatting?.styleId);
19
+ const inheritedReference = effectiveParagraphNumberingReference({
20
+ numPr: void 0,
21
+ numPrFromStyle: inherited
22
+ });
23
+ const sources = {
24
+ numPr: para.formatting?.numPr,
25
+ numPrFromStyle: para.formatting?.numPrFromStyle
26
+ };
27
+ const numbering = pkg?.numbering ? getCachedNumberingMap(pkg.numbering) : null;
28
+ const resolvedList = resolveParagraphList({
29
+ ...sources,
30
+ inheritedReference: inheritedReference ?? null
31
+ }, numbering, standaloneListRendering(para));
32
+ const resolvedRendering = resolvedList.kind === "marker" ? resolvedList.rendering : void 0;
33
+ const counterAttrs = listLabelAttrsFromRendering(resolvedRendering, {
34
+ ...sources,
35
+ numPrFromStyle: resolvedList.kind === "none" ? void 0 : inherited ?? sources.numPrFromStyle
36
+ });
37
+ if (sources.numPr === void 0 && sources.numPrFromStyle !== void 0 && inheritedReference === void 0 && para.propertyChanges !== void 0 && para.propertyChanges.length > 0) counterAttrs.numPr = paragraphNumberingAttr({ kind: "none" });
38
+ if (para.pPrMark) counterAttrs.pPrMark = para.pPrMark;
39
+ let originalList;
40
+ if (para.propertyChanges && ctx.opts.trackedChanges !== "clean") counterAttrs._propertyChanges = para.propertyChanges.map(({ previousFormatting, ...change }) => {
41
+ if (!previousFormatting) return change;
42
+ const previousInherited = ctx.paragraphNumbering(previousFormatting.styleId);
43
+ const previousInheritedReference = effectiveParagraphNumberingReference({
44
+ numPr: void 0,
45
+ numPrFromStyle: previousInherited
46
+ });
47
+ const previousSources = {
48
+ numPr: previousFormatting.numPr,
49
+ numPrFromStyle: previousFormatting.numPrFromStyle
50
+ };
51
+ const previousList = resolveParagraphList({
52
+ ...previousSources,
53
+ inheritedReference: previousInheritedReference ?? null
54
+ }, numbering);
55
+ const previousRendering = previousList.kind === "marker" ? previousList.rendering : void 0;
56
+ originalList ??= previousRendering;
57
+ const previousAttrs = listLabelAttrsFromRendering(previousRendering, {
58
+ ...previousSources,
59
+ numPrFromStyle: previousList.kind === "none" ? void 0 : previousInherited ?? previousSources.numPrFromStyle
60
+ });
61
+ return {
62
+ ...change,
63
+ previousFormatting: {
64
+ ...previousFormatting,
65
+ ...previousAttrs,
66
+ ...previousAttrs.numPr === void 0 ? { numPr: null } : { numPr: previousAttrs.numPr },
67
+ ...previousAttrs.numPrFromStyle === void 0 ? { numPrFromStyle: null } : { numPrFromStyle: previousAttrs.numPrFromStyle }
68
+ }
69
+ };
70
+ });
71
+ const countedList = ctx.nextListLabel(counterAttrs, resolvedRendering);
72
+ return {
73
+ visibleList: resolvedRendering ?? (sources.numPr?.kind === "levelOnly" ? void 0 : originalList),
74
+ label: countedList.kind === "marker" ? countedList.label : void 0
75
+ };
76
+ }
77
+ /**
78
+ * Count the paragraphs of a text box Markdown does not write, cells
79
+ * included, in document order. A text box holds no text box, so the walk
80
+ * stops at each paragraph.
81
+ */
82
+ function countTextBoxLists(ctx, pkg, shape) {
83
+ if (shape.textBody === void 0) return;
84
+ visitBlockTreeRecords(shape.textBody.content, (record) => {
85
+ if (record.type !== "paragraph") return BLOCK_TREE_DESCENT.descend;
86
+ countParagraphList(ctx, pkg, record);
87
+ return BLOCK_TREE_DESCENT.prune;
88
+ });
89
+ }
90
+ export { countParagraphList, countTextBoxLists };
@@ -1,8 +1,5 @@
1
1
  import { isQuoteStyle, resolveHeadingLevel } from "../docx/builtInStyles.js";
2
- import { getCachedNumberingMap } from "../docx/numberingParser.js";
3
- import { listLabelAttrsFromRendering } from "../prosemirror/listLabels.js";
4
- import { effectiveParagraphNumberingReference, paragraphNumberingAttr } from "../prosemirror/numberingAttr.js";
5
- import { resolveParagraphList } from "../prosemirror/paragraphList.js";
2
+ import { countParagraphList } from "./countParagraphList.js";
6
3
  import { inlineBreaksToHtml } from "./escape.js";
7
4
  import { renderParagraphInline } from "./renderRuns.js";
8
5
  // src/markdown/renderParagraph.ts
@@ -21,8 +18,6 @@ import { renderParagraphInline } from "./renderRuns.js";
21
18
  */
22
19
  /** Markdown has six heading levels; Word has nine. */
23
20
  const MAX_MARKDOWN_HEADING_LEVEL = 6;
24
- /** Explicit projection carried by canonical models that are not attached to a package. */
25
- const standaloneListRendering = (para) => para.listRendering;
26
21
  /**
27
22
  * Render a paragraph and return the block text. No surrounding blank line: the
28
23
  * caller joins blocks.
@@ -37,61 +32,7 @@ function renderParagraph(ctx, pkg, para) {
37
32
  * does not define as prose, as every other reader reads them.
38
33
  */
39
34
  function renderParagraphBlock(ctx, pkg, para) {
40
- const inherited = ctx.paragraphNumbering(para.formatting?.styleId);
41
- const inheritedReference = effectiveParagraphNumberingReference({
42
- numPr: void 0,
43
- numPrFromStyle: inherited
44
- });
45
- const sources = {
46
- numPr: para.formatting?.numPr,
47
- numPrFromStyle: para.formatting?.numPrFromStyle
48
- };
49
- const numbering = pkg?.numbering ? getCachedNumberingMap(pkg.numbering) : null;
50
- const resolvedList = resolveParagraphList({
51
- ...sources,
52
- inheritedReference: inheritedReference ?? null
53
- }, numbering, standaloneListRendering(para));
54
- const resolvedRendering = resolvedList.kind === "marker" ? resolvedList.rendering : void 0;
55
- const counterAttrs = listLabelAttrsFromRendering(resolvedRendering, {
56
- ...sources,
57
- numPrFromStyle: resolvedList.kind === "none" ? void 0 : inherited ?? sources.numPrFromStyle
58
- });
59
- if (sources.numPr === void 0 && sources.numPrFromStyle !== void 0 && inheritedReference === void 0 && para.propertyChanges !== void 0 && para.propertyChanges.length > 0) counterAttrs.numPr = paragraphNumberingAttr({ kind: "none" });
60
- if (para.pPrMark) counterAttrs.pPrMark = para.pPrMark;
61
- let originalList;
62
- if (para.propertyChanges && ctx.opts.trackedChanges !== "clean") counterAttrs._propertyChanges = para.propertyChanges.map(({ previousFormatting, ...change }) => {
63
- if (!previousFormatting) return change;
64
- const previousInherited = ctx.paragraphNumbering(previousFormatting.styleId);
65
- const previousInheritedReference = effectiveParagraphNumberingReference({
66
- numPr: void 0,
67
- numPrFromStyle: previousInherited
68
- });
69
- const previousSources = {
70
- numPr: previousFormatting.numPr,
71
- numPrFromStyle: previousFormatting.numPrFromStyle
72
- };
73
- const previousList = resolveParagraphList({
74
- ...previousSources,
75
- inheritedReference: previousInheritedReference ?? null
76
- }, numbering);
77
- const previousRendering = previousList.kind === "marker" ? previousList.rendering : void 0;
78
- originalList ??= previousRendering;
79
- const previousAttrs = listLabelAttrsFromRendering(previousRendering, {
80
- ...previousSources,
81
- numPrFromStyle: previousList.kind === "none" ? void 0 : previousInherited ?? previousSources.numPrFromStyle
82
- });
83
- return {
84
- ...change,
85
- previousFormatting: {
86
- ...previousFormatting,
87
- ...previousAttrs,
88
- ...previousAttrs.numPr === void 0 ? { numPr: null } : { numPr: previousAttrs.numPr },
89
- ...previousAttrs.numPrFromStyle === void 0 ? { numPrFromStyle: null } : { numPrFromStyle: previousAttrs.numPrFromStyle }
90
- }
91
- };
92
- });
93
- const countedList = ctx.nextListLabel(counterAttrs, resolvedRendering);
94
- const label = countedList.kind === "marker" ? countedList.label : void 0;
35
+ const { visibleList, label } = countParagraphList(ctx, pkg, para);
95
36
  const inline = renderParagraphInline(ctx, pkg, para.content, para.paraId);
96
37
  const styleId = para.formatting?.styleId;
97
38
  const headingLevel = markdownHeadingLevel(ctx, para);
@@ -108,7 +49,6 @@ function renderParagraphBlock(ctx, pkg, para) {
108
49
  type: "paragraph"
109
50
  };
110
51
  }
111
- const visibleList = resolvedRendering ?? (sources.numPr?.kind === "levelOnly" ? void 0 : originalList);
112
52
  if (visibleList && label !== void 0) return renderListItem(ctx, visibleList, label, inline);
113
53
  ctx.listIndentWidths = [];
114
54
  if (isQuoteStyle(styleId, ctx.builtInStyles)) return {
@@ -2,6 +2,7 @@ import { getHyperlinkRuns } from "../docx/hyperlinkParser.js";
2
2
  import { RELATIONSHIP_TYPES, resolveRelationshipIdOfType, resolveRelativePath } from "../docx/relsParser.js";
3
3
  import { decodeOoxmlSymbolCharacter } from "../utils/ooxmlSymbol.js";
4
4
  import { wrapComment, wrapDeletion, wrapInsertion, wrapMoveFrom, wrapMoveTo } from "./annotations.js";
5
+ import { countTextBoxLists } from "./countParagraphList.js";
5
6
  import { escapeAltText, escapeInline, escapeLinkUrl } from "./escape.js";
6
7
  import { registerImage } from "./images.js";
7
8
  import { numberNoteReference, pushWarning } from "./internals.js";
@@ -251,7 +252,9 @@ function renderRunContent(ctx, pkg, content, paraId) {
251
252
  pushWarning(ctx, `image rId=${item.image.rId ?? "(absent)"} not resolvable`);
252
253
  break;
253
254
  }
254
- case "shape": pushWarning(ctx, "shape not representable in markdown");
255
+ case "shape":
256
+ countTextBoxLists(ctx, pkg, item.shape);
257
+ pushWarning(ctx, "shape not representable in markdown");
255
258
  }
256
259
  return out;
257
260
  }
@@ -1972,7 +1972,7 @@ function extractParagraphContent(originalParagraph, _documentCounts, emptyHyperl
1972
1972
  currentTrackedChange = void 0;
1973
1973
  return;
1974
1974
  }
1975
- const { anchorId } = expectTextBoxAnchorAttrs(node);
1975
+ const { anchorId, _docxRunFormatting: carried } = expectTextBoxAnchorAttrs(node);
1976
1976
  if (textBoxAnchorMarkers.has(anchorId)) {
1977
1977
  flushCurrentInline();
1978
1978
  currentTrackedChange = void 0;
@@ -1986,8 +1986,10 @@ function extractParagraphContent(originalParagraph, _documentCounts, emptyHyperl
1986
1986
  text: ""
1987
1987
  }]
1988
1988
  };
1989
- const formatting = getAtomRunFormattingFromMarks(node.marks, formattingContext);
1990
- if (formatting) marker.formatting = formatting;
1989
+ if (carried === void 0) {
1990
+ const formatting = getAtomRunFormattingFromMarks(node.marks, formattingContext);
1991
+ if (formatting) marker.formatting = formatting;
1992
+ } else if (Object.keys(carried).length > 0) marker.formatting = structuredClone(carried);
1991
1993
  restoreRunRecord(marker, node.marks);
1992
1994
  textBoxAnchorMarkers.set(anchorId, marker);
1993
1995
  anchorRun = marker;
@@ -2180,7 +2180,10 @@ function convertRunContent({ content, marks, formatting, textBoxAnchors, authore
2180
2180
  const shp = content.shape;
2181
2181
  if (shp.textBody) {
2182
2182
  const anchorId = textBoxAnchors?.get(shp);
2183
- return anchorId ? [schema.node("textBoxAnchor", { anchorId }).mark(marks)] : [];
2183
+ return anchorId ? [schema.node("textBoxAnchor", {
2184
+ anchorId,
2185
+ _docxRunFormatting: { ...authoredFormatting }
2186
+ }).mark(marks)] : [];
2184
2187
  }
2185
2188
  return [withRunBoundaryMarks(withAlternateContent(convertShape(shp, carriedRunFormatting(authoredFormatting)), replayableShapeAlternateContent(content), content.alternateContent), marks)];
2186
2189
  }
@@ -19,7 +19,10 @@ const TextBoxAnchorExtension = createNodeExtension({
19
19
  marks: "_",
20
20
  atom: true,
21
21
  selectable: false,
22
- attrs: { anchorId: {} },
22
+ attrs: {
23
+ anchorId: {},
24
+ _docxRunFormatting: { default: null }
25
+ },
23
26
  parseDOM: [{
24
27
  tag: "span[data-docx-textbox-anchor]",
25
28
  getAttrs(dom) {
@@ -48,6 +48,12 @@ export declare const hasReplacedAnnotations: ({ carried, leading, trailing }: Re
48
48
  * replacement either.
49
49
  */
50
50
  export declare const surveyReplacedAnnotations: (doc: Node, from: number, to: number) => ReplacedAnnotations;
51
+ /**
52
+ * {@link surveyReplacedAnnotations} over the span as it reads once accepted:
53
+ * what a replacement of it carries, which a direct edit of the same text
54
+ * carries too.
55
+ */
56
+ export declare const surveyAcceptedReplacedAnnotations: (doc: Node, from: number, to: number) => ReplacedAnnotations;
51
57
  type AnnotatedReplacementOptions = {
52
58
  annotations: ReplacedAnnotations;
53
59
  /** The replacement's own inline content, already carrying its formatting. */
@@ -197,19 +197,21 @@ const NOTHING_REPLACED = {
197
197
  trailing: []
198
198
  };
199
199
  const hasReplacedAnnotations = ({ carried, leading, trailing }) => carried.length > 0 || leading.length > 0 || trailing.length > 0;
200
- /**
201
- * The annotations carried by `[from, to)`. An anchor counts only when the
202
- * range covers it whole: a partially covered atom is not deleted by the
203
- * replacement either.
204
- */
205
- const surveyReplacedAnnotations = (doc, from, to) => {
200
+ const lendsMarks = (node, content) => {
201
+ switch (content) {
202
+ case "asWritten": return true;
203
+ case "asAccepted": return !node.marks.some(({ type }) => type.name === "deletion");
204
+ default: throw new Error(`Unhandled replaced content ${String(content)}`);
205
+ }
206
+ };
207
+ const surveyAnnotations = (doc, { from, to, content }) => {
206
208
  if (to <= from) return NOTHING_REPLACED;
207
209
  const carried = [];
208
210
  const leading = [];
209
211
  const trailing = [];
210
212
  doc.nodesBetween(from, to, (node, position) => {
211
213
  if (!node.isInline) return true;
212
- for (const mark of node.marks) if (isCarried(mark) && !alreadyCarried(mark, carried)) carried.push(mark);
214
+ for (const mark of lendsMarks(node, content) ? node.marks : []) if (isCarried(mark) && !alreadyCarried(mark, carried)) carried.push(mark);
213
215
  const placement = anchorPlacement(node);
214
216
  if (placement === void 0 || position < from || position + node.nodeSize > to) return false;
215
217
  (opensRange(node, placement) ? leading : trailing).push(node);
@@ -222,6 +224,26 @@ const surveyReplacedAnnotations = (doc, from, to) => {
222
224
  };
223
225
  };
224
226
  /**
227
+ * The annotations carried by `[from, to)`. An anchor counts only when the
228
+ * range covers it whole: a partially covered atom is not deleted by the
229
+ * replacement either.
230
+ */
231
+ const surveyReplacedAnnotations = (doc, from, to) => surveyAnnotations(doc, {
232
+ from,
233
+ to,
234
+ content: "asWritten"
235
+ });
236
+ /**
237
+ * {@link surveyReplacedAnnotations} over the span as it reads once accepted:
238
+ * what a replacement of it carries, which a direct edit of the same text
239
+ * carries too.
240
+ */
241
+ const surveyAcceptedReplacedAnnotations = (doc, from, to) => surveyAnnotations(doc, {
242
+ from,
243
+ to,
244
+ content: "asAccepted"
245
+ });
246
+ /**
225
247
  * Whether the span already contributed this mark, or one the mark would evict.
226
248
  * Comments do not exclude each other, so every comment is collected; a
227
249
  * hyperlink excludes its own type, so the first address in the span wins
@@ -476,4 +498,4 @@ const inheritedReplacementMarks = (doc, from, to) => {
476
498
  const $from = doc.resolve(from);
477
499
  return (to === from ? $from.marks() : $from.marksAcross(doc.resolve(to))) ?? Mark.none;
478
500
  };
479
- export { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentPoint, commentsAcrossBlockBoundary, commentsForTrackedInsertion, createResolvedCommentPointCollector, hasReplacedAnnotations, inheritedReplacementMarks, joinCommentStretches, nonInclusiveMarkNames, placeTrackedInsertion, resolvedCommentPoints, surveyReplacedAnnotations };
501
+ export { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentPoint, commentsAcrossBlockBoundary, commentsForTrackedInsertion, createResolvedCommentPointCollector, hasReplacedAnnotations, inheritedReplacementMarks, joinCommentStretches, nonInclusiveMarkNames, placeTrackedInsertion, resolvedCommentPoints, surveyAcceptedReplacedAnnotations, surveyReplacedAnnotations };
@@ -969,6 +969,13 @@ export type TextBoxAttrs = {
969
969
  /** Internal inline position marker for an extracted text box block. */
970
970
  export type TextBoxAnchorAttrs = {
971
971
  anchorId: string;
972
+ /**
973
+ * The `w:rPr` of the run the text box came from, possibly empty. The
974
+ * anchor's formatting marks are rendered in the paragraph's style cascade
975
+ * and are not rebased when the paragraph is restyled, so the save writes
976
+ * this instead. Absent on an anchor not read from a package.
977
+ */
978
+ _docxRunFormatting?: document_d_exports.TextFormatting;
972
979
  };
973
980
  /**
974
981
  * Table node attributes
@@ -12,13 +12,21 @@ const readTextBoxAnchorAttrs = (node) => {
12
12
  path: "textBoxAnchor.attrs.anchorId",
13
13
  message: "Expected a non-empty string."
14
14
  });
15
+ const runFormatting = node.attrs["_docxRunFormatting"];
16
+ if (runFormatting !== null && runFormatting !== void 0 && (typeof runFormatting !== "object" || Array.isArray(runFormatting))) issues.push({
17
+ path: "textBoxAnchor.attrs._docxRunFormatting",
18
+ message: "Expected run formatting or null."
19
+ });
15
20
  if (issues.length > 0 || typeof anchorId !== "string") return {
16
21
  ok: false,
17
22
  issues
18
23
  };
19
24
  return {
20
25
  ok: true,
21
- value: { anchorId }
26
+ value: runFormatting === null || runFormatting === void 0 ? { anchorId } : {
27
+ anchorId,
28
+ _docxRunFormatting: runFormatting
29
+ }
22
30
  };
23
31
  };
24
32
  const expectTextBoxAnchorAttrs = (node) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-core",
3
- "version": "0.64.1",
3
+ "version": "0.66.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",
@@ -171,16 +171,14 @@
171
171
  "perf": "bun scripts/profile-editor.ts"
172
172
  },
173
173
  "dependencies": {
174
- "@stll/docx-core": "^0.39.2",
174
+ "@stll/docx-core": "^0.41.0",
175
175
  "@stll/docx-utils": "^0.2.0",
176
176
  "@stll/template-conditions": "^0.6.2",
177
- "@stll/utf8-boundaries": "0.1.2",
177
+ "@stll/utf8-boundaries": "0.2.0",
178
178
  "better-result": "3.0.1",
179
179
  "csstype": "^3.1.3",
180
180
  "dompurify": "^3.4.17",
181
- "fast-xml-parser": "^5.11.2",
182
181
  "hyphen": "1.14.1",
183
- "jszip": "3.10.2",
184
182
  "utif2": "^4.1.0",
185
183
  "valibot": "1.5.0"
186
184
  },
@@ -1,60 +0,0 @@
1
- import { XmlElement, XmlNamespaceScope } from "./xmlParser.js";
2
- type ParseXmlResult = {
3
- status: "parsed";
4
- value: XmlElement;
5
- } | {
6
- status: "unsupported";
7
- };
8
- type XmlSourceRange = {
9
- readonly start: number;
10
- readonly end: number;
11
- };
12
- /** Exact element source ranges, available only from the opt-in source-range reader. */
13
- export declare const getXmlSourceRange: (element: XmlElement) => XmlSourceRange | undefined;
14
- /**
15
- * Parse ordinary OOXML into Folio's existing element representation in one
16
- * pass. Unsupported or malformed constructs return a sentinel so callers can
17
- * retain the general-purpose parser as a compatibility fallback.
18
- */
19
- type AttributeValueSpan = {
20
- start: number;
21
- end: number;
22
- };
23
- type OpenTagVisitor = (element: XmlElement, attributeValueSpans: ReadonlyMap<string, AttributeValueSpan>) => ReadonlyMap<string, string> | null;
24
- type OpenTagScanVisitor = (...args: Parameters<OpenTagVisitor>) => null;
25
- type ElementScanOptions = {
26
- element: XmlElement;
27
- attributeValueSpans: ReadonlyMap<string, AttributeValueSpan>;
28
- nameEnd: number;
29
- parent: XmlElement | undefined;
30
- };
31
- type ElementScanVisitor = (options: ElementScanOptions) => void;
32
- /**
33
- * @param inheritedNamespaceScope the bindings in scope around `xml`, for a
34
- * fragment captured out of a part whose root declared them.
35
- */
36
- export declare const parseStreamingXml: (xml: string, inheritedNamespaceScope?: XmlNamespaceScope) => ParseXmlResult;
37
- /** Retain source spans for every element without changing the parsed namespace tree. */
38
- export declare const parseStreamingXmlWithSourceRanges: (xml: string, inheritedNamespaceScope?: XmlNamespaceScope) => ParseXmlResult;
39
- /** Retain the body tree while collecting identities for import repair. */
40
- export declare const parseStreamingXmlWithIdentityVisitor: (xml: string, visitOpenTag: OpenTagScanVisitor) => ParseXmlResult;
41
- /** Rewrite selected decimal attribute values while preserving every other source byte. */
42
- export declare const rewriteStreamingXmlDecimalAttributes: (xml: string, visitOpenTag: OpenTagVisitor) => {
43
- status: "rewritten";
44
- value: string;
45
- } | {
46
- status: "unsupported";
47
- };
48
- /** Visit identity attributes and namespace bindings without building an XML tree. */
49
- export declare const scanStreamingXmlNumericIdAttributes: (xml: string, visitOpenTag: OpenTagScanVisitor) => {
50
- status: "unsupported";
51
- } | {
52
- status: "scanned";
53
- };
54
- /** Visit namespace-resolved start tags and source spans without building a tree. */
55
- export declare const scanStreamingXmlElements: (xml: string, visitElement: ElementScanVisitor) => {
56
- status: "unsupported";
57
- } | {
58
- status: "scanned";
59
- };
60
- export {};