docx-diff-editor 1.0.45 → 1.0.46

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.
package/dist/index.js CHANGED
@@ -453,6 +453,170 @@ var TIMEOUTS = {
453
453
  CLEANUP_DELAY: 100
454
454
  };
455
455
 
456
+ // src/services/runPropertiesSync.ts
457
+ var PT_TO_TWIPS = 20;
458
+ function ptToTwips(ptValue) {
459
+ return Math.round(ptValue * PT_TO_TWIPS);
460
+ }
461
+ function stripHashFromColor(color) {
462
+ return color.replace(/^#/, "");
463
+ }
464
+ function parseFontSizeToPoints(fontSize) {
465
+ if (typeof fontSize === "number") {
466
+ return fontSize;
467
+ }
468
+ const value = parseFloat(fontSize);
469
+ if (isNaN(value)) {
470
+ return null;
471
+ }
472
+ if (fontSize.toLowerCase().includes("px")) {
473
+ return value * 0.75;
474
+ }
475
+ return value;
476
+ }
477
+ function cleanFontFamily(fontFamily) {
478
+ return fontFamily.split(",")[0].trim().replace(/^["']|["']$/g, "");
479
+ }
480
+ function marksToRunProperties(marks) {
481
+ const runProperties = {};
482
+ if (!marks || !Array.isArray(marks)) {
483
+ return runProperties;
484
+ }
485
+ for (const mark of marks) {
486
+ const type = mark.type;
487
+ const attrs = mark.attrs || {};
488
+ switch (type) {
489
+ // Boolean marks: bold, italic, strike
490
+ case "bold":
491
+ case "italic":
492
+ case "strike": {
493
+ const isNegated = attrs.value === "0" || attrs.value === false;
494
+ runProperties[type] = !isNegated;
495
+ break;
496
+ }
497
+ // Underline with optional type and color
498
+ case "underline": {
499
+ const underlineAttrs = {};
500
+ if (attrs.underlineType) {
501
+ underlineAttrs["w:val"] = String(attrs.underlineType);
502
+ } else {
503
+ underlineAttrs["w:val"] = "single";
504
+ }
505
+ if (attrs.underlineColor) {
506
+ underlineAttrs["w:color"] = stripHashFromColor(String(attrs.underlineColor));
507
+ }
508
+ if (Object.keys(underlineAttrs).length > 0) {
509
+ runProperties.underline = underlineAttrs;
510
+ }
511
+ break;
512
+ }
513
+ // Highlight (background color)
514
+ case "highlight": {
515
+ if (attrs.color) {
516
+ const color = String(attrs.color).toLowerCase();
517
+ if (color === "transparent") {
518
+ runProperties.highlight = { "w:val": "none" };
519
+ } else {
520
+ runProperties.highlight = { "w:val": color };
521
+ }
522
+ }
523
+ break;
524
+ }
525
+ // textStyle contains multiple style attributes
526
+ case "textStyle": {
527
+ if (attrs.color != null) {
528
+ runProperties.color = {
529
+ val: stripHashFromColor(String(attrs.color))
530
+ };
531
+ }
532
+ if (attrs.fontSize != null) {
533
+ const points = parseFontSizeToPoints(attrs.fontSize);
534
+ if (points !== null) {
535
+ runProperties.fontSize = points * 2;
536
+ }
537
+ }
538
+ if (attrs.fontFamily != null) {
539
+ const cleanedFont = cleanFontFamily(String(attrs.fontFamily));
540
+ runProperties.fontFamily = {
541
+ ascii: cleanedFont,
542
+ eastAsia: cleanedFont,
543
+ hAnsi: cleanedFont,
544
+ cs: cleanedFont
545
+ };
546
+ }
547
+ if (attrs.letterSpacing != null) {
548
+ const ptValue = parseFloat(String(attrs.letterSpacing));
549
+ if (!isNaN(ptValue)) {
550
+ runProperties.letterSpacing = ptToTwips(ptValue);
551
+ }
552
+ }
553
+ if (attrs.textTransform != null) {
554
+ runProperties.textTransform = String(attrs.textTransform);
555
+ }
556
+ break;
557
+ }
558
+ }
559
+ }
560
+ return runProperties;
561
+ }
562
+ function collectMarksRecursively(node, allMarks) {
563
+ if (node.type === "text" && node.marks && Array.isArray(node.marks)) {
564
+ allMarks.push(...node.marks);
565
+ }
566
+ if (node.content && Array.isArray(node.content)) {
567
+ for (const child of node.content) {
568
+ collectMarksRecursively(child, allMarks);
569
+ }
570
+ }
571
+ }
572
+ function collectMarksFromRunChildren(runNode) {
573
+ const allMarks = [];
574
+ if (!runNode.content || !Array.isArray(runNode.content)) {
575
+ return allMarks;
576
+ }
577
+ for (const child of runNode.content) {
578
+ collectMarksRecursively(child, allMarks);
579
+ }
580
+ const marksByType = /* @__PURE__ */ new Map();
581
+ for (const mark of allMarks) {
582
+ marksByType.set(mark.type, mark);
583
+ }
584
+ return Array.from(marksByType.values());
585
+ }
586
+ function normalizeNode(node) {
587
+ if (node.type === "run") {
588
+ const marks = collectMarksFromRunChildren(node);
589
+ if (marks.length > 0) {
590
+ const runPropsFromMarks = marksToRunProperties(marks);
591
+ const existingRunProps = node.attrs?.runProperties || {};
592
+ const mergedRunProps = {
593
+ ...existingRunProps,
594
+ ...runPropsFromMarks
595
+ };
596
+ return {
597
+ ...node,
598
+ attrs: {
599
+ ...node.attrs,
600
+ runProperties: mergedRunProps
601
+ },
602
+ // Also recursively process children (though runs usually just have text)
603
+ content: node.content?.map(normalizeNode)
604
+ };
605
+ }
606
+ }
607
+ if (node.content && Array.isArray(node.content)) {
608
+ return {
609
+ ...node,
610
+ content: node.content.map(normalizeNode)
611
+ };
612
+ }
613
+ return node;
614
+ }
615
+ function normalizeRunProperties(doc) {
616
+ const cloned = JSON.parse(JSON.stringify(doc));
617
+ return normalizeNode(cloned);
618
+ }
619
+
456
620
  // src/services/contentResolver.ts
457
621
  function detectContentType(content) {
458
622
  if (content instanceof File) {
@@ -519,7 +683,8 @@ async function parseHtmlToJson(html, SuperDoc) {
519
683
  try {
520
684
  const json = editor.getJSON();
521
685
  if (json?.content?.length > 0) {
522
- onSuccess(json);
686
+ const normalizedJson = normalizeRunProperties(json);
687
+ onSuccess(normalizedJson);
523
688
  } else {
524
689
  onFail();
525
690
  }
@@ -556,9 +721,10 @@ async function parseHtmlToJson(html, SuperDoc) {
556
721
  throw new Error("No active editor found");
557
722
  }
558
723
  const json = editor.getJSON();
724
+ const normalizedJson = normalizeRunProperties(json);
559
725
  resolved = true;
560
726
  cleanup();
561
- resolve(json);
727
+ resolve(normalizedJson);
562
728
  } catch (err) {
563
729
  resolved = true;
564
730
  cleanup();
@@ -2861,9 +3027,10 @@ var DocxDiffEditor = react.forwardRef(
2861
3027
  }
2862
3028
  newJson = content;
2863
3029
  }
3030
+ const normalizedNewJson = normalizeRunProperties(newJson);
2864
3031
  const structuralResult = mergeWithStructuralAwareness(
2865
3032
  sourceJson,
2866
- newJson,
3033
+ normalizedNewJson,
2867
3034
  author
2868
3035
  );
2869
3036
  const merged = structuralResult.mergedDoc;