docx-diff-editor 1.0.41 → 1.0.43

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.mjs CHANGED
@@ -460,6 +460,76 @@ function isProseMirrorJSON(content) {
460
460
  const obj = content;
461
461
  return typeof obj.type === "string" && (obj.type === "doc" || Array.isArray(obj.content));
462
462
  }
463
+ async function parseHtmlToJson(html, SuperDoc) {
464
+ const container = document.createElement("div");
465
+ container.style.cssText = "position:absolute;top:-9999px;left:-9999px;width:800px;height:600px;visibility:hidden;";
466
+ document.body.appendChild(container);
467
+ return new Promise((resolve, reject) => {
468
+ let superdoc = null;
469
+ let resolved = false;
470
+ const cleanup = () => {
471
+ setTimeout(() => {
472
+ if (superdoc) {
473
+ try {
474
+ const sd = superdoc;
475
+ superdoc = null;
476
+ sd.destroy?.();
477
+ } catch {
478
+ }
479
+ }
480
+ if (container.parentNode) {
481
+ container.parentNode.removeChild(container);
482
+ }
483
+ }, TIMEOUTS.CLEANUP_DELAY);
484
+ };
485
+ setTimeout(async () => {
486
+ if (resolved) return;
487
+ try {
488
+ superdoc = new SuperDoc({
489
+ selector: container,
490
+ html,
491
+ documentMode: "viewing",
492
+ rulers: false,
493
+ user: { name: "Parser", email: "parser@local" },
494
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
495
+ onReady: ({ superdoc: sd }) => {
496
+ if (resolved) return;
497
+ try {
498
+ const editor = sd?.activeEditor;
499
+ if (!editor) {
500
+ throw new Error("No active editor found");
501
+ }
502
+ const json = editor.getJSON();
503
+ resolved = true;
504
+ cleanup();
505
+ resolve(json);
506
+ } catch (err) {
507
+ resolved = true;
508
+ cleanup();
509
+ reject(err);
510
+ }
511
+ },
512
+ onException: ({ error: err }) => {
513
+ if (resolved) return;
514
+ resolved = true;
515
+ cleanup();
516
+ reject(err);
517
+ }
518
+ });
519
+ setTimeout(() => {
520
+ if (!resolved) {
521
+ resolved = true;
522
+ cleanup();
523
+ reject(new Error("HTML parsing timed out"));
524
+ }
525
+ }, TIMEOUTS.PARSE_TIMEOUT);
526
+ } catch (err) {
527
+ cleanup();
528
+ reject(err);
529
+ }
530
+ }, 50);
531
+ });
532
+ }
463
533
  async function parseDocxFile(file, SuperDoc) {
464
534
  const container = document.createElement("div");
465
535
  container.style.cssText = "position:absolute;top:-9999px;left:-9999px;width:800px;height:600px;visibility:hidden;";
@@ -703,204 +773,6 @@ function diffDocuments(docA, docB) {
703
773
  summary
704
774
  };
705
775
  }
706
- function createTrackInsertMark(author = DEFAULT_AUTHOR, id) {
707
- return {
708
- type: "trackInsert",
709
- attrs: {
710
- id: id ?? v4(),
711
- author: author.name,
712
- authorEmail: author.email,
713
- authorImage: "",
714
- date: (/* @__PURE__ */ new Date()).toISOString()
715
- }
716
- };
717
- }
718
- function createTrackDeleteMark(author = DEFAULT_AUTHOR, id) {
719
- return {
720
- type: "trackDelete",
721
- attrs: {
722
- id: id ?? v4(),
723
- author: author.name,
724
- authorEmail: author.email,
725
- authorImage: "",
726
- date: (/* @__PURE__ */ new Date()).toISOString()
727
- }
728
- };
729
- }
730
- function createTrackFormatMark(before, after, author = DEFAULT_AUTHOR) {
731
- return {
732
- type: "trackFormat",
733
- attrs: {
734
- id: v4(),
735
- author: author.name,
736
- authorEmail: author.email,
737
- authorImage: "",
738
- date: (/* @__PURE__ */ new Date()).toISOString(),
739
- before,
740
- after
741
- }
742
- };
743
- }
744
-
745
- // src/services/mergeDocuments.ts
746
- function cloneNode(node) {
747
- return JSON.parse(JSON.stringify(node));
748
- }
749
- function mergeDocuments(docA, docB, diffResult, author = DEFAULT_AUTHOR) {
750
- const merged = cloneNode(docA);
751
- const charStates = [];
752
- let insertions = [];
753
- const formatChanges = diffResult.formatChanges || [];
754
- function getFormatChangeAt(pos) {
755
- for (const fc of formatChanges) {
756
- if (pos >= fc.from && pos < fc.to) {
757
- return fc;
758
- }
759
- }
760
- return null;
761
- }
762
- let docAOffset = 0;
763
- const segments = diffResult.segments;
764
- for (let segIdx = 0; segIdx < segments.length; segIdx++) {
765
- const segment = segments[segIdx];
766
- if (segment.type === "equal") {
767
- for (let i = 0; i < segment.text.length; i++) {
768
- charStates[docAOffset + i] = { type: "equal" };
769
- }
770
- docAOffset += segment.text.length;
771
- } else if (segment.type === "delete") {
772
- const nextSegment = segments[segIdx + 1];
773
- const isReplacement = nextSegment && nextSegment.type === "insert";
774
- const replacementId = isReplacement ? v4() : void 0;
775
- for (let i = 0; i < segment.text.length; i++) {
776
- charStates[docAOffset + i] = { type: "delete", replacementId };
777
- }
778
- docAOffset += segment.text.length;
779
- if (isReplacement && nextSegment) {
780
- insertions.push({
781
- afterOffset: docAOffset,
782
- text: nextSegment.text,
783
- replacementId
784
- });
785
- segIdx++;
786
- }
787
- } else if (segment.type === "insert") {
788
- insertions.push({
789
- afterOffset: docAOffset,
790
- text: segment.text
791
- });
792
- }
793
- }
794
- function transformNode(node, nodeOffset, path) {
795
- if (node.type === "text" && node.text) {
796
- const text = node.text;
797
- const result = [];
798
- let i = 0;
799
- while (i < text.length) {
800
- const charOffset = nodeOffset + i;
801
- const charState = charStates[charOffset] || { type: "equal" };
802
- const insertionsHere = insertions.filter((ins) => ins.afterOffset === charOffset);
803
- for (const ins of insertionsHere) {
804
- result.push({
805
- type: "text",
806
- text: ins.text,
807
- marks: [...node.marks || [], createTrackInsertMark(author, ins.replacementId)]
808
- });
809
- }
810
- const currentFormatChange = getFormatChangeAt(nodeOffset + i);
811
- let j = i + 1;
812
- while (j < text.length) {
813
- const nextState = charStates[nodeOffset + j] || { type: "equal" };
814
- if (nextState.type !== charState.type) break;
815
- if (insertions.some((ins) => ins.afterOffset === nodeOffset + j)) break;
816
- const nextFormatChange = getFormatChangeAt(nodeOffset + j);
817
- if (currentFormatChange !== nextFormatChange) break;
818
- j++;
819
- }
820
- const chunk = text.substring(i, j);
821
- let marks = [...node.marks || []];
822
- if (charState.type === "delete") {
823
- marks.push(createTrackDeleteMark(author, charState.replacementId));
824
- } else if (charState.type === "equal") {
825
- if (currentFormatChange) {
826
- const trackFormatMark = createTrackFormatMark(
827
- currentFormatChange.before,
828
- currentFormatChange.after,
829
- author
830
- );
831
- marks = [...currentFormatChange.after, trackFormatMark];
832
- }
833
- }
834
- result.push({
835
- type: "text",
836
- text: chunk,
837
- marks: marks.length > 0 ? marks : void 0
838
- });
839
- i = j;
840
- }
841
- const endOffset = nodeOffset + text.length;
842
- const endInsertions = insertions.filter((ins) => ins.afterOffset === endOffset);
843
- for (const ins of endInsertions) {
844
- result.push({
845
- type: "text",
846
- text: ins.text,
847
- marks: [...node.marks || [], createTrackInsertMark(author, ins.replacementId)]
848
- });
849
- }
850
- insertions = insertions.filter(
851
- (ins) => ins.afterOffset < nodeOffset || ins.afterOffset > endOffset
852
- );
853
- return { nodes: result, consumedLength: text.length };
854
- }
855
- if (node.content && Array.isArray(node.content)) {
856
- const newContent = [];
857
- let offset = nodeOffset;
858
- for (const child of node.content) {
859
- const { nodes, consumedLength } = transformNode(child, offset);
860
- newContent.push(...nodes);
861
- offset += consumedLength;
862
- }
863
- return {
864
- nodes: [{ ...node, content: newContent }],
865
- consumedLength: offset - nodeOffset
866
- };
867
- }
868
- return { nodes: [node], consumedLength: 0 };
869
- }
870
- if (merged.content && Array.isArray(merged.content)) {
871
- const newContent = [];
872
- let offset = 0;
873
- for (let i = 0; i < merged.content.length; i++) {
874
- const child = merged.content[i];
875
- const { nodes, consumedLength } = transformNode(child, offset);
876
- newContent.push(...nodes);
877
- offset += consumedLength;
878
- }
879
- merged.content = newContent;
880
- }
881
- if (insertions.length > 0) {
882
- for (const ins of insertions) {
883
- const insertNode = {
884
- type: "paragraph",
885
- content: [
886
- {
887
- type: "run",
888
- content: [
889
- {
890
- type: "text",
891
- text: ins.text,
892
- marks: [createTrackInsertMark(author, ins.replacementId)]
893
- }
894
- ]
895
- }
896
- ]
897
- };
898
- if (!merged.content) merged.content = [];
899
- merged.content.push(insertNode);
900
- }
901
- }
902
- return merged;
903
- }
904
776
 
905
777
  // src/services/changeContextExtractor.ts
906
778
  function extractEnrichedChanges(mergedJson) {
@@ -1159,6 +1031,44 @@ function groupReplacements(changes) {
1159
1031
  }
1160
1032
  return result;
1161
1033
  }
1034
+ function createTrackInsertMark(author = DEFAULT_AUTHOR, id) {
1035
+ return {
1036
+ type: "trackInsert",
1037
+ attrs: {
1038
+ id: id ?? v4(),
1039
+ author: author.name,
1040
+ authorEmail: author.email,
1041
+ authorImage: "",
1042
+ date: (/* @__PURE__ */ new Date()).toISOString()
1043
+ }
1044
+ };
1045
+ }
1046
+ function createTrackDeleteMark(author = DEFAULT_AUTHOR, id) {
1047
+ return {
1048
+ type: "trackDelete",
1049
+ attrs: {
1050
+ id: id ?? v4(),
1051
+ author: author.name,
1052
+ authorEmail: author.email,
1053
+ authorImage: "",
1054
+ date: (/* @__PURE__ */ new Date()).toISOString()
1055
+ }
1056
+ };
1057
+ }
1058
+ function createTrackFormatMark(before, after, author = DEFAULT_AUTHOR) {
1059
+ return {
1060
+ type: "trackFormat",
1061
+ attrs: {
1062
+ id: v4(),
1063
+ author: author.name,
1064
+ authorEmail: author.email,
1065
+ authorImage: "",
1066
+ date: (/* @__PURE__ */ new Date()).toISOString(),
1067
+ before,
1068
+ after
1069
+ }
1070
+ };
1071
+ }
1162
1072
 
1163
1073
  // src/services/nodeAligner.ts
1164
1074
  init_nodeFingerprint();
@@ -1337,7 +1247,165 @@ function alignListItems(listA, listB, listPathA, listPathB) {
1337
1247
  for (const item of itemsB) {
1338
1248
  item.fingerprint = generateFingerprint2(item.node);
1339
1249
  }
1340
- return alignNodes(itemsA, itemsB);
1250
+ return alignNodes(itemsA, itemsB);
1251
+ }
1252
+ function cloneNode(node) {
1253
+ return JSON.parse(JSON.stringify(node));
1254
+ }
1255
+ function mergeDocuments(docA, docB, diffResult, author = DEFAULT_AUTHOR) {
1256
+ const merged = cloneNode(docA);
1257
+ const charStates = [];
1258
+ let insertions = [];
1259
+ const formatChanges = diffResult.formatChanges || [];
1260
+ function getFormatChangeAt(pos) {
1261
+ for (const fc of formatChanges) {
1262
+ if (pos >= fc.from && pos < fc.to) {
1263
+ return fc;
1264
+ }
1265
+ }
1266
+ return null;
1267
+ }
1268
+ let docAOffset = 0;
1269
+ const segments = diffResult.segments;
1270
+ for (let segIdx = 0; segIdx < segments.length; segIdx++) {
1271
+ const segment = segments[segIdx];
1272
+ if (segment.type === "equal") {
1273
+ for (let i = 0; i < segment.text.length; i++) {
1274
+ charStates[docAOffset + i] = { type: "equal" };
1275
+ }
1276
+ docAOffset += segment.text.length;
1277
+ } else if (segment.type === "delete") {
1278
+ const nextSegment = segments[segIdx + 1];
1279
+ const isReplacement = nextSegment && nextSegment.type === "insert";
1280
+ const replacementId = isReplacement ? v4() : void 0;
1281
+ for (let i = 0; i < segment.text.length; i++) {
1282
+ charStates[docAOffset + i] = { type: "delete", replacementId };
1283
+ }
1284
+ docAOffset += segment.text.length;
1285
+ if (isReplacement && nextSegment) {
1286
+ insertions.push({
1287
+ afterOffset: docAOffset,
1288
+ text: nextSegment.text,
1289
+ replacementId
1290
+ });
1291
+ segIdx++;
1292
+ }
1293
+ } else if (segment.type === "insert") {
1294
+ insertions.push({
1295
+ afterOffset: docAOffset,
1296
+ text: segment.text
1297
+ });
1298
+ }
1299
+ }
1300
+ function transformNode(node, nodeOffset, path) {
1301
+ if (node.type === "text" && node.text) {
1302
+ const text = node.text;
1303
+ const result = [];
1304
+ let i = 0;
1305
+ while (i < text.length) {
1306
+ const charOffset = nodeOffset + i;
1307
+ const charState = charStates[charOffset] || { type: "equal" };
1308
+ const insertionsHere = insertions.filter((ins) => ins.afterOffset === charOffset);
1309
+ for (const ins of insertionsHere) {
1310
+ result.push({
1311
+ type: "text",
1312
+ text: ins.text,
1313
+ marks: [...node.marks || [], createTrackInsertMark(author, ins.replacementId)]
1314
+ });
1315
+ }
1316
+ const currentFormatChange = getFormatChangeAt(nodeOffset + i);
1317
+ let j = i + 1;
1318
+ while (j < text.length) {
1319
+ const nextState = charStates[nodeOffset + j] || { type: "equal" };
1320
+ if (nextState.type !== charState.type) break;
1321
+ if (insertions.some((ins) => ins.afterOffset === nodeOffset + j)) break;
1322
+ const nextFormatChange = getFormatChangeAt(nodeOffset + j);
1323
+ if (currentFormatChange !== nextFormatChange) break;
1324
+ j++;
1325
+ }
1326
+ const chunk = text.substring(i, j);
1327
+ let marks = [...node.marks || []];
1328
+ if (charState.type === "delete") {
1329
+ marks.push(createTrackDeleteMark(author, charState.replacementId));
1330
+ } else if (charState.type === "equal") {
1331
+ if (currentFormatChange) {
1332
+ const trackFormatMark = createTrackFormatMark(
1333
+ currentFormatChange.before,
1334
+ currentFormatChange.after,
1335
+ author
1336
+ );
1337
+ marks = [...currentFormatChange.after, trackFormatMark];
1338
+ }
1339
+ }
1340
+ result.push({
1341
+ type: "text",
1342
+ text: chunk,
1343
+ marks: marks.length > 0 ? marks : void 0
1344
+ });
1345
+ i = j;
1346
+ }
1347
+ const endOffset = nodeOffset + text.length;
1348
+ const endInsertions = insertions.filter((ins) => ins.afterOffset === endOffset);
1349
+ for (const ins of endInsertions) {
1350
+ result.push({
1351
+ type: "text",
1352
+ text: ins.text,
1353
+ marks: [...node.marks || [], createTrackInsertMark(author, ins.replacementId)]
1354
+ });
1355
+ }
1356
+ insertions = insertions.filter(
1357
+ (ins) => ins.afterOffset < nodeOffset || ins.afterOffset > endOffset
1358
+ );
1359
+ return { nodes: result, consumedLength: text.length };
1360
+ }
1361
+ if (node.content && Array.isArray(node.content)) {
1362
+ const newContent = [];
1363
+ let offset = nodeOffset;
1364
+ for (const child of node.content) {
1365
+ const { nodes, consumedLength } = transformNode(child, offset);
1366
+ newContent.push(...nodes);
1367
+ offset += consumedLength;
1368
+ }
1369
+ return {
1370
+ nodes: [{ ...node, content: newContent }],
1371
+ consumedLength: offset - nodeOffset
1372
+ };
1373
+ }
1374
+ return { nodes: [node], consumedLength: 0 };
1375
+ }
1376
+ if (merged.content && Array.isArray(merged.content)) {
1377
+ const newContent = [];
1378
+ let offset = 0;
1379
+ for (let i = 0; i < merged.content.length; i++) {
1380
+ const child = merged.content[i];
1381
+ const { nodes, consumedLength } = transformNode(child, offset);
1382
+ newContent.push(...nodes);
1383
+ offset += consumedLength;
1384
+ }
1385
+ merged.content = newContent;
1386
+ }
1387
+ if (insertions.length > 0) {
1388
+ for (const ins of insertions) {
1389
+ const insertNode = {
1390
+ type: "paragraph",
1391
+ content: [
1392
+ {
1393
+ type: "run",
1394
+ content: [
1395
+ {
1396
+ type: "text",
1397
+ text: ins.text,
1398
+ marks: [createTrackInsertMark(author, ins.replacementId)]
1399
+ }
1400
+ ]
1401
+ }
1402
+ ]
1403
+ };
1404
+ if (!merged.content) merged.content = [];
1405
+ merged.content.push(insertNode);
1406
+ }
1407
+ }
1408
+ return merged;
1341
1409
  }
1342
1410
 
1343
1411
  // src/services/attrComparer.ts
@@ -1571,6 +1639,9 @@ function diffTables(tableA, tableB, tablePathA, tablePathB) {
1571
1639
  function isTable(node) {
1572
1640
  return node?.type === "table";
1573
1641
  }
1642
+ function isTableRow(node) {
1643
+ return node?.type === "tableRow";
1644
+ }
1574
1645
  function getRowLocation(tablePath, rowIndex, tableIndex) {
1575
1646
  return `Table ${tableIndex + 1}, Row ${rowIndex + 1}`;
1576
1647
  }
@@ -1607,6 +1678,9 @@ function extractCellText(cell) {
1607
1678
  function isList(node) {
1608
1679
  return node?.type === "bulletList" || node?.type === "orderedList";
1609
1680
  }
1681
+ function isListItem(node) {
1682
+ return node?.type === "listItem";
1683
+ }
1610
1684
  function extractListItemText(item) {
1611
1685
  const texts = [];
1612
1686
  function extract(node) {
@@ -1839,7 +1913,10 @@ function getImagePreview(node) {
1839
1913
  return "(image)";
1840
1914
  }
1841
1915
 
1842
- // src/services/blockLevelMerger.ts
1916
+ // src/services/structuralMerger.ts
1917
+ function cloneNode2(node) {
1918
+ return JSON.parse(JSON.stringify(node));
1919
+ }
1843
1920
  function markAllTextAsInserted(node, sharedId, author) {
1844
1921
  if (node.type === "text") {
1845
1922
  return {
@@ -1855,7 +1932,7 @@ function markAllTextAsInserted(node, sharedId, author) {
1855
1932
  )
1856
1933
  };
1857
1934
  }
1858
- return node;
1935
+ return { ...node };
1859
1936
  }
1860
1937
  function markAllTextAsDeleted(node, sharedId, author) {
1861
1938
  if (node.type === "text") {
@@ -1872,10 +1949,7 @@ function markAllTextAsDeleted(node, sharedId, author) {
1872
1949
  )
1873
1950
  };
1874
1951
  }
1875
- return node;
1876
- }
1877
- function cloneNode2(node) {
1878
- return JSON.parse(JSON.stringify(node));
1952
+ return { ...node };
1879
1953
  }
1880
1954
  function extractTextPreview(node, maxLength = 50) {
1881
1955
  const texts = [];
@@ -1896,197 +1970,361 @@ function extractTextPreview(node, maxLength = 50) {
1896
1970
  }
1897
1971
  return text || "(empty)";
1898
1972
  }
1899
- function processStructuralChanges(docA, docB, author = DEFAULT_AUTHOR) {
1900
- const changes = [];
1901
- const infos = [];
1973
+ function getNodeTypeDescription(node) {
1974
+ if (isTable(node)) return "Table";
1975
+ if (isList(node)) return "List";
1976
+ if (isListItem(node)) return "List item";
1977
+ if (isTableRow(node)) return "Table row";
1978
+ if (isImage(node)) return "Image";
1979
+ if (node.type === "heading") return `Heading ${node.attrs?.level || 1}`;
1980
+ if (node.type === "paragraph") return "Paragraph";
1981
+ if (node.type === "blockquote") return "Blockquote";
1982
+ if (node.type === "codeBlock") return "Code block";
1983
+ return node.type || "Block";
1984
+ }
1985
+ function mergeWithStructuralAwareness(docA, docB, author = DEFAULT_AUTHOR) {
1986
+ const structuralInfos = [];
1987
+ const summary = [];
1988
+ let textChangeCount = 0;
1902
1989
  const alignment = alignDocuments(docA, docB);
1903
- let tableIndex = 0;
1904
- let listIndex = 0;
1905
- let paragraphIndex = 0;
1906
- for (const inserted of alignment.insertions) {
1907
- const node = inserted.node;
1908
- const sharedId = v4();
1909
- const date = (/* @__PURE__ */ new Date()).toISOString();
1910
- let type = "paragraphInsert";
1911
- let location = "";
1912
- let preview = "";
1913
- if (isTable(node)) {
1914
- type = "rowInsert";
1915
- location = `New table at position ${inserted.path[0] + 1}`;
1916
- preview = `Table with ${node.content?.length || 0} rows`;
1917
- tableIndex++;
1918
- } else if (isList(node)) {
1919
- type = "listItemInsert";
1920
- location = `New list at position ${inserted.path[0] + 1}`;
1921
- preview = `List with ${node.content?.length || 0} items`;
1922
- listIndex++;
1923
- } else {
1924
- type = "paragraphInsert";
1925
- paragraphIndex++;
1926
- location = `Paragraph ${paragraphIndex}`;
1927
- preview = extractTextPreview(node);
1990
+ const operations = buildMergeOperations(alignment, docA, docB);
1991
+ const mergedContent = [];
1992
+ let blockIndex = 0;
1993
+ for (const op of operations) {
1994
+ blockIndex++;
1995
+ switch (op.type) {
1996
+ case "matched": {
1997
+ const { mergedNode, infos, changes } = mergeMatchedBlock(
1998
+ op.nodeA,
1999
+ op.nodeB,
2000
+ blockIndex,
2001
+ author
2002
+ );
2003
+ mergedContent.push(mergedNode);
2004
+ structuralInfos.push(...infos);
2005
+ textChangeCount += changes;
2006
+ break;
2007
+ }
2008
+ case "inserted": {
2009
+ const { markedNode, info } = createInsertedBlock(
2010
+ op.nodeB,
2011
+ blockIndex,
2012
+ author
2013
+ );
2014
+ mergedContent.push(markedNode);
2015
+ if (info) {
2016
+ structuralInfos.push(info);
2017
+ }
2018
+ break;
2019
+ }
2020
+ case "deleted": {
2021
+ const { markedNode, info } = createDeletedBlock(
2022
+ op.nodeA,
2023
+ blockIndex,
2024
+ author
2025
+ );
2026
+ mergedContent.push(markedNode);
2027
+ if (info) {
2028
+ structuralInfos.push(info);
2029
+ }
2030
+ break;
2031
+ }
1928
2032
  }
1929
- changes.push({
1930
- id: sharedId,
1931
- type,
1932
- nodeType: node.type,
1933
- path: inserted.path,
1934
- node: markAllTextAsInserted(cloneNode2(node), sharedId, author)
1935
- });
1936
- infos.push({
1937
- id: sharedId,
1938
- type,
1939
- nodeType: node.type,
1940
- location,
1941
- preview,
1942
- author,
1943
- date
1944
- });
1945
2033
  }
1946
- for (const deleted of alignment.deletions) {
1947
- const node = deleted.node;
1948
- const sharedId = v4();
1949
- const date = (/* @__PURE__ */ new Date()).toISOString();
1950
- let type = "paragraphDelete";
1951
- let location = "";
1952
- let preview = "";
1953
- if (isTable(node)) {
1954
- type = "rowDelete";
1955
- location = `Deleted table at position ${deleted.path[0] + 1}`;
1956
- preview = `Table with ${node.content?.length || 0} rows`;
1957
- } else if (isList(node)) {
1958
- type = "listItemDelete";
1959
- location = `Deleted list at position ${deleted.path[0] + 1}`;
1960
- preview = `List with ${node.content?.length || 0} items`;
2034
+ const mergedDoc = {
2035
+ type: "doc",
2036
+ content: mergedContent
2037
+ };
2038
+ const insertCount = structuralInfos.filter((i) => i.type.includes("Insert")).length;
2039
+ const deleteCount = structuralInfos.filter((i) => i.type.includes("Delete")).length;
2040
+ if (insertCount > 0) summary.push(`${insertCount} block(s) inserted`);
2041
+ if (deleteCount > 0) summary.push(`${deleteCount} block(s) deleted`);
2042
+ if (textChangeCount > 0) summary.push(`${textChangeCount} text change(s)`);
2043
+ return {
2044
+ mergedDoc,
2045
+ structuralInfos,
2046
+ summary,
2047
+ textChangeCount
2048
+ };
2049
+ }
2050
+ function buildMergeOperations(alignment, docA, docB) {
2051
+ const operations = [];
2052
+ const matchedFromA = /* @__PURE__ */ new Map();
2053
+ const matchedFromB = /* @__PURE__ */ new Map();
2054
+ for (const match of alignment.matched) {
2055
+ const idxA = match.pathA[0];
2056
+ const idxB = match.pathB[0];
2057
+ matchedFromA.set(idxA, { pathB: match.pathB, similarity: match.similarity });
2058
+ matchedFromB.set(idxB, { pathA: match.pathA, similarity: match.similarity });
2059
+ }
2060
+ const deletedIndices = new Set(alignment.deletions.map((d) => d.path[0]));
2061
+ const processedDeletions = /* @__PURE__ */ new Set();
2062
+ const contentB = docB.content || [];
2063
+ const contentA = docA.content || [];
2064
+ for (let idxB = 0; idxB < contentB.length; idxB++) {
2065
+ const nodeB = contentB[idxB];
2066
+ const match = matchedFromB.get(idxB);
2067
+ if (match) {
2068
+ const idxA = match.pathA[0];
2069
+ const nodeA = contentA[idxA];
2070
+ for (let checkIdx = 0; checkIdx < idxA; checkIdx++) {
2071
+ if (deletedIndices.has(checkIdx) && !processedDeletions.has(checkIdx)) {
2072
+ operations.push({
2073
+ type: "deleted",
2074
+ nodeA: contentA[checkIdx],
2075
+ pathA: [checkIdx]
2076
+ });
2077
+ processedDeletions.add(checkIdx);
2078
+ }
2079
+ }
2080
+ operations.push({
2081
+ type: "matched",
2082
+ nodeA,
2083
+ nodeB,
2084
+ pathA: match.pathA,
2085
+ pathB: [idxB]
2086
+ });
1961
2087
  } else {
1962
- type = "paragraphDelete";
1963
- location = `Deleted paragraph`;
1964
- preview = extractTextPreview(node);
2088
+ operations.push({
2089
+ type: "inserted",
2090
+ nodeB,
2091
+ pathB: [idxB]
2092
+ });
1965
2093
  }
1966
- changes.push({
1967
- id: sharedId,
1968
- type,
1969
- nodeType: node.type,
1970
- path: deleted.path,
1971
- node: markAllTextAsDeleted(cloneNode2(node), sharedId, author)
1972
- });
1973
- infos.push({
1974
- id: sharedId,
1975
- type,
1976
- nodeType: node.type,
1977
- location,
1978
- preview,
1979
- author,
1980
- date
1981
- });
1982
2094
  }
1983
- for (const match of alignment.matched) {
1984
- const nodeA = docA.content?.[match.pathA[0]];
1985
- const nodeB = docB.content?.[match.pathB[0]];
1986
- if (!nodeA || !nodeB) continue;
1987
- if (isTable(nodeA) && isTable(nodeB)) {
1988
- tableIndex++;
1989
- const tableResult = diffTables(nodeA, nodeB, match.pathA, match.pathB);
1990
- for (const rowChange of tableResult.rowChanges) {
1991
- const sharedId = rowChange.id;
1992
- const date = (/* @__PURE__ */ new Date()).toISOString();
1993
- const rowIndex = rowChange.path[rowChange.path.length - 1];
1994
- const isInsert = rowChange.type === "rowInsert";
1995
- const location = getRowLocation(rowChange.path, rowIndex, tableIndex - 1);
1996
- const preview = getRowPreview(rowChange.node);
1997
- const markedNode = isInsert ? markAllTextAsInserted(cloneNode2(rowChange.node), sharedId, author) : markAllTextAsDeleted(cloneNode2(rowChange.node), sharedId, author);
1998
- changes.push({
1999
- ...rowChange,
2000
- node: markedNode
2001
- });
2002
- infos.push({
2003
- id: sharedId,
2004
- type: rowChange.type,
2005
- nodeType: "tableRow",
2006
- location,
2007
- preview,
2008
- author,
2009
- date
2010
- });
2011
- }
2095
+ for (const deletion of alignment.deletions) {
2096
+ const idxA = deletion.path[0];
2097
+ if (!processedDeletions.has(idxA)) {
2098
+ operations.push({
2099
+ type: "deleted",
2100
+ nodeA: deletion.node,
2101
+ pathA: deletion.path
2102
+ });
2012
2103
  }
2013
- if (isList(nodeA) && isList(nodeB)) {
2014
- listIndex++;
2015
- const listResult = diffLists(nodeA, nodeB, match.pathA, match.pathB);
2016
- for (const itemChange of listResult.itemChanges) {
2017
- const sharedId = itemChange.id;
2018
- const date = (/* @__PURE__ */ new Date()).toISOString();
2019
- const itemIndex = itemChange.path[itemChange.path.length - 1];
2020
- const isInsert = itemChange.type === "listItemInsert";
2021
- const location = getListItemLocation(itemChange.path, itemIndex, listIndex - 1);
2022
- const preview = getListItemPreview(itemChange.node);
2023
- const markedNode = isInsert ? markAllTextAsInserted(cloneNode2(itemChange.node), sharedId, author) : markAllTextAsDeleted(cloneNode2(itemChange.node), sharedId, author);
2024
- changes.push({
2025
- ...itemChange,
2026
- node: markedNode
2027
- });
2028
- infos.push({
2029
- id: sharedId,
2030
- type: itemChange.type,
2031
- nodeType: "listItem",
2032
- location,
2033
- preview,
2034
- author,
2035
- date
2036
- });
2104
+ }
2105
+ return operations;
2106
+ }
2107
+ function mergeMatchedBlock(nodeA, nodeB, blockIndex, author) {
2108
+ const infos = [];
2109
+ let changes = 0;
2110
+ if (isTable(nodeA) && isTable(nodeB)) {
2111
+ const { mergedTable, tableInfos, changeCount } = mergeMatchedTable(
2112
+ nodeA,
2113
+ nodeB,
2114
+ blockIndex,
2115
+ author
2116
+ );
2117
+ return { mergedNode: mergedTable, infos: tableInfos, changes: changeCount };
2118
+ }
2119
+ if (isList(nodeA) && isList(nodeB)) {
2120
+ const { mergedList, listInfos, changeCount } = mergeMatchedList(
2121
+ nodeA,
2122
+ nodeB,
2123
+ blockIndex,
2124
+ author
2125
+ );
2126
+ return { mergedNode: mergedList, infos: listInfos, changes: changeCount };
2127
+ }
2128
+ const diff = diffDocuments(
2129
+ { type: "doc", content: [nodeA] },
2130
+ { type: "doc", content: [nodeB] }
2131
+ );
2132
+ changes = diff.segments.filter((s) => s.type !== "equal").length;
2133
+ changes += diff.formatChanges?.length || 0;
2134
+ const merged = mergeDocuments(
2135
+ { type: "doc", content: [nodeA] },
2136
+ { },
2137
+ diff,
2138
+ author
2139
+ );
2140
+ const mergedNode = merged.content?.[0] || cloneNode2(nodeB);
2141
+ return { mergedNode, infos, changes };
2142
+ }
2143
+ function mergeMatchedTable(tableA, tableB, tableIndex, author) {
2144
+ const tableInfos = [];
2145
+ let changeCount = 0;
2146
+ const rowAlignment = alignTableRows(tableA, tableB, [tableIndex - 1], [tableIndex - 1]);
2147
+ const mergedRows = [];
2148
+ const matchedFromA = /* @__PURE__ */ new Map();
2149
+ const matchedFromB = /* @__PURE__ */ new Map();
2150
+ for (const match of rowAlignment.matched) {
2151
+ const idxA = match.pathA[match.pathA.length - 1];
2152
+ const idxB = match.pathB[match.pathB.length - 1];
2153
+ matchedFromA.set(idxA, idxB);
2154
+ matchedFromB.set(idxB, idxA);
2155
+ }
2156
+ const deletedIndices = new Set(rowAlignment.deletions.map((d) => d.path[d.path.length - 1]));
2157
+ const processedDeletions = /* @__PURE__ */ new Set();
2158
+ const rowsA = tableA.content || [];
2159
+ const rowsB = tableB.content || [];
2160
+ for (let idxB = 0; idxB < rowsB.length; idxB++) {
2161
+ const rowB = rowsB[idxB];
2162
+ const matchedIdxA = matchedFromB.get(idxB);
2163
+ if (matchedIdxA !== void 0) {
2164
+ const rowA = rowsA[matchedIdxA];
2165
+ for (let checkIdx = 0; checkIdx < matchedIdxA; checkIdx++) {
2166
+ if (deletedIndices.has(checkIdx) && !processedDeletions.has(checkIdx)) {
2167
+ const deletedRow = rowsA[checkIdx];
2168
+ const changeId = v4();
2169
+ mergedRows.push(markAllTextAsDeleted(cloneNode2(deletedRow), changeId, author));
2170
+ tableInfos.push({
2171
+ id: changeId,
2172
+ type: "rowDelete",
2173
+ nodeType: "tableRow",
2174
+ location: `Table ${tableIndex}, Row ${checkIdx + 1}`,
2175
+ preview: extractTextPreview(deletedRow),
2176
+ author,
2177
+ date: (/* @__PURE__ */ new Date()).toISOString()
2178
+ });
2179
+ processedDeletions.add(checkIdx);
2180
+ }
2037
2181
  }
2182
+ const { mergedNode, changes } = mergeMatchedBlock(rowA, rowB, idxB, author);
2183
+ mergedRows.push(mergedNode);
2184
+ changeCount += changes;
2185
+ } else {
2186
+ const changeId = v4();
2187
+ mergedRows.push(markAllTextAsInserted(cloneNode2(rowB), changeId, author));
2188
+ tableInfos.push({
2189
+ id: changeId,
2190
+ type: "rowInsert",
2191
+ nodeType: "tableRow",
2192
+ location: `Table ${tableIndex}, Row ${idxB + 1}`,
2193
+ preview: extractTextPreview(rowB),
2194
+ author,
2195
+ date: (/* @__PURE__ */ new Date()).toISOString()
2196
+ });
2038
2197
  }
2039
2198
  }
2040
- const imageChanges = diffImages(docA, docB);
2041
- for (const imgInsert of imageChanges.inserted) {
2042
- const sharedId = imgInsert.id;
2043
- const date = (/* @__PURE__ */ new Date()).toISOString();
2044
- infos.push({
2045
- id: sharedId,
2046
- type: "imageInsert",
2047
- nodeType: "image",
2048
- location: getImageLocation(imgInsert.path),
2049
- preview: getImagePreview(imgInsert.node),
2050
- author,
2051
- date
2052
- });
2053
- changes.push(imgInsert);
2199
+ for (const deletion of rowAlignment.deletions) {
2200
+ const idxA = deletion.path[deletion.path.length - 1];
2201
+ if (!processedDeletions.has(idxA)) {
2202
+ const changeId = v4();
2203
+ mergedRows.push(markAllTextAsDeleted(cloneNode2(deletion.node), changeId, author));
2204
+ tableInfos.push({
2205
+ id: changeId,
2206
+ type: "rowDelete",
2207
+ nodeType: "tableRow",
2208
+ location: `Table ${tableIndex}, Row ${idxA + 1}`,
2209
+ preview: extractTextPreview(deletion.node),
2210
+ author,
2211
+ date: (/* @__PURE__ */ new Date()).toISOString()
2212
+ });
2213
+ }
2054
2214
  }
2055
- for (const imgDelete of imageChanges.deleted) {
2056
- const sharedId = imgDelete.id;
2057
- const date = (/* @__PURE__ */ new Date()).toISOString();
2058
- infos.push({
2059
- id: sharedId,
2060
- type: "imageDelete",
2061
- nodeType: "image",
2062
- location: getImageLocation(imgDelete.path),
2063
- preview: getImagePreview(imgDelete.node),
2064
- author,
2065
- date
2066
- });
2067
- changes.push(imgDelete);
2215
+ const mergedTable = {
2216
+ ...tableB,
2217
+ content: mergedRows
2218
+ };
2219
+ return { mergedTable, tableInfos, changeCount };
2220
+ }
2221
+ function mergeMatchedList(listA, listB, listIndex, author) {
2222
+ const listInfos = [];
2223
+ let changeCount = 0;
2224
+ const itemAlignment = alignListItems(listA, listB, [listIndex - 1], [listIndex - 1]);
2225
+ const mergedItems = [];
2226
+ const matchedFromA = /* @__PURE__ */ new Map();
2227
+ const matchedFromB = /* @__PURE__ */ new Map();
2228
+ for (const match of itemAlignment.matched) {
2229
+ const idxA = match.pathA[match.pathA.length - 1];
2230
+ const idxB = match.pathB[match.pathB.length - 1];
2231
+ matchedFromA.set(idxA, idxB);
2232
+ matchedFromB.set(idxB, idxA);
2233
+ }
2234
+ const deletedIndices = new Set(itemAlignment.deletions.map((d) => d.path[d.path.length - 1]));
2235
+ const processedDeletions = /* @__PURE__ */ new Set();
2236
+ const itemsA = listA.content || [];
2237
+ const itemsB = listB.content || [];
2238
+ for (let idxB = 0; idxB < itemsB.length; idxB++) {
2239
+ const itemB = itemsB[idxB];
2240
+ const matchedIdxA = matchedFromB.get(idxB);
2241
+ if (matchedIdxA !== void 0) {
2242
+ const itemA = itemsA[matchedIdxA];
2243
+ for (let checkIdx = 0; checkIdx < matchedIdxA; checkIdx++) {
2244
+ if (deletedIndices.has(checkIdx) && !processedDeletions.has(checkIdx)) {
2245
+ const deletedItem = itemsA[checkIdx];
2246
+ const changeId = v4();
2247
+ mergedItems.push(markAllTextAsDeleted(cloneNode2(deletedItem), changeId, author));
2248
+ listInfos.push({
2249
+ id: changeId,
2250
+ type: "listItemDelete",
2251
+ nodeType: "listItem",
2252
+ location: `List ${listIndex}, Item ${checkIdx + 1}`,
2253
+ preview: extractTextPreview(deletedItem),
2254
+ author,
2255
+ date: (/* @__PURE__ */ new Date()).toISOString()
2256
+ });
2257
+ processedDeletions.add(checkIdx);
2258
+ }
2259
+ }
2260
+ const { mergedNode, changes } = mergeMatchedBlock(itemA, itemB, idxB, author);
2261
+ mergedItems.push(mergedNode);
2262
+ changeCount += changes;
2263
+ } else {
2264
+ const changeId = v4();
2265
+ mergedItems.push(markAllTextAsInserted(cloneNode2(itemB), changeId, author));
2266
+ listInfos.push({
2267
+ id: changeId,
2268
+ type: "listItemInsert",
2269
+ nodeType: "listItem",
2270
+ location: `List ${listIndex}, Item ${idxB + 1}`,
2271
+ preview: extractTextPreview(itemB),
2272
+ author,
2273
+ date: (/* @__PURE__ */ new Date()).toISOString()
2274
+ });
2275
+ }
2068
2276
  }
2069
- return { changes, infos };
2070
- }
2071
- function generateStructuralChangeSummary(infos) {
2072
- const summary = [];
2073
- const rowInserts = infos.filter((i) => i.type === "rowInsert").length;
2074
- const rowDeletes = infos.filter((i) => i.type === "rowDelete").length;
2075
- const paragraphInserts = infos.filter((i) => i.type === "paragraphInsert").length;
2076
- const paragraphDeletes = infos.filter((i) => i.type === "paragraphDelete").length;
2077
- const listItemInserts = infos.filter((i) => i.type === "listItemInsert").length;
2078
- const listItemDeletes = infos.filter((i) => i.type === "listItemDelete").length;
2079
- const imageInserts = infos.filter((i) => i.type === "imageInsert").length;
2080
- const imageDeletes = infos.filter((i) => i.type === "imageDelete").length;
2081
- if (rowInserts > 0) summary.push(`${rowInserts} row(s) inserted`);
2082
- if (rowDeletes > 0) summary.push(`${rowDeletes} row(s) deleted`);
2083
- if (paragraphInserts > 0) summary.push(`${paragraphInserts} paragraph(s) inserted`);
2084
- if (paragraphDeletes > 0) summary.push(`${paragraphDeletes} paragraph(s) deleted`);
2085
- if (listItemInserts > 0) summary.push(`${listItemInserts} list item(s) inserted`);
2086
- if (listItemDeletes > 0) summary.push(`${listItemDeletes} list item(s) deleted`);
2087
- if (imageInserts > 0) summary.push(`${imageInserts} image(s) inserted`);
2088
- if (imageDeletes > 0) summary.push(`${imageDeletes} image(s) deleted`);
2089
- return summary;
2277
+ for (const deletion of itemAlignment.deletions) {
2278
+ const idxA = deletion.path[deletion.path.length - 1];
2279
+ if (!processedDeletions.has(idxA)) {
2280
+ const changeId = v4();
2281
+ mergedItems.push(markAllTextAsDeleted(cloneNode2(deletion.node), changeId, author));
2282
+ listInfos.push({
2283
+ id: changeId,
2284
+ type: "listItemDelete",
2285
+ nodeType: "listItem",
2286
+ location: `List ${listIndex}, Item ${idxA + 1}`,
2287
+ preview: extractTextPreview(deletion.node),
2288
+ author,
2289
+ date: (/* @__PURE__ */ new Date()).toISOString()
2290
+ });
2291
+ }
2292
+ }
2293
+ const mergedList = {
2294
+ ...listB,
2295
+ content: mergedItems
2296
+ };
2297
+ return { mergedList, listInfos, changeCount };
2298
+ }
2299
+ function createInsertedBlock(node, blockIndex, author) {
2300
+ const changeId = v4();
2301
+ const markedNode = markAllTextAsInserted(cloneNode2(node), changeId, author);
2302
+ const nodeDesc = getNodeTypeDescription(node);
2303
+ const info = {
2304
+ id: changeId,
2305
+ type: isTable(node) ? "rowInsert" : isList(node) ? "listItemInsert" : isImage(node) ? "imageInsert" : "paragraphInsert",
2306
+ nodeType: node.type || "unknown",
2307
+ location: `${nodeDesc} inserted at position ${blockIndex}`,
2308
+ preview: extractTextPreview(node),
2309
+ author,
2310
+ date: (/* @__PURE__ */ new Date()).toISOString()
2311
+ };
2312
+ return { markedNode, info };
2313
+ }
2314
+ function createDeletedBlock(node, blockIndex, author) {
2315
+ const changeId = v4();
2316
+ const markedNode = markAllTextAsDeleted(cloneNode2(node), changeId, author);
2317
+ const nodeDesc = getNodeTypeDescription(node);
2318
+ const info = {
2319
+ id: changeId,
2320
+ type: isTable(node) ? "rowDelete" : isList(node) ? "listItemDelete" : isImage(node) ? "imageDelete" : "paragraphDelete",
2321
+ nodeType: node.type || "unknown",
2322
+ location: `${nodeDesc} deleted from position ${blockIndex}`,
2323
+ preview: extractTextPreview(node),
2324
+ author,
2325
+ date: (/* @__PURE__ */ new Date()).toISOString()
2326
+ };
2327
+ return { markedNode, info };
2090
2328
  }
2091
2329
  var permissionResolver = ({ permission }) => {
2092
2330
  return TRACK_CHANGE_PERMISSIONS.includes(permission) ? true : void 0;
@@ -2525,10 +2763,16 @@ var DocxDiffEditor = forwardRef(
2525
2763
  }
2526
2764
  newJson = content;
2527
2765
  }
2766
+ const structuralResult = mergeWithStructuralAwareness(
2767
+ sourceJson,
2768
+ newJson,
2769
+ author
2770
+ );
2771
+ const merged = structuralResult.mergedDoc;
2772
+ const structInfos = structuralResult.structuralInfos;
2773
+ setMergedJson(merged);
2528
2774
  const diff = diffDocuments(sourceJson, newJson);
2529
2775
  setDiffResult(diff);
2530
- const merged = mergeDocuments(sourceJson, newJson, diff, author);
2531
- setMergedJson(merged);
2532
2776
  if (superdocRef.current?.activeEditor) {
2533
2777
  setEditorContent(superdocRef.current.activeEditor, merged);
2534
2778
  enableReviewMode(superdocRef.current);
@@ -2549,19 +2793,16 @@ var DocxDiffEditor = forwardRef(
2549
2793
  }, 50);
2550
2794
  }
2551
2795
  }
2552
- const { changes: structChanges, infos: structInfos } = processStructuralChanges(
2553
- sourceJson,
2554
- newJson,
2555
- author
2556
- );
2557
2796
  setStructuralChanges(structInfos);
2558
2797
  setIsPaneDismissed(false);
2559
2798
  const insertions = diff.segments.filter((s) => s.type === "insert").length;
2560
2799
  const deletions = diff.segments.filter((s) => s.type === "delete").length;
2561
2800
  const formatChanges = diff.formatChanges?.length || 0;
2562
2801
  const structuralChangeCount = structInfos.length;
2563
- const structuralSummary = generateStructuralChangeSummary(structInfos);
2564
- const combinedSummary = [...diff.summary, ...structuralSummary];
2802
+ const combinedSummary = [...structuralResult.summary];
2803
+ if (diff.summary.length > 0 && structuralResult.summary.length === 0) {
2804
+ combinedSummary.push(...diff.summary);
2805
+ }
2565
2806
  const result = {
2566
2807
  totalChanges: insertions + deletions + formatChanges + structuralChangeCount,
2567
2808
  insertions,
@@ -2865,6 +3106,16 @@ var DocxDiffEditor = forwardRef(
2865
3106
  console.warn("[DocxDiffEditor] Failed to set properties:", err);
2866
3107
  return false;
2867
3108
  }
3109
+ },
3110
+ /**
3111
+ * Parse HTML string to ProseMirror JSON using a hidden SuperDoc instance.
3112
+ * Useful for converting HTML content before using with other methods.
3113
+ */
3114
+ async parseHtml(html) {
3115
+ if (!SuperDocRef.current) {
3116
+ throw new Error("Editor not initialized");
3117
+ }
3118
+ return parseHtmlToJson(html, SuperDocRef.current);
2868
3119
  }
2869
3120
  }),
2870
3121
  [
@@ -2945,6 +3196,254 @@ var DocxDiffEditor_default = DocxDiffEditor;
2945
3196
 
2946
3197
  // src/services/index.ts
2947
3198
  init_nodeFingerprint();
3199
+ function markAllTextAsInserted2(node, sharedId, author) {
3200
+ if (node.type === "text") {
3201
+ return {
3202
+ ...node,
3203
+ marks: [...node.marks || [], createTrackInsertMark(author, sharedId)]
3204
+ };
3205
+ }
3206
+ if (node.content && Array.isArray(node.content)) {
3207
+ return {
3208
+ ...node,
3209
+ content: node.content.map(
3210
+ (child) => markAllTextAsInserted2(child, sharedId, author)
3211
+ )
3212
+ };
3213
+ }
3214
+ return node;
3215
+ }
3216
+ function markAllTextAsDeleted2(node, sharedId, author) {
3217
+ if (node.type === "text") {
3218
+ return {
3219
+ ...node,
3220
+ marks: [...node.marks || [], createTrackDeleteMark(author, sharedId)]
3221
+ };
3222
+ }
3223
+ if (node.content && Array.isArray(node.content)) {
3224
+ return {
3225
+ ...node,
3226
+ content: node.content.map(
3227
+ (child) => markAllTextAsDeleted2(child, sharedId, author)
3228
+ )
3229
+ };
3230
+ }
3231
+ return node;
3232
+ }
3233
+ function cloneNode3(node) {
3234
+ return JSON.parse(JSON.stringify(node));
3235
+ }
3236
+ function extractTextPreview2(node, maxLength = 50) {
3237
+ const texts = [];
3238
+ function extract(n) {
3239
+ if (n.type === "text") {
3240
+ texts.push(n.text || "");
3241
+ }
3242
+ if (n.content) {
3243
+ for (const child of n.content) {
3244
+ extract(child);
3245
+ }
3246
+ }
3247
+ }
3248
+ extract(node);
3249
+ const text = texts.join("").trim();
3250
+ if (text.length > maxLength) {
3251
+ return text.substring(0, maxLength - 3) + "...";
3252
+ }
3253
+ return text || "(empty)";
3254
+ }
3255
+ function processStructuralChanges(docA, docB, author = DEFAULT_AUTHOR) {
3256
+ const changes = [];
3257
+ const infos = [];
3258
+ const alignment = alignDocuments(docA, docB);
3259
+ let tableIndex = 0;
3260
+ let listIndex = 0;
3261
+ let paragraphIndex = 0;
3262
+ for (const inserted of alignment.insertions) {
3263
+ const node = inserted.node;
3264
+ const sharedId = v4();
3265
+ const date = (/* @__PURE__ */ new Date()).toISOString();
3266
+ let type = "paragraphInsert";
3267
+ let location = "";
3268
+ let preview = "";
3269
+ if (isTable(node)) {
3270
+ type = "rowInsert";
3271
+ location = `New table at position ${inserted.path[0] + 1}`;
3272
+ preview = `Table with ${node.content?.length || 0} rows`;
3273
+ tableIndex++;
3274
+ } else if (isList(node)) {
3275
+ type = "listItemInsert";
3276
+ location = `New list at position ${inserted.path[0] + 1}`;
3277
+ preview = `List with ${node.content?.length || 0} items`;
3278
+ listIndex++;
3279
+ } else {
3280
+ type = "paragraphInsert";
3281
+ paragraphIndex++;
3282
+ location = `Paragraph ${paragraphIndex}`;
3283
+ preview = extractTextPreview2(node);
3284
+ }
3285
+ changes.push({
3286
+ id: sharedId,
3287
+ type,
3288
+ nodeType: node.type,
3289
+ path: inserted.path,
3290
+ node: markAllTextAsInserted2(cloneNode3(node), sharedId, author)
3291
+ });
3292
+ infos.push({
3293
+ id: sharedId,
3294
+ type,
3295
+ nodeType: node.type,
3296
+ location,
3297
+ preview,
3298
+ author,
3299
+ date
3300
+ });
3301
+ }
3302
+ for (const deleted of alignment.deletions) {
3303
+ const node = deleted.node;
3304
+ const sharedId = v4();
3305
+ const date = (/* @__PURE__ */ new Date()).toISOString();
3306
+ let type = "paragraphDelete";
3307
+ let location = "";
3308
+ let preview = "";
3309
+ if (isTable(node)) {
3310
+ type = "rowDelete";
3311
+ location = `Deleted table at position ${deleted.path[0] + 1}`;
3312
+ preview = `Table with ${node.content?.length || 0} rows`;
3313
+ } else if (isList(node)) {
3314
+ type = "listItemDelete";
3315
+ location = `Deleted list at position ${deleted.path[0] + 1}`;
3316
+ preview = `List with ${node.content?.length || 0} items`;
3317
+ } else {
3318
+ type = "paragraphDelete";
3319
+ location = `Deleted paragraph`;
3320
+ preview = extractTextPreview2(node);
3321
+ }
3322
+ changes.push({
3323
+ id: sharedId,
3324
+ type,
3325
+ nodeType: node.type,
3326
+ path: deleted.path,
3327
+ node: markAllTextAsDeleted2(cloneNode3(node), sharedId, author)
3328
+ });
3329
+ infos.push({
3330
+ id: sharedId,
3331
+ type,
3332
+ nodeType: node.type,
3333
+ location,
3334
+ preview,
3335
+ author,
3336
+ date
3337
+ });
3338
+ }
3339
+ for (const match of alignment.matched) {
3340
+ const nodeA = docA.content?.[match.pathA[0]];
3341
+ const nodeB = docB.content?.[match.pathB[0]];
3342
+ if (!nodeA || !nodeB) continue;
3343
+ if (isTable(nodeA) && isTable(nodeB)) {
3344
+ tableIndex++;
3345
+ const tableResult = diffTables(nodeA, nodeB, match.pathA, match.pathB);
3346
+ for (const rowChange of tableResult.rowChanges) {
3347
+ const sharedId = rowChange.id;
3348
+ const date = (/* @__PURE__ */ new Date()).toISOString();
3349
+ const rowIndex = rowChange.path[rowChange.path.length - 1];
3350
+ const isInsert = rowChange.type === "rowInsert";
3351
+ const location = getRowLocation(rowChange.path, rowIndex, tableIndex - 1);
3352
+ const preview = getRowPreview(rowChange.node);
3353
+ const markedNode = isInsert ? markAllTextAsInserted2(cloneNode3(rowChange.node), sharedId, author) : markAllTextAsDeleted2(cloneNode3(rowChange.node), sharedId, author);
3354
+ changes.push({
3355
+ ...rowChange,
3356
+ node: markedNode
3357
+ });
3358
+ infos.push({
3359
+ id: sharedId,
3360
+ type: rowChange.type,
3361
+ nodeType: "tableRow",
3362
+ location,
3363
+ preview,
3364
+ author,
3365
+ date
3366
+ });
3367
+ }
3368
+ }
3369
+ if (isList(nodeA) && isList(nodeB)) {
3370
+ listIndex++;
3371
+ const listResult = diffLists(nodeA, nodeB, match.pathA, match.pathB);
3372
+ for (const itemChange of listResult.itemChanges) {
3373
+ const sharedId = itemChange.id;
3374
+ const date = (/* @__PURE__ */ new Date()).toISOString();
3375
+ const itemIndex = itemChange.path[itemChange.path.length - 1];
3376
+ const isInsert = itemChange.type === "listItemInsert";
3377
+ const location = getListItemLocation(itemChange.path, itemIndex, listIndex - 1);
3378
+ const preview = getListItemPreview(itemChange.node);
3379
+ const markedNode = isInsert ? markAllTextAsInserted2(cloneNode3(itemChange.node), sharedId, author) : markAllTextAsDeleted2(cloneNode3(itemChange.node), sharedId, author);
3380
+ changes.push({
3381
+ ...itemChange,
3382
+ node: markedNode
3383
+ });
3384
+ infos.push({
3385
+ id: sharedId,
3386
+ type: itemChange.type,
3387
+ nodeType: "listItem",
3388
+ location,
3389
+ preview,
3390
+ author,
3391
+ date
3392
+ });
3393
+ }
3394
+ }
3395
+ }
3396
+ const imageChanges = diffImages(docA, docB);
3397
+ for (const imgInsert of imageChanges.inserted) {
3398
+ const sharedId = imgInsert.id;
3399
+ const date = (/* @__PURE__ */ new Date()).toISOString();
3400
+ infos.push({
3401
+ id: sharedId,
3402
+ type: "imageInsert",
3403
+ nodeType: "image",
3404
+ location: getImageLocation(imgInsert.path),
3405
+ preview: getImagePreview(imgInsert.node),
3406
+ author,
3407
+ date
3408
+ });
3409
+ changes.push(imgInsert);
3410
+ }
3411
+ for (const imgDelete of imageChanges.deleted) {
3412
+ const sharedId = imgDelete.id;
3413
+ const date = (/* @__PURE__ */ new Date()).toISOString();
3414
+ infos.push({
3415
+ id: sharedId,
3416
+ type: "imageDelete",
3417
+ nodeType: "image",
3418
+ location: getImageLocation(imgDelete.path),
3419
+ preview: getImagePreview(imgDelete.node),
3420
+ author,
3421
+ date
3422
+ });
3423
+ changes.push(imgDelete);
3424
+ }
3425
+ return { changes, infos };
3426
+ }
3427
+ function generateStructuralChangeSummary(infos) {
3428
+ const summary = [];
3429
+ const rowInserts = infos.filter((i) => i.type === "rowInsert").length;
3430
+ const rowDeletes = infos.filter((i) => i.type === "rowDelete").length;
3431
+ const paragraphInserts = infos.filter((i) => i.type === "paragraphInsert").length;
3432
+ const paragraphDeletes = infos.filter((i) => i.type === "paragraphDelete").length;
3433
+ const listItemInserts = infos.filter((i) => i.type === "listItemInsert").length;
3434
+ const listItemDeletes = infos.filter((i) => i.type === "listItemDelete").length;
3435
+ const imageInserts = infos.filter((i) => i.type === "imageInsert").length;
3436
+ const imageDeletes = infos.filter((i) => i.type === "imageDelete").length;
3437
+ if (rowInserts > 0) summary.push(`${rowInserts} row(s) inserted`);
3438
+ if (rowDeletes > 0) summary.push(`${rowDeletes} row(s) deleted`);
3439
+ if (paragraphInserts > 0) summary.push(`${paragraphInserts} paragraph(s) inserted`);
3440
+ if (paragraphDeletes > 0) summary.push(`${paragraphDeletes} paragraph(s) deleted`);
3441
+ if (listItemInserts > 0) summary.push(`${listItemInserts} list item(s) inserted`);
3442
+ if (listItemDeletes > 0) summary.push(`${listItemDeletes} list item(s) deleted`);
3443
+ if (imageInserts > 0) summary.push(`${imageInserts} image(s) inserted`);
3444
+ if (imageDeletes > 0) summary.push(`${imageDeletes} image(s) deleted`);
3445
+ return summary;
3446
+ }
2948
3447
 
2949
3448
  // src/blankTemplate.ts
2950
3449
  var BLANK_DOCX_BASE64 = `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`;
@@ -2982,6 +3481,6 @@ function isValidDocxFile(file) {
2982
3481
  return validTypes.includes(file.type) || file.name.endsWith(".docx");
2983
3482
  }
2984
3483
 
2985
- export { CSS_PREFIX, DEFAULT_AUTHOR, DEFAULT_SUPERDOC_USER, DocxDiffEditor, StructuralChangesPane, alignDocuments, createTrackDeleteMark, createTrackFormatMark, createTrackInsertMark, DocxDiffEditor_default as default, detectContentType, diffDocuments, diffImages, diffLists, diffTables, extractEnrichedChanges, extractEnrichedChangesWithStructural, generateFingerprint, generateStructuralChangeSummary, getBlankTemplateBlob, getBlankTemplateFile, isAtomicNode, isImage, isList, isProseMirrorJSON, isTable, isValidDocxFile, mergeDocuments, parseDocxFile, processStructuralChanges };
3484
+ export { CSS_PREFIX, DEFAULT_AUTHOR, DEFAULT_SUPERDOC_USER, DocxDiffEditor, StructuralChangesPane, alignDocuments, createTrackDeleteMark, createTrackFormatMark, createTrackInsertMark, DocxDiffEditor_default as default, detectContentType, diffDocuments, diffImages, diffLists, diffTables, extractEnrichedChanges, extractEnrichedChangesWithStructural, generateFingerprint, generateStructuralChangeSummary, getBlankTemplateBlob, getBlankTemplateFile, isAtomicNode, isImage, isList, isProseMirrorJSON, isTable, isValidDocxFile, mergeDocuments, parseDocxFile, parseHtmlToJson, processStructuralChanges };
2986
3485
  //# sourceMappingURL=index.mjs.map
2987
3486
  //# sourceMappingURL=index.mjs.map