@json-to-office/core-docx 0.13.0 → 0.14.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: {
@@ -2203,7 +2203,8 @@ async function processDocument(document, theme, themeName) {
2203
2203
  metadata,
2204
2204
  sections,
2205
2205
  theme: effectiveTheme,
2206
- themeName
2206
+ themeName,
2207
+ trackRevisions: document.props.trackRevisions
2207
2208
  };
2208
2209
  }
2209
2210
  function createDocumentMetadata(props) {
@@ -2662,7 +2663,7 @@ Suggestion: ${suggestion}`
2662
2663
  import {
2663
2664
  Document,
2664
2665
  Paragraph as Paragraph7,
2665
- TextRun as TextRun5,
2666
+ TextRun as TextRun6,
2666
2667
  AlignmentType as AlignmentType5,
2667
2668
  BookmarkStart as BookmarkStart2,
2668
2669
  BookmarkEnd as BookmarkEnd2
@@ -3995,6 +3996,99 @@ var CacheKeyGenerator = class {
3995
3996
  }
3996
3997
  };
3997
3998
 
3999
+ // src/utils/revisionUtils.ts
4000
+ import { TextRun as TextRun3, InsertedTextRun, DeletedTextRun } from "docx";
4001
+ var DEFAULT_REVISION_AUTHOR = "json-to-office";
4002
+ var DEFAULT_REVISION_DATE = "1970-01-01T00:00:00Z";
4003
+ var RevisionIdRegistry = class {
4004
+ counter = 0;
4005
+ next() {
4006
+ this.counter += 1;
4007
+ return this.counter;
4008
+ }
4009
+ /** Test-only: deterministic ids for snapshot assertions. */
4010
+ clear() {
4011
+ this.counter = 0;
4012
+ }
4013
+ };
4014
+ var globalRevisionIdRegistry = new RevisionIdRegistry();
4015
+ var PLACEHOLDER_PATTERN = /\{[^}]+\}/;
4016
+ function segmentRuns(text, makeRun) {
4017
+ const lines = text.split("\n");
4018
+ return lines.map(
4019
+ (line, index) => makeRun(index === 0 ? { text: line } : { text: line, break: 1 })
4020
+ );
4021
+ }
4022
+ function createRevisionRuns(revision, baseStyle) {
4023
+ const author = revision.author || DEFAULT_REVISION_AUTHOR;
4024
+ const date = revision.date || DEFAULT_REVISION_DATE;
4025
+ const runs = [];
4026
+ for (const segment of revision.segments) {
4027
+ if (!segment.text) continue;
4028
+ const text = normalizeUnicodeText(segment.text);
4029
+ if (segment.type === "insert") {
4030
+ runs.push(
4031
+ ...segmentRuns(
4032
+ text,
4033
+ (options) => new InsertedTextRun({
4034
+ ...options,
4035
+ ...baseStyle,
4036
+ id: globalRevisionIdRegistry.next(),
4037
+ author,
4038
+ date
4039
+ })
4040
+ )
4041
+ );
4042
+ } else if (segment.type === "delete") {
4043
+ runs.push(
4044
+ ...segmentRuns(
4045
+ text,
4046
+ (options) => new DeletedTextRun({
4047
+ ...options,
4048
+ ...baseStyle,
4049
+ id: globalRevisionIdRegistry.next(),
4050
+ author,
4051
+ date
4052
+ })
4053
+ )
4054
+ );
4055
+ } else if (PLACEHOLDER_PATTERN.test(text)) {
4056
+ runs.push(
4057
+ ...processTextWithPlaceholders(text, baseStyle, {})
4058
+ );
4059
+ } else {
4060
+ runs.push(
4061
+ ...segmentRuns(
4062
+ text,
4063
+ (options) => new TextRun3({ ...options, ...baseStyle })
4064
+ )
4065
+ );
4066
+ }
4067
+ }
4068
+ return runs;
4069
+ }
4070
+ function componentHasRevision(component) {
4071
+ const props = component.props;
4072
+ if (props) {
4073
+ if (props.revision) return true;
4074
+ const items = props.items;
4075
+ if (Array.isArray(items)) {
4076
+ if (items.some(
4077
+ (item) => typeof item === "object" && item !== null && "revision" in item && item.revision
4078
+ )) {
4079
+ return true;
4080
+ }
4081
+ }
4082
+ }
4083
+ const children = component.children;
4084
+ if (Array.isArray(children)) {
4085
+ return children.some(
4086
+ (child) => typeof child === "object" && child !== null && componentHasRevision(child)
4087
+ );
4088
+ }
4089
+ return false;
4090
+ }
4091
+
3998
4092
  // src/core/cached-render.ts
3999
4093
  import { createHash as createHash2 } from "crypto";
4000
4094
  var componentCache = null;
@@ -4034,7 +4128,7 @@ function initializeComponentCache(cache) {
4034
4128
  }
4035
4129
  }
4036
4130
  async function renderComponentWithCache(component, theme, themeName, context, bypassCache = false) {
4037
- const forceBypassForType = component.name === "toc" || component.name === "section";
4131
+ const forceBypassForType = component.name === "toc" || component.name === "section" || componentHasRevision(component);
4038
4132
  if (!componentCache) {
4039
4133
  initializeComponentCache();
4040
4134
  }
@@ -4073,7 +4167,7 @@ async function renderComponentWithCache(component, theme, themeName, context, by
4073
4167
  // src/core/content.ts
4074
4168
  import {
4075
4169
  Paragraph,
4076
- TextRun as TextRun3,
4170
+ TextRun as TextRun4,
4077
4171
  Table,
4078
4172
  TableRow,
4079
4173
  TableCell,
@@ -4223,24 +4317,43 @@ function createText(content, theme, themeName, options = {}) {
4223
4317
  underline: options.underline ? { type: "single" } : void 0
4224
4318
  }
4225
4319
  };
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
- );
4320
+ if (options.revision) {
4321
+ const revisionRuns = createRevisionRuns(options.revision, baseTextStyle);
4322
+ if (options.bookmarkId) {
4323
+ globalBookmarkRegistry.register(
4324
+ options.bookmarkId,
4325
+ normalizedContent,
4326
+ "paragraph"
4327
+ );
4328
+ children.push(
4329
+ new Bookmark({
4330
+ id: options.bookmarkId,
4331
+ children: revisionRuns
4332
+ })
4333
+ );
4334
+ } else {
4335
+ children.push(...revisionRuns);
4336
+ }
4242
4337
  } else {
4243
- children.push(...textRuns);
4338
+ const textRuns = parseTextWithDecorators(normalizedContent, baseTextStyle, {
4339
+ boldColor: options.boldColor,
4340
+ enableHyperlinks: true
4341
+ });
4342
+ if (options.bookmarkId) {
4343
+ globalBookmarkRegistry.register(
4344
+ options.bookmarkId,
4345
+ normalizedContent,
4346
+ "paragraph"
4347
+ );
4348
+ children.push(
4349
+ new Bookmark({
4350
+ id: options.bookmarkId,
4351
+ children: textRuns
4352
+ })
4353
+ );
4354
+ } else {
4355
+ children.push(...textRuns);
4356
+ }
4244
4357
  }
4245
4358
  const isFloating = !!options.floating;
4246
4359
  const frameOptions = isFloating && options.floating ? mapFrameOptions(options.floating, theme, themeName) : void 0;
@@ -4361,7 +4474,24 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4361
4474
  underline: options.underline ? { type: "single" } : void 0
4362
4475
  }
4363
4476
  };
4364
- if (options.bookmarkId) {
4477
+ if (options.revision) {
4478
+ const revisionRuns = createRevisionRuns(options.revision, baseTextStyle);
4479
+ if (options.bookmarkId) {
4480
+ globalBookmarkRegistry.register(
4481
+ options.bookmarkId,
4482
+ normalizedText,
4483
+ "heading"
4484
+ );
4485
+ children.push(
4486
+ new Bookmark({
4487
+ id: options.bookmarkId,
4488
+ children: revisionRuns
4489
+ })
4490
+ );
4491
+ } else {
4492
+ children.push(...revisionRuns);
4493
+ }
4494
+ } else if (options.bookmarkId) {
4365
4495
  globalBookmarkRegistry.register(
4366
4496
  options.bookmarkId,
4367
4497
  normalizedText,
@@ -4376,7 +4506,7 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4376
4506
  headingTextChildren.push(...textRuns);
4377
4507
  } else {
4378
4508
  headingTextChildren.push(
4379
- new TextRun3({ text: normalizedText, ...baseTextStyle })
4509
+ new TextRun4({ text: normalizedText, ...baseTextStyle })
4380
4510
  );
4381
4511
  }
4382
4512
  children.push(
@@ -4393,7 +4523,7 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4393
4523
  });
4394
4524
  children.push(...textRuns);
4395
4525
  } else {
4396
- children.push(new TextRun3({ text: normalizedText, ...baseTextStyle }));
4526
+ children.push(new TextRun4({ text: normalizedText, ...baseTextStyle }));
4397
4527
  }
4398
4528
  }
4399
4529
  return new Paragraph({
@@ -4533,10 +4663,11 @@ function createList(items, _theme, _themeName, options = {}) {
4533
4663
  items.forEach((item, index) => {
4534
4664
  const itemText = typeof item === "string" ? item : item.text;
4535
4665
  const itemLevel = typeof item === "object" ? item.level || 0 : 0;
4536
- if (!itemText.trim()) {
4666
+ const itemRevision = typeof item === "object" ? item.revision : void 0;
4667
+ if (!itemText.trim() && !itemRevision) {
4537
4668
  return;
4538
4669
  }
4539
- const textRuns = parseTextWithDecorators(
4670
+ const textRuns = itemRevision ? createRevisionRuns(itemRevision, {}) : parseTextWithDecorators(
4540
4671
  itemText,
4541
4672
  {},
4542
4673
  {
@@ -4949,7 +5080,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4949
5080
  } catch (error) {
4950
5081
  const imageSource = imageComp.props.base64 || imageComp.props.path || "unknown";
4951
5082
  cellChildren = [
4952
- new TextRun3({
5083
+ new TextRun4({
4953
5084
  text: `[IMAGE: ${imageSource.substring(0, 50)}${imageSource.length > 50 ? "..." : ""}]`,
4954
5085
  font: mergedStyle.font,
4955
5086
  size: mergedStyle.size,
@@ -4959,7 +5090,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4959
5090
  }
4960
5091
  } else {
4961
5092
  cellChildren = [
4962
- new TextRun3({
5093
+ new TextRun4({
4963
5094
  text: `[Unsupported component type: ${cell.name}]`,
4964
5095
  font: mergedStyle.font,
4965
5096
  size: mergedStyle.size,
@@ -5356,7 +5487,9 @@ function renderHeadingComponent(component, theme, themeName) {
5356
5487
  keepNext: config.keepNext,
5357
5488
  keepLines: config.keepLines,
5358
5489
  // Bookmark ID for internal linking
5359
- bookmarkId
5490
+ bookmarkId,
5491
+ // Tracked-change segments (rendered as native Word revisions)
5492
+ revision: config.revision
5360
5493
  }
5361
5494
  );
5362
5495
  return [header];
@@ -5400,7 +5533,7 @@ function parseMarkdownList(text) {
5400
5533
  function renderParagraphComponent(component, theme, themeName) {
5401
5534
  if (!isParagraphComponent(component)) return [];
5402
5535
  const resolvedConfig = component.props;
5403
- const listData = parseMarkdownList(resolvedConfig.text);
5536
+ const listData = resolvedConfig.revision ? null : parseMarkdownList(resolvedConfig.text);
5404
5537
  if (listData) {
5405
5538
  const reference = globalNumberingRegistry.generateReference("markdown-list");
5406
5539
  const levels = [];
@@ -5477,7 +5610,9 @@ function renderParagraphComponent(component, theme, themeName) {
5477
5610
  // Pass keepLines property
5478
5611
  keepLines: resolvedConfig.keepLines,
5479
5612
  // Pass bookmark ID for internal linking
5480
- bookmarkId: resolvedConfig.id
5613
+ bookmarkId: resolvedConfig.id,
5614
+ // Tracked-change segments (rendered as native Word revisions)
5615
+ revision: resolvedConfig.revision
5481
5616
  });
5482
5617
  return [text];
5483
5618
  }
@@ -6107,7 +6242,7 @@ import {
6107
6242
  Paragraph as Paragraph6,
6108
6243
  TableOfContents,
6109
6244
  AlignmentType as AlignmentType4,
6110
- TextRun as TextRun4,
6245
+ TextRun as TextRun5,
6111
6246
  StyleLevel
6112
6247
  } from "docx";
6113
6248
  function parseDepthRange(rawDepth, fieldName, defaultFrom = 1, defaultTo = 3) {
@@ -6162,7 +6297,7 @@ function renderTocComponent(component, theme, context) {
6162
6297
  paragraphs.push(
6163
6298
  new Paragraph6({
6164
6299
  children: [
6165
- new TextRun4({
6300
+ new TextRun5({
6166
6301
  text: componentProps.title,
6167
6302
  bold: true,
6168
6303
  size: 28
@@ -6460,8 +6595,10 @@ async function renderDocument(structure, layout, options) {
6460
6595
  styles: createWordStyles(structure.theme),
6461
6596
  sections,
6462
6597
  features: {
6463
- updateFields: true
6598
+ updateFields: true,
6464
6599
  // Required for TOC fields to update correctly
6600
+ // Word opens the document in review mode (further edits are tracked)
6601
+ ...structure.trackRevisions && { trackRevisions: true }
6465
6602
  },
6466
6603
  // Add numbering configurations if any lists were rendered
6467
6604
  ...numberingConfigs.length > 0 && {
@@ -6506,7 +6643,7 @@ async function renderHeaderFooterComponents(components, theme, themeName, _conte
6506
6643
  elements.push(
6507
6644
  new Paragraph7({
6508
6645
  children: [
6509
- new TextRun5({
6646
+ new TextRun6({
6510
6647
  text: "[IMAGE: Missing path or base64 property]",
6511
6648
  font: getThemeFonts(theme).body.family,
6512
6649
  size: 20,
@@ -6588,7 +6725,7 @@ async function renderHeaderFooterComponents(components, theme, themeName, _conte
6588
6725
  elements.push(
6589
6726
  new Paragraph7({
6590
6727
  children: [
6591
- new TextRun5({
6728
+ new TextRun6({
6592
6729
  text: `[IMAGE: ${imageComp.props.path}]`,
6593
6730
  font: getThemeFonts(theme).body.family,
6594
6731
  size: 20,