docx-diff-editor 1.0.58 → 1.0.60

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.js CHANGED
@@ -970,6 +970,92 @@ async function parseHtmlToJson(html, SuperDoc) {
970
970
  }, 50);
971
971
  });
972
972
  }
973
+ function syncNumberingToParent(childEditor, parentEditor) {
974
+ try {
975
+ const childNumbering = childEditor?.converter?.numbering;
976
+ const parentNumbering = parentEditor?.converter?.numbering;
977
+ if (!childNumbering || !parentNumbering) {
978
+ return;
979
+ }
980
+ if (childNumbering.definitions) {
981
+ parentNumbering.definitions = {
982
+ ...parentNumbering.definitions,
983
+ ...childNumbering.definitions
984
+ };
985
+ }
986
+ if (childNumbering.abstracts) {
987
+ parentNumbering.abstracts = {
988
+ ...parentNumbering.abstracts,
989
+ ...childNumbering.abstracts
990
+ };
991
+ }
992
+ parentEditor.converter.numbering = parentNumbering;
993
+ } catch (err) {
994
+ console.warn("[syncNumberingToParent] Failed to sync numbering definitions:", err);
995
+ }
996
+ }
997
+ async function parseHtmlWithLinkedEditor(html, mainEditor) {
998
+ const container = document.createElement("div");
999
+ container.style.cssText = "position:absolute;top:-9999px;left:-9999px;width:800px;height:600px;visibility:hidden;";
1000
+ document.body.appendChild(container);
1001
+ return new Promise((resolve, reject) => {
1002
+ let resolved = false;
1003
+ let childEditor = null;
1004
+ const cleanup = () => {
1005
+ setTimeout(() => {
1006
+ if (childEditor) {
1007
+ try {
1008
+ childEditor.destroy?.();
1009
+ } catch {
1010
+ }
1011
+ childEditor = null;
1012
+ }
1013
+ if (container.parentNode) {
1014
+ container.parentNode.removeChild(container);
1015
+ }
1016
+ }, TIMEOUTS.CLEANUP_DELAY);
1017
+ };
1018
+ try {
1019
+ mainEditor.createChildEditor({
1020
+ element: container,
1021
+ html,
1022
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1023
+ onCreate: ({ editor: localEditor }) => {
1024
+ if (resolved) return;
1025
+ try {
1026
+ childEditor = localEditor;
1027
+ syncNumberingToParent(localEditor, mainEditor);
1028
+ const json = localEditor.getJSON();
1029
+ const normalizedJson = normalizeRunProperties(json);
1030
+ resolved = true;
1031
+ cleanup();
1032
+ resolve(normalizedJson);
1033
+ } catch (err) {
1034
+ resolved = true;
1035
+ cleanup();
1036
+ reject(err);
1037
+ }
1038
+ },
1039
+ onError: (error) => {
1040
+ if (resolved) return;
1041
+ resolved = true;
1042
+ cleanup();
1043
+ reject(error);
1044
+ }
1045
+ });
1046
+ setTimeout(() => {
1047
+ if (!resolved) {
1048
+ resolved = true;
1049
+ cleanup();
1050
+ reject(new Error("Linked HTML parsing timed out"));
1051
+ }
1052
+ }, TIMEOUTS.PARSE_TIMEOUT);
1053
+ } catch (err) {
1054
+ cleanup();
1055
+ reject(err);
1056
+ }
1057
+ });
1058
+ }
973
1059
  async function parseDocxFile(file, SuperDoc) {
974
1060
  const container = document.createElement("div");
975
1061
  container.style.cssText = "position:absolute;top:-9999px;left:-9999px;width:800px;height:600px;visibility:hidden;";
@@ -3672,13 +3758,30 @@ var DocxDiffEditor = react.forwardRef(
3672
3758
  }
3673
3759
  },
3674
3760
  /**
3675
- * Parse HTML string to ProseMirror JSON using a hidden SuperDoc instance.
3676
- * Useful for converting HTML content before using with other methods.
3761
+ * Parse HTML string to ProseMirror JSON.
3762
+ *
3763
+ * When the main editor is ready, this uses a linked child editor approach
3764
+ * which ensures list numbering definitions are synced to the main document.
3765
+ * This prevents crashes when parsed content with lists is spliced into
3766
+ * the main document via compareWith().
3767
+ *
3768
+ * Falls back to an isolated SuperDoc instance if the main editor isn't ready.
3677
3769
  */
3678
3770
  async parseHtml(html) {
3679
3771
  if (!SuperDocRef.current) {
3680
3772
  throw new Error("Editor not initialized");
3681
3773
  }
3774
+ const mainEditor = superdocRef.current?.activeEditor;
3775
+ if (mainEditor?.createChildEditor) {
3776
+ try {
3777
+ return await parseHtmlWithLinkedEditor(html, mainEditor);
3778
+ } catch (err) {
3779
+ console.warn(
3780
+ "[DocxDiffEditor] Linked HTML parsing failed, falling back to isolated approach:",
3781
+ err
3782
+ );
3783
+ }
3784
+ }
3682
3785
  return parseHtmlToJson(html, SuperDocRef.current);
3683
3786
  }
3684
3787
  }),