docx-diff-editor 1.0.43 → 1.0.45
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 +20 -1
- package/dist/index.d.mts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +110 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +110 -20
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -257,13 +257,19 @@ await editorRef.current?.setProperties({
|
|
|
257
257
|
|
|
258
258
|
## Parsing HTML to JSON
|
|
259
259
|
|
|
260
|
-
Convert HTML strings to ProseMirror JSON without visible rendering
|
|
260
|
+
Convert HTML strings to ProseMirror JSON without visible rendering. **Inline styles are preserved!**
|
|
261
261
|
|
|
262
262
|
```tsx
|
|
263
263
|
// Using the ref method (requires editor to be initialized)
|
|
264
264
|
const json = await editorRef.current?.parseHtml('<h1>Title</h1><p>Content here</p>');
|
|
265
265
|
console.log(json); // { type: 'doc', content: [...] }
|
|
266
266
|
|
|
267
|
+
// Inline styles are converted to marks
|
|
268
|
+
const styledJson = await editorRef.current?.parseHtml(
|
|
269
|
+
'<p><span style="color: red; font-weight: bold;">styled text</span></p>'
|
|
270
|
+
);
|
|
271
|
+
// Result: text with textStyle mark (color) and bold mark
|
|
272
|
+
|
|
267
273
|
// Use with other methods
|
|
268
274
|
await editorRef.current?.updateContent(json);
|
|
269
275
|
|
|
@@ -272,6 +278,19 @@ import { parseHtmlToJson } from 'docx-diff-editor';
|
|
|
272
278
|
const json = await parseHtmlToJson(htmlString, SuperDocClass);
|
|
273
279
|
```
|
|
274
280
|
|
|
281
|
+
### Supported Inline Styles
|
|
282
|
+
|
|
283
|
+
| CSS Property | ProseMirror Mark |
|
|
284
|
+
|--------------|------------------|
|
|
285
|
+
| `color` | `textStyle.color` |
|
|
286
|
+
| `font-size` | `textStyle.fontSize` |
|
|
287
|
+
| `font-family` | `textStyle.fontFamily` |
|
|
288
|
+
| `font-weight: bold` | `bold` |
|
|
289
|
+
| `font-style: italic` | `italic` |
|
|
290
|
+
| `text-decoration: underline` | `underline` |
|
|
291
|
+
| `text-decoration: line-through` | `strike` |
|
|
292
|
+
| `background-color` | `highlight.color` |
|
|
293
|
+
|
|
275
294
|
## Customization
|
|
276
295
|
|
|
277
296
|
### CSS Variables
|
package/dist/index.d.mts
CHANGED
|
@@ -424,6 +424,20 @@ declare function detectContentType(content: DocxContent): 'file' | 'html' | 'jso
|
|
|
424
424
|
declare function isProseMirrorJSON(content: unknown): boolean;
|
|
425
425
|
/**
|
|
426
426
|
* Parse an HTML string into ProseMirror JSON using a hidden SuperDoc instance.
|
|
427
|
+
*
|
|
428
|
+
* IMPORTANT: Uses the "paste" approach instead of the "import" approach.
|
|
429
|
+
* SuperDoc's import path (via `html` option) calls `stripHtmlStyles()` which
|
|
430
|
+
* removes all CSS styles except `text-align`. The paste path (via `view.pasteHTML()`)
|
|
431
|
+
* preserves inline styles like color, font-size, font-family, font-weight, etc.
|
|
432
|
+
*
|
|
433
|
+
* Flow:
|
|
434
|
+
* 1. Create SuperDoc with empty HTML document
|
|
435
|
+
* 2. Wait for editor to be ready
|
|
436
|
+
* 3. Select all content and delete it (start fresh)
|
|
437
|
+
* 4. Use editor.view.pasteHTML(html) - this uses the paste path which preserves styles
|
|
438
|
+
* 5. Return the resulting JSON
|
|
439
|
+
*
|
|
440
|
+
* Falls back to the standard import approach if paste fails.
|
|
427
441
|
*/
|
|
428
442
|
declare function parseHtmlToJson(html: string, SuperDoc: SuperDocConstructor): Promise<ProseMirrorJSON>;
|
|
429
443
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -424,6 +424,20 @@ declare function detectContentType(content: DocxContent): 'file' | 'html' | 'jso
|
|
|
424
424
|
declare function isProseMirrorJSON(content: unknown): boolean;
|
|
425
425
|
/**
|
|
426
426
|
* Parse an HTML string into ProseMirror JSON using a hidden SuperDoc instance.
|
|
427
|
+
*
|
|
428
|
+
* IMPORTANT: Uses the "paste" approach instead of the "import" approach.
|
|
429
|
+
* SuperDoc's import path (via `html` option) calls `stripHtmlStyles()` which
|
|
430
|
+
* removes all CSS styles except `text-align`. The paste path (via `view.pasteHTML()`)
|
|
431
|
+
* preserves inline styles like color, font-size, font-family, font-weight, etc.
|
|
432
|
+
*
|
|
433
|
+
* Flow:
|
|
434
|
+
* 1. Create SuperDoc with empty HTML document
|
|
435
|
+
* 2. Wait for editor to be ready
|
|
436
|
+
* 3. Select all content and delete it (start fresh)
|
|
437
|
+
* 4. Use editor.view.pasteHTML(html) - this uses the paste path which preserves styles
|
|
438
|
+
* 5. Return the resulting JSON
|
|
439
|
+
*
|
|
440
|
+
* Falls back to the standard import approach if paste fails.
|
|
427
441
|
*/
|
|
428
442
|
declare function parseHtmlToJson(html: string, SuperDoc: SuperDocConstructor): Promise<ProseMirrorJSON>;
|
|
429
443
|
/**
|
package/dist/index.js
CHANGED
|
@@ -490,38 +490,124 @@ async function parseHtmlToJson(html, SuperDoc) {
|
|
|
490
490
|
}
|
|
491
491
|
}, TIMEOUTS.CLEANUP_DELAY);
|
|
492
492
|
};
|
|
493
|
+
const createMockPasteEvent = (htmlContent) => {
|
|
494
|
+
const dataTransfer = new DataTransfer();
|
|
495
|
+
dataTransfer.setData("text/html", htmlContent);
|
|
496
|
+
dataTransfer.setData("text/plain", "");
|
|
497
|
+
const event = new ClipboardEvent("paste", {
|
|
498
|
+
bubbles: true,
|
|
499
|
+
cancelable: true,
|
|
500
|
+
clipboardData: dataTransfer
|
|
501
|
+
});
|
|
502
|
+
return event;
|
|
503
|
+
};
|
|
504
|
+
const tryPasteApproach = (sd, onSuccess, onFail) => {
|
|
505
|
+
try {
|
|
506
|
+
const editor = sd?.activeEditor;
|
|
507
|
+
if (!editor?.view?.pasteHTML) {
|
|
508
|
+
onFail();
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
editor.commands.focus?.();
|
|
512
|
+
if (editor.commands.selectAll && editor.commands.deleteSelection) {
|
|
513
|
+
editor.commands.selectAll();
|
|
514
|
+
editor.commands.deleteSelection();
|
|
515
|
+
}
|
|
516
|
+
const mockEvent = createMockPasteEvent(html);
|
|
517
|
+
editor.view.pasteHTML(html, mockEvent);
|
|
518
|
+
setTimeout(() => {
|
|
519
|
+
try {
|
|
520
|
+
const json = editor.getJSON();
|
|
521
|
+
if (json?.content?.length > 0) {
|
|
522
|
+
onSuccess(json);
|
|
523
|
+
} else {
|
|
524
|
+
onFail();
|
|
525
|
+
}
|
|
526
|
+
} catch {
|
|
527
|
+
onFail();
|
|
528
|
+
}
|
|
529
|
+
}, 100);
|
|
530
|
+
} catch (err) {
|
|
531
|
+
console.warn("[parseHtmlToJson] Paste approach error:", err);
|
|
532
|
+
onFail();
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
const fallbackToImport = () => {
|
|
536
|
+
if (superdoc) {
|
|
537
|
+
try {
|
|
538
|
+
superdoc.destroy?.();
|
|
539
|
+
} catch {
|
|
540
|
+
}
|
|
541
|
+
superdoc = null;
|
|
542
|
+
}
|
|
543
|
+
superdoc = new SuperDoc({
|
|
544
|
+
selector: container,
|
|
545
|
+
html,
|
|
546
|
+
// Use the actual HTML content
|
|
547
|
+
documentMode: "viewing",
|
|
548
|
+
rulers: false,
|
|
549
|
+
user: { name: "Parser", email: "parser@local" },
|
|
550
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
551
|
+
onReady: ({ superdoc: sd }) => {
|
|
552
|
+
if (resolved) return;
|
|
553
|
+
try {
|
|
554
|
+
const editor = sd?.activeEditor;
|
|
555
|
+
if (!editor) {
|
|
556
|
+
throw new Error("No active editor found");
|
|
557
|
+
}
|
|
558
|
+
const json = editor.getJSON();
|
|
559
|
+
resolved = true;
|
|
560
|
+
cleanup();
|
|
561
|
+
resolve(json);
|
|
562
|
+
} catch (err) {
|
|
563
|
+
resolved = true;
|
|
564
|
+
cleanup();
|
|
565
|
+
reject(err);
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
onException: ({ error: err }) => {
|
|
569
|
+
if (resolved) return;
|
|
570
|
+
resolved = true;
|
|
571
|
+
cleanup();
|
|
572
|
+
reject(err);
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
};
|
|
493
576
|
setTimeout(async () => {
|
|
494
577
|
if (resolved) return;
|
|
495
578
|
try {
|
|
496
579
|
superdoc = new SuperDoc({
|
|
497
580
|
selector: container,
|
|
498
|
-
html,
|
|
499
|
-
|
|
581
|
+
html: "<p></p>",
|
|
582
|
+
// Minimal empty document
|
|
583
|
+
documentMode: "editing",
|
|
584
|
+
// Need editing mode to use paste
|
|
500
585
|
rulers: false,
|
|
501
586
|
user: { name: "Parser", email: "parser@local" },
|
|
502
587
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
503
588
|
onReady: ({ superdoc: sd }) => {
|
|
504
589
|
if (resolved) return;
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
590
|
+
tryPasteApproach(
|
|
591
|
+
sd,
|
|
592
|
+
// Success callback
|
|
593
|
+
(json) => {
|
|
594
|
+
if (resolved) return;
|
|
595
|
+
resolved = true;
|
|
596
|
+
cleanup();
|
|
597
|
+
resolve(json);
|
|
598
|
+
},
|
|
599
|
+
// Fail callback - try fallback
|
|
600
|
+
() => {
|
|
601
|
+
if (resolved) return;
|
|
602
|
+
console.warn("[parseHtmlToJson] Paste approach failed, falling back to import");
|
|
603
|
+
fallbackToImport();
|
|
509
604
|
}
|
|
510
|
-
|
|
511
|
-
resolved = true;
|
|
512
|
-
cleanup();
|
|
513
|
-
resolve(json);
|
|
514
|
-
} catch (err) {
|
|
515
|
-
resolved = true;
|
|
516
|
-
cleanup();
|
|
517
|
-
reject(err);
|
|
518
|
-
}
|
|
605
|
+
);
|
|
519
606
|
},
|
|
520
607
|
onException: ({ error: err }) => {
|
|
521
608
|
if (resolved) return;
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
reject(err);
|
|
609
|
+
console.warn("[parseHtmlToJson] Paste approach exception, falling back:", err);
|
|
610
|
+
fallbackToImport();
|
|
525
611
|
}
|
|
526
612
|
});
|
|
527
613
|
setTimeout(() => {
|
|
@@ -532,8 +618,12 @@ async function parseHtmlToJson(html, SuperDoc) {
|
|
|
532
618
|
}
|
|
533
619
|
}, TIMEOUTS.PARSE_TIMEOUT);
|
|
534
620
|
} catch (err) {
|
|
535
|
-
|
|
536
|
-
|
|
621
|
+
try {
|
|
622
|
+
fallbackToImport();
|
|
623
|
+
} catch (fallbackErr) {
|
|
624
|
+
cleanup();
|
|
625
|
+
reject(fallbackErr);
|
|
626
|
+
}
|
|
537
627
|
}
|
|
538
628
|
}, 50);
|
|
539
629
|
});
|