@dxos/react-ui-editor 0.8.1 → 0.8.2-main.12df754

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 (112) hide show
  1. package/dist/lib/browser/index.mjs +499 -371
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs +67 -0
  5. package/dist/lib/browser/testing/index.mjs.map +7 -0
  6. package/dist/lib/node/index.cjs +515 -379
  7. package/dist/lib/node/index.cjs.map +4 -4
  8. package/dist/lib/node/meta.json +1 -1
  9. package/dist/lib/node/testing/index.cjs +101 -0
  10. package/dist/lib/node/testing/index.cjs.map +7 -0
  11. package/dist/lib/node-esm/index.mjs +499 -371
  12. package/dist/lib/node-esm/index.mjs.map +4 -4
  13. package/dist/lib/node-esm/meta.json +1 -1
  14. package/dist/lib/node-esm/testing/index.mjs +69 -0
  15. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  16. package/dist/types/src/components/EditorToolbar/util.d.ts +3 -3
  17. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  18. package/dist/types/src/components/EditorToolbar/{viewMode.d.ts → view-mode.d.ts} +1 -1
  19. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -0
  20. package/dist/types/src/defaults.d.ts +1 -0
  21. package/dist/types/src/defaults.d.ts.map +1 -1
  22. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  23. package/dist/types/src/extensions/command/action.d.ts +17 -0
  24. package/dist/types/src/extensions/command/action.d.ts.map +1 -0
  25. package/dist/types/src/extensions/command/command.d.ts +5 -10
  26. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  27. package/dist/types/src/extensions/command/hint.d.ts +4 -2
  28. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  29. package/dist/types/src/extensions/command/index.d.ts +1 -0
  30. package/dist/types/src/extensions/command/index.d.ts.map +1 -1
  31. package/dist/types/src/extensions/command/menu.d.ts +7 -2
  32. package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
  33. package/dist/types/src/extensions/command/state.d.ts +9 -11
  34. package/dist/types/src/extensions/command/state.d.ts.map +1 -1
  35. package/dist/types/src/extensions/comments.d.ts +9 -7
  36. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  37. package/dist/types/src/extensions/index.d.ts +1 -0
  38. package/dist/types/src/extensions/index.d.ts.map +1 -1
  39. package/dist/types/src/extensions/markdown/decorate.d.ts +4 -1
  40. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  41. package/dist/types/src/extensions/markdown/formatting.d.ts +2 -2
  42. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  43. package/dist/types/src/extensions/markdown/link.d.ts +4 -1
  44. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  45. package/dist/types/src/extensions/preview/index.d.ts +2 -0
  46. package/dist/types/src/extensions/preview/index.d.ts.map +1 -0
  47. package/dist/types/src/extensions/preview/preview.d.ts +39 -0
  48. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -0
  49. package/dist/types/src/hooks/useTextEditor.d.ts +2 -1
  50. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  51. package/dist/types/src/{InputMode.stories.d.ts → stories/InputMode.stories.d.ts} +1 -1
  52. package/dist/types/src/stories/InputMode.stories.d.ts.map +1 -0
  53. package/dist/types/src/{TextEditor.stories.d.ts → stories/TextEditorBasic.stories.d.ts} +2 -35
  54. package/dist/types/src/stories/TextEditorBasic.stories.d.ts.map +1 -0
  55. package/dist/types/src/stories/TextEditorComments.stories.d.ts +13 -0
  56. package/dist/types/src/stories/TextEditorComments.stories.d.ts.map +1 -0
  57. package/dist/types/src/stories/TextEditorPreview.stories.d.ts +13 -0
  58. package/dist/types/src/stories/TextEditorPreview.stories.d.ts.map +1 -0
  59. package/dist/types/src/stories/TextEditorSpecial.stories.d.ts +19 -0
  60. package/dist/types/src/stories/TextEditorSpecial.stories.d.ts.map +1 -0
  61. package/dist/types/src/stories/story-utils.d.ts +53 -0
  62. package/dist/types/src/stories/story-utils.d.ts.map +1 -0
  63. package/dist/types/src/testing/RefPopover.d.ts +21 -0
  64. package/dist/types/src/testing/RefPopover.d.ts.map +1 -0
  65. package/dist/types/src/testing/index.d.ts +2 -0
  66. package/dist/types/src/testing/index.d.ts.map +1 -0
  67. package/dist/types/src/types.d.ts +5 -0
  68. package/dist/types/src/types.d.ts.map +1 -1
  69. package/dist/types/src/util/react.d.ts +6 -1
  70. package/dist/types/src/util/react.d.ts.map +1 -1
  71. package/package.json +33 -27
  72. package/src/components/EditorToolbar/EditorToolbar.tsx +2 -2
  73. package/src/components/EditorToolbar/util.ts +3 -3
  74. package/src/defaults.ts +5 -3
  75. package/src/extensions/automerge/automerge.stories.tsx +3 -11
  76. package/src/extensions/command/action.ts +49 -0
  77. package/src/extensions/command/command.ts +9 -27
  78. package/src/extensions/command/hint.ts +33 -30
  79. package/src/extensions/command/index.ts +1 -0
  80. package/src/extensions/command/menu.ts +11 -8
  81. package/src/extensions/command/state.ts +41 -61
  82. package/src/extensions/comments.ts +9 -9
  83. package/src/extensions/folding.tsx +1 -1
  84. package/src/extensions/index.ts +1 -0
  85. package/src/extensions/markdown/decorate.ts +4 -3
  86. package/src/extensions/markdown/formatting.ts +2 -2
  87. package/src/extensions/markdown/image.ts +12 -11
  88. package/src/extensions/markdown/link.ts +33 -24
  89. package/src/extensions/preview/index.ts +5 -0
  90. package/src/extensions/preview/preview.ts +271 -0
  91. package/src/hooks/useTextEditor.ts +4 -3
  92. package/src/{InputMode.stories.tsx → stories/InputMode.stories.tsx} +4 -4
  93. package/src/stories/TextEditorBasic.stories.tsx +289 -0
  94. package/src/stories/TextEditorComments.stories.tsx +99 -0
  95. package/src/stories/TextEditorPreview.stories.tsx +239 -0
  96. package/src/stories/TextEditorSpecial.stories.tsx +107 -0
  97. package/src/stories/story-utils.tsx +329 -0
  98. package/src/testing/RefPopover.tsx +74 -0
  99. package/src/testing/index.ts +5 -0
  100. package/src/types.ts +7 -0
  101. package/src/util/react.tsx +20 -2
  102. package/dist/types/src/InputMode.stories.d.ts.map +0 -1
  103. package/dist/types/src/TextEditor.stories.d.ts.map +0 -1
  104. package/dist/types/src/components/EditorToolbar/viewMode.d.ts.map +0 -1
  105. package/dist/types/src/extensions/command/preview.d.ts +0 -12
  106. package/dist/types/src/extensions/command/preview.d.ts.map +0 -1
  107. package/dist/types/src/fragments.d.ts +0 -3
  108. package/dist/types/src/fragments.d.ts.map +0 -1
  109. package/src/TextEditor.stories.tsx +0 -856
  110. package/src/extensions/command/preview.ts +0 -79
  111. package/src/fragments.ts +0 -19
  112. /package/src/components/EditorToolbar/{viewMode.ts → view-mode.ts} +0 -0
@@ -1,856 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import '@dxos-theme';
6
-
7
- import { type Completion } from '@codemirror/autocomplete';
8
- import { javascript } from '@codemirror/lang-javascript';
9
- import { markdown } from '@codemirror/lang-markdown';
10
- import { openSearchPanel } from '@codemirror/search';
11
- import { type Extension } from '@codemirror/state';
12
- import { type EditorView } from '@codemirror/view';
13
- import { X } from '@phosphor-icons/react';
14
- import { effect, useSignal } from '@preact/signals-react';
15
- import defaultsDeep from 'lodash.defaultsdeep';
16
- import React, { useEffect, useState, type FC, type KeyboardEvent } from 'react';
17
- import { createRoot } from 'react-dom/client';
18
-
19
- import { Expando } from '@dxos/echo-schema';
20
- import { keySymbols, parseShortcut } from '@dxos/keyboard';
21
- import { PublicKey } from '@dxos/keys';
22
- import { create } from '@dxos/live-object';
23
- import { log } from '@dxos/log';
24
- import { faker } from '@dxos/random';
25
- import { createDocAccessor, createObject } from '@dxos/react-client/echo';
26
- import { Button, Icon, IconButton, Input, ThemeProvider, useThemeContext } from '@dxos/react-ui';
27
- import { baseSurface, defaultTx, getSize, mx } from '@dxos/react-ui-theme';
28
- import { type Meta, withLayout, withTheme } from '@dxos/storybook-utils';
29
-
30
- import { editorContent, editorGutter, editorMonospace } from './defaults';
31
- import {
32
- annotations,
33
- autocomplete,
34
- blast,
35
- command,
36
- comments,
37
- createBasicExtensions,
38
- createDataExtensions,
39
- createExternalCommentSync,
40
- createMarkdownExtensions,
41
- createThemeExtensions,
42
- debugTree,
43
- decorateMarkdown,
44
- defaultOptions,
45
- dropFile,
46
- folding,
47
- formattingKeymap,
48
- image,
49
- InputModeExtensions,
50
- linkTooltip,
51
- listener,
52
- mention,
53
- selectionState,
54
- table,
55
- typewriter,
56
- type CommandAction,
57
- type CommentsOptions,
58
- type DebugNode,
59
- type EditorSelectionState,
60
- } from './extensions';
61
- import { useTextEditor, type UseTextEditorProps } from './hooks';
62
- import translations from './translations';
63
- import { type Comment } from './types';
64
- import { renderRoot } from './util';
65
-
66
- faker.seed(101);
67
-
68
- const str = (...lines: string[]) => lines.join('\n');
69
-
70
- const num = () => faker.number.int({ min: 0, max: 9999 }).toLocaleString();
71
-
72
- const img = '![dxos](https://pbs.twimg.com/profile_banners/1268328127673044992/1684766689/1500x500)';
73
-
74
- const code = str(
75
- '// Code',
76
- 'const Component = () => {',
77
- ' const x = 100;',
78
- '',
79
- ' return () => <div>Test</div>;',
80
- '};',
81
- );
82
-
83
- const content = {
84
- tasks: str(
85
- //
86
- '### TaskList',
87
- '',
88
- `- [x] ${faker.lorem.sentences()}`,
89
- `- [ ] ${faker.lorem.sentences()}`,
90
- ` - [ ] ${faker.lorem.sentences()}`,
91
- ` - [ ] ${faker.lorem.sentences()}`,
92
- ` - [x] ${faker.lorem.sentences()}`,
93
- '',
94
- ),
95
-
96
- bullets: str(
97
- //
98
- '### BulletList',
99
- '',
100
- `- ${faker.lorem.sentences()}`,
101
- `- ${faker.lorem.sentences()}`,
102
- ` - ${faker.lorem.sentences()}`,
103
- ` - ${faker.lorem.sentences()}`,
104
- `- ${faker.lorem.sentences()}`,
105
- '',
106
- ),
107
-
108
- numbered: str(
109
- //
110
- '### OrderedList (part 1)',
111
- '',
112
- `1. ${faker.lorem.sentences()}`,
113
- `1. ${faker.lorem.sentences()}`,
114
- `1. ${faker.lorem.sentences()}`,
115
- ` 1. ${faker.lorem.sentences()}`,
116
- ` 1. ${faker.lorem.sentences()}`,
117
- ` 1. ${faker.lorem.sentences()}`,
118
- `1. ${faker.lorem.sentences()}`,
119
- '',
120
- '### OrderedList (part 2)',
121
- '',
122
- `1. ${faker.lorem.sentences()}`,
123
- '',
124
- ),
125
-
126
- typescript: code,
127
-
128
- codeblocks: str('### Code', '', '```bash', '$ ls -las', '```', '', '```tsx', code, '```', ''),
129
-
130
- comment: str('<!--', 'A comment', '-->', '', 'No comment.', 'Partial comment. <!-- comment. -->'),
131
-
132
- links: str(
133
- '### Links',
134
- '',
135
- 'This is a naked link https://dxos.org within a sentence.',
136
- '',
137
- 'Take a look at [DXOS](https://dxos.org) and how to [get started](https://docs.dxos.org/guide/getting-started.html).',
138
- '',
139
- 'This is all about https://dxos.org and related technologies.',
140
- '',
141
- ),
142
-
143
- table: str(
144
- '### Tables',
145
- '',
146
- `| ${faker.lorem.word().padStart(12)} | ${faker.lorem.word().padStart(12)} | ${faker.lorem.word().padStart(12)} |`,
147
- `|-${''.padStart(12, '-')}-|-${''.padStart(12, '-')}-|-${''.padStart(12, '-')}-|`,
148
- `| ${num().padStart(12)} | ${num().padStart(12)} | ${num().padStart(12)} |`,
149
- `| ${num().padStart(12)} | ${num().padStart(12)} | ${num().padStart(12)} |`,
150
- `| ${num().padStart(12)} | ${num().padStart(12)} | ${num().padStart(12)} |`,
151
- '',
152
- ),
153
-
154
- image: str('### Image', '', img),
155
-
156
- headings: str(
157
- ...[1, 2, 3, 4, 5, 6].map((level) => ['#'.repeat(level) + ` Heading ${level}`, faker.lorem.sentences(), '']).flat(),
158
- ),
159
-
160
- formatting: str('### Formatting', '', 'This this is **bold**, ~~strikethrough~~, _italic_, and `f(INLINE)`.', ''),
161
-
162
- blockquotes: str(
163
- '### Blockquotes',
164
- '',
165
- '> This is a block quote.',
166
- '',
167
- '> This is a long wrapping block quote. Neque reiciendis ullam quae error labore sit, at, et, nulla, aut at nostrum omnis quas nostrum, at consectetur vitae eos asperiores non omnis ullam in beatae at vitae deserunt asperiores sapiente.',
168
- '',
169
- '> This is ...',
170
- '... a multi-line ...',
171
- 'block quote.',
172
- '',
173
- ),
174
-
175
- paragraphs: str(...faker.helpers.multiple(() => [faker.lorem.paragraph(), ''], { count: 3 }).flat()),
176
-
177
- footer: str('', '', '', '', ''),
178
- };
179
-
180
- const text = str(
181
- '# Markdown',
182
- 'Composer Markdown Editor',
183
- '',
184
-
185
- '---',
186
- '## Basics',
187
- content.blockquotes,
188
- content.formatting,
189
- content.links,
190
-
191
- '---',
192
- '## Lists',
193
- content.bullets,
194
- content.tasks,
195
- content.numbered,
196
-
197
- '---',
198
- '## Misc',
199
- content.codeblocks,
200
- content.table,
201
- content.image,
202
- content.footer,
203
- '=== LAST LINE ===',
204
- );
205
-
206
- const links: Completion[] = [
207
- { label: 'DXOS', apply: '[DXOS](https://dxos.org)' },
208
- { label: 'GitHub', apply: '[DXOS GitHub](https://github.com/dxos)' },
209
- { label: 'Automerge', apply: '[Automerge](https://automerge.org/)' },
210
- { label: 'IPFS', apply: '[Protocol Labs](https://docs.ipfs.tech)' },
211
- { label: 'StackEdit', apply: '[StackEdit](https://stackedit.io/app)' },
212
- ];
213
-
214
- const names = ['adam', 'alice', 'alison', 'bob', 'carol', 'charlie', 'sayuri', 'shoko'];
215
-
216
- const hover =
217
- 'rounded-sm text-baseText text-primary-600 hover:text-primary-500 dark:text-primary-300 hover:dark:text-primary-200';
218
-
219
- const Key: FC<{ char: string }> = ({ char }) => (
220
- <span className='flex justify-center items-center w-[24px] h-[24px] rounded text-xs bg-neutral-200 text-black'>
221
- {char}
222
- </span>
223
- );
224
-
225
- const onCommentsHover: CommentsOptions['onHover'] = (el, shortcut) => {
226
- createRoot(el).render(
227
- <div className='flex items-center gap-2 px-2 py-2 bg-neutral-700 text-white text-xs rounded'>
228
- <div>Create comment</div>
229
- <div className='flex gap-1'>
230
- {keySymbols(parseShortcut(shortcut)).map((char) => (
231
- <Key key={char} char={char} />
232
- ))}
233
- </div>
234
- </div>,
235
- );
236
- };
237
-
238
- const renderLinkTooltip = (el: Element, url: string) => {
239
- const web = new URL(url);
240
- createRoot(el).render(
241
- <ThemeProvider tx={defaultTx}>
242
- <a href={url} target='_blank' rel='noreferrer' className={mx(hover, 'flex items-center gap-2')}>
243
- {web.origin}
244
- <Icon icon='ph--arrow-square-out--regular' size={4} />
245
- </a>
246
- </ThemeProvider>,
247
- );
248
- };
249
-
250
- const renderLinkButton = (el: Element, url: string) => {
251
- createRoot(el).render(
252
- <ThemeProvider tx={defaultTx}>
253
- <a href={url} target='_blank' rel='noreferrer' className={mx(hover)}>
254
- <Icon icon='ph--arrow-square-out--regular' size={4} classNames='inline-block mis-1 mb-[3px]' />
255
- </a>
256
- </ThemeProvider>,
257
- );
258
- };
259
-
260
- //
261
- // Story
262
- //
263
-
264
- type DebugMode = 'raw' | 'tree' | 'raw+tree';
265
-
266
- type StoryProps = {
267
- id?: string;
268
- debug?: DebugMode;
269
- text?: string;
270
- readOnly?: boolean;
271
- placeholder?: string;
272
- lineNumbers?: boolean;
273
- onReady?: (view: EditorView) => void;
274
- } & Pick<UseTextEditorProps, 'scrollTo' | 'selection' | 'extensions'>;
275
-
276
- const DefaultStory = ({
277
- id = 'editor-' + PublicKey.random().toHex().slice(0, 8),
278
- debug,
279
- text,
280
- extensions,
281
- readOnly,
282
- placeholder = 'New document.',
283
- scrollTo,
284
- selection,
285
- lineNumbers,
286
- onReady,
287
- }: StoryProps) => {
288
- const [object] = useState(createObject(create(Expando, { content: text ?? '' })));
289
- const { themeMode } = useThemeContext();
290
- const [tree, setTree] = useState<DebugNode>();
291
- const { parentRef, focusAttributes, view } = useTextEditor(
292
- () => ({
293
- id,
294
- initialValue: text,
295
- extensions: [
296
- createDataExtensions({ id, text: createDocAccessor(object, ['content']) }),
297
- createBasicExtensions({ readOnly, placeholder, lineNumbers, scrollPastEnd: true }),
298
- createMarkdownExtensions({ themeMode }),
299
- createThemeExtensions({
300
- themeMode,
301
- syntaxHighlighting: true,
302
- slots: {
303
- content: {
304
- className: editorContent,
305
- },
306
- },
307
- }),
308
- editorGutter,
309
- extensions || [],
310
- debug ? debugTree(setTree) : [],
311
- ],
312
- scrollTo,
313
- selection,
314
- }),
315
- [object, extensions, themeMode],
316
- );
317
-
318
- useEffect(() => {
319
- if (view) {
320
- onReady?.(view);
321
- }
322
- }, [view]);
323
-
324
- return (
325
- <div className='flex w-full'>
326
- <div role='none' className='flex w-full overflow-hidden' ref={parentRef} {...focusAttributes} />
327
- {debug && (
328
- <div className='flex flex-col w-[800px] border-l border-separator divide-y divide-separator overflow-auto'>
329
- {(debug === 'raw' || debug === 'raw+tree') && (
330
- <pre className='p-1 font-mono text-xs text-green-800 dark:text-green-200'>{view?.state.doc.toString()}</pre>
331
- )}
332
- {(debug === 'tree' || debug === 'raw+tree') && (
333
- <pre className='p-1 font-mono text-xs text-green-800 dark:text-green-200'>
334
- {JSON.stringify(tree, null, 2)}
335
- </pre>
336
- )}
337
- </div>
338
- )}
339
- </div>
340
- );
341
- };
342
-
343
- const meta: Meta<typeof DefaultStory> = {
344
- title: 'ui/react-ui-editor/TextEditor',
345
- decorators: [withTheme, withLayout({ fullscreen: true })],
346
- render: DefaultStory,
347
- parameters: { translations, layout: 'fullscreen' },
348
- };
349
-
350
- export default meta;
351
-
352
- const defaultExtensions: Extension[] = [
353
- decorateMarkdown({ renderLinkButton, selectionChangeDelay: 100 }),
354
- formattingKeymap(),
355
- linkTooltip(renderLinkTooltip),
356
- ];
357
-
358
- const allExtensions: Extension[] = [
359
- decorateMarkdown({ numberedHeadings: { from: 2, to: 4 }, renderLinkButton, selectionChangeDelay: 100 }),
360
- formattingKeymap(),
361
- linkTooltip(renderLinkTooltip),
362
- image(),
363
- table(),
364
- folding(),
365
- ];
366
-
367
- //
368
- // Default
369
- //
370
-
371
- export const Default = {
372
- render: () => <DefaultStory text={text} extensions={defaultExtensions} />,
373
- };
374
-
375
- //
376
- // Everything
377
- //
378
-
379
- export const Everything = {
380
- render: () => <DefaultStory text={text} extensions={allExtensions} selection={{ anchor: 99, head: 110 }} />,
381
- };
382
-
383
- //
384
- // Empty
385
- //
386
-
387
- export const Empty = {
388
- render: () => <DefaultStory extensions={defaultExtensions} />,
389
- };
390
-
391
- //
392
- // Readonly
393
- //
394
-
395
- export const Readonly = {
396
- render: () => <DefaultStory text={text} extensions={defaultExtensions} readOnly />,
397
- };
398
-
399
- //
400
- // No Extensions
401
- //
402
-
403
- export const NoExtensions = {
404
- render: () => <DefaultStory text={text} />,
405
- };
406
-
407
- //
408
- // Vim
409
- //
410
-
411
- export const Vim = {
412
- render: () => (
413
- <DefaultStory
414
- text={str('# Vim Mode', '', 'The distant future. The year 2000.', '', content.paragraphs)}
415
- extensions={[defaultExtensions, InputModeExtensions.vim]}
416
- />
417
- ),
418
- };
419
-
420
- //
421
- // Scrolling
422
- //
423
-
424
- const longText = faker.helpers.multiple(() => faker.lorem.paragraph({ min: 8, max: 16 }), { count: 20 }).join('\n\n');
425
-
426
- const largeWithImages = faker.helpers
427
- .multiple(() => [faker.lorem.paragraph({ min: 12, max: 16 }), img], { count: 20 })
428
- .flatMap((x) => x)
429
- .join('\n\n');
430
-
431
- const headings = str(
432
- ...[1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 2, 3, 3, 2, 2, 6, 1]
433
- .map((level) => ['#'.repeat(level) + ' ' + faker.lorem.sentence(3), faker.lorem.sentences(), ''])
434
- .flat(),
435
- );
436
-
437
- const global = new Map<string, EditorSelectionState>();
438
-
439
- export const Folding = {
440
- render: () => <DefaultStory text={text} extensions={[folding()]} />,
441
- };
442
-
443
- export const Scrolling = {
444
- render: () => (
445
- <DefaultStory
446
- text={str('# Large Document', '', longText)}
447
- extensions={selectionState({
448
- setState: (id, state) => global.set(id, state),
449
- getState: (id) => global.get(id),
450
- })}
451
- />
452
- ),
453
- };
454
-
455
- export const ScrollingWithImages = {
456
- render: () => (
457
- <DefaultStory text={str('# Large Document', '', largeWithImages)} extensions={[decorateMarkdown(), image()]} />
458
- ),
459
- };
460
-
461
- export const ScrollTo = {
462
- render: () => {
463
- // NOTE: Selection won't appear if text is reformatted.
464
- const word = 'Scroll to here...';
465
- const text = str('# Scroll To', longText, '', word, '', longText);
466
- const idx = text.indexOf(word);
467
- return (
468
- <DefaultStory
469
- text={text}
470
- extensions={defaultExtensions}
471
- scrollTo={idx}
472
- selection={{ anchor: idx, head: idx + word.length }}
473
- />
474
- );
475
- },
476
- };
477
-
478
- //
479
- // Markdown
480
- //
481
-
482
- export const Blockquote = {
483
- render: () => (
484
- <DefaultStory
485
- text={str('> Blockquote', 'continuation', content.footer)}
486
- extensions={decorateMarkdown()}
487
- debug='raw'
488
- />
489
- ),
490
- };
491
-
492
- export const Headings = {
493
- render: () => (
494
- <DefaultStory text={headings} extensions={decorateMarkdown({ numberedHeadings: { from: 2, to: 4 } })} />
495
- ),
496
- };
497
-
498
- export const Links = {
499
- render: () => (
500
- <DefaultStory text={str(content.links, content.footer)} extensions={[linkTooltip(renderLinkTooltip)]} />
501
- ),
502
- };
503
-
504
- export const Image = {
505
- render: () => <DefaultStory text={str(content.image, content.footer)} extensions={[image()]} />,
506
- };
507
-
508
- export const Code = {
509
- render: () => <DefaultStory text={str(content.codeblocks, content.footer)} extensions={[decorateMarkdown()]} />,
510
- };
511
-
512
- export const Lists = {
513
- render: () => (
514
- <DefaultStory
515
- text={str(content.tasks, '', content.bullets, '', content.numbered, content.footer)}
516
- extensions={[decorateMarkdown()]}
517
- />
518
- ),
519
- };
520
-
521
- export const BulletList = {
522
- render: () => <DefaultStory text={str(content.bullets, content.footer)} extensions={[decorateMarkdown()]} />,
523
- };
524
-
525
- export const OrderedList = {
526
- render: () => <DefaultStory text={str(content.numbered, content.footer)} extensions={[decorateMarkdown()]} />,
527
- };
528
-
529
- export const TaskList = {
530
- render: () => (
531
- <DefaultStory text={str(content.tasks, content.footer)} extensions={[decorateMarkdown()]} debug='raw+tree' />
532
- ),
533
- };
534
-
535
- export const Table = {
536
- render: () => <DefaultStory text={str(content.table, content.footer)} extensions={[decorateMarkdown(), table()]} />,
537
- };
538
-
539
- //
540
- // Commented out
541
- //
542
-
543
- export const CommentedOut = {
544
- render: () => (
545
- <DefaultStory
546
- text={str('# Commented out', '', content.comment, content.footer)}
547
- extensions={[
548
- decorateMarkdown(),
549
- markdown(),
550
- // commentBlock()
551
- ]}
552
- />
553
- ),
554
- };
555
-
556
- //
557
- // Typescript
558
- //
559
-
560
- export const Typescript = {
561
- render: () => (
562
- <DefaultStory
563
- text={content.typescript}
564
- lineNumbers
565
- extensions={[editorMonospace, javascript({ typescript: true })]}
566
- />
567
- ),
568
- };
569
-
570
- //
571
- // Custom
572
- //
573
-
574
- export const Autocomplete = {
575
- render: () => (
576
- <DefaultStory
577
- text={str('# Autocomplete', '', 'Press Ctrl-Space...', content.footer)}
578
- extensions={[
579
- decorateMarkdown({ renderLinkButton }),
580
- autocomplete({
581
- onSearch: (text) => {
582
- return links.filter(({ label }) => label.toLowerCase().includes(text.toLowerCase()));
583
- },
584
- }),
585
- ]}
586
- />
587
- ),
588
- };
589
-
590
- //
591
- // Mention
592
- //
593
-
594
- export const Mention = {
595
- render: () => (
596
- <DefaultStory
597
- text={str('# Mention', '', 'Type @...', content.footer)}
598
- extensions={[
599
- mention({
600
- onSearch: (text) => names.filter((name) => name.toLowerCase().startsWith(text.toLowerCase())),
601
- }),
602
- ]}
603
- />
604
- ),
605
- };
606
-
607
- //
608
- // Search
609
- //
610
-
611
- export const Search = {
612
- render: () => (
613
- <DefaultStory
614
- text={str('# Search', text)}
615
- extensions={defaultExtensions}
616
- onReady={(view) => openSearchPanel(view)}
617
- />
618
- ),
619
- };
620
-
621
- //
622
- // Command
623
- //
624
-
625
- export const Command = {
626
- render: () => (
627
- <DefaultStory
628
- text={str('# Command', '', '', '[Test](dxn:queue:data:123)', '', '', '')}
629
- extensions={[
630
- command({
631
- onHint: () => 'Press / for commands.',
632
- onRenderMenu: (el, onClick) => {
633
- renderRoot(
634
- el,
635
- <ThemeProvider tx={defaultTx}>
636
- <Button classNames='p-1 aspect-square' onClick={onClick}>
637
- <Icon icon={'ph--sparkle--regular'} size={5} />
638
- </Button>
639
- </ThemeProvider>,
640
- );
641
- },
642
- onRenderDialog: (el, onClose) => {
643
- renderRoot(el, <CommandDialog onClose={onClose} />);
644
- },
645
- onRenderPreview: (el, { text }) => {
646
- faker.seed(text.length);
647
- const data = Array.from({ length: 2 }, () => faker.lorem.sentences(2));
648
- renderRoot(
649
- el,
650
- <ThemeProvider tx={defaultTx}>
651
- <div className='flex flex-col gap-2'>
652
- <div className='flex items-center gap-4'>
653
- <div className='grow truncate'>
654
- <span className='text-xs text-subdued mie-2'>Prompt</span>
655
- {text}
656
- </div>
657
- <div className='flex gap-1'>
658
- <IconButton classNames='text-green-500' label='Apply' icon={'ph--check--regular'} />
659
- <IconButton classNames='text-red-500' label='Cancel' icon={'ph--x--regular'} />
660
- </div>
661
- </div>
662
- <div>{data.join('\n\n')}</div>
663
- </div>
664
- </ThemeProvider>,
665
- );
666
- },
667
- }),
668
- ]}
669
- />
670
- ),
671
- };
672
-
673
- const CommandDialog = ({ onClose }: { onClose: (action?: CommandAction) => void }) => {
674
- const [text, setText] = useState('');
675
- const handleInsert = () => {
676
- // TODO(burdon): Use queue ref.
677
- const link = `[${text}](dxn:queue:data:123)`;
678
- console.log({ link });
679
- onClose(text.length ? { insert: link } : undefined);
680
- };
681
-
682
- const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
683
- switch (event.key) {
684
- case 'Enter': {
685
- handleInsert();
686
- break;
687
- }
688
- case 'Escape': {
689
- onClose();
690
- break;
691
- }
692
- }
693
- };
694
-
695
- return (
696
- <div className='flex w-full justify-center'>
697
- <div
698
- className={mx(
699
- 'flex w-full p-2 gap-2 items-center border border-separator rounded-md',
700
- editorContent,
701
- baseSurface,
702
- )}
703
- >
704
- <Input.Root>
705
- <Input.TextInput
706
- autoFocus={true}
707
- placeholder='Ask a question...'
708
- value={text}
709
- onChange={({ target: { value } }) => setText(value)}
710
- onKeyDown={handleKeyDown}
711
- />
712
- </Input.Root>
713
- <Button variant='ghost' classNames='pli-0' onClick={() => onClose()}>
714
- <X className={getSize(5)} />
715
- </Button>
716
- </div>
717
- </div>
718
- );
719
- };
720
-
721
- //
722
- // Comments
723
- //
724
-
725
- export const Comments = {
726
- render: () => {
727
- const _comments = useSignal<Comment[]>([]);
728
- return (
729
- <DefaultStory
730
- text={str('# Comments', '', content.paragraphs, content.footer)}
731
- extensions={[
732
- createExternalCommentSync(
733
- 'test',
734
- (sink) => effect(() => sink()),
735
- () => _comments.value,
736
- ),
737
- comments({
738
- id: 'test',
739
- onHover: onCommentsHover,
740
- onCreate: ({ cursor }) => {
741
- const id = PublicKey.random().toHex();
742
- _comments.value = [..._comments.value, { id, cursor }];
743
- return id;
744
- },
745
- onSelect: (state) => {
746
- const debug = false;
747
- if (debug) {
748
- console.log(
749
- 'update',
750
- JSON.stringify({
751
- comments: state.comments.length,
752
- active: state.selection.current?.slice(0, 8),
753
- closest: state.selection.closest?.slice(0, 8),
754
- }),
755
- );
756
- }
757
- },
758
- }),
759
- ]}
760
- />
761
- );
762
- },
763
- };
764
-
765
- //
766
- // Annotations
767
- //
768
-
769
- export const Annotations = {
770
- render: () => (
771
- <DefaultStory text={str('# Annotations', '', longText)} extensions={[annotations({ match: /volup/gi })]} />
772
- ),
773
- };
774
-
775
- //
776
- // DND
777
- //
778
-
779
- export const DND = {
780
- render: () => (
781
- <DefaultStory
782
- text={str('# DND', '')}
783
- extensions={[
784
- dropFile({
785
- onDrop: (view, event) => {
786
- log.info('drop', event);
787
- },
788
- }),
789
- ]}
790
- />
791
- ),
792
- };
793
-
794
- //
795
- // Listener
796
- //
797
-
798
- export const Listener = {
799
- render: () => (
800
- <DefaultStory
801
- text={str('# Listener', '', content.footer)}
802
- extensions={[
803
- listener({
804
- onFocus: (focusing) => {
805
- console.log({ focusing });
806
- },
807
- onChange: (text) => {
808
- console.log({ text });
809
- },
810
- }),
811
- ]}
812
- />
813
- ),
814
- };
815
-
816
- //
817
- // Typewriter
818
- //
819
-
820
- const typewriterItems = localStorage.getItem('dxos.org/plugin/markdown/typewriter')?.split(',');
821
-
822
- export const Typewriter = {
823
- render: () => (
824
- <DefaultStory
825
- text={str('# Typewriter', '', content.paragraphs, content.footer)}
826
- extensions={[typewriter({ items: typewriterItems })]}
827
- />
828
- ),
829
- };
830
-
831
- //
832
- // Blast
833
- //
834
-
835
- export const Blast = {
836
- render: () => (
837
- <DefaultStory
838
- text={str('# Blast', '', content.paragraphs, content.codeblocks, content.paragraphs)}
839
- extensions={[
840
- typewriter({ items: typewriterItems }),
841
- blast(
842
- defaultsDeep(
843
- {
844
- effect: 2,
845
- particleGravity: 0.2,
846
- particleShrinkRate: 0.995,
847
- color: () => [faker.number.int({ min: 100, max: 200 }), 0, 0],
848
- // color: () => [faker.number.int(256), faker.number.int(256), faker.number.int(256)],
849
- },
850
- defaultOptions,
851
- ),
852
- ),
853
- ]}
854
- />
855
- ),
856
- };