@moldable-ai/editor 0.1.7 → 0.1.9

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 (43) hide show
  1. package/dist/components/markdown-editor.d.ts +12 -3
  2. package/dist/components/markdown-editor.d.ts.map +1 -1
  3. package/dist/components/markdown-editor.js +23 -9
  4. package/dist/index.d.ts +6 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +6 -2
  7. package/dist/lib/lexical/editor-theme.d.ts +2 -0
  8. package/dist/lib/lexical/editor-theme.d.ts.map +1 -1
  9. package/dist/lib/lexical/editor-theme.js +2 -0
  10. package/dist/lib/lexical/file-reference-plugin.d.ts +9 -0
  11. package/dist/lib/lexical/file-reference-plugin.d.ts.map +1 -0
  12. package/dist/lib/lexical/file-reference-plugin.js +99 -0
  13. package/dist/lib/lexical/find-replace-plugin.d.ts +31 -0
  14. package/dist/lib/lexical/find-replace-plugin.d.ts.map +1 -0
  15. package/dist/lib/lexical/find-replace-plugin.js +205 -0
  16. package/dist/lib/lexical/headless-editor.d.ts.map +1 -1
  17. package/dist/lib/lexical/headless-editor.js +6 -0
  18. package/dist/lib/lexical/image-component.d.ts +8 -0
  19. package/dist/lib/lexical/image-component.d.ts.map +1 -0
  20. package/dist/lib/lexical/image-component.js +99 -0
  21. package/dist/lib/lexical/image-node.d.ts +36 -0
  22. package/dist/lib/lexical/image-node.d.ts.map +1 -0
  23. package/dist/lib/lexical/image-node.js +105 -0
  24. package/dist/lib/lexical/markdown-transformers.d.ts +4 -1
  25. package/dist/lib/lexical/markdown-transformers.d.ts.map +1 -1
  26. package/dist/lib/lexical/markdown-transformers.js +78 -0
  27. package/dist/lib/lexical/media-context.d.ts +7 -0
  28. package/dist/lib/lexical/media-context.d.ts.map +1 -0
  29. package/dist/lib/lexical/media-context.js +7 -0
  30. package/dist/lib/lexical/media-node.d.ts +32 -0
  31. package/dist/lib/lexical/media-node.d.ts.map +1 -0
  32. package/dist/lib/lexical/media-node.js +97 -0
  33. package/dist/lib/lexical/media-plugin.d.ts +12 -0
  34. package/dist/lib/lexical/media-plugin.d.ts.map +1 -0
  35. package/dist/lib/lexical/media-plugin.js +139 -0
  36. package/dist/lib/lexical/sync-plugin.d.ts +1 -0
  37. package/dist/lib/lexical/sync-plugin.d.ts.map +1 -1
  38. package/dist/lib/lexical/sync-plugin.js +13 -5
  39. package/package.json +19 -18
  40. package/src/styles/index.css +130 -0
  41. package/dist/lib/lexical/translation.test.d.ts +0 -2
  42. package/dist/lib/lexical/translation.test.d.ts.map +0 -1
  43. package/dist/lib/lexical/translation.test.js +0 -329
@@ -1,329 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { createMoldableHeadlessEditor } from './headless-editor';
3
- import { $extractTranslatableSegments, $applyTranslations, $getTextSegmentsForTranslation, translateEditorState, } from './translation';
4
- import { $convertFromMarkdownString, $convertToMarkdownString, markdownTransformers } from './markdown-transformers';
5
- describe('Translation Utilities', () => {
6
- describe('$extractTranslatableSegments', () => {
7
- it('extracts text from a simple paragraph', async () => {
8
- const editor = createMoldableHeadlessEditor();
9
- await new Promise((resolve) => {
10
- editor.update(() => {
11
- $convertFromMarkdownString({ markdown: 'Hello world' });
12
- resolve();
13
- });
14
- });
15
- const segments = await new Promise((resolve) => {
16
- editor.getEditorState().read(() => {
17
- resolve($extractTranslatableSegments());
18
- });
19
- });
20
- expect(segments).toHaveLength(1);
21
- expect(segments[0].text).toBe('Hello world');
22
- });
23
- it('extracts text from multiple paragraphs', async () => {
24
- const editor = createMoldableHeadlessEditor();
25
- await new Promise((resolve) => {
26
- editor.update(() => {
27
- $convertFromMarkdownString({ markdown: 'First paragraph\n\nSecond paragraph' });
28
- resolve();
29
- });
30
- });
31
- const segments = await new Promise((resolve) => {
32
- editor.getEditorState().read(() => {
33
- resolve($extractTranslatableSegments());
34
- });
35
- });
36
- expect(segments).toHaveLength(2);
37
- expect(segments[0].text).toBe('First paragraph');
38
- expect(segments[1].text).toBe('Second paragraph');
39
- });
40
- it('extracts text from headings', async () => {
41
- const editor = createMoldableHeadlessEditor();
42
- await new Promise((resolve) => {
43
- editor.update(() => {
44
- $convertFromMarkdownString({ markdown: '# Title\n\nContent' });
45
- resolve();
46
- });
47
- });
48
- const segments = await new Promise((resolve) => {
49
- editor.getEditorState().read(() => {
50
- resolve($extractTranslatableSegments());
51
- });
52
- });
53
- expect(segments).toHaveLength(2);
54
- expect(segments[0].text).toBe('Title');
55
- expect(segments[1].text).toBe('Content');
56
- });
57
- it('extracts text from list items', async () => {
58
- const editor = createMoldableHeadlessEditor();
59
- await new Promise((resolve) => {
60
- editor.update(() => {
61
- $convertFromMarkdownString({ markdown: '- Item 1\n- Item 2\n- Item 3' });
62
- resolve();
63
- });
64
- });
65
- const segments = await new Promise((resolve) => {
66
- editor.getEditorState().read(() => {
67
- resolve($extractTranslatableSegments());
68
- });
69
- });
70
- expect(segments).toHaveLength(3);
71
- expect(segments[0].text).toBe('Item 1');
72
- expect(segments[1].text).toBe('Item 2');
73
- expect(segments[2].text).toBe('Item 3');
74
- });
75
- it('handles bold and italic text as separate segments', async () => {
76
- const editor = createMoldableHeadlessEditor();
77
- await new Promise((resolve) => {
78
- editor.update(() => {
79
- $convertFromMarkdownString({ markdown: 'This is **bold** and *italic* text' });
80
- resolve();
81
- });
82
- });
83
- const segments = await new Promise((resolve) => {
84
- editor.getEditorState().read(() => {
85
- resolve($extractTranslatableSegments());
86
- });
87
- });
88
- // Bold and italic are separate text nodes
89
- expect(segments.length).toBeGreaterThanOrEqual(3);
90
- const allText = segments.map(s => s.text).join('');
91
- expect(allText).toContain('bold');
92
- expect(allText).toContain('italic');
93
- });
94
- it('extracts text from blockquotes', async () => {
95
- const editor = createMoldableHeadlessEditor();
96
- await new Promise((resolve) => {
97
- editor.update(() => {
98
- $convertFromMarkdownString({ markdown: '> This is a quote' });
99
- resolve();
100
- });
101
- });
102
- const segments = await new Promise((resolve) => {
103
- editor.getEditorState().read(() => {
104
- resolve($extractTranslatableSegments());
105
- });
106
- });
107
- expect(segments).toHaveLength(1);
108
- expect(segments[0].text).toBe('This is a quote');
109
- });
110
- });
111
- describe('$applyTranslations', () => {
112
- it('applies translations to text nodes', async () => {
113
- const editor = createMoldableHeadlessEditor();
114
- // Set up initial content
115
- await new Promise((resolve) => {
116
- editor.update(() => {
117
- $convertFromMarkdownString({ markdown: 'Hello world' });
118
- resolve();
119
- });
120
- });
121
- // Get segments
122
- const segments = await new Promise((resolve) => {
123
- editor.getEditorState().read(() => {
124
- resolve($extractTranslatableSegments());
125
- });
126
- });
127
- // Apply translations
128
- const translationMap = new Map();
129
- translationMap.set(segments[0].nodeKey, 'Hej verden');
130
- await new Promise((resolve) => {
131
- editor.update(() => {
132
- $applyTranslations(translationMap);
133
- resolve();
134
- });
135
- });
136
- // Verify translation was applied
137
- const result = await new Promise((resolve) => {
138
- editor.getEditorState().read(() => {
139
- resolve($convertToMarkdownString({ transformers: markdownTransformers }));
140
- });
141
- });
142
- expect(result).toBe('Hej verden');
143
- });
144
- it('preserves structure when applying translations', async () => {
145
- const editor = createMoldableHeadlessEditor();
146
- await new Promise((resolve) => {
147
- editor.update(() => {
148
- $convertFromMarkdownString({ markdown: '# Title\n\n- Item 1\n- Item 2' });
149
- resolve();
150
- });
151
- });
152
- const segments = await new Promise((resolve) => {
153
- editor.getEditorState().read(() => {
154
- resolve($extractTranslatableSegments());
155
- });
156
- });
157
- // Translate each segment
158
- const translationMap = new Map();
159
- segments.forEach((seg) => {
160
- if (seg.text === 'Title')
161
- translationMap.set(seg.nodeKey, 'Titel');
162
- if (seg.text === 'Item 1')
163
- translationMap.set(seg.nodeKey, 'Element 1');
164
- if (seg.text === 'Item 2')
165
- translationMap.set(seg.nodeKey, 'Element 2');
166
- });
167
- await new Promise((resolve) => {
168
- editor.update(() => {
169
- $applyTranslations(translationMap);
170
- resolve();
171
- });
172
- });
173
- const result = await new Promise((resolve) => {
174
- editor.getEditorState().read(() => {
175
- resolve($convertToMarkdownString({ transformers: markdownTransformers }));
176
- });
177
- });
178
- expect(result).toContain('# Titel');
179
- expect(result).toContain('- Element 1');
180
- expect(result).toContain('- Element 2');
181
- });
182
- });
183
- describe('$getTextSegmentsForTranslation', () => {
184
- it('returns parallel arrays of texts and keys', async () => {
185
- const editor = createMoldableHeadlessEditor();
186
- await new Promise((resolve) => {
187
- editor.update(() => {
188
- $convertFromMarkdownString({ markdown: 'Hello\n\nWorld' });
189
- resolve();
190
- });
191
- });
192
- const result = await new Promise((resolve) => {
193
- editor.getEditorState().read(() => {
194
- resolve($getTextSegmentsForTranslation());
195
- });
196
- });
197
- expect(result.texts).toHaveLength(2);
198
- expect(result.nodeKeys).toHaveLength(2);
199
- expect(result.texts[0]).toBe('Hello');
200
- expect(result.texts[1]).toBe('World');
201
- });
202
- });
203
- describe('translateEditorState', () => {
204
- it('translates content and returns markdown', async () => {
205
- const editor = createMoldableHeadlessEditor();
206
- await new Promise((resolve) => {
207
- editor.update(() => {
208
- $convertFromMarkdownString({ markdown: '# Hello\n\nWorld' });
209
- resolve();
210
- });
211
- });
212
- // Mock translate function
213
- const translateFn = async (texts) => {
214
- return texts.map((t) => {
215
- if (t === 'Hello')
216
- return 'Bonjour';
217
- if (t === 'World')
218
- return 'Monde';
219
- return t;
220
- });
221
- };
222
- const result = await translateEditorState(editor, translateFn);
223
- expect(result).toContain('# Bonjour');
224
- expect(result).toContain('Monde');
225
- });
226
- it('preserves formatting during translation', async () => {
227
- const editor = createMoldableHeadlessEditor();
228
- await new Promise((resolve) => {
229
- editor.update(() => {
230
- $convertFromMarkdownString({
231
- markdown: '**Bold text** and *italic text*'
232
- });
233
- resolve();
234
- });
235
- });
236
- const translateFn = async (texts) => {
237
- return texts.map((t) => `[${t}]`); // Just wrap for testing
238
- };
239
- const result = await translateEditorState(editor, translateFn);
240
- // Should preserve bold and italic markers
241
- expect(result).toContain('**');
242
- expect(result).toContain('*');
243
- });
244
- it('handles complex medical note structure', async () => {
245
- const editor = createMoldableHeadlessEditor();
246
- const medicalNote = `# Patient Notes
247
-
248
- ## Chief Complaint
249
- Patient presents with chest pain.
250
-
251
- ## Assessment
252
- - Stable condition
253
- - No immediate concerns
254
-
255
- > Doctor's note: Follow up in 2 weeks`;
256
- await new Promise((resolve) => {
257
- editor.update(() => {
258
- $convertFromMarkdownString({ markdown: medicalNote });
259
- resolve();
260
- });
261
- });
262
- // Simple translation that uppercases everything
263
- const translateFn = async (texts) => {
264
- return texts.map((t) => t.toUpperCase());
265
- };
266
- const result = await translateEditorState(editor, translateFn);
267
- // Should preserve structure
268
- expect(result).toContain('# PATIENT NOTES');
269
- expect(result).toContain('## CHIEF COMPLAINT');
270
- expect(result).toContain('- STABLE CONDITION');
271
- expect(result).toContain('>');
272
- });
273
- it('handles empty content', async () => {
274
- const editor = createMoldableHeadlessEditor();
275
- await new Promise((resolve) => {
276
- editor.update(() => {
277
- $convertFromMarkdownString({ markdown: '' });
278
- resolve();
279
- });
280
- });
281
- const translateFn = async (texts) => texts;
282
- const result = await translateEditorState(editor, translateFn);
283
- expect(result).toBe('');
284
- });
285
- });
286
- describe('round-trip translation', () => {
287
- const testCases = [
288
- { name: 'simple paragraph', markdown: 'Hello world' },
289
- { name: 'heading', markdown: '# Title' },
290
- { name: 'multiple headings', markdown: '# H1\n\n## H2\n\n### H3' },
291
- { name: 'bullet list', markdown: '- Item 1\n- Item 2\n- Item 3' },
292
- { name: 'numbered list', markdown: '1. First\n2. Second\n3. Third' },
293
- { name: 'blockquote', markdown: '> This is a quote' },
294
- { name: 'bold text', markdown: '**bold**' },
295
- { name: 'italic text', markdown: '*italic*' },
296
- { name: 'mixed formatting', markdown: 'Normal **bold** and *italic*' },
297
- { name: 'code inline', markdown: 'Use `code` here' },
298
- { name: 'horizontal rule', markdown: 'Before\n\n***\n\nAfter' },
299
- ];
300
- testCases.forEach(({ name, markdown }) => {
301
- it(`preserves structure for ${name}`, async () => {
302
- const editor = createMoldableHeadlessEditor();
303
- await new Promise((resolve) => {
304
- editor.update(() => {
305
- $convertFromMarkdownString({ markdown });
306
- resolve();
307
- });
308
- });
309
- // Identity translation (no change)
310
- const translateFn = async (texts) => texts;
311
- const result = await translateEditorState(editor, translateFn);
312
- // Re-import and re-export to normalize
313
- const editor2 = createMoldableHeadlessEditor();
314
- await new Promise((resolve) => {
315
- editor2.update(() => {
316
- $convertFromMarkdownString({ markdown });
317
- resolve();
318
- });
319
- });
320
- const expected = await new Promise((resolve) => {
321
- editor2.getEditorState().read(() => {
322
- resolve($convertToMarkdownString({ transformers: markdownTransformers }));
323
- });
324
- });
325
- expect(result).toBe(expected);
326
- });
327
- });
328
- });
329
- });