@dxos/react-ui-editor 0.8.2-main.f11618f → 0.8.2-staging.7ac8446

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 +371 -499
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +379 -515
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +371 -499
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/{stories/InputMode.stories.d.ts → InputMode.stories.d.ts} +1 -1
  11. package/dist/types/src/InputMode.stories.d.ts.map +1 -0
  12. package/dist/types/src/{stories/TextEditorBasic.stories.d.ts → TextEditor.stories.d.ts} +35 -2
  13. package/dist/types/src/TextEditor.stories.d.ts.map +1 -0
  14. package/dist/types/src/components/EditorToolbar/util.d.ts +3 -3
  15. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  16. package/dist/types/src/components/EditorToolbar/{view-mode.d.ts → viewMode.d.ts} +1 -1
  17. package/dist/types/src/components/EditorToolbar/viewMode.d.ts.map +1 -0
  18. package/dist/types/src/defaults.d.ts +0 -1
  19. package/dist/types/src/defaults.d.ts.map +1 -1
  20. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  21. package/dist/types/src/extensions/command/command.d.ts +10 -5
  22. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  23. package/dist/types/src/extensions/command/hint.d.ts +2 -4
  24. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  25. package/dist/types/src/extensions/command/index.d.ts +0 -1
  26. package/dist/types/src/extensions/command/index.d.ts.map +1 -1
  27. package/dist/types/src/extensions/command/menu.d.ts +2 -7
  28. package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
  29. package/dist/types/src/extensions/command/preview.d.ts +12 -0
  30. package/dist/types/src/extensions/command/preview.d.ts.map +1 -0
  31. package/dist/types/src/extensions/command/state.d.ts +11 -9
  32. package/dist/types/src/extensions/command/state.d.ts.map +1 -1
  33. package/dist/types/src/extensions/comments.d.ts +7 -9
  34. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  35. package/dist/types/src/extensions/index.d.ts +0 -1
  36. package/dist/types/src/extensions/index.d.ts.map +1 -1
  37. package/dist/types/src/extensions/markdown/decorate.d.ts +1 -4
  38. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  39. package/dist/types/src/extensions/markdown/formatting.d.ts +2 -2
  40. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  41. package/dist/types/src/extensions/markdown/link.d.ts +1 -4
  42. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  43. package/dist/types/src/fragments.d.ts +3 -0
  44. package/dist/types/src/fragments.d.ts.map +1 -0
  45. package/dist/types/src/hooks/useTextEditor.d.ts +1 -2
  46. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  47. package/dist/types/src/types.d.ts +0 -5
  48. package/dist/types/src/types.d.ts.map +1 -1
  49. package/dist/types/src/util/react.d.ts +1 -6
  50. package/dist/types/src/util/react.d.ts.map +1 -1
  51. package/package.json +27 -33
  52. package/src/{stories/InputMode.stories.tsx → InputMode.stories.tsx} +4 -4
  53. package/src/TextEditor.stories.tsx +856 -0
  54. package/src/components/EditorToolbar/EditorToolbar.tsx +2 -2
  55. package/src/components/EditorToolbar/util.ts +3 -3
  56. package/src/defaults.ts +3 -5
  57. package/src/extensions/automerge/automerge.stories.tsx +11 -3
  58. package/src/extensions/command/command.ts +27 -9
  59. package/src/extensions/command/hint.ts +30 -33
  60. package/src/extensions/command/index.ts +0 -1
  61. package/src/extensions/command/menu.ts +8 -11
  62. package/src/extensions/command/preview.ts +79 -0
  63. package/src/extensions/command/state.ts +61 -41
  64. package/src/extensions/comments.ts +9 -9
  65. package/src/extensions/folding.tsx +1 -1
  66. package/src/extensions/index.ts +0 -1
  67. package/src/extensions/markdown/decorate.ts +3 -4
  68. package/src/extensions/markdown/formatting.ts +2 -2
  69. package/src/extensions/markdown/image.ts +11 -12
  70. package/src/extensions/markdown/link.ts +24 -33
  71. package/src/fragments.ts +19 -0
  72. package/src/hooks/useTextEditor.ts +3 -4
  73. package/src/types.ts +0 -7
  74. package/src/util/react.tsx +2 -20
  75. package/dist/lib/browser/testing/index.mjs +0 -67
  76. package/dist/lib/browser/testing/index.mjs.map +0 -7
  77. package/dist/lib/node/testing/index.cjs +0 -101
  78. package/dist/lib/node/testing/index.cjs.map +0 -7
  79. package/dist/lib/node-esm/testing/index.mjs +0 -69
  80. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  81. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +0 -1
  82. package/dist/types/src/extensions/command/action.d.ts +0 -17
  83. package/dist/types/src/extensions/command/action.d.ts.map +0 -1
  84. package/dist/types/src/extensions/preview/index.d.ts +0 -2
  85. package/dist/types/src/extensions/preview/index.d.ts.map +0 -1
  86. package/dist/types/src/extensions/preview/preview.d.ts +0 -39
  87. package/dist/types/src/extensions/preview/preview.d.ts.map +0 -1
  88. package/dist/types/src/stories/InputMode.stories.d.ts.map +0 -1
  89. package/dist/types/src/stories/TextEditorBasic.stories.d.ts.map +0 -1
  90. package/dist/types/src/stories/TextEditorComments.stories.d.ts +0 -13
  91. package/dist/types/src/stories/TextEditorComments.stories.d.ts.map +0 -1
  92. package/dist/types/src/stories/TextEditorPreview.stories.d.ts +0 -13
  93. package/dist/types/src/stories/TextEditorPreview.stories.d.ts.map +0 -1
  94. package/dist/types/src/stories/TextEditorSpecial.stories.d.ts +0 -19
  95. package/dist/types/src/stories/TextEditorSpecial.stories.d.ts.map +0 -1
  96. package/dist/types/src/stories/story-utils.d.ts +0 -53
  97. package/dist/types/src/stories/story-utils.d.ts.map +0 -1
  98. package/dist/types/src/testing/RefPopover.d.ts +0 -21
  99. package/dist/types/src/testing/RefPopover.d.ts.map +0 -1
  100. package/dist/types/src/testing/index.d.ts +0 -2
  101. package/dist/types/src/testing/index.d.ts.map +0 -1
  102. package/src/extensions/command/action.ts +0 -49
  103. package/src/extensions/preview/index.ts +0 -5
  104. package/src/extensions/preview/preview.ts +0 -271
  105. package/src/stories/TextEditorBasic.stories.tsx +0 -289
  106. package/src/stories/TextEditorComments.stories.tsx +0 -99
  107. package/src/stories/TextEditorPreview.stories.tsx +0 -239
  108. package/src/stories/TextEditorSpecial.stories.tsx +0 -107
  109. package/src/stories/story-utils.tsx +0 -329
  110. package/src/testing/RefPopover.tsx +0 -74
  111. package/src/testing/index.ts +0 -5
  112. /package/src/components/EditorToolbar/{view-mode.ts → viewMode.ts} +0 -0
@@ -1,289 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import '@dxos-theme';
6
-
7
- import { javascript } from '@codemirror/lang-javascript';
8
- import { markdown } from '@codemirror/lang-markdown';
9
- import { openSearchPanel } from '@codemirror/search';
10
- import React from 'react';
11
-
12
- import { withLayout, withTheme, type Meta } from '@dxos/storybook-utils';
13
-
14
- import {
15
- DefaultStory,
16
- defaultExtensions,
17
- allExtensions,
18
- text,
19
- str,
20
- content,
21
- longText,
22
- largeWithImages,
23
- headings,
24
- global,
25
- renderLinkButton,
26
- renderLinkTooltip,
27
- links,
28
- names,
29
- } from './story-utils';
30
- import { editorMonospace } from '../defaults';
31
- import {
32
- InputModeExtensions,
33
- selectionState,
34
- decorateMarkdown,
35
- folding,
36
- image,
37
- linkTooltip,
38
- table,
39
- autocomplete,
40
- mention,
41
- } from '../extensions';
42
-
43
- const meta: Meta<typeof DefaultStory> = {
44
- title: 'ui/react-ui-editor/TextEditor',
45
- decorators: [withTheme, withLayout({ fullscreen: true })],
46
- render: DefaultStory,
47
- parameters: { layout: 'fullscreen' },
48
- };
49
-
50
- export default meta;
51
-
52
- //
53
- // Default
54
- //
55
-
56
- export const Default = {
57
- render: () => <DefaultStory text={text} extensions={defaultExtensions} />,
58
- };
59
-
60
- //
61
- // Everything
62
- //
63
-
64
- export const Everything = {
65
- render: () => <DefaultStory text={text} extensions={allExtensions} selection={{ anchor: 99, head: 110 }} />,
66
- };
67
-
68
- //
69
- // Empty
70
- //
71
-
72
- export const Empty = {
73
- render: () => <DefaultStory extensions={defaultExtensions} />,
74
- };
75
-
76
- //
77
- // Readonly
78
- //
79
-
80
- export const Readonly = {
81
- render: () => <DefaultStory text={text} extensions={defaultExtensions} readOnly />,
82
- };
83
-
84
- //
85
- // No Extensions
86
- //
87
-
88
- export const NoExtensions = {
89
- render: () => <DefaultStory text={text} />,
90
- };
91
-
92
- //
93
- // Vim
94
- //
95
-
96
- export const Vim = {
97
- render: () => (
98
- <DefaultStory
99
- text={str('# Vim Mode', '', 'The distant future. The year 2000.', '', content.paragraphs)}
100
- extensions={[defaultExtensions, InputModeExtensions.vim]}
101
- />
102
- ),
103
- };
104
-
105
- //
106
- // Scrolling
107
- //
108
-
109
- export const Folding = {
110
- render: () => <DefaultStory text={text} extensions={[folding()]} />,
111
- };
112
-
113
- export const Scrolling = {
114
- render: () => (
115
- <DefaultStory
116
- text={str('# Large Document', '', longText)}
117
- extensions={selectionState({
118
- setState: (id, state) => global.set(id, state),
119
- getState: (id) => global.get(id),
120
- })}
121
- />
122
- ),
123
- };
124
-
125
- export const ScrollingWithImages = {
126
- render: () => (
127
- <DefaultStory text={str('# Large Document', '', largeWithImages)} extensions={[decorateMarkdown(), image()]} />
128
- ),
129
- };
130
-
131
- export const ScrollTo = {
132
- render: () => {
133
- // NOTE: Selection won't appear if text is reformatted.
134
- const word = 'Scroll to here...';
135
- const text = str('# Scroll To', longText, '', word, '', longText);
136
- const idx = text.indexOf(word);
137
- return (
138
- <DefaultStory
139
- text={text}
140
- extensions={defaultExtensions}
141
- scrollTo={idx}
142
- selection={{ anchor: idx, head: idx + word.length }}
143
- />
144
- );
145
- },
146
- };
147
-
148
- //
149
- // Markdown
150
- //
151
-
152
- export const Blockquote = {
153
- render: () => (
154
- <DefaultStory
155
- text={str('> Blockquote', 'continuation', content.footer)}
156
- extensions={decorateMarkdown()}
157
- debug='raw'
158
- />
159
- ),
160
- };
161
-
162
- export const Headings = {
163
- render: () => (
164
- <DefaultStory text={headings} extensions={decorateMarkdown({ numberedHeadings: { from: 2, to: 4 } })} />
165
- ),
166
- };
167
-
168
- export const Links = {
169
- render: () => (
170
- <DefaultStory text={str(content.links, content.footer)} extensions={[linkTooltip(renderLinkTooltip)]} />
171
- ),
172
- };
173
-
174
- export const Image = {
175
- render: () => <DefaultStory text={str(content.image, content.footer)} extensions={[image()]} />,
176
- };
177
-
178
- export const Code = {
179
- render: () => <DefaultStory text={str(content.codeblocks, content.footer)} extensions={[decorateMarkdown()]} />,
180
- };
181
-
182
- export const Lists = {
183
- render: () => (
184
- <DefaultStory
185
- text={str(content.tasks, '', content.bullets, '', content.numbered, content.footer)}
186
- extensions={[decorateMarkdown()]}
187
- />
188
- ),
189
- };
190
-
191
- export const BulletList = {
192
- render: () => <DefaultStory text={str(content.bullets, content.footer)} extensions={[decorateMarkdown()]} />,
193
- };
194
-
195
- export const OrderedList = {
196
- render: () => <DefaultStory text={str(content.numbered, content.footer)} extensions={[decorateMarkdown()]} />,
197
- };
198
-
199
- export const TaskList = {
200
- render: () => (
201
- <DefaultStory text={str(content.tasks, content.footer)} extensions={[decorateMarkdown()]} debug='raw+tree' />
202
- ),
203
- };
204
-
205
- export const Table = {
206
- render: () => <DefaultStory text={str(content.table, content.footer)} extensions={[decorateMarkdown(), table()]} />,
207
- };
208
-
209
- //
210
- // Commented out
211
- //
212
-
213
- export const CommentedOut = {
214
- render: () => (
215
- <DefaultStory
216
- text={str('# Commented out', '', content.comment, content.footer)}
217
- extensions={[
218
- decorateMarkdown(),
219
- markdown(),
220
- // commentBlock()
221
- ]}
222
- />
223
- ),
224
- };
225
-
226
- //
227
- // Typescript
228
- //
229
-
230
- export const Typescript = {
231
- render: () => (
232
- <DefaultStory
233
- text={content.typescript}
234
- lineNumbers
235
- extensions={[editorMonospace, javascript({ typescript: true })]}
236
- />
237
- ),
238
- };
239
-
240
- //
241
- // Custom
242
- //
243
-
244
- export const Autocomplete = {
245
- render: () => (
246
- <DefaultStory
247
- text={str('# Autocomplete', '', 'Press Ctrl-Space...', content.footer)}
248
- extensions={[
249
- decorateMarkdown({ renderLinkButton }),
250
- autocomplete({
251
- onSearch: (text) => {
252
- return links.filter(({ label }) => label.toLowerCase().includes(text.toLowerCase()));
253
- },
254
- }),
255
- ]}
256
- />
257
- ),
258
- };
259
-
260
- //
261
- // Mention
262
- //
263
-
264
- export const Mention = {
265
- render: () => (
266
- <DefaultStory
267
- text={str('# Mention', '', 'Type @...', content.footer)}
268
- extensions={[
269
- mention({
270
- onSearch: (text) => names.filter((name) => name.toLowerCase().startsWith(text.toLowerCase())),
271
- }),
272
- ]}
273
- />
274
- ),
275
- };
276
-
277
- //
278
- // Search
279
- //
280
-
281
- export const Search = {
282
- render: () => (
283
- <DefaultStory
284
- text={str('# Search', text)}
285
- extensions={defaultExtensions}
286
- onReady={(view) => openSearchPanel(view)}
287
- />
288
- ),
289
- };
@@ -1,99 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import '@dxos-theme';
6
-
7
- import { effect, useSignal } from '@preact/signals-react';
8
- import React, { type FC } from 'react';
9
-
10
- import { keySymbols, parseShortcut } from '@dxos/keyboard';
11
- import { PublicKey } from '@dxos/keys';
12
- import { withLayout, withTheme, type Meta } from '@dxos/storybook-utils';
13
-
14
- import { DefaultStory, str, content, longText } from './story-utils';
15
- import { annotations, comments, createExternalCommentSync } from '../extensions';
16
- import { type Comment } from '../types';
17
- import { createRenderer } from '../util';
18
-
19
- const meta: Meta<typeof DefaultStory> = {
20
- title: 'ui/react-ui-editor/TextEditor',
21
- decorators: [withTheme, withLayout({ fullscreen: true })],
22
- render: DefaultStory,
23
- parameters: { layout: 'fullscreen' },
24
- };
25
-
26
- export default meta;
27
-
28
- //
29
- // Comments
30
- //
31
-
32
- export const Comments = {
33
- render: () => {
34
- const _comments = useSignal<Comment[]>([]);
35
- return (
36
- <DefaultStory
37
- text={str('# Comments', '', content.paragraphs, content.footer)}
38
- extensions={[
39
- createExternalCommentSync(
40
- 'test',
41
- (sink) => effect(() => sink()),
42
- () => _comments.value,
43
- ),
44
- comments({
45
- id: 'test',
46
- renderTooltip: createRenderer(CommentTooltip),
47
- onCreate: ({ cursor }) => {
48
- const id = PublicKey.random().toHex();
49
- _comments.value = [..._comments.value, { id, cursor }];
50
- return id;
51
- },
52
- onSelect: (state) => {
53
- const debug = false;
54
- if (debug) {
55
- console.log(
56
- 'update',
57
- JSON.stringify({
58
- comments: state.comments.length,
59
- active: state.selection.current?.slice(0, 8),
60
- closest: state.selection.closest?.slice(0, 8),
61
- }),
62
- );
63
- }
64
- },
65
- }),
66
- ]}
67
- />
68
- );
69
- },
70
- };
71
-
72
- const Key: FC<{ char: string }> = ({ char }) => (
73
- <span className='flex justify-center items-center w-[24px] h-[24px] rounded text-xs bg-neutral-200 text-black'>
74
- {char}
75
- </span>
76
- );
77
-
78
- const CommentTooltip: FC<{ shortcut: string }> = ({ shortcut }) => {
79
- return (
80
- <div className='flex items-center gap-2 px-2 py-2 bg-neutral-700 text-white text-xs rounded'>
81
- <div>Create comment</div>
82
- <div className='flex gap-1'>
83
- {keySymbols(parseShortcut(shortcut)).map((char) => (
84
- <Key key={char} char={char} />
85
- ))}
86
- </div>
87
- </div>
88
- );
89
- };
90
-
91
- //
92
- // Annotations
93
- //
94
-
95
- export const Annotations = {
96
- render: () => (
97
- <DefaultStory text={str('# Annotations', '', longText)} extensions={[annotations({ match: /volup/gi })]} />
98
- ),
99
- };
@@ -1,239 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import '@dxos-theme';
6
-
7
- import React, { useState, useEffect, type FC, type KeyboardEvent } from 'react';
8
-
9
- import { faker } from '@dxos/random';
10
- import { Button, Icon, IconButton, Input, Popover } from '@dxos/react-ui';
11
- import { mx, hoverableHidden } from '@dxos/react-ui-theme';
12
- import { withLayout, withTheme, type Meta } from '@dxos/storybook-utils';
13
-
14
- import { DefaultStory, str } from './story-utils';
15
- import { editorWidth } from '../defaults';
16
- import {
17
- preview,
18
- command,
19
- image,
20
- type PreviewOptions,
21
- type PreviewLinkRef,
22
- type PreviewLinkTarget,
23
- type PreviewRenderProps,
24
- type Action,
25
- } from '../extensions';
26
- import { RefPopover, useRefPopover } from '../testing';
27
- import { createRenderer } from '../util';
28
-
29
- const meta: Meta<typeof DefaultStory> = {
30
- title: 'ui/react-ui-editor/TextEditor',
31
- decorators: [withTheme, withLayout({ fullscreen: true })],
32
- render: DefaultStory,
33
- parameters: { layout: 'fullscreen' },
34
- };
35
-
36
- export default meta;
37
-
38
- //
39
- // Preview
40
- //
41
-
42
- export const Preview = {
43
- render: () => (
44
- <RefPopover.Provider onLookup={handlePreviewLookup}>
45
- <DefaultStory
46
- text={str(
47
- '# Preview',
48
- '',
49
- 'This project is part of the [DXOS][dxn:queue:data:123] SDK.',
50
- '',
51
- '![DXOS][?dxn:queue:data:123]',
52
- '',
53
- 'It consists of [ECHO][dxn:queue:data:echo], [HALO][dxn:queue:data:halo], and [MESH][dxn:queue:data:mesh].',
54
- '',
55
- '## Deep dive',
56
- '',
57
- '![ECHO][dxn:queue:data:echo]',
58
- '',
59
- '',
60
- )}
61
- extensions={[
62
- image(),
63
- preview({
64
- renderBlock: createRenderer(PreviewBlock),
65
- onLookup: handlePreviewLookup,
66
- }),
67
- ]}
68
- />
69
- <PreviewCard />
70
- </RefPopover.Provider>
71
- ),
72
- };
73
-
74
- const handlePreviewLookup = async ({ label, ref }: PreviewLinkRef): Promise<PreviewLinkTarget> => {
75
- // Random text.
76
- faker.seed(ref.split('').reduce((acc: number, char: string) => acc + char.charCodeAt(0), 1));
77
- const text = Array.from({ length: 2 }, () => faker.lorem.paragraphs()).join('\n\n');
78
- return {
79
- label,
80
- text,
81
- };
82
- };
83
-
84
- // Async lookup.
85
- // TODO(burdon): Handle error.s
86
- const useRefTarget = (link: PreviewLinkRef, onLookup: PreviewOptions['onLookup']): PreviewLinkTarget | undefined => {
87
- const [target, setTarget] = useState<PreviewLinkTarget | undefined>();
88
- useEffect(() => {
89
- void onLookup?.(link).then((target) => setTarget(target ?? undefined));
90
- }, [link, onLookup]);
91
-
92
- return target;
93
- };
94
-
95
- const PreviewCard = () => {
96
- const { link, target } = useRefPopover('PreviewCard');
97
- return (
98
- <Popover.Portal>
99
- <Popover.Content onOpenAutoFocus={(e) => e.preventDefault()}>
100
- <Popover.Viewport>
101
- <div className='grow truncate'>{link?.label}</div>
102
- {target && <div className='line-clamp-3'>{target.text}</div>}
103
- </Popover.Viewport>
104
- <Popover.Arrow />
105
- </Popover.Content>
106
- </Popover.Portal>
107
- );
108
- };
109
-
110
- // TODO(burdon): Replace with card.
111
- const PreviewBlock: FC<PreviewRenderProps> = ({ readonly, link, onAction, onLookup }) => {
112
- const target = useRefTarget(link, onLookup);
113
- return (
114
- <div className='group flex flex-col gap-2'>
115
- <div className='flex items-center gap-4'>
116
- <div className='grow truncate'>
117
- {/* <span className='text-xs text-subdued mie-2'>Prompt</span> */}
118
- {link.label}
119
- </div>
120
- {!readonly && (
121
- <div className='flex gap-1'>
122
- {(link.suggest && (
123
- <>
124
- {target && (
125
- <IconButton
126
- classNames='text-green-500'
127
- label='Apply'
128
- icon={'ph--check--regular'}
129
- onClick={() => onAction({ type: 'insert', link, target })}
130
- />
131
- )}
132
- <IconButton
133
- classNames='text-red-500'
134
- label='Cancel'
135
- icon={'ph--x--regular'}
136
- onClick={() => onAction({ type: 'delete', link })}
137
- />
138
- </>
139
- )) || (
140
- <IconButton
141
- iconOnly
142
- label='Delete'
143
- icon={'ph--x--regular'}
144
- classNames={hoverableHidden}
145
- onClick={() => onAction({ type: 'delete', link })}
146
- />
147
- )}
148
- </div>
149
- )}
150
- </div>
151
- {target && <div className='line-clamp-3'>{target.text}</div>}
152
- </div>
153
- );
154
- };
155
-
156
- //
157
- // Command
158
- //
159
-
160
- export const Command = {
161
- render: () => (
162
- <DefaultStory
163
- text={str(
164
- '# Preview',
165
- '',
166
- 'This project is part of the [DXOS][dxn:queue:data:123] SDK.',
167
- '',
168
- '![DXOS][dxn:queue:data:123]',
169
- '',
170
- )}
171
- extensions={[
172
- preview({
173
- renderBlock: createRenderer(PreviewBlock),
174
- onLookup: handlePreviewLookup,
175
- }),
176
- command({
177
- renderMenu: createRenderer(CommandMenu),
178
- renderDialog: createRenderer(CommandDialog),
179
- onHint: () => 'Press / for commands.',
180
- }),
181
- ]}
182
- />
183
- ),
184
- };
185
-
186
- const CommandMenu = ({ onAction }: { onAction: () => void }) => {
187
- return (
188
- <Button classNames='p-1 aspect-square' onClick={onAction}>
189
- <Icon icon='ph--sparkle--regular' size={5} />
190
- </Button>
191
- );
192
- };
193
-
194
- const CommandDialog = ({ onAction }: { onAction: (action?: Action) => void }) => {
195
- const [text, setText] = useState('');
196
-
197
- const handleInsert = () => {
198
- // TODO(burdon): Use queue ref.
199
- const link = `[${text}](dxn:queue:data:123)`;
200
- onAction(text.length ? { type: 'insert', text: link } : undefined);
201
- };
202
-
203
- const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
204
- switch (event.key) {
205
- case 'Enter': {
206
- handleInsert();
207
- break;
208
- }
209
- case 'Escape': {
210
- onAction();
211
- break;
212
- }
213
- }
214
- };
215
-
216
- return (
217
- <div className='flex w-full justify-center'>
218
- <div
219
- className={mx(
220
- 'flex w-full p-2 gap-2 items-center bg-modalSurface border border-separator rounded-md',
221
- editorWidth,
222
- )}
223
- >
224
- <Input.Root>
225
- <Input.TextInput
226
- autoFocus={true}
227
- placeholder='Ask a question...'
228
- value={text}
229
- onChange={(ev) => setText(ev.target.value)}
230
- onKeyDown={handleKeyDown}
231
- />
232
- </Input.Root>
233
- <Button variant='ghost' classNames='pli-0' onClick={() => onAction({ type: 'cancel' })}>
234
- <Icon icon='ph--x--regular' size={5} />
235
- </Button>
236
- </div>
237
- </div>
238
- );
239
- };