@stll/folio-core 0.60.0 → 0.62.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 (173) hide show
  1. package/README.md +13 -0
  2. package/dist/ai-edits/apply.js +139 -32
  3. package/dist/ai-edits/headless.d.ts +3 -0
  4. package/dist/ai-edits/headless.js +70 -75
  5. package/dist/ai-edits/note-story-inventory.d.ts +7 -0
  6. package/dist/ai-edits/note-story-inventory.js +11 -0
  7. package/dist/ai-edits/read.js +16 -9
  8. package/dist/ai-edits/revisionStretches.js +52 -1
  9. package/dist/ai-edits/table-row-column-mutations.js +6 -1
  10. package/dist/compare/reproducible-package.js +2 -1
  11. package/dist/compare/verification.js +1 -0
  12. package/dist/compat/eigenpal.d.ts +3 -1
  13. package/dist/controller/canonicalClipboard.js +2 -1
  14. package/dist/controller/canonicalListRendering.d.ts +4 -6
  15. package/dist/controller/canonicalListRendering.js +3 -157
  16. package/dist/controller/headerFooterEditorManager.d.ts +1 -0
  17. package/dist/controller/headerFooterEditorManager.js +11 -0
  18. package/dist/controller/hiddenEditorManager.d.ts +1 -1
  19. package/dist/controller/hiddenEditorManager.js +3 -1
  20. package/dist/controller/layoutPipeline.d.ts +3 -1
  21. package/dist/controller/layoutPipeline.js +13 -4
  22. package/dist/controller/layoutSession.d.ts +2 -1
  23. package/dist/controller/noteEditorManager.d.ts +1 -1
  24. package/dist/display-list/build/imagePrimitives.js +28 -12
  25. package/dist/display-list/build/paragraphPrimitives.js +15 -2
  26. package/dist/display-list/dom/renderDisplayListToDom.js +8 -5
  27. package/dist/display-list/types.d.ts +7 -3
  28. package/dist/docx/canonicalStoryRepack.js +3 -2
  29. package/dist/docx/commentTextParagraph.d.ts +6 -0
  30. package/dist/docx/commentTextParagraph.js +15 -0
  31. package/dist/docx/conformance.js +6 -1
  32. package/dist/docx/diagramPreview.js +2 -1
  33. package/dist/docx/documentParser.d.ts +13 -1
  34. package/dist/docx/documentParser.js +1 -1
  35. package/dist/docx/encryption/compoundFile.d.ts +0 -8
  36. package/dist/docx/encryption/compoundFile.js +2 -1
  37. package/dist/docx/encryption/cryptoBytes.d.ts +2 -2
  38. package/dist/docx/encryption/cryptoBytes.js +3 -18
  39. package/dist/docx/encryption/encryptionInfo.d.ts +0 -5
  40. package/dist/docx/encryption/encryptionInfo.js +2 -1
  41. package/dist/docx/ensureParaIds.js +2 -1
  42. package/dist/docx/groupDrawingParser.js +28 -13
  43. package/dist/docx/listModelNormalization.d.ts +10 -0
  44. package/dist/docx/listModelNormalization.js +166 -0
  45. package/dist/docx/metadataPrivacy.js +3 -2
  46. package/dist/docx/metafileRaster.js +2 -1
  47. package/dist/docx/packageParts.js +6 -5
  48. package/dist/docx/paragraphParser.js +6 -4
  49. package/dist/docx/parser.js +5 -4
  50. package/dist/docx/preservedRunContent.js +11 -2
  51. package/dist/docx/removeHeaderFooterParts.js +8 -7
  52. package/dist/docx/replyToComment.js +3 -13
  53. package/dist/docx/rezip.js +88 -89
  54. package/dist/docx/runParser.js +12 -6
  55. package/dist/docx/runPropertyPresence.d.ts +11 -0
  56. package/dist/docx/runPropertyPresence.js +14 -0
  57. package/dist/docx/selectiveSave.js +15 -14
  58. package/dist/docx/serializer/runSerializer.js +3 -2
  59. package/dist/docx/serializer/tableSerializer.js +2 -1
  60. package/dist/docx/server/applyDocxXmlPatchProposal.js +2 -2
  61. package/dist/docx/server/boundedArchive.js +2 -1
  62. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +2 -1
  63. package/dist/docx/server/inspectDocxPackage.js +5 -1
  64. package/dist/docx/server/validateDocxConformance.js +2 -1
  65. package/dist/docx/tableParser.js +7 -4
  66. package/dist/docx/tableRowPropertyPresence.d.ts +9 -0
  67. package/dist/docx/tableRowPropertyPresence.js +15 -0
  68. package/dist/docx/unzip.js +2 -1
  69. package/dist/docx/xmlEncoding.js +6 -5
  70. package/dist/fonts/fontsourceFaces.js +2 -1
  71. package/dist/fonts/sfnt/parse.js +6 -9
  72. package/dist/fonts/sfnt/subset.js +2 -1
  73. package/dist/fonts/sfnt/tables.js +2 -3
  74. package/dist/fonts/sfnt/woff.js +2 -1
  75. package/dist/i18n/messages/ar.gen.js +1 -0
  76. package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
  77. package/dist/i18n/messages/catalogs.gen.js +17 -0
  78. package/dist/i18n/messages/cs.gen.js +1 -0
  79. package/dist/i18n/messages/de.gen.js +1 -0
  80. package/dist/i18n/messages/en.gen.js +1 -0
  81. package/dist/i18n/messages/es.gen.js +1 -0
  82. package/dist/i18n/messages/et.gen.js +1 -0
  83. package/dist/i18n/messages/fr.gen.js +1 -0
  84. package/dist/i18n/messages/he.gen.js +1 -0
  85. package/dist/i18n/messages/hi.gen.js +1 -0
  86. package/dist/i18n/messages/hu.gen.js +1 -0
  87. package/dist/i18n/messages/lt.gen.js +1 -0
  88. package/dist/i18n/messages/lv.gen.js +1 -0
  89. package/dist/i18n/messages/messages.gen.d.ts +1 -0
  90. package/dist/i18n/messages/pl.gen.js +1 -0
  91. package/dist/i18n/messages/pt-BR.gen.js +1 -0
  92. package/dist/i18n/messages/sk.gen.js +1 -0
  93. package/dist/i18n/messages/tr.gen.js +1 -0
  94. package/dist/i18n/messages/zh-CN.gen.js +1 -0
  95. package/dist/index.d.ts +3 -1
  96. package/dist/internal/revisionResolutionInline.d.ts +2 -1
  97. package/dist/internal/revisionResolutionInline.js +22 -1
  98. package/dist/internal/wholeStoryRevisionResolution.js +24 -7
  99. package/dist/layout-bridge/convert/endnoteLayout.d.ts +1 -1
  100. package/dist/layout-bridge/convert/endnoteLayout.js +1 -3
  101. package/dist/layout-bridge/convert/flowConversionShared.d.ts +2 -0
  102. package/dist/layout-bridge/convert/footnoteLayout.d.ts +4 -0
  103. package/dist/layout-bridge/convert/footnoteLayout.js +13 -3
  104. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +5 -0
  105. package/dist/layout-bridge/convert/headerFooterLayout.js +12 -15
  106. package/dist/layout-bridge/convert/paragraphRuns.js +2 -1
  107. package/dist/layout-engine/index.js +2 -2
  108. package/dist/layout-engine/measure/crossRunWords.js +12 -2
  109. package/dist/layout-engine/noteAreaFlow.d.ts +4 -4
  110. package/dist/layout-engine/noteAreaFlow.js +12 -6
  111. package/dist/layout-engine/types.d.ts +5 -2
  112. package/dist/layout-painter/index.d.ts +1 -1
  113. package/dist/managers/AutoSaveManager.js +2 -1
  114. package/dist/managers/EditorModeManager.d.ts +10 -1
  115. package/dist/managers/EditorModeManager.js +15 -1
  116. package/dist/managers/FindReplaceManager.d.ts +13 -6
  117. package/dist/managers/FindReplaceManager.js +15 -9
  118. package/dist/managers/TableSelectionManager.d.ts +1 -1
  119. package/dist/managers/autoSaveCodec.js +2 -6
  120. package/dist/managers/reviewDisplayMode.d.ts +8 -0
  121. package/dist/managers/reviewDisplayMode.js +15 -0
  122. package/dist/paged-layout/editorScrollRoot.d.ts +3 -1
  123. package/dist/paged-layout/editorScrollRoot.js +2 -2
  124. package/dist/paged-layout/pageText.d.ts +11 -2
  125. package/dist/paged-layout/pageText.js +8 -3
  126. package/dist/pdf/fonts.js +5 -14
  127. package/dist/pdf/images.js +4 -2
  128. package/dist/pdf/objects.d.ts +0 -11
  129. package/dist/pdf/objects.js +7 -9
  130. package/dist/pdf/writePdf.js +2 -1
  131. package/dist/prosemirror/commands/comments.js +51 -13
  132. package/dist/prosemirror/commentRangeInlineAtoms.d.ts +40 -0
  133. package/dist/prosemirror/commentRangeInlineAtoms.js +33 -0
  134. package/dist/prosemirror/containerFinalParagraph.d.ts +8 -8
  135. package/dist/prosemirror/containerFinalParagraph.js +8 -8
  136. package/dist/prosemirror/conversion/fromProseDoc.js +14 -8
  137. package/dist/prosemirror/conversion/toProseDoc.js +16 -23
  138. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +16 -2
  139. package/dist/prosemirror/fieldRepresentation.js +4 -2
  140. package/dist/prosemirror/findReplaceSelection.d.ts +48 -2
  141. package/dist/prosemirror/findReplaceSelection.js +153 -38
  142. package/dist/prosemirror/inlineDisplayText.d.ts +10 -0
  143. package/dist/prosemirror/inlineDisplayText.js +29 -0
  144. package/dist/prosemirror/markupViewProjection.d.ts +10 -1
  145. package/dist/prosemirror/markupViewProjection.js +21 -3
  146. package/dist/prosemirror/plugins/transactionInvariants.js +6 -1
  147. package/dist/prosemirror/replacedAnnotations.d.ts +15 -1
  148. package/dist/prosemirror/replacedAnnotations.js +71 -12
  149. package/dist/prosemirror/runFormattingReconciliation.d.ts +3 -1
  150. package/dist/prosemirror/runFormattingReconciliation.js +10 -2
  151. package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +5 -7
  152. package/dist/prosemirror/runIdentityAcrossRevisions.js +66 -38
  153. package/dist/prosemirror/runPropertyChangeResolution.js +13 -1
  154. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -1
  155. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -1
  156. package/dist/utils/base64.d.ts +1 -31
  157. package/dist/utils/base64.js +19 -33
  158. package/dist/utils/clipboard.js +5 -4
  159. package/dist/utils/colorResolver.js +11 -2
  160. package/dist/utils/findMatchView.d.ts +32 -0
  161. package/dist/utils/findMatchView.js +406 -0
  162. package/dist/utils/findReplace.d.ts +67 -4
  163. package/dist/utils/findReplace.js +359 -37
  164. package/dist/utils/findSearchKey.d.ts +29 -0
  165. package/dist/utils/findSearchKey.js +34 -0
  166. package/dist/utils/replaceText.js +138 -94
  167. package/dist/utils/sliceText.d.ts +2 -0
  168. package/dist/utils/sliceText.js +2 -0
  169. package/dist/utils/textEncoding.d.ts +2 -0
  170. package/dist/utils/textEncoding.js +2 -0
  171. package/dist/utils/tiffConverter.d.ts +0 -9
  172. package/dist/utils/tiffConverter.js +2 -4
  173. package/package.json +51 -25
@@ -1,160 +1,6 @@
1
- import { foldListNumberFields, planParagraphListNumberFold, unfoldedListNumberContent } from "../docx/foldedListNumberFields.js";
2
- import { computeListMarker } from "../docx/listMarkerComputation.js";
3
- import { listRenderingDefinitionsMatch } from "../docx/listRendering.js";
4
- import { computeListRendering, getCachedNumberingMap } from "../docx/numberingParser.js";
5
- import { assignParagraphIndentationProjection, cloneParagraphWithPropertySource, paragraphFormattingWithAuthoredIndentation, sameParagraphIndentationProjection } from "../docx/paragraphPropertySource.js";
6
- import { effectiveParagraphNumberingReference, paragraphNumberingSourcesFromBoundary } from "../prosemirror/numberingAttr.js";
7
- import { paragraphIndentationFromFormatting } from "../prosemirror/paragraphIndentation.js";
8
- import { listIndentationProvenancePatch } from "../prosemirror/styles/resolvedStyleAttrs.js";
9
- import { createStyleResolver } from "../prosemirror/styles/styleResolver.js";
10
- import { canonicalJson } from "../utils/canonicalJson.js";
11
- import { panic } from "better-result";
12
- import { DOCUMENT_OP_TYPES, EMPTY_PROPERTY_SETS, OP_STORIES, registerParagraphPropertySnapshot, withBodyContent } from "@stll/docx-core/ops";
1
+ import { normalizeListModelRendering } from "../docx/listModelNormalization.js";
13
2
  //#region src/controller/canonicalListRendering.ts
14
- /** Canonical edits retain authored fields; list rendering follows the current numbering. */
15
- const normalizeCanonicalListRendering = (document) => {
16
- const inverse = [];
17
- const styles = createStyleResolver(document.package.styles);
18
- const numbering = document.package.numbering === void 0 ? null : getCachedNumberingMap(document.package.numbering);
19
- const normalizeBody = (body, story) => {
20
- const counters = {
21
- numbering,
22
- listCounters: /* @__PURE__ */ new Map(),
23
- abstractCounters: /* @__PURE__ */ new Map(),
24
- restartedNumIds: /* @__PURE__ */ new Set(),
25
- previousList: {
26
- abstractNumId: null,
27
- fromStyle: false,
28
- numId: null
29
- }
30
- };
31
- const content = body.content.map((paragraph) => {
32
- if (paragraph.type !== "paragraph") return paragraph;
33
- registerParagraphPropertySnapshot({
34
- paragraph,
35
- snapshot: paragraphFormattingWithAuthoredIndentation(paragraph)
36
- });
37
- const formatting = paragraph.formatting;
38
- const next = cloneParagraphWithPropertySource(paragraph, {});
39
- if (formatting?.numPrFromStyle !== void 0 && (formatting.numPr?.kind === "reference" || formatting.numPr?.kind === "none")) {
40
- next.formatting = { ...formatting };
41
- delete next.formatting.numPrFromStyle;
42
- inverse.push({
43
- type: DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS,
44
- story,
45
- blockId: paragraph.paraId ?? panic("Canonical numbering normalization requires paragraph identities."),
46
- patch: { numPrFromStyle: formatting.numPrFromStyle }
47
- });
48
- }
49
- const numPr = effectiveParagraphNumberingReference(paragraphNumberingSourcesFromBoundary({
50
- numPr: next.formatting?.numPr,
51
- numPrFromStyle: next.formatting?.numPrFromStyle
52
- }));
53
- if (paragraph.listRendering === void 0 && formatting?.numPrFromStyle === void 0 && (numPr?.kind !== "reference" || numbering === null)) {
54
- computeListMarker(paragraph, counters);
55
- return paragraph;
56
- }
57
- const definition = numPr?.kind === "reference" && numbering !== null ? computeListRendering(numPr, numbering) : null;
58
- const cached = paragraph.listRendering;
59
- const rendering = definition !== null && cached !== void 0 && listRenderingDefinitionsMatch(cached, definition) ? { ...cached } : definition;
60
- const authoredFormatting = paragraphFormattingWithAuthoredIndentation(next);
61
- assignParagraphIndentationProjection({
62
- paragraph: next,
63
- authored: authoredFormatting,
64
- inherited: paragraphIndentationFromFormatting(listIndentationProvenancePatch({
65
- direct: paragraphIndentationFromFormatting(authoredFormatting),
66
- styleFormatting: styles.resolveParagraphStyle(next.formatting?.styleId).paragraphFormatting,
67
- numberingSource: next.formatting?.numPr?.kind === "reference" || next.formatting?.numPrFromStyle === void 0 ? "paragraph" : "style",
68
- numPr: numPr?.kind === "reference" ? {
69
- numId: numPr.numId,
70
- ilvl: numPr.ilvl ?? 0
71
- } : void 0,
72
- numbering
73
- })._resolvedFormatting) ?? {}
74
- });
75
- if (canonicalJson(paragraphIndentationFromFormatting(next.formatting)) !== canonicalJson(paragraphIndentationFromFormatting(formatting))) inverse.unshift({
76
- type: DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS,
77
- story,
78
- blockId: paragraph.paraId ?? panic("Canonical numbering normalization requires paragraph identities."),
79
- patch: {
80
- indentLeft: formatting?.indentLeft ?? null,
81
- indentRight: formatting?.indentRight ?? null,
82
- indentFirstLine: formatting?.indentFirstLine ?? null,
83
- hangingIndent: formatting?.hangingIndent ?? null
84
- },
85
- whenEmpty: formatting === void 0 ? EMPTY_PROPERTY_SETS.OMIT : EMPTY_PROPERTY_SETS.KEEP
86
- });
87
- if (rendering === null) delete next.listRendering;
88
- else {
89
- const fields = foldListNumberFields(paragraph.content.map(unfoldedListNumberContent), () => void 0).fieldCount;
90
- if (fields === 0) delete rendering.implicitChildLevelAdvances;
91
- else rendering.implicitChildLevelAdvances = fields;
92
- const template = rendering.markerTemplate ?? rendering.marker;
93
- const fold = planParagraphListNumberFold(paragraph.content, !rendering.isBullet && template !== "" && !template.includes(" "));
94
- if (fold.suffix === void 0) delete rendering.foldedMarkerSuffix;
95
- else rendering.foldedMarkerSuffix = fold.suffix;
96
- const child = numbering?.getLevel(rendering.numId, rendering.level + 1)?.pPr;
97
- if (fold.suffix !== void 0 && child?.hangingIndent === true && child.indentFirstLine !== void 0 && child.indentFirstLine < 0) rendering.markerSecondSlotOffsetTwips = -child.indentFirstLine;
98
- else delete rendering.markerSecondSlotOffsetTwips;
99
- rendering.marker = rendering.foldedMarkerSuffix !== void 0 && !template.includes(" ") ? `${template}\t${rendering.foldedMarkerSuffix}` : template;
100
- next.listRendering = rendering;
101
- }
102
- computeListMarker(next, counters);
103
- registerParagraphPropertySnapshot({
104
- paragraph: next,
105
- snapshot: authoredFormatting
106
- });
107
- if (sameParagraphIndentationProjection({
108
- left: next,
109
- right: paragraph
110
- }) && canonicalJson(next.formatting) === canonicalJson(formatting) && canonicalJson(next.listRendering) === canonicalJson(cached)) return paragraph;
111
- return next;
112
- });
113
- return content.every((paragraph, index) => paragraph === body.content[index]) ? body : {
114
- ...body,
115
- ...withBodyContent(body, content)
116
- };
117
- };
118
- const pkg = document.package;
119
- const body = normalizeBody(pkg.document, OP_STORIES.MAIN);
120
- const normalizeParts = (parts, kind) => {
121
- if (parts === void 0) return parts;
122
- const normalized = new Map([...parts].map(([id, part]) => [id, normalizeBody(part, {
123
- kind,
124
- rId: id
125
- })]));
126
- return [...normalized].every(([id, part]) => part === parts.get(id)) ? parts : normalized;
127
- };
128
- const normalizeNotes = (notes, kind) => {
129
- if (notes === void 0) return notes;
130
- const normalized = notes.map((note) => normalizeBody(note, {
131
- kind,
132
- id: note.id
133
- }));
134
- return normalized.every((note, index) => note === notes[index]) ? notes : normalized;
135
- };
136
- const headers = normalizeParts(pkg.headers, "header");
137
- const footers = normalizeParts(pkg.footers, "footer");
138
- const footnotes = normalizeNotes(pkg.footnotes, "footnote");
139
- const endnotes = normalizeNotes(pkg.endnotes, "endnote");
140
- if (body === pkg.document && headers === pkg.headers && footers === pkg.footers && footnotes === pkg.footnotes && endnotes === pkg.endnotes) return {
141
- document,
142
- inverse
143
- };
144
- return {
145
- inverse,
146
- document: {
147
- ...document,
148
- package: {
149
- ...pkg,
150
- document: body,
151
- ...headers === void 0 ? {} : { headers },
152
- ...footers === void 0 ? {} : { footers },
153
- ...footnotes === void 0 ? {} : { footnotes },
154
- ...endnotes === void 0 ? {} : { endnotes }
155
- }
156
- }
157
- };
158
- };
3
+ /** Canonical edits share list model normalization with the headless projection. */
4
+ const normalizeCanonicalListRendering = normalizeListModelRendering;
159
5
  //#endregion
160
6
  export { normalizeCanonicalListRendering };
@@ -18,6 +18,7 @@ type HeaderFooterEditorTransaction = {
18
18
  type HeaderFooterEditorManagerDeps = {
19
19
  createView?: ((mountNode: HTMLElement, props: DirectEditorProps) => EditorView) | undefined;
20
20
  getDocument: () => document_d_exports.Document | null;
21
+ getReadOnly?: (() => boolean) | undefined;
21
22
  getCanonicalApi?: (() => HiddenEditorApi | null) | undefined;
22
23
  getExperimentalSession?: (() => "canonical" | undefined) | undefined;
23
24
  onSessionRefusal?: ((reason: string, gap: CanonicalGap, error?: Error) => void) | undefined;
@@ -91,6 +91,15 @@ const createHeaderFooterEditorManager = (deps) => {
91
91
  const sync = () => {
92
92
  const host = deps.getHost();
93
93
  if (!host) return;
94
+ const getReadOnly = deps.getReadOnly;
95
+ if (getReadOnly) {
96
+ const readOnly = getReadOnly();
97
+ for (const part of mounted.values()) {
98
+ const expectedEditable = !readOnly;
99
+ if (part.view.editable === expectedEditable) continue;
100
+ part.view.setProps({ editable: () => !getReadOnly() });
101
+ }
102
+ }
94
103
  if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting) && !deps.getCanonicalApi?.()?.getCanonicalStoryProjection(OP_STORIES.MAIN)) return;
95
104
  const document = getOwnedDocument();
96
105
  const styles = deps.getStyles();
@@ -179,11 +188,13 @@ const createHeaderFooterEditorManager = (deps) => {
179
188
  }
180
189
  const view = (deps.createView ?? createEditorView)(mountNode, {
181
190
  ...canonical.props,
191
+ editable: () => !(deps.getReadOnly?.() ?? false),
182
192
  state: canonicalProjection ? EditorState.create({
183
193
  doc: canonicalProjection,
184
194
  plugins: [createDocumentStyleContextPlugin(styles), createDocumentNumberingPlugin(numbering)]
185
195
  }) : buildInitialState(headerFooter, styles, theme, numbering, manager),
186
196
  dispatchTransaction(transaction) {
197
+ if ((deps.getReadOnly?.() ?? false) && transaction.docChanged) return;
187
198
  if (canonical.dispatch(transaction)) return;
188
199
  const nextState = view.state.apply(transaction);
189
200
  const docChanged = !nextState.doc.eq(view.state.doc);
@@ -1,10 +1,10 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { CanonicalGap } from "../types/canonicalCapabilities.js";
3
+ import { EditorMode } from "../managers/EditorModeManager.js";
3
4
  import { HiddenEditorApi } from "./hiddenEditorApi.js";
4
5
  import { HiddenEditorStateReason } from "../layout-engine/layoutInstrumentation.js";
5
6
  import { ExtensionManager } from "../prosemirror/extensions/ExtensionManager.js";
6
7
  import { RemoteSelection } from "../types/remote-selection.js";
7
- import { EditorMode } from "../managers/EditorModeManager.js";
8
8
  import { EditorState, Plugin, Transaction } from "prosemirror-state";
9
9
  import { EditorView } from "prosemirror-view";
10
10
  import * as Yjs from "yjs";
@@ -812,7 +812,9 @@ const createHiddenEditorManager = (deps) => {
812
812
  };
813
813
  const syncEditable = () => {
814
814
  if (!view) return;
815
- syncHiddenEditorAccessibility(view, deps.getReadOnly());
815
+ const readOnly = deps.getReadOnly();
816
+ if (view.editable !== !readOnly) view.setProps({ editable: () => !deps.getReadOnly() });
817
+ syncHiddenEditorAccessibility(view, readOnly);
816
818
  };
817
819
  const publishStoryCommit = (storyView, commit) => {
818
820
  if (!view || editorSession.type !== "canonical") return false;
@@ -1,8 +1,8 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { TemplatePreviewEntry, TemplatePreviewHiddenRange } from "../prosemirror/plugins/templatePreviewValues.js";
3
+ import { DisplayMode } from "../managers/EditorModeManager.js";
3
4
  import { ColumnLayout, FlowBlock, FootnoteContent, HeaderFooterContent, Layout, Measure, PageHeaderFooterRefs, PageMargins } from "../layout-engine/types.js";
4
5
  import { LayoutRunOptions } from "./layoutRunOptions.js";
5
- import { DisplayMode } from "../managers/EditorModeManager.js";
6
6
  import { LayoutSession } from "./layoutSession.js";
7
7
  import { HyphenationReadiness } from "./hyphenationReadiness.js";
8
8
  import { ConvertHeaderFooterOptions, HeaderFooterMetrics } from "../layout-bridge/convert/headerFooterLayout.js";
@@ -79,6 +79,8 @@ type LayoutPipelineDeps<THfPMs> = {
79
79
  * authored text with runs hidden afterwards.
80
80
  */
81
81
  markupView: DisplayMode;
82
+ /** Independent highlight visibility in All Markup; resolved views always hide comments. */
83
+ showCommentHighlights?: boolean;
82
84
  };
83
85
  declare function runLayoutPipeline<THfPMs>(deps: LayoutPipelineDeps<THfPMs>, state: EditorState, options?: LayoutRunOptions): LayoutOutcome;
84
86
  //#endregion
@@ -42,7 +42,7 @@ const formatEndnoteTexts = (numbers, formatNumber) => {
42
42
  for (const [id, displayNumber] of numbers) texts.set(id, formatNumber(displayNumber));
43
43
  return texts;
44
44
  };
45
- const sameMeasureInputs = (left, right) => left.embeddedFontFamilyMap === right.embeddedFontFamilyMap && left.styles === right.styles && left.theme === right.theme && left.defaultTabStop === right.defaultTabStop && left.pageContentHeight === right.pageContentHeight && left.fontSet === right.fontSet && left.fontAlternates.size === right.fontAlternates.size && [...left.fontAlternates].every(([name, alternate]) => right.fontAlternates.get(name) === alternate) && left.markupView === right.markupView;
45
+ const sameMeasureInputs = (left, right) => left.embeddedFontFamilyMap === right.embeddedFontFamilyMap && left.styles === right.styles && left.theme === right.theme && left.defaultTabStop === right.defaultTabStop && left.pageContentHeight === right.pageContentHeight && left.fontSet === right.fontSet && left.fontAlternates.size === right.fontAlternates.size && [...left.fontAlternates].every(([name, alternate]) => right.fontAlternates.get(name) === alternate) && left.markupView === right.markupView && left.showCommentHighlights === right.showCommentHighlights;
46
46
  /**
47
47
  * The range an incremental pass re-measures, or `null` for a full measure: the
48
48
  * committed measures must come from the same measure inputs, and the range is
@@ -136,7 +136,7 @@ function runLayoutPipeline(deps, state, options = {}) {
136
136
  return result;
137
137
  }
138
138
  function runLayoutPipelineMeasured(deps, state, options) {
139
- const { contentWidth, columns, pageSize, margins, pageGap, showMarginGuides, marginGuideColor, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, readFontSetSignature, buildFootnoteRenderItems, describeInvalidHighlightMarks, pageRenderer = PAGE_RENDERER.legacy, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES, emptyTemplatePreviewHidden: EMPTY_TEMPLATE_PREVIEW_HIDDEN, markupView } = deps;
139
+ const { contentWidth, columns, pageSize, margins, pageGap, showMarginGuides, marginGuideColor, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, readFontSetSignature, buildFootnoteRenderItems, describeInvalidHighlightMarks, pageRenderer = PAGE_RENDERER.legacy, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES, emptyTemplatePreviewHidden: EMPTY_TEMPLATE_PREVIEW_HIDDEN, markupView, showCommentHighlights = true } = deps;
140
140
  let outcome = {};
141
141
  let pendingArtifacts;
142
142
  let pendingTemplatePreview;
@@ -154,6 +154,7 @@ function runLayoutPipelineMeasured(deps, state, options) {
154
154
  let phaseStartedAt = performance.now();
155
155
  const pageContentHeight = pageSize.h - margins.top - margins.bottom;
156
156
  const fontAlternates = buildFontAlternates(document?.package.fontTable);
157
+ const displayCommentHighlights = showCommentHighlights && markupView !== "no-markup" && markupView !== "original";
157
158
  const embeddedFontFamilies = session.embeddedFontFamilyMap ? { embeddedFontFamilyMap: session.embeddedFontFamilyMap } : {};
158
159
  const measureInputs = {
159
160
  ...embeddedFontFamilies,
@@ -163,9 +164,11 @@ function runLayoutPipelineMeasured(deps, state, options) {
163
164
  pageContentHeight,
164
165
  fontSet,
165
166
  fontAlternates,
166
- markupView
167
+ markupView,
168
+ showCommentHighlights: displayCommentHighlights
167
169
  };
168
170
  const flowOpts = {
171
+ showCommentHighlights: displayCommentHighlights,
169
172
  ...embeddedFontFamilies,
170
173
  pageContentHeight,
171
174
  fontAlternates,
@@ -246,6 +249,8 @@ function runLayoutPipelineMeasured(deps, state, options) {
246
249
  const buildHfOptions = (pageCount, fieldInputs) => {
247
250
  const hfMeasureBlocks = (hfBlocks, hfWidth) => measureBlocks(hfBlocks, hfWidth, void 0, void 0, buildHeaderFooterFieldValues(hfBlocks, pageCount, hfClock, fieldInputs), { allowEndTabOverflow: true });
248
251
  return {
252
+ markupView,
253
+ showCommentHighlights: displayCommentHighlights,
249
254
  ...flowOpts.styles ? { styles: flowOpts.styles } : {},
250
255
  ...document?.package.numbering === void 0 ? {} : { numbering: document.package.numbering },
251
256
  ...flowOpts.defaultSize === void 0 ? {} : { defaultSize: flowOpts.defaultSize },
@@ -378,7 +383,11 @@ function runLayoutPipelineMeasured(deps, state, options) {
378
383
  outcome.measures = newMeasures;
379
384
  recordPhaseDuration("measure-blocks", phaseStartedAt);
380
385
  const buildNoteOptions = () => {
381
- const noteOptions = { measureBlocks };
386
+ const noteOptions = {
387
+ measureBlocks,
388
+ showCommentHighlights: displayCommentHighlights,
389
+ markupView
390
+ };
382
391
  if (flowOpts.styles) noteOptions.styles = flowOpts.styles;
383
392
  if (flowOpts.defaultSize !== void 0) noteOptions.defaultSize = flowOpts.defaultSize;
384
393
  if (_theme !== void 0) noteOptions.theme = _theme;
@@ -1,7 +1,7 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { TemplatePreviewEntry, TemplatePreviewHiddenRange, TemplatePreviewValues } from "../prosemirror/plugins/templatePreviewValues.js";
3
- import { FlowBlock, Measure } from "../layout-engine/types.js";
4
3
  import { DisplayMode } from "../managers/EditorModeManager.js";
4
+ import { FlowBlock, Measure } from "../layout-engine/types.js";
5
5
  import { FontAlternates } from "../fonts/fontAlternates.js";
6
6
  import { EditorState } from "prosemirror-state";
7
7
  //#region src/controller/layoutSession.d.ts
@@ -36,6 +36,7 @@ type LayoutMeasureInputs = {
36
36
  fontAlternates: FontAlternates;
37
37
  /** The review view: each lays out different text, so a measure is reusable only within one. */
38
38
  markupView: DisplayMode;
39
+ showCommentHighlights?: boolean;
39
40
  };
40
41
  type LayoutSession = {
41
42
  embeddedFontFamilyMap?: ReadonlyMap<string, string>;
@@ -1,7 +1,7 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { CanonicalGap } from "../types/canonicalCapabilities.js";
3
- import { HiddenEditorApi } from "./hiddenEditorApi.js";
4
3
  import { NoteStoryKey } from "../types/editor-story.js";
4
+ import { HiddenEditorApi } from "./hiddenEditorApi.js";
5
5
  import { Plugin } from "prosemirror-state";
6
6
  import { DirectEditorProps, EditorView } from "prosemirror-view";
7
7
  //#region src/controller/noteEditorManager.d.ts
@@ -1,5 +1,6 @@
1
1
  import { parseRotationDegrees } from "../../utils/rotationBoundingBox.js";
2
2
  import { sanitizeImageSrc } from "../../utils/sanitizeImageSrc.js";
3
+ import { base64ToBytes, encodeUtf8 } from "../../utils/textEncoding.js";
3
4
  import { paintPreview } from "./previewPrimitives.js";
4
5
  import { resolveBorderStroke } from "./strokes.js";
5
6
  import { UNSUPPORTED_CONSTRUCT } from "./unsupported.js";
@@ -10,23 +11,19 @@ import { Result } from "better-result";
10
11
  *
11
12
  * The display list carries raw bytes plus the format they are already in, so a
12
13
  * backend never owns a codec. This module therefore decodes only far enough to
13
- * fill `pixelWidth`/`pixelHeight` and to know PNG from JPEG. It transcodes
14
- * nothing: a WMF, EMF, TIFF, SVG, GIF or BMP source is reported through
15
- * `unsupported` and paints nothing, because a silently missing picture is worse
16
- * than a named one.
14
+ * fill `pixelWidth`/`pixelHeight` and identify PNG, JPEG, or a generated SVG
15
+ * preview. It transcodes nothing: WMF, EMF, TIFF, GIF, and BMP are reported
16
+ * through `unsupported` and paint nothing, because a silently missing picture
17
+ * is worse than a named one.
17
18
  */
18
- const DATA_URL_PATTERN = /^data:([^;,]*)(;base64)?,(.*)$/su;
19
+ const DATA_URL_PATTERN = /^data:([^;,]*)(?:;charset=[^;,]*)?(;base64)?,(.*)$/su;
19
20
  /**
20
21
  * `undefined` for a payload `atob` rejects: a malformed `data:` URL is a
21
22
  * document the builder reports, not an exception that aborts the build.
22
23
  */
23
24
  const decodeBase64 = (encoded) => {
24
- const decoded = Result.try(() => atob(encoded));
25
- if (decoded.isErr()) return;
26
- const binary = decoded.value;
27
- const bytes = new Uint8Array(binary.length);
28
- for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
29
- return bytes;
25
+ const decoded = Result.try(() => base64ToBytes(encoded));
26
+ return decoded.isOk() ? decoded.value : void 0;
30
27
  };
31
28
  const readUint32BE = (bytes, offset) => bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3];
32
29
  const readUint16BE = (bytes, offset) => bytes[offset] << 8 | bytes[offset + 1];
@@ -164,7 +161,26 @@ var ImageTable = class {
164
161
  if (safeSrc === void 0) return fail("image source is not a data: or blob: URL");
165
162
  const match = DATA_URL_PATTERN.exec(safeSrc);
166
163
  if (!match) return fail("image bytes are not inline (blob: URLs need a fetch the builder cannot do)");
167
- if (match[2] === void 0) return fail("only base64 data: URLs carry decodable bytes");
164
+ if (match[1] === "image/svg+xml" && match[2] === void 0) {
165
+ const decoded = Result.try(() => decodeURIComponent(match[3]));
166
+ if (decoded.isErr()) return fail("SVG data: URL payload is not valid percent-encoding");
167
+ if (!decoded.value.includes("data-folio-preview=\"wpGroup\"")) return fail("SVG is not a supported generated group preview");
168
+ const width = /<svg\b[^>]*\bwidth="([0-9.]+)"/u.exec(decoded.value)?.at(1);
169
+ const height = /<svg\b[^>]*\bheight="([0-9.]+)"/u.exec(decoded.value)?.at(1);
170
+ const pixelWidth = width === void 0 ? 0 : Number(width);
171
+ const pixelHeight = height === void 0 ? 0 : Number(height);
172
+ if (!(pixelWidth > 0 && pixelHeight > 0)) return fail("SVG carries no positive numeric width and height");
173
+ const ref = this.sources.length;
174
+ this.sources.push({
175
+ format: "svg",
176
+ bytes: encodeUtf8(decoded.value),
177
+ pixelWidth,
178
+ pixelHeight
179
+ });
180
+ this.refBySrc.set(src, ref);
181
+ return ref;
182
+ }
183
+ if (match[2] === void 0) return fail("only base64 data: URLs and SVG previews carry decodable bytes");
168
184
  const bytes = decodeBase64(match[3]);
169
185
  if (bytes === void 0) return fail("image data: URL payload is not valid base64");
170
186
  if (bytes.length === 0) return fail("image data: URL decoded to no bytes");
@@ -353,7 +353,7 @@ const smallCapsGlyphRunSegments = (glyphs, fontSizePx) => {
353
353
  * first, then the glyphs, then the decorations whose geometry CSS would have
354
354
  * derived from the font.
355
355
  */
356
- const emitGlyphRun = ({ sink, context, run, glyphs, style, paintXPx, baselineYPx, lineTopYPx, lineHeightPx, isRtl, pmRange, collapsedEdge }) => {
356
+ const emitGlyphRun = ({ sink, context, run, glyphs, style, paintXPx, baselineYPx, lineTopYPx, lineHeightPx, isRtl, pmRange, decorationRange, collapsedEdge }) => {
357
357
  if (glyphs.text.length === 0) return;
358
358
  const fontSizePx = ptToPx(style.fontSize ?? 11);
359
359
  const color = resolveRunColor(run, context);
@@ -423,6 +423,7 @@ const emitGlyphRun = ({ sink, context, run, glyphs, style, paintXPx, baselineYPx
423
423
  ...unicodeBidi === void 0 ? {} : { unicodeBidi },
424
424
  ...stroke === void 0 ? {} : { stroke },
425
425
  ...pmRange === void 0 ? {} : { pmRange },
426
+ ...decorationRange === void 0 ? {} : { decorationRange },
426
427
  ...collapsedEdge === void 0 ? {} : { collapsedEdge }
427
428
  };
428
429
  const smallCapsRuns = style.fontVariant === "small-caps" && direction === "ltr" ? smallCapsGlyphRunSegments(glyphs, fontSizePx) : void 0;
@@ -436,6 +437,7 @@ const emitGlyphRun = ({ sink, context, run, glyphs, style, paintXPx, baselineYPx
436
437
  else {
437
438
  const adjustments = glyphs.adjustments === void 0 ? {} : { adjustments: glyphs.adjustments };
438
439
  let segmentXPx = paintXPx;
440
+ let segmentTextOffset = 0;
439
441
  for (const segment of smallCapsRuns) {
440
442
  sink.glyphs.push({
441
443
  kind: "glyphRun",
@@ -446,9 +448,14 @@ const emitGlyphRun = ({ sink, context, run, glyphs, style, paintXPx, baselineYPx
446
448
  kerning: glyphs.kerning,
447
449
  smallCaps: true,
448
450
  ...adjustments,
449
- ...commonFields
451
+ ...commonFields,
452
+ ...decorationRange === void 0 ? {} : { decorationRange: {
453
+ ...decorationRange,
454
+ textOffset: decorationRange.textOffset + segmentTextOffset
455
+ } }
450
456
  });
451
457
  segmentXPx += segment.widthPx;
458
+ segmentTextOffset += segment.text.length;
452
459
  }
453
460
  }
454
461
  emitDecorations({
@@ -681,6 +688,12 @@ const paintLine = ({ block, line, geometry, context, alignment, isRtl, isFirstLi
681
688
  kind: "text",
682
689
  text: glyphs.text
683
690
  },
691
+ ...run.pmStart === void 0 || run.pmEnd === void 0 || resolveFieldText(run, context) !== run.fallback ? {} : { decorationRange: {
692
+ start: run.pmStart,
693
+ end: run.pmEnd,
694
+ story: context.story,
695
+ textOffset: 0
696
+ } },
684
697
  glyphs,
685
698
  style,
686
699
  paintXPx: paintXOf(),
@@ -48,7 +48,8 @@ const PAGE_ELEMENT_ID_PREFIX = "page-";
48
48
  const DEGREES_PER_RADIAN = 180 / Math.PI;
49
49
  const IMAGE_MIME_TYPES = {
50
50
  png: "image/png",
51
- jpeg: "image/jpeg"
51
+ jpeg: "image/jpeg",
52
+ svg: "image/svg+xml"
52
53
  };
53
54
  /**
54
55
  * The display list does not say whether an embedded face is sfnt or WOFF, so a
@@ -252,11 +253,11 @@ const applyAdjustments = (span, adjustments) => {
252
253
  * and in each note, so a surface has to know which one it landed in before it
253
254
  * can use the number at all.
254
255
  */
255
- const applyModelRange = (span, range) => {
256
+ const applyModelRange = (span, range, purpose = "editing") => {
256
257
  if (range === void 0) return;
257
- span.className = "layout-run-text";
258
- span.dataset["pmStart"] = String(range.start);
259
- span.dataset["pmEnd"] = String(range.end);
258
+ if (purpose === "editing") span.className = "layout-run-text";
259
+ span.dataset[purpose === "editing" ? "pmStart" : "decorationStart"] = String(range.start);
260
+ span.dataset[purpose === "editing" ? "pmEnd" : "decorationEnd"] = String(range.end);
260
261
  const { story } = range;
261
262
  span.dataset["story"] = story.kind;
262
263
  switch (story.kind) {
@@ -300,6 +301,8 @@ const paintGlyphRun = (run, context) => {
300
301
  applyAdjustments(span, run.adjustments);
301
302
  span.dataset["advanceSum"] = String(advanceSum);
302
303
  applyModelRange(span, run.pmRange);
304
+ applyModelRange(span, run.decorationRange, "decoration");
305
+ if (run.decorationRange) span.dataset["decorationTextOffset"] = String(run.decorationRange.textOffset);
303
306
  if (run.collapsedEdge !== void 0) {
304
307
  span.dataset[run.collapsedEdge.side === "leading" ? "collapsedLeadingSpaces" : "collapsedTrailingSpaces"] = "true";
305
308
  span.dataset["collapsedSpaceAdvance"] = String(run.collapsedEdge.spaceAdvancePx);
@@ -124,11 +124,11 @@ type DisplayFontRef = number;
124
124
  type DisplayImageRef = number;
125
125
  /**
126
126
  * Raw image bytes plus the format they are already in. `png` and `jpeg`
127
- * pass through to PDF unchanged; every other source format is transcoded by
128
- * the producer, so a backend never owns a codec.
127
+ * pass through to PDF unchanged. SVG is retained for vector previews in the
128
+ * DOM backend; PDF reports it as unsupported rather than silently rasterizing.
129
129
  */
130
130
  type DisplayImageSource = {
131
- readonly format: "png" | "jpeg";
131
+ readonly format: "png" | "jpeg" | "svg";
132
132
  readonly bytes: Uint8Array;
133
133
  readonly pixelWidth: number;
134
134
  readonly pixelHeight: number;
@@ -278,6 +278,10 @@ type DisplayGlyphRun = {
278
278
  * substituted field value, a tab leader.
279
279
  */
280
280
  readonly pmRange?: DisplayModelRange;
281
+ /** Source atom for view-only decorations; never used for editing positions. */
282
+ readonly decorationRange?: DisplayModelRange & {
283
+ readonly textOffset: number;
284
+ };
281
285
  };
282
286
  /** An axis-aligned rectangle, filled and/or stroked. At least one is set. */
283
287
  type DisplayRectPrimitive = {
@@ -1,5 +1,6 @@
1
1
  import { withTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
2
2
  import { withSectionReferenceResolution } from "../internal/sectionReferenceResolution.js";
3
+ import { decodeText, encodeUtf8 } from "../utils/textEncoding.js";
3
4
  import { readDocumentSectionFacts } from "./documentSectionFacts.js";
4
5
  import { parseDocx } from "./parser.js";
5
6
  import { assertSectionCarriersMatchModel } from "./rezip.js";
@@ -9,7 +10,7 @@ import JSZip from "jszip";
9
10
  /** Canonical snapshots own explicit section references; the PM projection does not. */
10
11
  const referenceKey = ({ element, type, rId }) => `${element}:${type}:${rId}`;
11
12
  const xmlFingerprint = async (xml) => {
12
- const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(xml));
13
+ const digest = await crypto.subtle.digest("SHA-256", encodeUtf8(xml));
13
14
  let fingerprint = "";
14
15
  for (const byte of new Uint8Array(digest)) fingerprint += byte.toString(16).padStart(2, "0");
15
16
  return fingerprint;
@@ -18,7 +19,7 @@ const xmlFingerprint = async (xml) => {
18
19
  const repackWithCanonicalStoryRemovals = async ({ document, repack }) => {
19
20
  const originalBuffer = document.originalBuffer;
20
21
  if (!originalBuffer) return repack();
21
- const xml = await (await JSZip.loadAsync(originalBuffer)).file("word/document.xml")?.async("text");
22
+ const xml = await (await JSZip.loadAsync(originalBuffer)).file("word/document.xml")?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
22
23
  if (!xml) return repack();
23
24
  const before = readDocumentSectionFacts(xml);
24
25
  const serializedXml = serializeDocument(document);
@@ -0,0 +1,6 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ //#region src/docx/commentTextParagraph.d.ts
3
+ /** Plain comment text has no authored paragraph formatting. */
4
+ declare const commentTextParagraph: (text: string) => document_d_exports.Paragraph;
5
+ //#endregion
6
+ export { commentTextParagraph };
@@ -0,0 +1,15 @@
1
+ //#region src/docx/commentTextParagraph.ts
2
+ /** Plain comment text has no authored paragraph formatting. */
3
+ const commentTextParagraph = (text) => ({
4
+ type: "paragraph",
5
+ content: [{
6
+ type: "run",
7
+ formatting: {},
8
+ content: [{
9
+ type: "text",
10
+ text
11
+ }]
12
+ }]
13
+ });
14
+ //#endregion
15
+ export { commentTextParagraph };
@@ -1,3 +1,4 @@
1
+ import { sliceText } from "../utils/sliceText.js";
1
2
  import { getLocalName, getNamespacePrefix, parseXmlDocument } from "./xmlParser.js";
2
3
  import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
3
4
  //#region src/docx/conformance.ts
@@ -62,7 +63,11 @@ function scanRootStartTag(scanTarget) {
62
63
  }
63
64
  const detectDocxConformanceClass = (documentXml) => {
64
65
  if (documentXml === null) return DOCX_CONFORMANCE_CLASSES.UNKNOWN;
65
- const rootStartTag = scanRootStartTag(documentXml.length <= ROOT_START_TAG_SCAN_LIMIT ? documentXml : documentXml.slice(0, ROOT_START_TAG_SCAN_LIMIT));
66
+ const rootStartTag = scanRootStartTag(documentXml.length <= ROOT_START_TAG_SCAN_LIMIT ? documentXml : sliceText({
67
+ text: documentXml,
68
+ units: "codeUnits",
69
+ end: ROOT_START_TAG_SCAN_LIMIT
70
+ }));
66
71
  const root = (rootStartTag === void 0 ? null : parseXmlDocument(rootStartTag.endsWith("/>") ? rootStartTag : `${rootStartTag.slice(0, -1)}/>`)) ?? parseXmlDocument(documentXml);
67
72
  return detectDocxConformanceClassFromRoot(root);
68
73
  };
@@ -1,3 +1,4 @@
1
+ import { decodeText } from "../utils/textEncoding.js";
1
2
  import { resolveRelationshipIdOfType, resolveRelativePath } from "./relsParser.js";
2
3
  import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, findChildByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getLocalName, parseNumericAttribute, parseXmlDocument } from "./xmlParser.js";
3
4
  //#region src/docx/diagramPreview.ts
@@ -31,7 +32,7 @@ const partByRelationshipId = ({ rels, media, rId, type, cache }) => {
31
32
  const cached = cache.get(path);
32
33
  if (cached !== void 0) return cached;
33
34
  const file = media.get(path);
34
- const parsed = file?.data ? parseXmlDocument(new TextDecoder().decode(file.data)) : null;
35
+ const parsed = file?.data ? parseXmlDocument(decodeText(new Uint8Array(file.data))) : null;
35
36
  cache.set(path, parsed);
36
37
  return parsed;
37
38
  };
@@ -19,6 +19,18 @@ declare function extractTemplateVariables(text: string): string[];
19
19
  * @returns Array of unique variable names
20
20
  */
21
21
  declare function extractAllTemplateVariables(content: document_d_exports.BlockContent[]): string[];
22
+ /**
23
+ * Build sections from content based on section properties in paragraphs
24
+ *
25
+ * In OOXML, sections are delimited by:
26
+ * 1. w:pPr/w:sectPr within a paragraph (marks end of a section)
27
+ * 2. w:body/w:sectPr (final section properties)
28
+ *
29
+ * @param content - All block content
30
+ * @param finalSectPr - Final section properties from body
31
+ * @returns Array of sections
32
+ */
33
+ declare function buildSections(content: document_d_exports.BlockContent[], finalSectPr: document_d_exports.SectionProperties | undefined): document_d_exports.Section[];
22
34
  /**
23
35
  * Parse document.xml body content
24
36
  *
@@ -67,4 +79,4 @@ declare function getSectionCount(body: document_d_exports.DocumentBody): number;
67
79
  */
68
80
  declare function hasTemplateVariables(body: document_d_exports.DocumentBody): boolean;
69
81
  //#endregion
70
- export { extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getParagraphCount, getSectionCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };
82
+ export { buildSections, extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getParagraphCount, getSectionCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };
@@ -257,4 +257,4 @@ function hasTemplateVariables(body) {
257
257
  return extractAllTemplateVariables(body.content).length > 0;
258
258
  }
259
259
  //#endregion
260
- export { extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getParagraphCount, getSectionCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };
260
+ export { buildSections, extractAllTemplateVariables, extractTemplateVariables, getAllParagraphs, getAllTables, getParagraphCount, getSectionCount, hasTemplateVariables, parseDocumentBody, parseDocumentBodyTree };