@alpaca-editor/core 1.0.4045 → 1.0.4048

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.
Files changed (41) hide show
  1. package/dist/editor/field-types/RichTextEditorComponent.js +3 -10
  2. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  3. package/dist/editor/field-types/richtext/components/ReactSlate.js +300 -342
  4. package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
  5. package/dist/editor/field-types/richtext/components/SimpleRichTextEditor.js.map +1 -1
  6. package/dist/editor/field-types/richtext/components/SimpleToolbar.js +9 -9
  7. package/dist/editor/field-types/richtext/components/SimpleToolbar.js.map +1 -1
  8. package/dist/editor/field-types/richtext/config/pluginFactory.d.ts +7 -6
  9. package/dist/editor/field-types/richtext/config/pluginFactory.js +2 -1
  10. package/dist/editor/field-types/richtext/config/pluginFactory.js.map +1 -1
  11. package/dist/editor/field-types/richtext/hooks/useProfileCache.js +24 -18
  12. package/dist/editor/field-types/richtext/hooks/useProfileCache.js.map +1 -1
  13. package/dist/editor/field-types/richtext/hooks/useRichTextProfile.js +1 -1
  14. package/dist/editor/field-types/richtext/hooks/useRichTextProfile.js.map +1 -1
  15. package/dist/editor/field-types/richtext/types.d.ts +236 -90
  16. package/dist/editor/field-types/richtext/types.js +3 -3
  17. package/dist/editor/field-types/richtext/types.js.map +1 -1
  18. package/dist/editor/field-types/richtext/utils/conversion.d.ts +4 -2
  19. package/dist/editor/field-types/richtext/utils/conversion.js +79 -12
  20. package/dist/editor/field-types/richtext/utils/conversion.js.map +1 -1
  21. package/dist/editor/field-types/richtext/utils/plugins.d.ts +66 -39
  22. package/dist/editor/field-types/richtext/utils/plugins.js +377 -233
  23. package/dist/editor/field-types/richtext/utils/plugins.js.map +1 -1
  24. package/dist/editor/field-types/richtext/utils/profileMapper.js +22 -2
  25. package/dist/editor/field-types/richtext/utils/profileMapper.js.map +1 -1
  26. package/dist/revision.d.ts +2 -2
  27. package/dist/revision.js +2 -2
  28. package/package.json +1 -1
  29. package/src/editor/field-types/RichTextEditorComponent.tsx +4 -10
  30. package/src/editor/field-types/richtext/components/ReactSlate.css +85 -24
  31. package/src/editor/field-types/richtext/components/ReactSlate.tsx +375 -428
  32. package/src/editor/field-types/richtext/components/SimpleRichTextEditor.tsx +4 -2
  33. package/src/editor/field-types/richtext/components/SimpleToolbar.tsx +3 -3
  34. package/src/editor/field-types/richtext/config/pluginFactory.tsx +2 -1
  35. package/src/editor/field-types/richtext/hooks/useProfileCache.ts +25 -19
  36. package/src/editor/field-types/richtext/hooks/useRichTextProfile.ts +1 -1
  37. package/src/editor/field-types/richtext/types.ts +150 -112
  38. package/src/editor/field-types/richtext/utils/conversion.ts +100 -27
  39. package/src/editor/field-types/richtext/utils/plugins.ts +469 -268
  40. package/src/editor/field-types/richtext/utils/profileMapper.ts +26 -3
  41. package/src/revision.ts +2 -2
@@ -1,5 +1,17 @@
1
1
  import { Descendant, Element as SlateElement } from 'slate';
2
- import { CustomText, Alignment, CustomElement, LinkElement, SimplifiedProfile, SLATE_MARKS, SLATE_BLOCKS } from '../types';
2
+ import {
3
+ CustomText,
4
+ Alignment,
5
+ CustomElement,
6
+ LinkElement,
7
+ SimplifiedProfile,
8
+ SLATE_MARKS,
9
+ SLATE_BLOCKS,
10
+ MarkId,
11
+ BlockId,
12
+ HtmlToSlateResult,
13
+ SlateToHtmlResult
14
+ } from '../types';
3
15
 
4
16
  // Elements that should be completely preserved as raw HTML
5
17
  const PRESERVE_AS_RAW_ELEMENTS = new Set([
@@ -10,8 +22,8 @@ const PRESERVE_AS_RAW_ELEMENTS = new Set([
10
22
  'script', 'style', 'noscript'
11
23
  ]);
12
24
 
13
- // Shared utility: Get default block type from profile
14
- const getDefaultBlockType = (blocks: string[]): string => {
25
+ // Shared utility: Get default block type from profile with strict typing
26
+ const getDefaultBlockType = (blocks: readonly BlockId[]): BlockId => {
15
27
  return blocks.length > 0 && blocks[0] ? blocks[0] : 'paragraph';
16
28
  };
17
29
 
@@ -43,16 +55,16 @@ const shouldPreserveAsRaw = (element: HTMLElement): boolean => {
43
55
  // Check for complex styling that would be hard to preserve
44
56
  const style = element.getAttribute('style') || '';
45
57
  const hasComplexStyling = style.includes('background-color') ||
46
- style.includes('font-family') ||
47
- style.includes('font-size') ||
48
- style.includes('color:');
58
+ style.includes('font-family') ||
59
+ style.includes('font-size') ||
60
+ style.includes('color:');
49
61
 
50
62
  return hasComplexStyling && element.children.length > 0;
51
63
  };
52
64
 
53
- // Shared utility: Process marks from element
54
- const getMarksFromElement = (element: HTMLElement, configuredMarks: string[]): Record<string, boolean> => {
55
- const marks: Record<string, boolean> = {};
65
+ // Shared utility: Process marks from element with strict typing
66
+ const getMarksFromElement = (element: HTMLElement, configuredMarks: readonly MarkId[]): Record<MarkId, boolean> => {
67
+ const marks: Partial<Record<MarkId, boolean>> = {};
56
68
 
57
69
  // Check for HTML tag-based marks
58
70
  const tagName = element.tagName.toLowerCase();
@@ -69,23 +81,50 @@ const getMarksFromElement = (element: HTMLElement, configuredMarks: string[]): R
69
81
  const style = element.getAttribute('style');
70
82
  if (style) {
71
83
  if (style.includes('font-weight: bold') || style.includes('font-weight:bold')) {
72
- marks['bold'] = true;
84
+ if (configuredMarks.includes('bold')) marks['bold'] = true;
73
85
  }
74
86
  if (style.includes('font-style: italic') || style.includes('font-style:italic')) {
75
- marks['italic'] = true;
87
+ if (configuredMarks.includes('italic')) marks['italic'] = true;
76
88
  }
77
89
  if (style.includes('text-decoration: underline') || style.includes('text-decoration:underline')) {
78
- marks['underline'] = true;
90
+ if (configuredMarks.includes('underline')) marks['underline'] = true;
79
91
  }
80
92
  if (style.includes('text-decoration: line-through') || style.includes('text-decoration:line-through')) {
81
- marks['strikethrough'] = true;
93
+ if (configuredMarks.includes('strikethrough')) marks['strikethrough'] = true;
82
94
  }
83
95
  }
84
96
 
85
- return marks;
97
+ return marks as Record<MarkId, boolean>;
86
98
  };
87
99
 
100
+ // Enhanced htmlToSlate with better error handling and strict typing
88
101
  export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendant[] => {
102
+ try {
103
+ return htmlToSlateInternal(html, profile);
104
+ } catch (error) {
105
+ console.warn("HTML to Slate conversion failed:", error);
106
+ const defaultBlockType = getDefaultBlockType(profile.blocks);
107
+ return [{ type: defaultBlockType, children: [{ text: '' }] }];
108
+ }
109
+ };
110
+
111
+ // Enhanced htmlToSlate with result type for better error handling
112
+ export const htmlToSlateWithResult = (html: string, profile: SimplifiedProfile): HtmlToSlateResult => {
113
+ try {
114
+ const data = htmlToSlateInternal(html, profile);
115
+ return { success: true, data };
116
+ } catch (error) {
117
+ const defaultBlockType = getDefaultBlockType(profile.blocks);
118
+ const fallback = [{ type: defaultBlockType, children: [{ text: '' }] }];
119
+ return {
120
+ success: false,
121
+ error: error instanceof Error ? error : new Error(String(error)),
122
+ fallback
123
+ };
124
+ }
125
+ };
126
+
127
+ const htmlToSlateInternal = (html: string, profile: SimplifiedProfile): Descendant[] => {
89
128
  const defaultBlockType = getDefaultBlockType(profile.blocks);
90
129
 
91
130
  if (!html || html === '<p><br></p>') {
@@ -97,8 +136,8 @@ export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendan
97
136
 
98
137
  const nodes: Descendant[] = [];
99
138
 
100
- // Determine block type for element
101
- const getBlockTypeForElement = (element: HTMLElement): string => {
139
+ // Determine block type for element with strict typing
140
+ const getBlockTypeForElement = (element: HTMLElement): BlockId => {
102
141
  const tagName = element.tagName.toLowerCase();
103
142
 
104
143
  // Check if we have a configured block for this element
@@ -144,8 +183,8 @@ export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendan
144
183
  });
145
184
  };
146
185
 
147
- // Process inline elements and text, excluding nested lists
148
- const processNodeWithInlinesExcludingNestedLists = (element: HTMLElement, configuredMarks: string[]): (CustomText | CustomElement)[] => {
186
+ // Process inline elements and text, excluding nested lists - with strict typing
187
+ const processNodeWithInlinesExcludingNestedLists = (element: HTMLElement, configuredMarks: readonly MarkId[]): (CustomText | CustomElement)[] => {
149
188
  const results: (CustomText | CustomElement)[] = [];
150
189
  const currentElementMarks = getMarksFromElement(element, configuredMarks);
151
190
 
@@ -216,8 +255,8 @@ export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendan
216
255
  return results;
217
256
  };
218
257
 
219
- // Process inline elements and text
220
- const processNodeWithInlines = (element: HTMLElement, configuredMarks: string[]): (CustomText | CustomElement)[] => {
258
+ // Process inline elements and text - with strict typing
259
+ const processNodeWithInlines = (element: HTMLElement, configuredMarks: readonly MarkId[]): (CustomText | CustomElement)[] => {
221
260
  const results: (CustomText | CustomElement)[] = [];
222
261
  const currentElementMarks = getMarksFromElement(element, configuredMarks);
223
262
 
@@ -336,6 +375,12 @@ export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendan
336
375
  type: defaultBlockType,
337
376
  children: [{ text: '<br>' }]
338
377
  });
378
+ } else if (tagName === 'hr') {
379
+ // Handle horizontal rule elements
380
+ nodes.push({
381
+ type: 'horizontal-rule',
382
+ children: [{ text: '' }]
383
+ });
339
384
  } else if (tagName === 'ul' || tagName === 'ol') {
340
385
  if (profile.lists) {
341
386
  processListElement(element);
@@ -394,7 +439,8 @@ export const htmlToSlate = (html: string, profile: SimplifiedProfile): Descendan
394
439
  return nodes.length > 0 ? nodes : [{ type: defaultBlockType, children: [{ text: '' }] }];
395
440
  };
396
441
 
397
- export const formatTextWithMarks = (child: CustomText, configuredMarks: string[]): string => {
442
+ // Enhanced formatTextWithMarks with strict typing
443
+ export const formatTextWithMarks = (child: CustomText, configuredMarks: readonly MarkId[]): string => {
398
444
  // If this is raw HTML, return it as-is with unescaping
399
445
  if (child.isRawHtml) {
400
446
  return unescapeHtmlEntities(child.text);
@@ -405,7 +451,7 @@ export const formatTextWithMarks = (child: CustomText, configuredMarks: string[]
405
451
  // Convert literal <br> text back to <br> tags
406
452
  text = text.replace(/<br>/g, '<br>');
407
453
 
408
- // Apply marks using Slate's built-in mark system
454
+ // Apply marks using Slate's built-in mark system with strict typing
409
455
  configuredMarks.forEach(markId => {
410
456
  const markConfig = SLATE_MARKS[markId];
411
457
  if (markConfig && (child as any)[markId]) {
@@ -416,7 +462,31 @@ export const formatTextWithMarks = (child: CustomText, configuredMarks: string[]
416
462
  return text;
417
463
  };
418
464
 
465
+ // Enhanced slateToHtml with better error handling
419
466
  export const slateToHtml = (value: Descendant[], profile: SimplifiedProfile): string => {
467
+ try {
468
+ return slateToHtmlInternal(value, profile);
469
+ } catch (error) {
470
+ console.warn("Slate to HTML conversion failed:", error);
471
+ return '';
472
+ }
473
+ };
474
+
475
+ // Enhanced slateToHtml with result type for better error handling
476
+ export const slateToHtmlWithResult = (value: Descendant[], profile: SimplifiedProfile): SlateToHtmlResult => {
477
+ try {
478
+ const data = slateToHtmlInternal(value, profile);
479
+ return { success: true, data };
480
+ } catch (error) {
481
+ return {
482
+ success: false,
483
+ error: error instanceof Error ? error : new Error(String(error)),
484
+ fallback: ''
485
+ };
486
+ }
487
+ };
488
+
489
+ const slateToHtmlInternal = (value: Descendant[], profile: SimplifiedProfile): string => {
420
490
  let html = '';
421
491
  let i = 0;
422
492
 
@@ -487,8 +557,8 @@ export const slateToHtml = (value: Descendant[], profile: SimplifiedProfile): st
487
557
  return { html: listHtml, nextIndex: currentIndex };
488
558
  };
489
559
 
490
- // Process children with inline elements
491
- const processChildrenWithInlines = (children: (CustomText | CustomElement)[], configuredMarks: string[]): string => {
560
+ // Process children with inline elements - with strict typing
561
+ const processChildrenWithInlines = (children: (CustomText | CustomElement)[], configuredMarks: readonly MarkId[]): string => {
492
562
  let result = '';
493
563
 
494
564
  children.forEach(child => {
@@ -546,14 +616,17 @@ export const slateToHtml = (value: Descendant[], profile: SimplifiedProfile): st
546
616
 
547
617
  const alignStyle = element.align ? ` style="text-align: ${element.align};"` : '';
548
618
 
549
- if (element.type === 'preserved-element') {
619
+ // Handle horizontal rule elements
620
+ if (element.type === 'horizontal-rule') {
621
+ html += '<hr>';
622
+ } else if (element.type === 'preserved-element') {
550
623
  // Handle preserved elements by restoring their original HTML
551
624
  const preservedElement = element as any;
552
625
  html += preservedElement.originalHtml;
553
626
  } else {
554
- const blockConfig = SLATE_BLOCKS[element.type];
627
+ const blockConfig = SLATE_BLOCKS[element.type as BlockId];
555
628
 
556
- if (blockConfig && profile.blocks.includes(element.type) && element.children) {
629
+ if (blockConfig && profile.blocks.includes(element.type as BlockId) && element.children) {
557
630
  const processedChildren = processChildrenWithInlines(element.children as CustomText[], profile.marks);
558
631
  html += blockConfig.renderHtml(processedChildren, alignStyle);
559
632
  } else {