@json-to-office/core-docx 0.13.0 → 0.15.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.
@@ -225,7 +225,7 @@ var init_minimal_docx_theme = __esm({
225
225
  size: 11
226
226
  },
227
227
  mono: {
228
- family: "SF Mono",
228
+ family: "Menlo",
229
229
  size: 10
230
230
  },
231
231
  light: {
@@ -543,7 +543,7 @@ var init_modern_docx_theme = __esm({
543
543
  fonts: {
544
544
  heading: { family: "Helvetica", size: 28 },
545
545
  body: { family: "Helvetica", size: 11 },
546
- mono: { family: "SF Mono", size: 10 },
546
+ mono: { family: "Menlo", size: 10 },
547
547
  light: { family: "Helvetica", size: 28 }
548
548
  },
549
549
  page: {
@@ -850,12 +850,20 @@ function getPageDimensions(size) {
850
850
  }
851
851
  return size;
852
852
  }
853
- var PAGE_SIZES, getTableStyle, getDocumentMargins, getPageSetup;
853
+ var resolveTableParagraphSpacing, PAGE_SIZES, getTableStyle, getDocumentMargins, getPageSetup;
854
854
  var init_layoutUtils = __esm({
855
855
  "src/styles/utils/layoutUtils.ts"() {
856
856
  "use strict";
857
857
  init_colorUtils();
858
858
  init_styleHelpers();
859
+ resolveTableParagraphSpacing = (style) => {
860
+ const lineSpacing = convertLineSpacing(style?.lineSpacing) ?? convertLineSpacing({ type: "single" });
861
+ return {
862
+ before: style?.spacing?.before !== void 0 ? pointsToTwips(style.spacing.before) : 0,
863
+ after: style?.spacing?.after !== void 0 ? pointsToTwips(style.spacing.after) : 0,
864
+ ...lineSpacing
865
+ };
866
+ };
859
867
  PAGE_SIZES = {
860
868
  A4: { width: 11906, height: 16838 },
861
869
  A3: { width: 16838, height: 23811 },
@@ -866,7 +874,10 @@ var init_layoutUtils = __esm({
866
874
  const themeConfig = resolveTheme(theme, themeName);
867
875
  if (themeConfig && themeConfig.styles?.normal) {
868
876
  const normalStyle = themeConfig.styles.normal;
877
+ const styles = themeConfig.styles;
869
878
  return {
879
+ cellParagraph: resolveTableParagraphSpacing(styles.tableCell),
880
+ headerParagraph: resolveTableParagraphSpacing(styles.tableHeader),
870
881
  tableHeader: {
871
882
  fill: resolveColor(
872
883
  themeConfig.colors?.primary || "#000000",
@@ -932,6 +943,8 @@ var init_layoutUtils = __esm({
932
943
  };
933
944
  }
934
945
  return {
946
+ cellParagraph: resolveTableParagraphSpacing(),
947
+ headerParagraph: resolveTableParagraphSpacing(),
935
948
  tableHeader: {
936
949
  fill: "000000",
937
950
  color: "FFFFFF",
@@ -2203,7 +2216,8 @@ async function processDocument(document, theme, themeName) {
2203
2216
  metadata,
2204
2217
  sections,
2205
2218
  theme: effectiveTheme,
2206
- themeName
2219
+ themeName,
2220
+ trackRevisions: document.props.trackRevisions
2207
2221
  };
2208
2222
  }
2209
2223
  function createDocumentMetadata(props) {
@@ -2662,7 +2676,7 @@ Suggestion: ${suggestion}`
2662
2676
  import {
2663
2677
  Document,
2664
2678
  Paragraph as Paragraph7,
2665
- TextRun as TextRun5,
2679
+ TextRun as TextRun6,
2666
2680
  AlignmentType as AlignmentType5,
2667
2681
  BookmarkStart as BookmarkStart2,
2668
2682
  BookmarkEnd as BookmarkEnd2
@@ -3995,6 +4009,99 @@ var CacheKeyGenerator = class {
3995
4009
  }
3996
4010
  };
3997
4011
 
4012
+ // src/utils/revisionUtils.ts
4013
+ import { TextRun as TextRun3, InsertedTextRun, DeletedTextRun } from "docx";
4014
+ var DEFAULT_REVISION_AUTHOR = "json-to-office";
4015
+ var DEFAULT_REVISION_DATE = "1970-01-01T00:00:00Z";
4016
+ var RevisionIdRegistry = class {
4017
+ counter = 0;
4018
+ next() {
4019
+ this.counter += 1;
4020
+ return this.counter;
4021
+ }
4022
+ /** Test-only: deterministic ids for snapshot assertions. */
4023
+ clear() {
4024
+ this.counter = 0;
4025
+ }
4026
+ };
4027
+ var globalRevisionIdRegistry = new RevisionIdRegistry();
4028
+ var PLACEHOLDER_PATTERN = /\{[^}]+\}/;
4029
+ function segmentRuns(text, makeRun) {
4030
+ const lines = text.split("\n");
4031
+ return lines.map(
4032
+ (line, index) => makeRun(index === 0 ? { text: line } : { text: line, break: 1 })
4033
+ );
4034
+ }
4035
+ function createRevisionRuns(revision, baseStyle) {
4036
+ const author = revision.author || DEFAULT_REVISION_AUTHOR;
4037
+ const date = revision.date || DEFAULT_REVISION_DATE;
4038
+ const runs = [];
4039
+ for (const segment of revision.segments) {
4040
+ if (!segment.text) continue;
4041
+ const text = normalizeUnicodeText(segment.text);
4042
+ if (segment.type === "insert") {
4043
+ runs.push(
4044
+ ...segmentRuns(
4045
+ text,
4046
+ (options) => new InsertedTextRun({
4047
+ ...options,
4048
+ ...baseStyle,
4049
+ id: globalRevisionIdRegistry.next(),
4050
+ author,
4051
+ date
4052
+ })
4053
+ )
4054
+ );
4055
+ } else if (segment.type === "delete") {
4056
+ runs.push(
4057
+ ...segmentRuns(
4058
+ text,
4059
+ (options) => new DeletedTextRun({
4060
+ ...options,
4061
+ ...baseStyle,
4062
+ id: globalRevisionIdRegistry.next(),
4063
+ author,
4064
+ date
4065
+ })
4066
+ )
4067
+ );
4068
+ } else if (PLACEHOLDER_PATTERN.test(text)) {
4069
+ runs.push(
4070
+ ...processTextWithPlaceholders(text, baseStyle, {})
4071
+ );
4072
+ } else {
4073
+ runs.push(
4074
+ ...segmentRuns(
4075
+ text,
4076
+ (options) => new TextRun3({ ...options, ...baseStyle })
4077
+ )
4078
+ );
4079
+ }
4080
+ }
4081
+ return runs;
4082
+ }
4083
+ function componentHasRevision(component) {
4084
+ const props = component.props;
4085
+ if (props) {
4086
+ if (props.revision) return true;
4087
+ const items = props.items;
4088
+ if (Array.isArray(items)) {
4089
+ if (items.some(
4090
+ (item) => typeof item === "object" && item !== null && "revision" in item && item.revision
4091
+ )) {
4092
+ return true;
4093
+ }
4094
+ }
4095
+ }
4096
+ const children = component.children;
4097
+ if (Array.isArray(children)) {
4098
+ return children.some(
4099
+ (child) => typeof child === "object" && child !== null && componentHasRevision(child)
4100
+ );
4101
+ }
4102
+ return false;
4103
+ }
4104
+
3998
4105
  // src/core/cached-render.ts
3999
4106
  import { createHash as createHash2 } from "crypto";
4000
4107
  var componentCache = null;
@@ -4034,7 +4141,7 @@ function initializeComponentCache(cache) {
4034
4141
  }
4035
4142
  }
4036
4143
  async function renderComponentWithCache(component, theme, themeName, context, bypassCache = false) {
4037
- const forceBypassForType = component.name === "toc" || component.name === "section";
4144
+ const forceBypassForType = component.name === "toc" || component.name === "section" || componentHasRevision(component);
4038
4145
  if (!componentCache) {
4039
4146
  initializeComponentCache();
4040
4147
  }
@@ -4073,7 +4180,7 @@ async function renderComponentWithCache(component, theme, themeName, context, by
4073
4180
  // src/core/content.ts
4074
4181
  import {
4075
4182
  Paragraph,
4076
- TextRun as TextRun3,
4183
+ TextRun as TextRun4,
4077
4184
  Table,
4078
4185
  TableRow,
4079
4186
  TableCell,
@@ -4223,24 +4330,43 @@ function createText(content, theme, themeName, options = {}) {
4223
4330
  underline: options.underline ? { type: "single" } : void 0
4224
4331
  }
4225
4332
  };
4226
- const textRuns = parseTextWithDecorators(normalizedContent, baseTextStyle, {
4227
- boldColor: options.boldColor,
4228
- enableHyperlinks: true
4229
- });
4230
- if (options.bookmarkId) {
4231
- globalBookmarkRegistry.register(
4232
- options.bookmarkId,
4233
- normalizedContent,
4234
- "paragraph"
4235
- );
4236
- children.push(
4237
- new Bookmark({
4238
- id: options.bookmarkId,
4239
- children: textRuns
4240
- })
4241
- );
4333
+ if (options.revision) {
4334
+ const revisionRuns = createRevisionRuns(options.revision, baseTextStyle);
4335
+ if (options.bookmarkId) {
4336
+ globalBookmarkRegistry.register(
4337
+ options.bookmarkId,
4338
+ normalizedContent,
4339
+ "paragraph"
4340
+ );
4341
+ children.push(
4342
+ new Bookmark({
4343
+ id: options.bookmarkId,
4344
+ children: revisionRuns
4345
+ })
4346
+ );
4347
+ } else {
4348
+ children.push(...revisionRuns);
4349
+ }
4242
4350
  } else {
4243
- children.push(...textRuns);
4351
+ const textRuns = parseTextWithDecorators(normalizedContent, baseTextStyle, {
4352
+ boldColor: options.boldColor,
4353
+ enableHyperlinks: true
4354
+ });
4355
+ if (options.bookmarkId) {
4356
+ globalBookmarkRegistry.register(
4357
+ options.bookmarkId,
4358
+ normalizedContent,
4359
+ "paragraph"
4360
+ );
4361
+ children.push(
4362
+ new Bookmark({
4363
+ id: options.bookmarkId,
4364
+ children: textRuns
4365
+ })
4366
+ );
4367
+ } else {
4368
+ children.push(...textRuns);
4369
+ }
4244
4370
  }
4245
4371
  const isFloating = !!options.floating;
4246
4372
  const frameOptions = isFloating && options.floating ? mapFrameOptions(options.floating, theme, themeName) : void 0;
@@ -4361,7 +4487,24 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4361
4487
  underline: options.underline ? { type: "single" } : void 0
4362
4488
  }
4363
4489
  };
4364
- if (options.bookmarkId) {
4490
+ if (options.revision) {
4491
+ const revisionRuns = createRevisionRuns(options.revision, baseTextStyle);
4492
+ if (options.bookmarkId) {
4493
+ globalBookmarkRegistry.register(
4494
+ options.bookmarkId,
4495
+ normalizedText,
4496
+ "heading"
4497
+ );
4498
+ children.push(
4499
+ new Bookmark({
4500
+ id: options.bookmarkId,
4501
+ children: revisionRuns
4502
+ })
4503
+ );
4504
+ } else {
4505
+ children.push(...revisionRuns);
4506
+ }
4507
+ } else if (options.bookmarkId) {
4365
4508
  globalBookmarkRegistry.register(
4366
4509
  options.bookmarkId,
4367
4510
  normalizedText,
@@ -4376,7 +4519,7 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4376
4519
  headingTextChildren.push(...textRuns);
4377
4520
  } else {
4378
4521
  headingTextChildren.push(
4379
- new TextRun3({ text: normalizedText, ...baseTextStyle })
4522
+ new TextRun4({ text: normalizedText, ...baseTextStyle })
4380
4523
  );
4381
4524
  }
4382
4525
  children.push(
@@ -4393,7 +4536,7 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4393
4536
  });
4394
4537
  children.push(...textRuns);
4395
4538
  } else {
4396
- children.push(new TextRun3({ text: normalizedText, ...baseTextStyle }));
4539
+ children.push(new TextRun4({ text: normalizedText, ...baseTextStyle }));
4397
4540
  }
4398
4541
  }
4399
4542
  return new Paragraph({
@@ -4533,10 +4676,11 @@ function createList(items, _theme, _themeName, options = {}) {
4533
4676
  items.forEach((item, index) => {
4534
4677
  const itemText = typeof item === "string" ? item : item.text;
4535
4678
  const itemLevel = typeof item === "object" ? item.level || 0 : 0;
4536
- if (!itemText.trim()) {
4679
+ const itemRevision = typeof item === "object" ? item.revision : void 0;
4680
+ if (!itemText.trim() && !itemRevision) {
4537
4681
  return;
4538
4682
  }
4539
- const textRuns = parseTextWithDecorators(
4683
+ const textRuns = itemRevision ? createRevisionRuns(itemRevision, {}) : parseTextWithDecorators(
4540
4684
  itemText,
4541
4685
  {},
4542
4686
  {
@@ -4949,7 +5093,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4949
5093
  } catch (error) {
4950
5094
  const imageSource = imageComp.props.base64 || imageComp.props.path || "unknown";
4951
5095
  cellChildren = [
4952
- new TextRun3({
5096
+ new TextRun4({
4953
5097
  text: `[IMAGE: ${imageSource.substring(0, 50)}${imageSource.length > 50 ? "..." : ""}]`,
4954
5098
  font: mergedStyle.font,
4955
5099
  size: mergedStyle.size,
@@ -4959,7 +5103,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4959
5103
  }
4960
5104
  } else {
4961
5105
  cellChildren = [
4962
- new TextRun3({
5106
+ new TextRun4({
4963
5107
  text: `[Unsupported component type: ${cell.name}]`,
4964
5108
  font: mergedStyle.font,
4965
5109
  size: mergedStyle.size,
@@ -5038,6 +5182,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5038
5182
  children: [
5039
5183
  new Paragraph({
5040
5184
  ...tableConfig.keepInOnePage && { keepNext: true },
5185
+ spacing: tableStyle.headerParagraph,
5041
5186
  alignment: getAlignment(horizontalAlignment),
5042
5187
  children: cellChildren
5043
5188
  })
@@ -5132,6 +5277,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5132
5277
  children: [
5133
5278
  new Paragraph({
5134
5279
  ...tableConfig.keepInOnePage && !isLastRow || isLastRow && tableConfig.keepNext ? { keepNext: true } : {},
5280
+ spacing: tableStyle.cellParagraph,
5135
5281
  alignment: AlignmentType2.LEFT,
5136
5282
  children: []
5137
5283
  })
@@ -5207,6 +5353,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5207
5353
  children: [
5208
5354
  new Paragraph({
5209
5355
  ...tableConfig.keepInOnePage && !isLastRow || isLastRow && tableConfig.keepNext ? { keepNext: true } : {},
5356
+ spacing: tableStyle.cellParagraph,
5210
5357
  alignment: getAlignment(horizontalAlignment),
5211
5358
  children: cellChildren
5212
5359
  })
@@ -5356,7 +5503,9 @@ function renderHeadingComponent(component, theme, themeName) {
5356
5503
  keepNext: config.keepNext,
5357
5504
  keepLines: config.keepLines,
5358
5505
  // Bookmark ID for internal linking
5359
- bookmarkId
5506
+ bookmarkId,
5507
+ // Tracked-change segments (rendered as native Word revisions)
5508
+ revision: config.revision
5360
5509
  }
5361
5510
  );
5362
5511
  return [header];
@@ -5400,7 +5549,7 @@ function parseMarkdownList(text) {
5400
5549
  function renderParagraphComponent(component, theme, themeName) {
5401
5550
  if (!isParagraphComponent(component)) return [];
5402
5551
  const resolvedConfig = component.props;
5403
- const listData = parseMarkdownList(resolvedConfig.text);
5552
+ const listData = resolvedConfig.revision ? null : parseMarkdownList(resolvedConfig.text);
5404
5553
  if (listData) {
5405
5554
  const reference = globalNumberingRegistry.generateReference("markdown-list");
5406
5555
  const levels = [];
@@ -5477,7 +5626,9 @@ function renderParagraphComponent(component, theme, themeName) {
5477
5626
  // Pass keepLines property
5478
5627
  keepLines: resolvedConfig.keepLines,
5479
5628
  // Pass bookmark ID for internal linking
5480
- bookmarkId: resolvedConfig.id
5629
+ bookmarkId: resolvedConfig.id,
5630
+ // Tracked-change segments (rendered as native Word revisions)
5631
+ revision: resolvedConfig.revision
5481
5632
  });
5482
5633
  return [text];
5483
5634
  }
@@ -6107,7 +6258,7 @@ import {
6107
6258
  Paragraph as Paragraph6,
6108
6259
  TableOfContents,
6109
6260
  AlignmentType as AlignmentType4,
6110
- TextRun as TextRun4,
6261
+ TextRun as TextRun5,
6111
6262
  StyleLevel
6112
6263
  } from "docx";
6113
6264
  function parseDepthRange(rawDepth, fieldName, defaultFrom = 1, defaultTo = 3) {
@@ -6162,7 +6313,7 @@ function renderTocComponent(component, theme, context) {
6162
6313
  paragraphs.push(
6163
6314
  new Paragraph6({
6164
6315
  children: [
6165
- new TextRun4({
6316
+ new TextRun5({
6166
6317
  text: componentProps.title,
6167
6318
  bold: true,
6168
6319
  size: 28
@@ -6460,8 +6611,10 @@ async function renderDocument(structure, layout, options) {
6460
6611
  styles: createWordStyles(structure.theme),
6461
6612
  sections,
6462
6613
  features: {
6463
- updateFields: true
6614
+ updateFields: true,
6464
6615
  // Required for TOC fields to update correctly
6616
+ // Word opens the document in review mode (further edits are tracked)
6617
+ ...structure.trackRevisions && { trackRevisions: true }
6465
6618
  },
6466
6619
  // Add numbering configurations if any lists were rendered
6467
6620
  ...numberingConfigs.length > 0 && {
@@ -6506,7 +6659,7 @@ async function renderHeaderFooterComponents(components, theme, themeName, _conte
6506
6659
  elements.push(
6507
6660
  new Paragraph7({
6508
6661
  children: [
6509
- new TextRun5({
6662
+ new TextRun6({
6510
6663
  text: "[IMAGE: Missing path or base64 property]",
6511
6664
  font: getThemeFonts(theme).body.family,
6512
6665
  size: 20,
@@ -6588,7 +6741,7 @@ async function renderHeaderFooterComponents(components, theme, themeName, _conte
6588
6741
  elements.push(
6589
6742
  new Paragraph7({
6590
6743
  children: [
6591
- new TextRun5({
6744
+ new TextRun6({
6592
6745
  text: `[IMAGE: ${imageComp.props.path}]`,
6593
6746
  font: getThemeFonts(theme).body.family,
6594
6747
  size: 20,