@stll/folio-core 0.7.0 → 0.9.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 (193) hide show
  1. package/README.md +39 -0
  2. package/dist/ai-edits/apply.js +18 -4
  3. package/dist/ai-edits/blockRange.d.ts +13 -2
  4. package/dist/ai-edits/blockRange.js +23 -2
  5. package/dist/ai-edits/headless.d.ts +31 -4
  6. package/dist/ai-edits/headless.js +148 -35
  7. package/dist/ai-edits/index.d.ts +6 -4
  8. package/dist/ai-edits/index.js +4 -1
  9. package/dist/ai-edits/scoped-reading.d.ts +9 -0
  10. package/dist/ai-edits/scoped-reading.js +60 -0
  11. package/dist/ai-edits/snapshot.js +53 -7
  12. package/dist/ai-edits/types.d.ts +46 -3
  13. package/dist/compat/eigenpal.d.ts +8 -3
  14. package/dist/compat/eigenpal.js +7 -2
  15. package/dist/controller/fontReadiness.d.ts +29 -0
  16. package/dist/controller/fontReadiness.js +139 -0
  17. package/dist/controller/latestRequestGate.d.ts +13 -0
  18. package/dist/controller/latestRequestGate.js +16 -0
  19. package/dist/controller/layoutPipeline.js +88 -10
  20. package/dist/document-operations.d.ts +10 -3
  21. package/dist/document-operations.js +29 -14
  22. package/dist/docx/blockContentParser.js +53 -10
  23. package/dist/docx/commentParser.js +1 -1
  24. package/dist/docx/compatibility.d.ts +30 -2
  25. package/dist/docx/compatibility.js +118 -39
  26. package/dist/docx/conformance.d.ts +6 -0
  27. package/dist/docx/conformance.js +18 -0
  28. package/dist/docx/encryption/agileDecryption.js +1 -1
  29. package/dist/docx/encryption/encryptionInfo.js +1 -1
  30. package/dist/docx/encryption/openEncryptedDocx.js +1 -1
  31. package/dist/docx/ensureParaIds.d.ts +29 -0
  32. package/dist/docx/ensureParaIds.js +423 -0
  33. package/dist/docx/fontTableParser.d.ts +6 -0
  34. package/dist/docx/fontTableParser.js +72 -0
  35. package/dist/docx/groupDrawingParser.js +7 -2
  36. package/dist/docx/headerFooterParser.js +1 -1
  37. package/dist/docx/normalizeBaseDirection.js +1 -1
  38. package/dist/docx/numberingParser.js +2 -0
  39. package/dist/docx/paragraphParser.js +21 -1
  40. package/dist/docx/paragraphTextBoxEnrichment.js +141 -2
  41. package/dist/docx/parser.d.ts +1 -1
  42. package/dist/docx/parser.js +14 -9
  43. package/dist/docx/rezip.js +70 -5
  44. package/dist/docx/runConsolidator.js +4 -0
  45. package/dist/docx/runParser.js +19 -1
  46. package/dist/docx/selectiveSave.js +3 -3
  47. package/dist/docx/selectiveXmlPatch.d.ts +10 -1
  48. package/dist/docx/selectiveXmlPatch.js +1 -1
  49. package/dist/docx/serializer/fontTableSerializer.d.ts +6 -0
  50. package/dist/docx/serializer/fontTableSerializer.js +26 -0
  51. package/dist/docx/serializer/headerFooterSerializer.js +1 -1
  52. package/dist/docx/serializer/paragraphSerializer.js +8 -0
  53. package/dist/docx/serializer/runSerializer.js +14 -2
  54. package/dist/docx/serializer/settingsSerializer.d.ts +6 -0
  55. package/dist/docx/serializer/settingsSerializer.js +16 -0
  56. package/dist/docx/serializer/stylesSerializer.d.ts +6 -0
  57. package/dist/docx/serializer/stylesSerializer.js +56 -0
  58. package/dist/docx/serializer/tableSerializer.js +29 -26
  59. package/dist/docx/serializer/themeSerializer.d.ts +6 -0
  60. package/dist/docx/serializer/themeSerializer.js +36 -0
  61. package/dist/docx/settingsParser.js +46 -0
  62. package/dist/docx/styleParser.js +26 -1
  63. package/dist/docx/tableParser.js +19 -1
  64. package/dist/docx/textBoxParser.js +6 -1
  65. package/dist/docx/vmlImageParser.js +146 -1
  66. package/dist/i18n/messages/catalogs.gen.d.ts +5916 -5916
  67. package/dist/i18n/messages/catalogs.gen.js +5910 -5910
  68. package/dist/index.d.ts +8 -3
  69. package/dist/index.js +7 -2
  70. package/dist/layout-bridge/convert/footnoteLayout.d.ts +3 -0
  71. package/dist/layout-bridge/convert/footnoteLayout.js +3 -1
  72. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -0
  73. package/dist/layout-bridge/convert/headerFooterLayout.js +22 -3
  74. package/dist/layout-bridge/convert/paragraphFrames.d.ts +7 -0
  75. package/dist/layout-bridge/convert/paragraphFrames.js +136 -0
  76. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +14 -2
  77. package/dist/layout-bridge/convert/toFlowBlocks.js +93 -17
  78. package/dist/layout-bridge/engine/hitTest.js +2 -1
  79. package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
  80. package/dist/layout-bridge/engine/measuring/index.js +2 -1
  81. package/dist/layout-bridge/engine/selectionRects.js +2 -1
  82. package/dist/layout-engine/headerFooterRefs.d.ts +7 -0
  83. package/dist/layout-engine/headerFooterRefs.js +40 -0
  84. package/dist/layout-engine/index.d.ts +3 -2
  85. package/dist/layout-engine/index.js +68 -106
  86. package/dist/layout-engine/measure/cache.js +11 -3
  87. package/dist/layout-engine/measure/font-metrics.worker.js +1 -0
  88. package/dist/layout-engine/measure/index.d.ts +2 -1
  89. package/dist/layout-engine/measure/index.js +2 -1
  90. package/dist/layout-engine/measure/lineBreakProvider.d.ts +33 -0
  91. package/dist/layout-engine/measure/lineBreakProvider.js +252 -0
  92. package/dist/layout-engine/measure/lineBreaks.d.ts +7 -2
  93. package/dist/layout-engine/measure/lineBreaks.js +11 -18
  94. package/dist/layout-engine/measure/listMarkerWidth.d.ts +4 -1
  95. package/dist/layout-engine/measure/listMarkerWidth.js +33 -8
  96. package/dist/layout-engine/measure/measureBlocks.d.ts +4 -2
  97. package/dist/layout-engine/measure/measureBlocks.js +138 -86
  98. package/dist/layout-engine/measure/measureContainer.js +32 -9
  99. package/dist/layout-engine/measure/measureHelpers.js +5 -2
  100. package/dist/layout-engine/measure/measureParagraph.js +346 -74
  101. package/dist/layout-engine/measure/measureTypes.d.ts +2 -1
  102. package/dist/layout-engine/measure/measureWorker.d.ts +2 -2
  103. package/dist/layout-engine/measure/measureWorker.js +8 -19
  104. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +3 -14
  105. package/dist/layout-engine/measure/tableCellFloating.d.ts +10 -3
  106. package/dist/layout-engine/measure/tableCellFloating.js +37 -29
  107. package/dist/layout-engine/measure/tableCellFlow.d.ts +27 -0
  108. package/dist/layout-engine/measure/tableCellFlow.js +73 -0
  109. package/dist/layout-engine/measure/tableCellGrid.d.ts +15 -0
  110. package/dist/layout-engine/measure/tableCellGrid.js +62 -0
  111. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +18 -0
  112. package/dist/layout-engine/measure/textBoxParagraphLayout.js +32 -0
  113. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +18 -0
  114. package/dist/layout-engine/measure/textMeasurementPolicy.js +24 -0
  115. package/dist/layout-engine/paginator.d.ts +2 -1
  116. package/dist/layout-engine/paginator.js +7 -6
  117. package/dist/layout-engine/paragraphFrame.d.ts +22 -0
  118. package/dist/layout-engine/paragraphFrame.js +17 -0
  119. package/dist/layout-engine/paragraphSequence.d.ts +7 -0
  120. package/dist/layout-engine/paragraphSequence.js +25 -0
  121. package/dist/layout-engine/paragraphSpacing.d.ts +17 -0
  122. package/dist/layout-engine/paragraphSpacing.js +30 -0
  123. package/dist/layout-engine/renderedBreakReconciliation.d.ts +63 -0
  124. package/dist/layout-engine/renderedBreakReconciliation.js +86 -0
  125. package/dist/layout-engine/tableRowBreak.d.ts +5 -2
  126. package/dist/layout-engine/tableRowBreak.js +62 -29
  127. package/dist/layout-engine/types.d.ts +54 -12
  128. package/dist/layout-engine/types.js +15 -2
  129. package/dist/layout-painter/anchoredImagePosition.d.ts +23 -0
  130. package/dist/layout-painter/anchoredImagePosition.js +95 -0
  131. package/dist/layout-painter/borderStroke.d.ts +23 -0
  132. package/dist/layout-painter/borderStroke.js +39 -0
  133. package/dist/layout-painter/renderImage.d.ts +5 -4
  134. package/dist/layout-painter/renderImage.js +18 -4
  135. package/dist/layout-painter/renderPage.d.ts +2 -27
  136. package/dist/layout-painter/renderPage.js +15 -101
  137. package/dist/layout-painter/renderParagraph.js +45 -26
  138. package/dist/layout-painter/renderTable.d.ts +2 -0
  139. package/dist/layout-painter/renderTable.js +257 -47
  140. package/dist/layout-painter/renderTextBox.js +17 -7
  141. package/dist/managers/DocumentLoaderManager.d.ts +1 -1
  142. package/dist/managers/DocumentLoaderManager.js +2 -2
  143. package/dist/paged-layout/sectionBlockWidths.d.ts +3 -0
  144. package/dist/paged-layout/sectionBlockWidths.js +9 -0
  145. package/dist/paged-layout/sectionGeometry.js +1 -1
  146. package/dist/prosemirror/attrs/index.d.ts +4 -2
  147. package/dist/prosemirror/attrs/index.js +32 -2
  148. package/dist/prosemirror/commands/propertyChangeScope.js +1 -1
  149. package/dist/prosemirror/conversion/fromProseDoc.js +35 -18
  150. package/dist/prosemirror/conversion/toProseDoc.js +73 -37
  151. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  152. package/dist/prosemirror/extensions/core/ParagraphExtension.js +8 -2
  153. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +1 -1
  154. package/dist/prosemirror/extensions/features/ListExtension.js +2 -0
  155. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +68 -16
  156. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +1 -0
  157. package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
  158. package/dist/prosemirror/extensions/marks/LanguageExtension.d.ts +7 -0
  159. package/dist/prosemirror/extensions/marks/LanguageExtension.js +30 -0
  160. package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
  161. package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
  162. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +1 -1
  163. package/dist/prosemirror/extensions/marks/markUtils.js +12 -0
  164. package/dist/prosemirror/extensions/nodes/ImageExtension.js +2 -1
  165. package/dist/prosemirror/extensions/nodes/TableExtension.js +4 -2
  166. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -1
  167. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +7 -0
  168. package/dist/prosemirror/schema/index.d.ts +2 -2
  169. package/dist/prosemirror/schema/marks.d.ts +6 -1
  170. package/dist/prosemirror/schema/nodes.d.ts +16 -12
  171. package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
  172. package/dist/prosemirror/utils/tabCalculator.d.ts +3 -1
  173. package/dist/prosemirror/utils/tabCalculator.js +12 -10
  174. package/dist/prosemirror/validation.js +4 -1
  175. package/dist/redline.js +13 -3
  176. package/dist/server.d.ts +9 -4
  177. package/dist/server.js +7 -2
  178. package/dist/style-sets/extract.d.ts +35 -0
  179. package/dist/style-sets/extract.js +123 -0
  180. package/dist/style-sets/stellaStyle.d.ts +13 -0
  181. package/dist/style-sets/stellaStyle.js +516 -0
  182. package/dist/style-sets/types.d.ts +31 -0
  183. package/dist/style-sets/types.js +5 -0
  184. package/dist/types/index.d.ts +2 -1
  185. package/dist/utils/createDocument.d.ts +12 -2
  186. package/dist/utils/createDocument.js +55 -36
  187. package/dist/utils/fontResolver.js +6 -6
  188. package/dist/utils/formatToStyle.js +1 -1
  189. package/dist/utils/hexId.d.ts +8 -1
  190. package/dist/utils/hexId.js +11 -3
  191. package/dist/utils/paragraphFormattingMerge.d.ts +14 -3
  192. package/dist/utils/paragraphFormattingMerge.js +14 -4
  193. package/package.json +5 -1
@@ -0,0 +1,31 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+
3
+ //#region src/style-sets/types.d.ts
4
+ declare const DOCUMENT_STYLE_SET_VERSION: 1;
5
+ declare const DOCUMENT_PRESET_VERSION: 1;
6
+ /**
7
+ * A content-free, portable set of DOCX formatting resources.
8
+ *
9
+ * Source document content, properties, relationships, media, comments, and
10
+ * revision data are deliberately excluded. Numbering and theme resources live
11
+ * beside styles because paragraph styles can depend on them for their meaning.
12
+ */
13
+ type DocumentStyleSet = {
14
+ version: typeof DOCUMENT_STYLE_SET_VERSION;
15
+ name: string;
16
+ initialParagraphStyleId: string;
17
+ styles: document_d_exports.StyleDefinitions;
18
+ numbering?: document_d_exports.NumberingDefinitions;
19
+ theme?: document_d_exports.Theme;
20
+ fontTable?: document_d_exports.FontTable;
21
+ settings?: document_d_exports.DocumentSettings;
22
+ };
23
+ /** Page-level choices are separate from reusable style resources. */
24
+ type DocumentPreset = {
25
+ version: typeof DOCUMENT_PRESET_VERSION;
26
+ name: string;
27
+ styleSet: DocumentStyleSet;
28
+ sectionProperties: document_d_exports.SectionProperties;
29
+ };
30
+ //#endregion
31
+ export { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DocumentPreset, DocumentStyleSet };
@@ -0,0 +1,5 @@
1
+ //#region src/style-sets/types.ts
2
+ const DOCUMENT_STYLE_SET_VERSION = 1;
3
+ const DOCUMENT_PRESET_VERSION = 1;
4
+ //#endregion
5
+ export { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION };
@@ -13,6 +13,7 @@ type ConditionalFormatStyle = document_d_exports.ConditionalFormatStyle;
13
13
  type DocDefaults = document_d_exports.DocDefaults;
14
14
  type Document = document_d_exports.Document;
15
15
  type DocumentBody = document_d_exports.DocumentBody;
16
+ type DocxConformanceClass = document_d_exports.DocxConformanceClass;
16
17
  type DocxPackage = document_d_exports.DocxPackage;
17
18
  type DrawingContent = document_d_exports.DrawingContent;
18
19
  type EmphasisMark = document_d_exports.EmphasisMark;
@@ -104,4 +105,4 @@ type ThemeFont = document_d_exports.ThemeFont;
104
105
  type ThemeFontScheme = document_d_exports.ThemeFontScheme;
105
106
  type UnderlineStyle = document_d_exports.UnderlineStyle;
106
107
  type VerticalAlign = document_d_exports.VerticalAlign;
107
- export type { AbstractNumbering, BlockContent, BookmarkEnd, BookmarkStart, BorderSpec, BreakContent, CellMargins, ColorValue, Column, ComplexField, ConditionalFormatStyle, DocDefaults, Document, DocumentBody, DocxPackage, DrawingContent, EmphasisMark, Endnote, EndnotePosition, EndnoteProperties, Field, FieldCharContent, FieldType, FloatingTableProperties, FontInfo, FontTable, FooterReference, Footnote, FootnotePosition, FootnoteProperties, HeaderFooter, HeaderFooterType, HeaderReference, Hyperlink, Image, ImageCrop, ImagePadding, ImagePosition, ImageSize, ImageTransform, ImageWrap, InstrTextContent, LevelSuffix, LineNumberRestart, LineSpacingRule, ListLevel, ListRendering, MediaFile, NoBreakHyphenContent, NoteNumberRestart, NoteReferenceContent, NumberFormat, NumberingDefinitions, NumberingInstance, PageOrientation, Paragraph, ParagraphAlignment, ParagraphContent, ParagraphFormatting, Relationship, RelationshipMap, RelationshipType, Run, RunContent, Section, SectionProperties, SectionStart, ShadingProperties, Shape, ShapeContent, ShapeFill, ShapeOutline, ShapeTextBody, ShapeType, SimpleField, SoftHyphenContent, Style, StyleDefinitions, StyleType, SymbolContent, TabContent, TabLeader, TabStop, TabStopAlignment, Table, TableBorders, TableCell, TableCellFormatting, TableFormatting, TableLook, TableMeasurement, TableRow, TableRowFormatting, TableWidthType, TextBox, TextContent, TextEffect, TextFormatting, Theme, ThemeColorScheme, ThemeColorSlot, ThemeFont, ThemeFontScheme, UnderlineStyle, VerticalAlign };
108
+ export type { AbstractNumbering, BlockContent, BookmarkEnd, BookmarkStart, BorderSpec, BreakContent, CellMargins, ColorValue, Column, ComplexField, ConditionalFormatStyle, DocDefaults, Document, DocumentBody, DocxConformanceClass, DocxPackage, DrawingContent, EmphasisMark, Endnote, EndnotePosition, EndnoteProperties, Field, FieldCharContent, FieldType, FloatingTableProperties, FontInfo, FontTable, FooterReference, Footnote, FootnotePosition, FootnoteProperties, HeaderFooter, HeaderFooterType, HeaderReference, Hyperlink, Image, ImageCrop, ImagePadding, ImagePosition, ImageSize, ImageTransform, ImageWrap, InstrTextContent, LevelSuffix, LineNumberRestart, LineSpacingRule, ListLevel, ListRendering, MediaFile, NoBreakHyphenContent, NoteNumberRestart, NoteReferenceContent, NumberFormat, NumberingDefinitions, NumberingInstance, PageOrientation, Paragraph, ParagraphAlignment, ParagraphContent, ParagraphFormatting, Relationship, RelationshipMap, RelationshipType, Run, RunContent, Section, SectionProperties, SectionStart, ShadingProperties, Shape, ShapeContent, ShapeFill, ShapeOutline, ShapeTextBody, ShapeType, SimpleField, SoftHyphenContent, Style, StyleDefinitions, StyleType, SymbolContent, TabContent, TabLeader, TabStop, TabStopAlignment, Table, TableBorders, TableCell, TableCellFormatting, TableFormatting, TableLook, TableMeasurement, TableRow, TableRowFormatting, TableWidthType, TextBox, TextContent, TextEffect, TextFormatting, Theme, ThemeColorScheme, ThemeColorSlot, ThemeFont, ThemeFontScheme, UnderlineStyle, VerticalAlign };
@@ -1,10 +1,11 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { DocumentPreset, DocumentStyleSet } from "../style-sets/types.js";
2
3
 
3
4
  //#region src/utils/createDocument.d.ts
4
5
  /**
5
6
  * Options for creating an empty document
6
7
  */
7
- type CreateEmptyDocumentOptions = {
8
+ type CreateEmptyDocumentBaseOptions = {
8
9
  /** Page width in twips (default: 12240 = 8.5 inches) */pageWidth?: number; /** Page height in twips (default: 15840 = 11 inches) */
9
10
  pageHeight?: number; /** Page orientation (default: 'portrait') */
10
11
  orientation?: "portrait" | "landscape"; /** Top margin in twips (default: 1440 = 1 inch) */
@@ -14,6 +15,15 @@ type CreateEmptyDocumentOptions = {
14
15
  marginRight?: number; /** Initial text content (default: empty string) */
15
16
  initialText?: string;
16
17
  };
18
+ type CreateEmptyDocumentStyleOptions = {
19
+ /** Reusable formatting resources for the new document. */styleSet?: DocumentStyleSet;
20
+ preset?: never;
21
+ } | {
22
+ /** Style resources plus page-level defaults for the new document. */preset: DocumentPreset;
23
+ styleSet?: never;
24
+ };
25
+ type CreateEmptyDocumentOptions = CreateEmptyDocumentBaseOptions & CreateEmptyDocumentStyleOptions;
26
+ type CreateDocumentWithTextOptions = Omit<CreateEmptyDocumentBaseOptions, "initialText"> & CreateEmptyDocumentStyleOptions;
17
27
  /**
18
28
  * Create an empty document with a single paragraph
19
29
  *
@@ -45,6 +55,6 @@ declare function createEmptyDocument(options?: CreateEmptyDocumentOptions): docu
45
55
  * @param options - Optional configuration for the document
46
56
  * @returns A new Document object with the specified text
47
57
  */
48
- declare function createDocumentWithText(text: string, options?: Omit<CreateEmptyDocumentOptions, "initialText">): document_d_exports.Document;
58
+ declare function createDocumentWithText(text: string, options?: CreateDocumentWithTextOptions): document_d_exports.Document;
49
59
  //#endregion
50
60
  export { CreateEmptyDocumentOptions, createDocumentWithText, createEmptyDocument };
@@ -1,5 +1,13 @@
1
+ import "../style-sets/types.js";
2
+ import { panic } from "better-result";
3
+ import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
1
4
  //#region src/utils/createDocument.ts
2
5
  /**
6
+ * Create Document Utility
7
+ *
8
+ * Provides functions to create new documents programmatically.
9
+ */
10
+ /**
3
11
  * Get default paragraph styles (matching Google Docs defaults)
4
12
  *
5
13
  * Font sizes are in half-points (e.g., 22 = 11pt, 40 = 20pt)
@@ -195,7 +203,10 @@ function getDefaultSectionProperties() {
195
203
  * ```
196
204
  */
197
205
  function createEmptyDocument(options = {}) {
198
- const sectionProps = getDefaultSectionProperties();
206
+ const styleSet = options.preset?.styleSet ?? options.styleSet;
207
+ validateDocumentStyleSet(styleSet);
208
+ if (options.preset?.version !== void 0 && options.preset.version !== 1) return panic(`Unsupported document preset version: ${options.preset.version}`);
209
+ const sectionProps = structuredClone(options.preset?.sectionProperties ?? getDefaultSectionProperties());
199
210
  if (options.pageWidth !== void 0) sectionProps.pageWidth = Math.round(options.pageWidth);
200
211
  if (options.pageHeight !== void 0) sectionProps.pageHeight = Math.round(options.pageHeight);
201
212
  if (options.orientation !== void 0) sectionProps.orientation = options.orientation;
@@ -203,48 +214,56 @@ function createEmptyDocument(options = {}) {
203
214
  if (options.marginBottom !== void 0) sectionProps.marginBottom = Math.round(options.marginBottom);
204
215
  if (options.marginLeft !== void 0) sectionProps.marginLeft = Math.round(options.marginLeft);
205
216
  if (options.marginRight !== void 0) sectionProps.marginRight = Math.round(options.marginRight);
206
- const textContent = {
207
- type: "text",
208
- text: options.initialText || ""
217
+ const initialText = options.initialText ?? "";
218
+ const documentBody = {
219
+ content: [{
220
+ type: "paragraph",
221
+ content: [{
222
+ type: "run",
223
+ content: initialText ? [{
224
+ type: "text",
225
+ text: initialText
226
+ }] : [],
227
+ formatting: {}
228
+ }],
229
+ formatting: { styleId: styleSet?.initialParagraphStyleId ?? "Normal" }
230
+ }],
231
+ finalSectionProperties: sectionProps
209
232
  };
210
- return {
211
- package: {
212
- document: {
213
- content: [{
214
- type: "paragraph",
215
- content: [{
216
- type: "run",
217
- content: options.initialText ? [textContent] : [],
218
- formatting: {
219
- fontSize: 22,
220
- fontFamily: {
221
- ascii: "Arial",
222
- hAnsi: "Arial"
223
- }
224
- }
225
- }],
226
- formatting: { lineSpacing: 276 }
227
- }],
228
- finalSectionProperties: sectionProps
233
+ const defaultStyleDefinitions = {
234
+ docDefaults: {
235
+ rPr: {
236
+ fontSize: 22,
237
+ fontFamily: {
238
+ ascii: "Arial",
239
+ hAnsi: "Arial"
240
+ }
229
241
  },
230
- styles: {
231
- docDefaults: {
232
- rPr: {
233
- fontSize: 22,
234
- fontFamily: {
235
- ascii: "Arial",
236
- hAnsi: "Arial"
237
- }
238
- },
239
- pPr: { lineSpacing: 276 }
240
- },
241
- styles: getDefaultStyles()
242
- }
242
+ pPr: { lineSpacing: 276 }
243
243
  },
244
+ styles: getDefaultStyles()
245
+ };
246
+ const docxPackage = {
247
+ conformanceClass: DOCX_CONFORMANCE_CLASSES.TRANSITIONAL,
248
+ document: documentBody,
249
+ styles: structuredClone(styleSet?.styles ?? defaultStyleDefinitions)
250
+ };
251
+ if (styleSet?.numbering) docxPackage.numbering = structuredClone(styleSet.numbering);
252
+ if (styleSet?.theme) docxPackage.theme = structuredClone(styleSet.theme);
253
+ if (styleSet?.fontTable) docxPackage.fontTable = structuredClone(styleSet.fontTable);
254
+ if (styleSet?.settings) docxPackage.settings = structuredClone(styleSet.settings);
255
+ return {
256
+ package: docxPackage,
244
257
  templateVariables: [],
245
258
  warnings: []
246
259
  };
247
260
  }
261
+ const validateDocumentStyleSet = (styleSet) => {
262
+ if (!styleSet) return;
263
+ if (styleSet.version !== 1) return panic(`Unsupported document style set version: ${styleSet.version}`);
264
+ if (styleSet.name.trim().length === 0) return panic("Document style set name cannot be empty");
265
+ if (styleSet.styles.styles.find((style) => style.styleId === styleSet.initialParagraphStyleId)?.type !== "paragraph") return panic(`Initial paragraph style "${styleSet.initialParagraphStyleId}" is missing from style set "${styleSet.name}"`);
266
+ };
248
267
  /**
249
268
  * Create a document with a single paragraph containing the given text
250
269
  *
@@ -59,9 +59,9 @@ const APTOS_MEASURED_LINE_HEIGHT = {
59
59
  * font's `FontLineHeight`, never hand-written. For "hhea" entries the raw
60
60
  * `hheaAscent`/`hheaDescent`/`hheaLineGap`/`unitsPerEm` fields below are read
61
61
  * directly from the real font files' `hhea` table; the ratio is whatever
62
- * falls out of the formula in `singleLineRatioOf`. "legacy" entries (e.g.
63
- * garamond, lucida sans, lucida console) are unverified hand-transcribed
64
- * ratios carried over unchanged, pending measurement.
62
+ * falls out of the formula in `singleLineRatioOf`. The remaining "legacy"
63
+ * entry (lucida sans) is an unverified hand-transcribed ratio carried over
64
+ * unchanged, pending measurement.
65
65
  */
66
66
  const FONT_MAPPINGS = {
67
67
  aptos: {
@@ -355,9 +355,9 @@ const FONT_MAPPINGS = {
355
355
  "serif"
356
356
  ],
357
357
  singleLineRatio: singleLineRatioOf({
358
- source: "legacy",
359
- ratio: 1.068,
360
- note: "Unverified hand-transcribed ratio; not yet measured against real Word output."
358
+ source: "measured",
359
+ ratio: 1.1273,
360
+ note: "Measured at 11pt against a 12.4pt reference-layout line pitch."
361
361
  })
362
362
  },
363
363
  "century gothic": {
@@ -1,6 +1,6 @@
1
1
  import { AUTO_PARAGRAPH_SPACING_PX, eighthsToPixels, formatPx, halfPointsToPixels, halfPointsToPoints, twipsToPixels } from "./units.js";
2
- import { resolveFontFamily, resolveThemeFont } from "./fontResolver.js";
3
2
  import { resolveColor, resolveHighlightToCss, resolveShadingColor } from "./colorResolver.js";
3
+ import { resolveFontFamily, resolveThemeFont } from "./fontResolver.js";
4
4
  //#region src/utils/formatToStyle.ts
5
5
  /**
6
6
  * Convert TextFormatting to CSS properties for a run/span
@@ -3,6 +3,8 @@
3
3
  * Lifted from
4
4
  * https://github.com/eigenpal/docx-editor/blob/main/packages/core/src/utils/hexId.ts
5
5
  * (Apache-2.0). Keep the bound and the comments in sync upstream.
6
+ * folio divergence: `00000000` is excluded from both generators — Word
7
+ * treats a zero `w14:paraId` as unassigned, so it must never be minted.
6
8
  */
7
9
  /**
8
10
  * Strictest OOXML `ST_LongHexNumber` upper bound (exclusive) across the
@@ -17,7 +19,8 @@ declare const MAX_HEX_ID_EXCLUSIVE = 2147483647;
17
19
  * Random 8-char uppercase hex id, matching Microsoft's `w14:paraId`
18
20
  * extension format (also reused for comment `paraId` / `durableId`).
19
21
  *
20
- * Range is `[0, MAX_HEX_ID_EXCLUSIVE)` = `[0, 0x7FFFFFFE]`.
22
+ * Range is `[1, MAX_HEX_ID_EXCLUSIVE)` = `[1, 0x7FFFFFFE]` — zero is
23
+ * excluded because Word reads `w14:paraId="00000000"` as "no id".
21
24
  *
22
25
  * Uses `Math.random()` rather than `crypto.randomUUID()` so the
23
26
  * generator works in non-secure contexts (file://, web workers).
@@ -27,6 +30,10 @@ declare const generateHexId: () => string;
27
30
  * Deterministic 8-char uppercase hex id (FNV-1a over `seed`), `< 0x7FFFFFFF`.
28
31
  * Re-deriving from the same seed mints the same id, so content/position-derived
29
32
  * ids (block paraIds, comment thread keys) are stable across saves.
33
+ *
34
+ * A hash of exactly zero is remapped to `1`: `00000000` is Word's "no id"
35
+ * sentinel, and remapping only that one (already invalid) output keeps every
36
+ * previously shipped deterministic id unchanged.
30
37
  */
31
38
  declare const deterministicHexId: (seed: string) => string;
32
39
  //#endregion
@@ -3,6 +3,8 @@
3
3
  * Lifted from
4
4
  * https://github.com/eigenpal/docx-editor/blob/main/packages/core/src/utils/hexId.ts
5
5
  * (Apache-2.0). Keep the bound and the comments in sync upstream.
6
+ * folio divergence: `00000000` is excluded from both generators — Word
7
+ * treats a zero `w14:paraId` as unassigned, so it must never be minted.
6
8
  */
7
9
  /**
8
10
  * Strictest OOXML `ST_LongHexNumber` upper bound (exclusive) across the
@@ -17,21 +19,27 @@ const MAX_HEX_ID_EXCLUSIVE = 2147483647;
17
19
  * Random 8-char uppercase hex id, matching Microsoft's `w14:paraId`
18
20
  * extension format (also reused for comment `paraId` / `durableId`).
19
21
  *
20
- * Range is `[0, MAX_HEX_ID_EXCLUSIVE)` = `[0, 0x7FFFFFFE]`.
22
+ * Range is `[1, MAX_HEX_ID_EXCLUSIVE)` = `[1, 0x7FFFFFFE]` — zero is
23
+ * excluded because Word reads `w14:paraId="00000000"` as "no id".
21
24
  *
22
25
  * Uses `Math.random()` rather than `crypto.randomUUID()` so the
23
26
  * generator works in non-secure contexts (file://, web workers).
24
27
  */
25
- const generateHexId = () => Math.floor(Math.random() * MAX_HEX_ID_EXCLUSIVE).toString(16).toUpperCase().padStart(8, "0");
28
+ const generateHexId = () => (Math.floor(Math.random() * (MAX_HEX_ID_EXCLUSIVE - 1)) + 1).toString(16).toUpperCase().padStart(8, "0");
26
29
  /**
27
30
  * Deterministic 8-char uppercase hex id (FNV-1a over `seed`), `< 0x7FFFFFFF`.
28
31
  * Re-deriving from the same seed mints the same id, so content/position-derived
29
32
  * ids (block paraIds, comment thread keys) are stable across saves.
33
+ *
34
+ * A hash of exactly zero is remapped to `1`: `00000000` is Word's "no id"
35
+ * sentinel, and remapping only that one (already invalid) output keeps every
36
+ * previously shipped deterministic id unchanged.
30
37
  */
31
38
  const deterministicHexId = (seed) => {
32
39
  let hash = 2166136261;
33
40
  for (const character of seed) hash = Math.imul(hash ^ (character.codePointAt(0) ?? 0), 16777619) >>> 0;
34
- return (hash % MAX_HEX_ID_EXCLUSIVE).toString(16).toUpperCase().padStart(8, "0");
41
+ const value = hash % MAX_HEX_ID_EXCLUSIVE;
42
+ return (value === 0 ? 1 : value).toString(16).toUpperCase().padStart(8, "0");
35
43
  };
36
44
  //#endregion
37
45
  export { MAX_HEX_ID_EXCLUSIVE, deterministicHexId, generateHexId };
@@ -1,14 +1,25 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
 
3
3
  //#region src/utils/paragraphFormattingMerge.d.ts
4
+ /**
5
+ * Merge custom tab stops across OOXML paragraph-property layers.
6
+ *
7
+ * Tabs cascade by position rather than replacing the inherited collection:
8
+ * a higher-priority stop supersedes the stop at the same position, while a
9
+ * `clear` stop removes that inherited position during layout. Keeping the
10
+ * clear entry also lets the tab calculator suppress an automatic stop at the
11
+ * same position.
12
+ */
13
+ declare function mergeParagraphTabStops(inherited: document_d_exports.TabStop[] | undefined, direct: document_d_exports.TabStop[]): document_d_exports.TabStop[];
14
+ declare function mergeParagraphTabStops(inherited: document_d_exports.TabStop[] | undefined, direct: document_d_exports.TabStop[] | undefined): document_d_exports.TabStop[] | undefined;
4
15
  /**
5
16
  * Merge paragraph properties for OOXML style cascade resolution.
6
17
  *
7
18
  * The source is the higher-priority layer. Most `w:pPr` properties replace an
8
19
  * inherited value when present; nested child containers merge by child field;
9
- * tabs replace as a complete ordered collection; paragraph mark `w:rPr` uses
10
- * the run-formatting merge rules.
20
+ * tabs merge by position; paragraph mark `w:rPr` uses the run-formatting
21
+ * merge rules.
11
22
  */
12
23
  declare function mergeParagraphFormatting(target: document_d_exports.ParagraphFormatting | undefined, source: document_d_exports.ParagraphFormatting | undefined): document_d_exports.ParagraphFormatting | undefined;
13
24
  //#endregion
14
- export { mergeParagraphFormatting };
25
+ export { mergeParagraphFormatting, mergeParagraphTabStops };
@@ -3,6 +3,8 @@ import { mergeTextFormatting } from "./textFormattingMerge.js";
3
3
  const PARAGRAPH_REPLACE_KEYS = [
4
4
  "alignment",
5
5
  "bidi",
6
+ "kinsoku",
7
+ "overflowPunctuation",
6
8
  "spaceBefore",
7
9
  "spaceAfter",
8
10
  "lineSpacing",
@@ -30,13 +32,20 @@ const copyDefinedParagraphProperty = (target, source, key) => {
30
32
  const value = source[key];
31
33
  if (value !== void 0) target[key] = value;
32
34
  };
35
+ function mergeParagraphTabStops(inherited, direct) {
36
+ if (direct === void 0) return inherited === void 0 ? void 0 : [...inherited];
37
+ const stopsByPosition = /* @__PURE__ */ new Map();
38
+ for (const stop of inherited ?? []) stopsByPosition.set(stop.position, stop);
39
+ for (const stop of direct) stopsByPosition.set(stop.position, stop);
40
+ return [...stopsByPosition.values()].toSorted((a, b) => a.position - b.position);
41
+ }
33
42
  /**
34
43
  * Merge paragraph properties for OOXML style cascade resolution.
35
44
  *
36
45
  * The source is the higher-priority layer. Most `w:pPr` properties replace an
37
46
  * inherited value when present; nested child containers merge by child field;
38
- * tabs replace as a complete ordered collection; paragraph mark `w:rPr` uses
39
- * the run-formatting merge rules.
47
+ * tabs merge by position; paragraph mark `w:rPr` uses the run-formatting
48
+ * merge rules.
40
49
  */
41
50
  function mergeParagraphFormatting(target, source) {
42
51
  if (!source) return target;
@@ -47,6 +56,7 @@ function mergeParagraphFormatting(target, source) {
47
56
  }
48
57
  const result = { ...target };
49
58
  for (const key of PARAGRAPH_REPLACE_KEYS) copyDefinedParagraphProperty(result, source, key);
59
+ if (source.indentFirstLine !== void 0) result.hangingIndent = source.hangingIndent === true;
50
60
  const mergedRunProperties = mergeTextFormatting(result.runProperties, source.runProperties);
51
61
  if (mergedRunProperties) result.runProperties = mergedRunProperties;
52
62
  if (source.borders !== void 0) result.borders = {
@@ -61,8 +71,8 @@ function mergeParagraphFormatting(target, source) {
61
71
  ...result.frame,
62
72
  ...source.frame
63
73
  };
64
- if (source.tabs !== void 0) result.tabs = [...source.tabs];
74
+ if (source.tabs !== void 0) result.tabs = mergeParagraphTabStops(result.tabs, source.tabs);
65
75
  return result;
66
76
  }
67
77
  //#endregion
68
- export { mergeParagraphFormatting };
78
+ export { mergeParagraphFormatting, mergeParagraphTabStops };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-core",
3
- "version": "0.7.0",
3
+ "version": "0.9.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",
@@ -113,6 +113,7 @@
113
113
  "better-result": "2.9.2",
114
114
  "csstype": "^3.1.3",
115
115
  "fast-xml-parser": "^5.9.3",
116
+ "hyphen": "1.14.1",
116
117
  "jszip": "3.10.1",
117
118
  "marked": "^18.0.5",
118
119
  "prosemirror-commands": "^1.7.1",
@@ -130,6 +131,9 @@
130
131
  "y-prosemirror": "^1.3.7",
131
132
  "yjs": "^13.6.31"
132
133
  },
134
+ "devDependencies": {
135
+ "@types/hyphen": "1.14.0"
136
+ },
133
137
  "main": "./dist/index.js",
134
138
  "types": "./dist/index.d.ts",
135
139
  "files": [