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 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
- setEditorContent(superdocRef.current.activeEditor, normalizedMerged);
3318
- enableReviewMode(superdocRef.current);
3319
- const sd = superdocRef.current;
3320
- if (sd.commentsStore?.processLoadedDocxComments) {
3321
- setTimeout(() => {
3322
- try {
3323
- sd.commentsStore.processLoadedDocxComments({
3324
- superdoc: sd,
3325
- editor: sd.activeEditor,
3326
- comments: [],
3327
- // Empty array - we just want to trigger createCommentForTrackChanges
3328
- documentId: sd.activeEditor?.options?.documentId || "primary"
3329
- });
3330
- } catch (err) {
3331
- console.warn("[DocxDiffEditor] Failed to process track changes for bubbles:", err);
3332
- }
3333
- }, 50);
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
- setStructuralChanges(structInfos);
3337
- setIsPaneDismissed(false);
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 using a hidden SuperDoc instance.
3652
- * Useful for converting HTML content before using with other methods.
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
  }),