docx-diff-editor 1.0.57 → 1.0.59
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/README.md +13 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +126 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +126 -24
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +63 -50
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -269,6 +269,11 @@ Convert HTML strings to ProseMirror JSON without visible rendering. **Inline sty
|
|
|
269
269
|
const json = await editorRef.current?.parseHtml('<h1>Title</h1><p>Content here</p>');
|
|
270
270
|
console.log(json); // { type: 'doc', content: [...] }
|
|
271
271
|
|
|
272
|
+
// Lists work correctly - numbering definitions are synced to the main document
|
|
273
|
+
const listJson = await editorRef.current?.parseHtml(
|
|
274
|
+
'<ul><li>Item 1</li><li>Item 2</li></ul>'
|
|
275
|
+
);
|
|
276
|
+
|
|
272
277
|
// Inline styles are converted to marks
|
|
273
278
|
const styledJson = await editorRef.current?.parseHtml(
|
|
274
279
|
'<p><span style="color: red; font-weight: bold;">styled text</span></p>'
|
|
@@ -283,6 +288,14 @@ import { parseHtmlToJson } from 'docx-diff-editor';
|
|
|
283
288
|
const json = await parseHtmlToJson(htmlString, SuperDocClass);
|
|
284
289
|
```
|
|
285
290
|
|
|
291
|
+
### Linked Parsing for Lists
|
|
292
|
+
|
|
293
|
+
When the main editor is ready, `parseHtml()` automatically uses a **linked child editor** approach. This ensures that list numbering definitions (for `<ol>` and `<ul>` elements) are synced to the main document's numbering store.
|
|
294
|
+
|
|
295
|
+
This prevents crashes when parsed content with lists is later spliced into the main document and rendered via `compareWith()`.
|
|
296
|
+
|
|
297
|
+
If the main editor isn't ready yet, the method falls back to an isolated SuperDoc instance.
|
|
298
|
+
|
|
286
299
|
### Supported Inline Styles
|
|
287
300
|
|
|
288
301
|
| CSS Property | ProseMirror Mark |
|
package/dist/index.d.mts
CHANGED
|
@@ -189,6 +189,13 @@ interface ComparisonResult {
|
|
|
189
189
|
mergedJson: ProseMirrorJSON;
|
|
190
190
|
/** Metadata for structural changes (for the pane) */
|
|
191
191
|
structuralChangeInfos: StructuralChangeInfo[];
|
|
192
|
+
/**
|
|
193
|
+
* True if comparison fell back to direct content update (without track bubbles).
|
|
194
|
+
* This happens when SuperDoc's internal plugins crash on certain content structures
|
|
195
|
+
* (e.g., ordered lists with missing numbering definitions).
|
|
196
|
+
* The content is still applied correctly, but track change visualization is unavailable.
|
|
197
|
+
*/
|
|
198
|
+
usedFallback?: boolean;
|
|
192
199
|
}
|
|
193
200
|
/**
|
|
194
201
|
* Location context for a change
|
package/dist/index.d.ts
CHANGED
|
@@ -189,6 +189,13 @@ interface ComparisonResult {
|
|
|
189
189
|
mergedJson: ProseMirrorJSON;
|
|
190
190
|
/** Metadata for structural changes (for the pane) */
|
|
191
191
|
structuralChangeInfos: StructuralChangeInfo[];
|
|
192
|
+
/**
|
|
193
|
+
* True if comparison fell back to direct content update (without track bubbles).
|
|
194
|
+
* This happens when SuperDoc's internal plugins crash on certain content structures
|
|
195
|
+
* (e.g., ordered lists with missing numbering definitions).
|
|
196
|
+
* The content is still applied correctly, but track change visualization is unavailable.
|
|
197
|
+
*/
|
|
198
|
+
usedFallback?: boolean;
|
|
192
199
|
}
|
|
193
200
|
/**
|
|
194
201
|
* Location context for a change
|
package/dist/index.js
CHANGED
|
@@ -970,6 +970,67 @@ async function parseHtmlToJson(html, SuperDoc) {
|
|
|
970
970
|
}, 50);
|
|
971
971
|
});
|
|
972
972
|
}
|
|
973
|
+
async function parseHtmlWithLinkedEditor(html, mainEditor) {
|
|
974
|
+
const container = document.createElement("div");
|
|
975
|
+
container.style.cssText = "position:absolute;top:-9999px;left:-9999px;width:800px;height:600px;visibility:hidden;";
|
|
976
|
+
document.body.appendChild(container);
|
|
977
|
+
return new Promise((resolve, reject) => {
|
|
978
|
+
let resolved = false;
|
|
979
|
+
let childEditor = null;
|
|
980
|
+
const cleanup = () => {
|
|
981
|
+
setTimeout(() => {
|
|
982
|
+
if (childEditor) {
|
|
983
|
+
try {
|
|
984
|
+
childEditor.destroy?.();
|
|
985
|
+
} catch {
|
|
986
|
+
}
|
|
987
|
+
childEditor = null;
|
|
988
|
+
}
|
|
989
|
+
if (container.parentNode) {
|
|
990
|
+
container.parentNode.removeChild(container);
|
|
991
|
+
}
|
|
992
|
+
}, TIMEOUTS.CLEANUP_DELAY);
|
|
993
|
+
};
|
|
994
|
+
try {
|
|
995
|
+
mainEditor.createChildEditor({
|
|
996
|
+
element: container,
|
|
997
|
+
html,
|
|
998
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
999
|
+
onCreate: ({ editor: localEditor }) => {
|
|
1000
|
+
if (resolved) return;
|
|
1001
|
+
try {
|
|
1002
|
+
childEditor = localEditor;
|
|
1003
|
+
const json = localEditor.getJSON();
|
|
1004
|
+
const normalizedJson = normalizeRunProperties(json);
|
|
1005
|
+
resolved = true;
|
|
1006
|
+
cleanup();
|
|
1007
|
+
resolve(normalizedJson);
|
|
1008
|
+
} catch (err) {
|
|
1009
|
+
resolved = true;
|
|
1010
|
+
cleanup();
|
|
1011
|
+
reject(err);
|
|
1012
|
+
}
|
|
1013
|
+
},
|
|
1014
|
+
onError: (error) => {
|
|
1015
|
+
if (resolved) return;
|
|
1016
|
+
resolved = true;
|
|
1017
|
+
cleanup();
|
|
1018
|
+
reject(error);
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1021
|
+
setTimeout(() => {
|
|
1022
|
+
if (!resolved) {
|
|
1023
|
+
resolved = true;
|
|
1024
|
+
cleanup();
|
|
1025
|
+
reject(new Error("Linked HTML parsing timed out"));
|
|
1026
|
+
}
|
|
1027
|
+
}, TIMEOUTS.PARSE_TIMEOUT);
|
|
1028
|
+
} catch (err) {
|
|
1029
|
+
cleanup();
|
|
1030
|
+
reject(err);
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
973
1034
|
async function parseDocxFile(file, SuperDoc) {
|
|
974
1035
|
const container = document.createElement("div");
|
|
975
1036
|
container.style.cssText = "position:absolute;top:-9999px;left:-9999px;width:800px;height:600px;visibility:hidden;";
|
|
@@ -3313,36 +3374,59 @@ var DocxDiffEditor = react.forwardRef(
|
|
|
3313
3374
|
setMergedJson(normalizedMerged);
|
|
3314
3375
|
const diff = diffDocuments(cleanBaseline, cleanNewJson);
|
|
3315
3376
|
setDiffResult(diff);
|
|
3377
|
+
let usedFallback = false;
|
|
3316
3378
|
if (superdocRef.current?.activeEditor) {
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3379
|
+
try {
|
|
3380
|
+
setEditorContent(superdocRef.current.activeEditor, normalizedMerged);
|
|
3381
|
+
enableReviewMode(superdocRef.current);
|
|
3382
|
+
const sd = superdocRef.current;
|
|
3383
|
+
if (sd.commentsStore?.processLoadedDocxComments) {
|
|
3384
|
+
setTimeout(() => {
|
|
3385
|
+
try {
|
|
3386
|
+
sd.commentsStore.processLoadedDocxComments({
|
|
3387
|
+
superdoc: sd,
|
|
3388
|
+
editor: sd.activeEditor,
|
|
3389
|
+
comments: [],
|
|
3390
|
+
// Empty array - we just want to trigger createCommentForTrackChanges
|
|
3391
|
+
documentId: sd.activeEditor?.options?.documentId || "primary"
|
|
3392
|
+
});
|
|
3393
|
+
} catch (err) {
|
|
3394
|
+
console.warn("[DocxDiffEditor] Failed to process track changes for bubbles:", err);
|
|
3395
|
+
}
|
|
3396
|
+
}, 50);
|
|
3397
|
+
}
|
|
3398
|
+
} catch (contentErr) {
|
|
3399
|
+
console.warn(
|
|
3400
|
+
"[DocxDiffEditor] Failed to apply merged content with track changes. Falling back to direct content update without track bubbles.",
|
|
3401
|
+
contentErr
|
|
3402
|
+
);
|
|
3403
|
+
usedFallback = true;
|
|
3404
|
+
try {
|
|
3405
|
+
setEditorContent(superdocRef.current.activeEditor, normalizedNewJson);
|
|
3406
|
+
setEditingMode(superdocRef.current);
|
|
3407
|
+
} catch (fallbackErr) {
|
|
3408
|
+
console.error("[DocxDiffEditor] Fallback content update also failed:", fallbackErr);
|
|
3409
|
+
throw contentErr;
|
|
3410
|
+
}
|
|
3334
3411
|
}
|
|
3335
3412
|
}
|
|
3336
|
-
|
|
3337
|
-
|
|
3413
|
+
if (!usedFallback) {
|
|
3414
|
+
setStructuralChanges(structInfos);
|
|
3415
|
+
setIsPaneDismissed(false);
|
|
3416
|
+
} else {
|
|
3417
|
+
setStructuralChanges([]);
|
|
3418
|
+
}
|
|
3338
3419
|
const insertions = diff.segments.filter((s) => s.type === "insert").length;
|
|
3339
3420
|
const deletions = diff.segments.filter((s) => s.type === "delete").length;
|
|
3340
3421
|
const formatChanges = diff.formatChanges?.length || 0;
|
|
3341
|
-
const structuralChangeCount = structInfos.length;
|
|
3422
|
+
const structuralChangeCount = usedFallback ? 0 : structInfos.length;
|
|
3342
3423
|
const combinedSummary = [...structuralResult.summary];
|
|
3343
3424
|
if (diff.summary.length > 0 && structuralResult.summary.length === 0) {
|
|
3344
3425
|
combinedSummary.push(...diff.summary);
|
|
3345
3426
|
}
|
|
3427
|
+
if (usedFallback) {
|
|
3428
|
+
combinedSummary.push("Note: Track change visualization unavailable for this content");
|
|
3429
|
+
}
|
|
3346
3430
|
const result = {
|
|
3347
3431
|
totalChanges: insertions + deletions + formatChanges + structuralChangeCount,
|
|
3348
3432
|
insertions,
|
|
@@ -3350,8 +3434,9 @@ var DocxDiffEditor = react.forwardRef(
|
|
|
3350
3434
|
formatChanges,
|
|
3351
3435
|
structuralChanges: structuralChangeCount,
|
|
3352
3436
|
summary: combinedSummary,
|
|
3353
|
-
mergedJson: merged,
|
|
3354
|
-
structuralChangeInfos: structInfos
|
|
3437
|
+
mergedJson: usedFallback ? normalizedNewJson : merged,
|
|
3438
|
+
structuralChangeInfos: usedFallback ? [] : structInfos,
|
|
3439
|
+
usedFallback
|
|
3355
3440
|
};
|
|
3356
3441
|
onComparisonComplete?.(result);
|
|
3357
3442
|
return result;
|
|
@@ -3648,13 +3733,30 @@ var DocxDiffEditor = react.forwardRef(
|
|
|
3648
3733
|
}
|
|
3649
3734
|
},
|
|
3650
3735
|
/**
|
|
3651
|
-
* Parse HTML string to ProseMirror JSON
|
|
3652
|
-
*
|
|
3736
|
+
* Parse HTML string to ProseMirror JSON.
|
|
3737
|
+
*
|
|
3738
|
+
* When the main editor is ready, this uses a linked child editor approach
|
|
3739
|
+
* which ensures list numbering definitions are synced to the main document.
|
|
3740
|
+
* This prevents crashes when parsed content with lists is spliced into
|
|
3741
|
+
* the main document via compareWith().
|
|
3742
|
+
*
|
|
3743
|
+
* Falls back to an isolated SuperDoc instance if the main editor isn't ready.
|
|
3653
3744
|
*/
|
|
3654
3745
|
async parseHtml(html) {
|
|
3655
3746
|
if (!SuperDocRef.current) {
|
|
3656
3747
|
throw new Error("Editor not initialized");
|
|
3657
3748
|
}
|
|
3749
|
+
const mainEditor = superdocRef.current?.activeEditor;
|
|
3750
|
+
if (mainEditor?.createChildEditor) {
|
|
3751
|
+
try {
|
|
3752
|
+
return await parseHtmlWithLinkedEditor(html, mainEditor);
|
|
3753
|
+
} catch (err) {
|
|
3754
|
+
console.warn(
|
|
3755
|
+
"[DocxDiffEditor] Linked HTML parsing failed, falling back to isolated approach:",
|
|
3756
|
+
err
|
|
3757
|
+
);
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3658
3760
|
return parseHtmlToJson(html, SuperDocRef.current);
|
|
3659
3761
|
}
|
|
3660
3762
|
}),
|