@dxos/react-ui-editor 0.3.11-main.4eab977 → 0.3.11-main.508f304

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 (107) hide show
  1. package/dist/lib/browser/index.mjs +779 -760
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +16 -13
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +20 -17
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +6 -6
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
  11. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
  12. package/dist/types/src/components/TextEditor/yjs.stories.d.ts +1 -1
  13. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  14. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  15. package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
  16. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  17. package/dist/types/src/extensions/automerge/defs.d.ts +7 -9
  18. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  19. package/dist/types/src/extensions/automerge/index.d.ts +0 -1
  20. package/dist/types/src/extensions/automerge/index.d.ts.map +1 -1
  21. package/dist/types/src/extensions/automerge/semaphore.d.ts +7 -12
  22. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  23. package/dist/types/src/extensions/automerge/update-automerge.d.ts +2 -2
  24. package/dist/types/src/extensions/awareness.d.ts +2 -0
  25. package/dist/types/src/extensions/awareness.d.ts.map +1 -1
  26. package/dist/types/src/extensions/basic.d.ts +4 -5
  27. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  28. package/dist/types/src/extensions/code.d.ts.map +1 -1
  29. package/dist/types/src/extensions/comments.d.ts +31 -18
  30. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  31. package/dist/types/src/{util → extensions}/cursor.d.ts +10 -2
  32. package/dist/types/src/extensions/cursor.d.ts.map +1 -0
  33. package/dist/types/src/extensions/hr.d.ts.map +1 -1
  34. package/dist/types/src/extensions/index.d.ts +2 -0
  35. package/dist/types/src/extensions/index.d.ts.map +1 -1
  36. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -5
  37. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  38. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  39. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  40. package/dist/types/src/extensions/outliner.d.ts +4 -0
  41. package/dist/types/src/extensions/outliner.d.ts.map +1 -0
  42. package/dist/types/src/extensions/tasklist.d.ts.map +1 -1
  43. package/dist/types/src/extensions/yjs/cursor.d.ts +1 -1
  44. package/dist/types/src/extensions/yjs/cursor.d.ts.map +1 -1
  45. package/dist/types/src/hooks/automerge.d.ts +2 -1
  46. package/dist/types/src/hooks/automerge.d.ts.map +1 -1
  47. package/dist/types/src/hooks/defs.d.ts +28 -0
  48. package/dist/types/src/hooks/defs.d.ts.map +1 -0
  49. package/dist/types/src/hooks/index.d.ts +2 -1
  50. package/dist/types/src/hooks/index.d.ts.map +1 -1
  51. package/dist/types/src/hooks/useTextModel.d.ts +2 -32
  52. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  53. package/dist/types/src/hooks/yjs.d.ts +2 -1
  54. package/dist/types/src/hooks/yjs.d.ts.map +1 -1
  55. package/dist/types/src/index.d.ts +1 -0
  56. package/dist/types/src/index.d.ts.map +1 -1
  57. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  58. package/dist/types/src/themes/default.d.ts.map +1 -1
  59. package/dist/types/src/util.d.ts +7 -0
  60. package/dist/types/src/util.d.ts.map +1 -0
  61. package/package.json +32 -31
  62. package/src/components/TextEditor/MarkdownEditor.stories.tsx +77 -79
  63. package/src/components/TextEditor/TextEditor.stories.tsx +12 -46
  64. package/src/components/TextEditor/TextEditor.tsx +124 -92
  65. package/src/components/TextEditor/comments.stories.tsx +357 -0
  66. package/src/extensions/autocomplete.ts +6 -3
  67. package/src/extensions/automerge/automerge.ts +40 -46
  68. package/src/extensions/automerge/cursor.ts +2 -1
  69. package/src/extensions/automerge/defs.ts +10 -31
  70. package/src/extensions/automerge/index.ts +0 -1
  71. package/src/extensions/automerge/semaphore.ts +23 -20
  72. package/src/extensions/automerge/update-automerge.ts +2 -2
  73. package/src/extensions/awareness.ts +45 -34
  74. package/src/extensions/basic.ts +22 -17
  75. package/src/extensions/code.ts +24 -18
  76. package/src/extensions/comments.ts +242 -234
  77. package/src/extensions/cursor.ts +50 -0
  78. package/src/extensions/hr.ts +49 -50
  79. package/src/extensions/image.ts +2 -2
  80. package/src/extensions/index.ts +2 -0
  81. package/src/extensions/link.ts +7 -7
  82. package/src/extensions/markdown/bundle.ts +18 -24
  83. package/src/extensions/markdown/highlight.ts +0 -8
  84. package/src/extensions/mention.ts +9 -2
  85. package/src/extensions/outliner.ts +12 -0
  86. package/src/extensions/table.ts +2 -2
  87. package/src/extensions/tasklist.ts +78 -86
  88. package/src/extensions/yjs/cursor.ts +2 -1
  89. package/src/extensions/yjs/yjs.test.ts +1 -1
  90. package/src/extensions/yjs/yjs.ts +2 -2
  91. package/src/hooks/automerge.ts +2 -1
  92. package/src/hooks/defs.ts +47 -0
  93. package/src/hooks/index.ts +3 -1
  94. package/src/hooks/useTextModel.ts +3 -49
  95. package/src/hooks/yjs.ts +2 -23
  96. package/src/index.ts +1 -0
  97. package/src/styles/markdown.ts +0 -2
  98. package/src/themes/default.ts +17 -50
  99. package/src/util.ts +41 -0
  100. package/dist/types/src/util/cursor.d.ts.map +0 -1
  101. package/dist/types/src/util/index.d.ts +0 -3
  102. package/dist/types/src/util/index.d.ts.map +0 -1
  103. package/dist/types/src/util/util.d.ts +0 -5
  104. package/dist/types/src/util/util.d.ts.map +0 -1
  105. package/src/util/cursor.ts +0 -29
  106. package/src/util/index.ts +0 -6
  107. package/src/util/util.ts +0 -18
@@ -4,7 +4,7 @@
4
4
 
5
5
  import '@dxosTheme';
6
6
 
7
- import { EditorView } from '@codemirror/view';
7
+ import { type EditorView } from '@codemirror/view';
8
8
  import { faker } from '@faker-js/faker';
9
9
  import { ArrowSquareOut } from '@phosphor-icons/react';
10
10
  import defaultsDeep from 'lodash.defaultsdeep';
@@ -12,11 +12,12 @@ import React, { type FC, StrictMode, useRef, useState } from 'react';
12
12
  import { createRoot } from 'react-dom/client';
13
13
 
14
14
  import { TextObject } from '@dxos/echo-schema';
15
+ import { keySymbols, parseShortcut } from '@dxos/keyboard';
15
16
  import { PublicKey } from '@dxos/keys';
16
17
  import { fixedInsetFlexLayout, getSize, groupSurface, mx } from '@dxos/react-ui-theme';
17
18
  import { withTheme } from '@dxos/storybook-utils';
18
19
 
19
- import { MarkdownEditor, type TextEditorProps, type TextEditorRef } from './TextEditor';
20
+ import { MarkdownEditor, type TextEditorProps } from './TextEditor';
20
21
  import {
21
22
  autocomplete,
22
23
  blast,
@@ -25,7 +26,6 @@ import {
25
26
  defaultOptions,
26
27
  hr,
27
28
  image,
28
- listener,
29
29
  link,
30
30
  mention,
31
31
  table,
@@ -33,8 +33,9 @@ import {
33
33
  typewriter,
34
34
  type CommentsOptions,
35
35
  type LinkOptions,
36
+ useComments,
36
37
  } from '../../extensions';
37
- import { type CommentRange, useTextModel } from '../../hooks';
38
+ import { type Comment, useTextModel } from '../../hooks';
38
39
 
39
40
  // Extensions:
40
41
  // TODO(burdon): Table of contents.
@@ -54,8 +55,8 @@ const text = {
54
55
  '- [x] decorator',
55
56
  '- [ ] checkbox',
56
57
  ' - [ ] state',
57
- ' - [ ] indent',
58
- ' - [x] style',
58
+ ' - [ ] indent',
59
+ ' - [x] style',
59
60
  '',
60
61
  ),
61
62
 
@@ -161,6 +162,8 @@ const links = [
161
162
  { label: 'StackEdit', apply: '[StackEdit](https://stackedit.io/app)' },
162
163
  ];
163
164
 
165
+ const names = ['adam', 'alice', 'alison', 'bob', 'carol', 'charlie', 'sayuri', 'shoko'];
166
+
164
167
  const hover =
165
168
  'rounded-sm text-base text-primary-600 hover:text-primary-500 dark:text-primary-300 hover:dark:text-primary-200';
166
169
 
@@ -182,15 +185,15 @@ const Key: FC<{ char: string }> = ({ char }) => (
182
185
  </span>
183
186
  );
184
187
 
185
- const onCommentsHover: CommentsOptions['onHover'] = (el) => {
188
+ const onCommentsHover: CommentsOptions['onHover'] = (el, shortcut) => {
186
189
  createRoot(el).render(
187
190
  <StrictMode>
188
191
  <div className='flex items-center gap-2 px-2 py-2 bg-neutral-700 text-white text-xs rounded'>
189
192
  <div>Create comment</div>
190
- {/* TODO(burdon): Unify shortcuts. */}
191
193
  <div className='flex gap-1'>
192
- <Key char='⌘' />
193
- <Key char="'" />
194
+ {keySymbols(parseShortcut(shortcut)).map((char) => (
195
+ <Key key={char} char={char} />
196
+ ))}
194
197
  </div>
195
198
  </div>
196
199
  </StrictMode>,
@@ -201,7 +204,7 @@ const onRenderLink: LinkOptions['onRender'] = (el, url) => {
201
204
  createRoot(el).render(
202
205
  <StrictMode>
203
206
  <a href={url} target='_blank' rel='noreferrer' className={hover}>
204
- <ArrowSquareOut weight='bold' className={mx(getSize(4), 'inline-block leading-none mis-1 mb-1')} />
207
+ <ArrowSquareOut weight='bold' className={mx(getSize(4), 'inline-block leading-none mis-1 mb-[2px]')} />
205
208
  </a>
206
209
  </StrictMode>,
207
210
  );
@@ -209,23 +212,24 @@ const onRenderLink: LinkOptions['onRender'] = (el, url) => {
209
212
 
210
213
  type StoryProps = {
211
214
  text?: string;
215
+ comments?: Comment[];
212
216
  automerge?: boolean;
213
- } & Pick<TextEditorProps, 'comments' | 'readonly' | 'extensions' | 'slots'>;
217
+ } & Pick<TextEditorProps, 'readonly' | 'placeholder' | 'slots' | 'extensions'>;
214
218
 
215
- const Story = ({ text, automerge, ...props }: StoryProps) => {
216
- const ref = useRef<TextEditorRef>(null);
219
+ const Story = ({ text, comments, automerge, placeholder = 'New document.', ...props }: StoryProps) => {
217
220
  const [item] = useState({ text: new TextObject(text, undefined, undefined, { automerge }) });
221
+ const view = useRef<EditorView>(null);
218
222
  const model = useTextModel({ text: item.text });
223
+ useComments(view.current, comments);
219
224
  if (!model) {
220
225
  return null;
221
226
  }
222
227
 
223
228
  return (
224
229
  <div className={mx(fixedInsetFlexLayout, groupSurface)}>
225
- <div className='flex justify-center overflow-y-scroll'>
226
- <div className='flex flex-col w-[800px] py-16'>
227
- <MarkdownEditor ref={ref} model={model} {...props} />
228
- <div className='flex shrink-0 h-[300px]'></div>
230
+ <div className='flex h-full justify-center'>
231
+ <div className='flex flex-col h-full w-[800px]'>
232
+ <MarkdownEditor ref={view} model={model} placeholder={placeholder} {...props} />
229
233
  </div>
230
234
  </div>
231
235
  </div>
@@ -239,7 +243,7 @@ export default {
239
243
  render: Story,
240
244
  };
241
245
 
242
- const extensions = [
246
+ const defaults = [
243
247
  autocomplete({
244
248
  onSearch: (text) => links.filter(({ label }) => label.toLowerCase().includes(text.toLowerCase())),
245
249
  }),
@@ -247,36 +251,33 @@ const extensions = [
247
251
  hr(),
248
252
  image(),
249
253
  link({ onRender: onRenderLink, onHover: onHoverLinkTooltip }),
254
+ // mention({
255
+ // onSearch: (text) => names.filter((name) => name.toLowerCase().startsWith(text.toLowerCase())),
256
+ // }),
250
257
  table(),
251
258
  tasklist(),
252
259
  ];
253
260
 
254
261
  export const Default = {
255
- render: () => <Story text={document} extensions={extensions} />,
262
+ render: () => <Story text={document} extensions={defaults} />,
256
263
  };
257
264
 
258
265
  export const Readonly = {
259
- render: () => <Story text={document} extensions={extensions} readonly />,
260
- };
261
-
262
- const large = faker.helpers.multiple(() => faker.lorem.paragraph({ min: 8, max: 16 }), { count: 20 }).join('\n\n');
263
-
264
- export const Large = {
265
- render: () => <Story text={str('# Large Document', '', large)} extensions={[]} />,
266
+ render: () => <Story text={document} extensions={defaults} readonly />,
266
267
  };
267
268
 
268
269
  export const NoExtensions = {
269
270
  render: () => <Story text={document} />,
270
271
  };
271
272
 
272
- // TODO(burdon): Cursor bug if no newline after BR (cursor down just loops back to start of each paragraph).
273
- export const HorizontalRule = {
274
- render: () => (
275
- <Story
276
- text={str('# Horizontal Rule', '', text.paragraphs, '---', '', text.paragraphs, '---', '', text.paragraphs)}
277
- extensions={[hr()]}
278
- />
279
- ),
273
+ const large = faker.helpers.multiple(() => faker.lorem.paragraph({ min: 8, max: 16 }), { count: 20 }).join('\n\n');
274
+
275
+ export const Empty = {
276
+ render: () => <Story />,
277
+ };
278
+
279
+ export const Scrolling = {
280
+ render: () => <Story text={str('# Large Document', '', large)} extensions={[]} />,
280
281
  };
281
282
 
282
283
  export const Links = {
@@ -292,34 +293,33 @@ export const Code = {
292
293
  render: () => <Story text={str(text.code, text.footer)} extensions={[code()]} readonly />,
293
294
  };
294
295
 
295
- export const Table = {
296
- render: () => <Story text={str(text.table, text.footer)} extensions={[table()]} />,
297
- };
298
-
299
296
  export const Image = {
300
297
  render: () => <Story text={str(text.image, text.footer)} readonly extensions={[image()]} />,
301
298
  };
302
299
 
303
- export const Tasklist = {
300
+ export const Lists = {
304
301
  render: () => (
305
- <Story
306
- text={str(text.tasks, '', text.list, text.footer)}
307
- extensions={[
308
- tasklist(),
309
- listener({
310
- onChange: (text) => {
311
- console.log(text);
312
- },
313
- }),
314
- ]}
315
- />
302
+ <Story text={str(text.tasks, '', text.list, '', text.numbered, text.footer)} extensions={[tasklist()]} />
316
303
  ),
317
304
  };
318
305
 
306
+ export const Table = {
307
+ render: () => <Story text={str(text.table, text.footer)} extensions={[table()]} />,
308
+ };
309
+
310
+ // export const Outliner = {
311
+ // render: () => (
312
+ // <Story
313
+ // text={str('# Outliner', '', 'Block', ': this is a block', ': with multiple lines', text.footer)}
314
+ // extensions={[outliner()]}
315
+ // />
316
+ // ),
317
+ // };
318
+
319
319
  export const Autocomplete = {
320
320
  render: () => (
321
321
  <Story
322
- text={str('# Autocomplete', '', 'Press CTRL-SPACE', text.footer)}
322
+ text={str('# Autocomplete', '', 'Press Ctrl-Space...', text.footer)}
323
323
  extensions={[
324
324
  link({ onRender: onRenderLink }),
325
325
  autocomplete({
@@ -330,8 +330,6 @@ export const Autocomplete = {
330
330
  ),
331
331
  };
332
332
 
333
- const names = ['adam', 'alice', 'alison', 'bob', 'carol', 'charlie', 'sayuri', 'shoko'];
334
-
335
333
  export const Mention = {
336
334
  render: () => (
337
335
  <Story
@@ -347,18 +345,18 @@ export const Mention = {
347
345
 
348
346
  export const Comments = {
349
347
  render: () => {
350
- const [commentsStates, setCommentStates] = useState<CommentRange[]>([]);
348
+ const [commentRanges, setCommentRanges] = useState<Comment[]>([]);
351
349
 
352
350
  return (
353
351
  <Story
354
352
  text={str('# Comments', '', text.paragraphs, text.footer)}
355
- comments={commentsStates}
353
+ comments={commentRanges}
356
354
  extensions={[
357
355
  comments({
358
356
  onHover: onCommentsHover,
359
- onCreate: (relPos) => {
357
+ onCreate: (range) => {
360
358
  const id = PublicKey.random().toHex();
361
- setCommentStates((comments) => [...comments, { id, cursor: relPos }]);
359
+ setCommentRanges((commentRanges) => [...commentRanges, { id, cursor: range }]);
362
360
  return id;
363
361
  },
364
362
  onSelect: (state) => {
@@ -367,9 +365,9 @@ export const Comments = {
367
365
  console.log(
368
366
  'update',
369
367
  JSON.stringify({
370
- active: state.active?.slice(0, 8),
371
- closest: state.closest?.slice(0, 8),
372
- ranges: state.ranges.length,
368
+ comments: state.comments.length,
369
+ active: state.selection.active?.slice(0, 8),
370
+ closest: state.selection.closest?.slice(0, 8),
373
371
  }),
374
372
  );
375
373
  }
@@ -381,8 +379,24 @@ export const Comments = {
381
379
  },
382
380
  };
383
381
 
382
+ export const HorizontalRule = {
383
+ render: () => (
384
+ <Story
385
+ text={str('# Horizontal Rule', '', text.paragraphs, '---', text.paragraphs, '---', text.paragraphs)}
386
+ extensions={[hr()]}
387
+ />
388
+ ),
389
+ };
390
+
391
+ const typewriterItems = localStorage.getItem('dxos.org/plugin/markdown/typewriter')?.split(',');
392
+
384
393
  export const Typewriter = {
385
- render: () => <Story text={str('# Typewriter', '', text.paragraphs, text.footer)} extensions={[typewriter()]} />,
394
+ render: () => (
395
+ <Story
396
+ text={str('# Typewriter', '', text.paragraphs, text.footer)}
397
+ extensions={[typewriter({ items: typewriterItems })]}
398
+ />
399
+ ),
386
400
  };
387
401
 
388
402
  export const Blast = {
@@ -390,9 +404,7 @@ export const Blast = {
390
404
  <Story
391
405
  text={str('# Blast', '', text.paragraphs, text.code, text.paragraphs)}
392
406
  extensions={[
393
- typewriter({
394
- items: localStorage.getItem('dxos.composer.extension.demo')?.split(','),
395
- }),
407
+ typewriter({ items: typewriterItems }),
396
408
  blast(
397
409
  defaultsDeep(
398
410
  {
@@ -409,17 +421,3 @@ export const Blast = {
409
421
  />
410
422
  ),
411
423
  };
412
-
413
- export const Diagnostics = {
414
- render: () => (
415
- <Story
416
- text={document}
417
- extensions={[
418
- // Cursor moved.
419
- EditorView.updateListener.of((update) => {
420
- console.log('update', update.view.state.selection.main.head);
421
- }),
422
- ]}
423
- />
424
- ),
425
- };
@@ -4,17 +4,15 @@
4
4
 
5
5
  import '@dxosTheme';
6
6
 
7
- import defaultsDeep from 'lodash.defaultsdeep';
8
- import React, { useRef, useState } from 'react';
7
+ import React, { useState } from 'react';
9
8
 
10
9
  import { TextObject } from '@dxos/echo-schema';
11
10
  import { fixedInsetFlexLayout, groupSurface, mx } from '@dxos/react-ui-theme';
12
11
  import { withTheme } from '@dxos/storybook-utils';
13
12
 
14
- import { defaultSlots, TextEditor, type TextEditorProps, type TextEditorRef, type TextEditorSlots } from './TextEditor';
13
+ import { TextEditor, type TextEditorProps } from './TextEditor';
15
14
  import { listener } from '../../extensions';
16
15
  import { useTextModel } from '../../hooks';
17
- import { textTheme } from '../../themes';
18
16
 
19
17
  const initialText = [
20
18
  '# TextEditor',
@@ -30,8 +28,7 @@ const Story = ({
30
28
  text,
31
29
  automerge,
32
30
  ...props
33
- }: { text?: string; automerge?: boolean } & Pick<TextEditorProps, 'extensions' | 'slots'>) => {
34
- const ref = useRef<TextEditorRef>(null);
31
+ }: { text?: string; automerge?: boolean } & Pick<TextEditorProps, 'extensions' | 'placeholder' | 'slots'>) => {
35
32
  const [item] = useState({ text: new TextObject(text, undefined, undefined, { automerge }) });
36
33
  const model = useTextModel({ text: item.text });
37
34
  if (!model) {
@@ -40,10 +37,9 @@ const Story = ({
40
37
 
41
38
  return (
42
39
  <div className={mx(fixedInsetFlexLayout, groupSurface)}>
43
- <div className='flex justify-center overflow-y-scroll'>
40
+ <div className='flex justify-center'>
44
41
  <div className='flex flex-col w-[800px] py-16'>
45
- <TextEditor ref={ref} model={model} {...props} />
46
- <div className='flex shrink-0 h-[300px]'></div>
42
+ <TextEditor model={model} {...props} />
47
43
  </div>
48
44
  </div>
49
45
  </div>
@@ -58,49 +54,19 @@ export default {
58
54
  };
59
55
 
60
56
  export const Default = {
61
- render: () => (
62
- <Story
63
- slots={defaultsDeep(
64
- { editor: { theme: textTheme, placeholder: 'Enter text...' } } satisfies TextEditorSlots,
65
- defaultSlots,
66
- )}
67
- />
68
- ),
69
- };
70
-
71
- export const Listener = {
72
- render: () => (
73
- <Story
74
- slots={defaultsDeep(
75
- { editor: { theme: textTheme, placeholder: 'Enter text...' } } satisfies TextEditorSlots,
76
- defaultSlots,
77
- )}
78
- extensions={[listener({ onChange: (text) => console.log(text) })]}
79
- />
80
- ),
57
+ render: () => <Story placeholder='Enter text...' />,
81
58
  };
82
59
 
83
60
  export const Text = {
84
- render: () => (
85
- <Story
86
- text={initialText}
87
- slots={defaultsDeep(
88
- { editor: { theme: textTheme, placeholder: 'Enter text...' } } satisfies TextEditorSlots,
89
- defaultSlots,
90
- )}
91
- />
92
- ),
61
+ render: () => <Story text={initialText} placeholder='Enter text...' />,
93
62
  };
94
63
 
95
64
  export const Automerge = {
65
+ render: () => <Story text={initialText} placeholder='Enter text...' automerge />,
66
+ };
67
+
68
+ export const Listener = {
96
69
  render: () => (
97
- <Story
98
- automerge
99
- text={initialText}
100
- slots={defaultsDeep(
101
- { editor: { theme: textTheme, placeholder: 'Enter text...' } } satisfies TextEditorSlots,
102
- defaultSlots,
103
- )}
104
- />
70
+ <Story placeholder='Enter text...' extensions={[listener({ onChange: (text) => console.log(text) })]} />
105
71
  ),
106
72
  };