@dxos/react-ui-editor 0.3.11-main.668f0ff → 0.3.11-main.68db74e
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.
- package/dist/lib/browser/index.mjs +1742 -1382
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +9 -8
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +12 -17
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +6 -3
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/{hooks/automerge → components/TextEditor}/automerge.stories.d.ts +6 -1
- package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
- package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/index.d.ts +0 -2
- package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
- package/dist/types/src/{hooks/yjs → components/TextEditor}/yjs.stories.d.ts +1 -1
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts +9 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts +5 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/defs.d.ts +26 -0
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/index.d.ts +4 -0
- package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
- package/dist/types/src/{hooks → extensions}/automerge/semaphore.d.ts +4 -4
- package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +5 -0
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -0
- package/dist/types/src/extensions/awareness.d.ts +46 -0
- package/dist/types/src/extensions/awareness.d.ts.map +1 -0
- package/dist/types/src/extensions/basic.d.ts.map +1 -0
- package/dist/types/src/extensions/blast.d.ts.map +1 -0
- package/dist/types/src/extensions/code.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/comments.d.ts +6 -4
- package/dist/types/src/extensions/comments.d.ts.map +1 -0
- package/dist/types/src/extensions/cursor.d.ts +25 -0
- package/dist/types/src/extensions/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/hr.d.ts.map +1 -0
- package/dist/types/src/extensions/image.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/index.d.ts +4 -2
- package/dist/types/src/extensions/index.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/link.d.ts +1 -0
- package/dist/types/src/extensions/link.d.ts.map +1 -0
- package/dist/types/src/extensions/listener.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/index.d.ts.map +1 -0
- package/dist/types/src/extensions/mention.d.ts.map +1 -0
- package/dist/types/src/extensions/table.d.ts.map +1 -0
- package/dist/types/src/extensions/tasklist.d.ts +7 -0
- package/dist/types/src/extensions/tasklist.d.ts.map +1 -0
- package/dist/types/src/extensions/typewriter.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/cursor.d.ts +4 -0
- package/dist/types/src/extensions/yjs/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/index.d.ts +2 -0
- package/dist/types/src/extensions/yjs/index.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/yjs.d.ts +4 -0
- package/dist/types/src/extensions/yjs/yjs.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +1 -0
- package/dist/types/src/hooks/automerge.d.ts +3 -0
- package/dist/types/src/hooks/automerge.d.ts.map +1 -0
- package/dist/types/src/hooks/awareness-provider.d.ts +28 -0
- package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +2 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextModel.d.ts +4 -8
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/hooks/{yjs/space-provider.d.ts → yjs.d.ts} +8 -8
- package/dist/types/src/hooks/yjs.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +5 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/styles/cursors.d.ts.map +1 -0
- package/dist/types/src/styles/index.d.ts +1 -0
- package/dist/types/src/styles/index.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/styles/tokens.d.ts +1 -0
- package/dist/types/src/styles/tokens.d.ts.map +1 -1
- package/dist/types/src/testing/replicator.d.ts +2 -2
- package/dist/types/src/testing/replicator.d.ts.map +1 -1
- package/dist/types/src/{components/TextEditor/themes → themes}/default.d.ts +1 -1
- package/dist/types/src/themes/default.d.ts.map +1 -0
- package/dist/types/src/themes/index.d.ts.map +1 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/package.json +24 -22
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +36 -36
- package/src/components/TextEditor/TextEditor.stories.tsx +13 -34
- package/src/components/TextEditor/TextEditor.tsx +71 -67
- package/src/{hooks/automerge → components/TextEditor}/automerge.stories.tsx +66 -6
- package/src/components/TextEditor/codemirror.stories.ts +3 -2
- package/src/components/TextEditor/comments.stories.tsx +315 -0
- package/src/components/TextEditor/index.ts +0 -3
- package/src/{hooks/yjs → components/TextEditor}/yjs.stories.tsx +1 -1
- package/src/{components/TextEditor/extensions → extensions}/autocomplete.ts +18 -17
- package/src/extensions/automerge/automerge.ts +94 -0
- package/src/extensions/automerge/cursor.ts +52 -0
- package/src/extensions/automerge/defs.ts +62 -0
- package/src/extensions/automerge/index.ts +7 -0
- package/src/{hooks → extensions}/automerge/semaphore.ts +14 -12
- package/src/{hooks → extensions}/automerge/update-automerge.ts +5 -6
- package/src/{hooks → extensions}/automerge/update-codemirror.ts +13 -5
- package/src/extensions/awareness.ts +309 -0
- package/src/{components/TextEditor/extensions → extensions}/code.ts +14 -7
- package/src/{components/TextEditor/extensions → extensions}/comments.ts +49 -113
- package/src/extensions/cursor.ts +46 -0
- package/src/{components/TextEditor/extensions → extensions}/hr.ts +4 -5
- package/src/extensions/image.ts +87 -0
- package/src/{components/TextEditor/extensions → extensions}/index.ts +4 -2
- package/src/{components/TextEditor/extensions → extensions}/link.ts +116 -62
- package/src/extensions/listener.ts +35 -0
- package/src/{components/TextEditor/extensions → extensions}/markdown/highlight.ts +4 -13
- package/src/{components/TextEditor/extensions → extensions}/table.ts +10 -4
- package/src/extensions/tasklist.ts +95 -0
- package/src/{components/TextEditor/extensions → extensions}/typewriter.ts +1 -1
- package/src/extensions/yjs/cursor.ts +22 -0
- package/src/extensions/yjs/index.ts +5 -0
- package/src/{hooks → extensions}/yjs/yjs.test.ts +2 -26
- package/src/extensions/yjs/yjs.ts +16 -0
- package/src/hooks/automerge.ts +52 -0
- package/src/hooks/awareness-provider.ts +120 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useTextModel.ts +20 -96
- package/src/hooks/{yjs/space-provider.ts → yjs.ts} +49 -19
- package/src/index.ts +5 -0
- package/src/styles/index.ts +1 -0
- package/src/styles/markdown.ts +0 -2
- package/src/styles/tokens.ts +3 -0
- package/src/testing/replicator.ts +10 -15
- package/src/{components/TextEditor/themes → themes}/default.ts +52 -79
- package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/hr.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts +0 -3
- package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +0 -3
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts +0 -12
- package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/typewriter.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/util.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/themes/default.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/themes/index.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/handle.d.ts +0 -9
- package/dist/types/src/hooks/automerge/handle.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/index.d.ts +0 -3
- package/dist/types/src/hooks/automerge/index.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/plugin.d.ts +0 -26
- package/dist/types/src/hooks/automerge/plugin.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/semaphore.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/update-automerge.d.ts +0 -6
- package/dist/types/src/hooks/automerge/update-automerge.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/update-codemirror.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/cursors.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/index.d.ts +0 -3
- package/dist/types/src/hooks/yjs/index.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/space-provider.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/yjs.test.d.ts.map +0 -1
- package/src/components/TextEditor/extensions/image.ts +0 -69
- package/src/components/TextEditor/extensions/listener.ts +0 -39
- package/src/components/TextEditor/extensions/mermaid.ts +0 -11
- package/src/components/TextEditor/extensions/tasklist.ts +0 -121
- package/src/components/TextEditor/extensions/tooltip.ts +0 -61
- package/src/hooks/automerge/handle.ts +0 -14
- package/src/hooks/automerge/index.ts +0 -6
- package/src/hooks/automerge/plugin.ts +0 -134
- package/src/hooks/yjs/index.ts +0 -6
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/autocomplete.d.ts +0 -0
- /package/dist/types/src/{hooks → extensions}/automerge/update-codemirror.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/basic.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/blast.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/code.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/hr.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/image.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/listener.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/bundle.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/highlight.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/index.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/mention.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/table.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/typewriter.d.ts +0 -0
- /package/dist/types/src/{hooks → extensions}/yjs/yjs.test.d.ts +0 -0
- /package/dist/types/src/{hooks/yjs → styles}/cursors.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/themes → themes}/index.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions/util.d.ts → util.d.ts} +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/basic.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/blast.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/markdown/bundle.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/markdown/index.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/mention.ts +0 -0
- /package/src/{hooks/yjs → styles}/cursors.ts +0 -0
- /package/src/{components/TextEditor/themes → themes}/index.ts +0 -0
- /package/src/{components/TextEditor/extensions/util.ts → util.ts} +0 -0
|
@@ -8,15 +8,16 @@ import { 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';
|
|
11
|
-
import React, { type FC, StrictMode,
|
|
11
|
+
import React, { type FC, StrictMode, 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
|
|
20
|
+
import { MarkdownEditor, type TextEditorProps } from './TextEditor';
|
|
20
21
|
import {
|
|
21
22
|
autocomplete,
|
|
22
23
|
blast,
|
|
@@ -30,13 +31,10 @@ import {
|
|
|
30
31
|
mention,
|
|
31
32
|
table,
|
|
32
33
|
tasklist,
|
|
33
|
-
tooltip,
|
|
34
34
|
typewriter,
|
|
35
35
|
type CommentsOptions,
|
|
36
|
-
type TooltipOptions,
|
|
37
36
|
type LinkOptions,
|
|
38
|
-
|
|
39
|
-
} from './extensions';
|
|
37
|
+
} from '../../extensions';
|
|
40
38
|
import { type CommentRange, useTextModel } from '../../hooks';
|
|
41
39
|
|
|
42
40
|
// Extensions:
|
|
@@ -125,8 +123,6 @@ const text = {
|
|
|
125
123
|
|
|
126
124
|
paragraphs: str(...faker.helpers.multiple(() => [faker.lorem.paragraph(), ''], { count: 3 }).flat()),
|
|
127
125
|
|
|
128
|
-
mermaid: str('```mermaid', 'graph TD;', 'A-->B;', 'A-->C;', 'B-->D;', 'C-->D;', '```'),
|
|
129
|
-
|
|
130
126
|
footer: str('', '', '', '', '')
|
|
131
127
|
};
|
|
132
128
|
|
|
@@ -169,7 +165,7 @@ const links = [
|
|
|
169
165
|
const hover =
|
|
170
166
|
'rounded-sm text-base text-primary-600 hover:text-primary-500 dark:text-primary-300 hover:dark:text-primary-200';
|
|
171
167
|
|
|
172
|
-
const
|
|
168
|
+
const onHoverLinkTooltip: LinkOptions['onHover'] = (el, url) => {
|
|
173
169
|
const web = new URL(url);
|
|
174
170
|
createRoot(el).render(
|
|
175
171
|
<StrictMode>
|
|
@@ -187,26 +183,26 @@ const Key: FC<{ char: string }> = ({ char }) => (
|
|
|
187
183
|
</span>
|
|
188
184
|
);
|
|
189
185
|
|
|
190
|
-
const onCommentsHover: CommentsOptions['onHover'] = (el) => {
|
|
186
|
+
const onCommentsHover: CommentsOptions['onHover'] = (el, shortcut) => {
|
|
191
187
|
createRoot(el).render(
|
|
192
188
|
<StrictMode>
|
|
193
189
|
<div className='flex items-center gap-2 px-2 py-2 bg-neutral-700 text-white text-xs rounded'>
|
|
194
190
|
<div>Create comment</div>
|
|
195
|
-
{/* TODO(burdon): Unify shortcuts. */}
|
|
196
191
|
<div className='flex gap-1'>
|
|
197
|
-
|
|
198
|
-
|
|
192
|
+
{keySymbols(parseShortcut(shortcut)).map((char) => (
|
|
193
|
+
<Key key={char} char={char} />
|
|
194
|
+
))}
|
|
199
195
|
</div>
|
|
200
196
|
</div>
|
|
201
197
|
</StrictMode>,
|
|
202
198
|
);
|
|
203
199
|
};
|
|
204
200
|
|
|
205
|
-
const
|
|
201
|
+
const onRenderLink: LinkOptions['onRender'] = (el, url) => {
|
|
206
202
|
createRoot(el).render(
|
|
207
203
|
<StrictMode>
|
|
208
204
|
<a href={url} target='_blank' rel='noreferrer' className={hover}>
|
|
209
|
-
<ArrowSquareOut weight='bold' className={mx(getSize(4), 'inline-block leading-none mis-1 mb-
|
|
205
|
+
<ArrowSquareOut weight='bold' className={mx(getSize(4), 'inline-block leading-none mis-1 mb-[2px]')} />
|
|
210
206
|
</a>
|
|
211
207
|
</StrictMode>,
|
|
212
208
|
);
|
|
@@ -218,7 +214,6 @@ type StoryProps = {
|
|
|
218
214
|
} & Pick<TextEditorProps, 'comments' | 'readonly' | 'extensions' | 'slots'>;
|
|
219
215
|
|
|
220
216
|
const Story = ({ text, automerge, ...props }: StoryProps) => {
|
|
221
|
-
const ref = useRef<TextEditorRef>(null);
|
|
222
217
|
const [item] = useState({ text: new TextObject(text, undefined, undefined, { automerge }) });
|
|
223
218
|
const model = useTextModel({ text: item.text });
|
|
224
219
|
if (!model) {
|
|
@@ -227,10 +222,9 @@ const Story = ({ text, automerge, ...props }: StoryProps) => {
|
|
|
227
222
|
|
|
228
223
|
return (
|
|
229
224
|
<div className={mx(fixedInsetFlexLayout, groupSurface)}>
|
|
230
|
-
<div className='flex justify-center
|
|
231
|
-
<div className='flex flex-col w-[800px]
|
|
232
|
-
<MarkdownEditor
|
|
233
|
-
<div className='flex shrink-0 h-[300px]'></div>
|
|
225
|
+
<div className='flex h-full justify-center'>
|
|
226
|
+
<div className='flex flex-col h-full w-[800px]'>
|
|
227
|
+
<MarkdownEditor model={model} {...props} />
|
|
234
228
|
</div>
|
|
235
229
|
</div>
|
|
236
230
|
</div>
|
|
@@ -251,10 +245,9 @@ const extensions = [
|
|
|
251
245
|
code(),
|
|
252
246
|
hr(),
|
|
253
247
|
image(),
|
|
254
|
-
link({ onRender:
|
|
248
|
+
link({ onRender: onRenderLink, onHover: onHoverLinkTooltip }),
|
|
255
249
|
table(),
|
|
256
250
|
tasklist(),
|
|
257
|
-
tooltip({ onHover: onTooltipHover }),
|
|
258
251
|
];
|
|
259
252
|
|
|
260
253
|
export const Default = {
|
|
@@ -265,6 +258,16 @@ export const Readonly = {
|
|
|
265
258
|
render: () => <Story text={document} extensions={extensions} readonly />,
|
|
266
259
|
};
|
|
267
260
|
|
|
261
|
+
const large = faker.helpers.multiple(() => faker.lorem.paragraph({ min: 8, max: 16 }), { count: 20 }).join('\n\n');
|
|
262
|
+
|
|
263
|
+
export const Large = {
|
|
264
|
+
render: () => <Story text={str('# Large Document', '', large)} extensions={[]} />,
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
export const Empty = {
|
|
268
|
+
render: () => <Story />,
|
|
269
|
+
};
|
|
270
|
+
|
|
268
271
|
export const NoExtensions = {
|
|
269
272
|
render: () => <Story text={document} />,
|
|
270
273
|
};
|
|
@@ -279,12 +282,13 @@ export const HorizontalRule = {
|
|
|
279
282
|
),
|
|
280
283
|
};
|
|
281
284
|
|
|
282
|
-
export const Tooltips = {
|
|
283
|
-
render: () => <Story text={str(text.links, text.footer)} extensions={[tooltip({ onHover: onTooltipHover })]} />,
|
|
284
|
-
};
|
|
285
|
-
|
|
286
285
|
export const Links = {
|
|
287
|
-
render: () =>
|
|
286
|
+
render: () => (
|
|
287
|
+
<Story
|
|
288
|
+
text={str(text.links, text.footer)}
|
|
289
|
+
extensions={[link({ onHover: onHoverLinkTooltip, onRender: onRenderLink })]}
|
|
290
|
+
/>
|
|
291
|
+
),
|
|
288
292
|
};
|
|
289
293
|
|
|
290
294
|
export const Code = {
|
|
@@ -320,7 +324,7 @@ export const Autocomplete = {
|
|
|
320
324
|
<Story
|
|
321
325
|
text={str('# Autocomplete', '', 'Press CTRL-SPACE', text.footer)}
|
|
322
326
|
extensions={[
|
|
323
|
-
link({ onRender:
|
|
327
|
+
link({ onRender: onRenderLink }),
|
|
324
328
|
autocomplete({
|
|
325
329
|
onSearch: (text) => links.filter(({ label }) => label.toLowerCase().includes(text.toLowerCase())),
|
|
326
330
|
}),
|
|
@@ -344,24 +348,20 @@ export const Mention = {
|
|
|
344
348
|
),
|
|
345
349
|
};
|
|
346
350
|
|
|
347
|
-
export const Mermaid = {
|
|
348
|
-
render: () => <Story text={str('# Mermaid', '', text.mermaid, text.footer)} extensions={[code(), mermaid()]} />,
|
|
349
|
-
};
|
|
350
|
-
|
|
351
351
|
export const Comments = {
|
|
352
352
|
render: () => {
|
|
353
|
-
const [
|
|
353
|
+
const [commentRanges, setCommentRanges] = useState<CommentRange[]>([]);
|
|
354
354
|
|
|
355
355
|
return (
|
|
356
356
|
<Story
|
|
357
357
|
text={str('# Comments', '', text.paragraphs, text.footer)}
|
|
358
|
-
comments={
|
|
358
|
+
comments={commentRanges}
|
|
359
359
|
extensions={[
|
|
360
360
|
comments({
|
|
361
361
|
onHover: onCommentsHover,
|
|
362
|
-
onCreate: (
|
|
362
|
+
onCreate: (range) => {
|
|
363
363
|
const id = PublicKey.random().toHex();
|
|
364
|
-
|
|
364
|
+
setCommentRanges((commentRanges) => [...commentRanges, { id, cursor: range }]);
|
|
365
365
|
return id;
|
|
366
366
|
},
|
|
367
367
|
onSelect: (state) => {
|
|
@@ -4,15 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxosTheme';
|
|
6
6
|
|
|
7
|
-
import
|
|
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 {
|
|
15
|
-
import {
|
|
13
|
+
import { TextEditor, type TextEditorProps } from './TextEditor';
|
|
14
|
+
import { listener } from '../../extensions';
|
|
16
15
|
import { useTextModel } from '../../hooks';
|
|
17
16
|
|
|
18
17
|
const initialText = [
|
|
@@ -29,8 +28,7 @@ const Story = ({
|
|
|
29
28
|
text,
|
|
30
29
|
automerge,
|
|
31
30
|
...props
|
|
32
|
-
}: { text?: string; automerge?: boolean } & Pick<TextEditorProps, 'extensions' | 'slots'>) => {
|
|
33
|
-
const ref = useRef<TextEditorRef>(null);
|
|
31
|
+
}: { text?: string; automerge?: boolean } & Pick<TextEditorProps, 'extensions' | 'placeholder' | 'slots'>) => {
|
|
34
32
|
const [item] = useState({ text: new TextObject(text, undefined, undefined, { automerge }) });
|
|
35
33
|
const model = useTextModel({ text: item.text });
|
|
36
34
|
if (!model) {
|
|
@@ -39,10 +37,9 @@ const Story = ({
|
|
|
39
37
|
|
|
40
38
|
return (
|
|
41
39
|
<div className={mx(fixedInsetFlexLayout, groupSurface)}>
|
|
42
|
-
<div className='flex justify-center
|
|
40
|
+
<div className='flex justify-center'>
|
|
43
41
|
<div className='flex flex-col w-[800px] py-16'>
|
|
44
|
-
<TextEditor
|
|
45
|
-
<div className='flex shrink-0 h-[300px]'></div>
|
|
42
|
+
<TextEditor model={model} {...props} />
|
|
46
43
|
</div>
|
|
47
44
|
</div>
|
|
48
45
|
</div>
|
|
@@ -57,37 +54,19 @@ export default {
|
|
|
57
54
|
};
|
|
58
55
|
|
|
59
56
|
export const Default = {
|
|
60
|
-
render: () =>
|
|
61
|
-
<Story
|
|
62
|
-
slots={defaultsDeep(
|
|
63
|
-
{ editor: { theme: textTheme, placeholder: 'Enter text...' } } satisfies TextEditorSlots,
|
|
64
|
-
defaultSlots,
|
|
65
|
-
)}
|
|
66
|
-
/>
|
|
67
|
-
),
|
|
57
|
+
render: () => <Story placeholder='Enter text...' />,
|
|
68
58
|
};
|
|
69
59
|
|
|
70
60
|
export const Text = {
|
|
71
|
-
render: () =>
|
|
72
|
-
<Story
|
|
73
|
-
text={initialText}
|
|
74
|
-
slots={defaultsDeep(
|
|
75
|
-
{ editor: { theme: textTheme, placeholder: 'Enter text...' } } satisfies TextEditorSlots,
|
|
76
|
-
defaultSlots,
|
|
77
|
-
)}
|
|
78
|
-
/>
|
|
79
|
-
),
|
|
61
|
+
render: () => <Story text={initialText} placeholder='Enter text...' />,
|
|
80
62
|
};
|
|
81
63
|
|
|
82
64
|
export const Automerge = {
|
|
65
|
+
render: () => <Story text={initialText} placeholder='Enter text...' automerge />,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const Listener = {
|
|
83
69
|
render: () => (
|
|
84
|
-
<Story
|
|
85
|
-
automerge
|
|
86
|
-
text={initialText}
|
|
87
|
-
slots={defaultsDeep(
|
|
88
|
-
{ editor: { theme: textTheme, placeholder: 'Enter text...' } } satisfies TextEditorSlots,
|
|
89
|
-
defaultSlots,
|
|
90
|
-
)}
|
|
91
|
-
/>
|
|
70
|
+
<Story placeholder='Enter text...' extensions={[listener({ onChange: (text) => console.log(text) })]} />
|
|
92
71
|
),
|
|
93
72
|
};
|
|
@@ -15,16 +15,17 @@ import React, {
|
|
|
15
15
|
useEffect,
|
|
16
16
|
useImperativeHandle,
|
|
17
17
|
useState,
|
|
18
|
+
useRef,
|
|
18
19
|
} from 'react';
|
|
19
20
|
|
|
20
21
|
import { generateName } from '@dxos/display-name';
|
|
21
22
|
import { useThemeContext } from '@dxos/react-ui';
|
|
22
23
|
import { getColorForValue, inputSurface, mx } from '@dxos/react-ui-theme';
|
|
23
24
|
|
|
24
|
-
import { basicBundle, markdownBundle, setCommentRange } from '
|
|
25
|
-
import { defaultTheme, markdownTheme, textTheme } from './themes';
|
|
25
|
+
import { basicBundle, markdownBundle, setCommentRange } from '../../extensions';
|
|
26
26
|
import { type CommentRange, type EditorModel } from '../../hooks';
|
|
27
27
|
import { type ThemeStyles } from '../../styles';
|
|
28
|
+
import { defaultTheme, markdownTheme, textTheme } from '../../themes';
|
|
28
29
|
|
|
29
30
|
export const EditorModes = ['default', 'vim'] as const;
|
|
30
31
|
export type EditorMode = (typeof EditorModes)[number];
|
|
@@ -34,50 +35,55 @@ export type CursorInfo = {
|
|
|
34
35
|
to: number;
|
|
35
36
|
line: number;
|
|
36
37
|
lines: number;
|
|
38
|
+
length: number;
|
|
37
39
|
after?: string;
|
|
38
40
|
};
|
|
39
41
|
|
|
40
|
-
export type TextEditorRef = {
|
|
41
|
-
root: HTMLDivElement | null;
|
|
42
|
-
state?: EditorState;
|
|
43
|
-
view?: EditorView;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
42
|
export type TextEditorSlots = {
|
|
47
43
|
root?: Omit<ComponentProps<'div'>, 'ref'>;
|
|
48
|
-
editor?: {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
spellCheck?: boolean;
|
|
52
|
-
tabIndex?: number;
|
|
53
|
-
theme?: ThemeStyles;
|
|
54
|
-
};
|
|
44
|
+
// editor?: {
|
|
45
|
+
// className?: string;
|
|
46
|
+
// };
|
|
55
47
|
};
|
|
56
48
|
|
|
49
|
+
// TODO(burdon): Spellcheck?
|
|
57
50
|
export type TextEditorProps = {
|
|
58
51
|
model: EditorModel;
|
|
52
|
+
focus?: boolean;
|
|
53
|
+
selection?: { anchor: number; head?: number };
|
|
59
54
|
readonly?: boolean; // TODO(burdon): Move into model.
|
|
60
55
|
comments?: CommentRange[]; // TODO(burdon): Move into extension.
|
|
61
56
|
extensions?: Extension[];
|
|
62
57
|
editorMode?: EditorMode;
|
|
58
|
+
placeholder?: string;
|
|
59
|
+
theme?: ThemeStyles;
|
|
63
60
|
slots?: TextEditorSlots;
|
|
64
61
|
};
|
|
65
62
|
|
|
66
63
|
/**
|
|
67
64
|
* Base text editor.
|
|
68
|
-
* NOTE: Rather than adding properties, try to create extensions that can be reused.
|
|
69
65
|
*/
|
|
70
|
-
export const BaseTextEditor = forwardRef<
|
|
71
|
-
(
|
|
72
|
-
|
|
66
|
+
export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
67
|
+
(
|
|
68
|
+
{ model, focus, selection, readonly, comments, extensions = [], editorMode, theme, slots = defaultSlots },
|
|
69
|
+
forwardedRef,
|
|
70
|
+
) => {
|
|
73
71
|
const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
>();
|
|
78
|
-
|
|
72
|
+
const { themeMode } = useThemeContext();
|
|
73
|
+
|
|
74
|
+
// The editor view ref should only be used as an escape hatch.
|
|
75
|
+
const rootRef = useRef<HTMLDivElement>(null);
|
|
76
|
+
const [view, setView] = useState<EditorView | null>(null);
|
|
77
|
+
useImperativeHandle<EditorView | null, EditorView | null>(forwardedRef, () => view, [view]);
|
|
78
|
+
|
|
79
|
+
// Focus.
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (view && focus) {
|
|
82
|
+
view.focus();
|
|
83
|
+
}
|
|
84
|
+
}, [view, focus]);
|
|
79
85
|
|
|
80
|
-
// TODO(burdon): Factor out
|
|
86
|
+
// TODO(burdon): Factor out as extension.
|
|
81
87
|
const { awareness, peer } = model;
|
|
82
88
|
useEffect(() => {
|
|
83
89
|
if (awareness && peer) {
|
|
@@ -91,7 +97,7 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
91
97
|
|
|
92
98
|
// TODO(burdon): Factor out as extension.
|
|
93
99
|
useEffect(() => {
|
|
94
|
-
if (view && comments
|
|
100
|
+
if (view && comments !== undefined) {
|
|
95
101
|
view.dispatch({
|
|
96
102
|
effects: setCommentRange.of({ model, comments }),
|
|
97
103
|
});
|
|
@@ -99,14 +105,15 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
99
105
|
}, [view, comments]);
|
|
100
106
|
|
|
101
107
|
useEffect(() => {
|
|
102
|
-
if (!
|
|
108
|
+
if (!model || !rootRef.current) {
|
|
103
109
|
return;
|
|
104
110
|
}
|
|
105
111
|
|
|
106
|
-
// TODO(burdon): Remember cursor position.
|
|
107
112
|
// https://codemirror.net/docs/ref/#state.EditorStateConfig
|
|
108
113
|
const state = EditorState.create({
|
|
109
114
|
doc: model.text(),
|
|
115
|
+
// TODO(burdon): Composer should store and set selection when switching documents.
|
|
116
|
+
selection,
|
|
110
117
|
extensions: [
|
|
111
118
|
readonly && EditorState.readOnly.of(readonly),
|
|
112
119
|
|
|
@@ -116,7 +123,7 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
116
123
|
// Theme.
|
|
117
124
|
// TODO(burdon): Make theme configurable.
|
|
118
125
|
EditorView.baseTheme(defaultTheme),
|
|
119
|
-
EditorView.theme(
|
|
126
|
+
EditorView.theme(theme ?? {}),
|
|
120
127
|
EditorView.darkTheme.of(themeMode === 'dark'),
|
|
121
128
|
|
|
122
129
|
// Storage and replication.
|
|
@@ -131,36 +138,35 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
131
138
|
// If the new state is derived from the old state, it will likely not be visible other than the cursor resetting.
|
|
132
139
|
// Ideally this should not happen except when changing between text objects.
|
|
133
140
|
view?.destroy();
|
|
134
|
-
|
|
141
|
+
const newView = new EditorView({
|
|
142
|
+
parent: rootRef.current,
|
|
135
143
|
state,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// dispatch: (transaction, view) => {
|
|
142
|
-
// view.update([transaction]);
|
|
143
|
-
// },
|
|
144
|
-
}),
|
|
144
|
+
// NOTE: Uncomment to spy on all transactions.
|
|
145
|
+
// https://codemirror.net/docs/ref/#view.EditorView.dispatch
|
|
146
|
+
// dispatch: (transaction, view) => {
|
|
147
|
+
// view.update([transaction]);
|
|
148
|
+
// },
|
|
145
149
|
});
|
|
146
150
|
|
|
151
|
+
setView(newView);
|
|
147
152
|
return () => {
|
|
148
|
-
|
|
149
|
-
|
|
153
|
+
newView?.destroy();
|
|
154
|
+
setView(null);
|
|
150
155
|
};
|
|
151
|
-
}, [
|
|
156
|
+
}, [rootRef, model, readonly, editorMode, themeMode]);
|
|
152
157
|
|
|
153
158
|
const handleKeyUp = useCallback(
|
|
154
159
|
(event: KeyboardEvent) => {
|
|
155
160
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
156
161
|
switch (key) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
// TODO(burdon): Is this required (for vim mode?)
|
|
163
|
+
// case 'Enter': {
|
|
164
|
+
// view?.contentDOM.focus();
|
|
165
|
+
// break;
|
|
166
|
+
// }
|
|
161
167
|
|
|
162
168
|
case 'Escape': {
|
|
163
|
-
editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey) &&
|
|
169
|
+
editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey) && rootRef.current?.focus();
|
|
164
170
|
break;
|
|
165
171
|
}
|
|
166
172
|
}
|
|
@@ -171,42 +177,46 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
171
177
|
return (
|
|
172
178
|
<div
|
|
173
179
|
key={model.id}
|
|
174
|
-
|
|
180
|
+
role='none'
|
|
181
|
+
ref={rootRef}
|
|
175
182
|
tabIndex={0}
|
|
176
|
-
{...slots?.root}
|
|
177
|
-
{...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
178
183
|
onKeyUp={handleKeyUp}
|
|
184
|
+
{...slots.root}
|
|
185
|
+
{...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
179
186
|
/>
|
|
180
187
|
);
|
|
181
188
|
},
|
|
182
189
|
);
|
|
183
190
|
|
|
184
|
-
export const TextEditor = forwardRef<
|
|
185
|
-
({ readonly, extensions = [],
|
|
191
|
+
export const TextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
192
|
+
({ readonly, placeholder, extensions = [], theme = textTheme, slots, ...props }, forwardedRef) => {
|
|
186
193
|
const { themeMode } = useThemeContext();
|
|
187
|
-
const
|
|
194
|
+
const updatedSlots = defaultsDeep({}, slots, defaultTextSlots);
|
|
188
195
|
return (
|
|
189
196
|
<BaseTextEditor
|
|
190
197
|
ref={forwardedRef}
|
|
191
198
|
readonly={readonly}
|
|
192
|
-
extensions={[basicBundle({ readonly, themeMode, placeholder
|
|
193
|
-
|
|
199
|
+
extensions={[basicBundle({ readonly, themeMode, placeholder }), ...extensions]}
|
|
200
|
+
theme={theme}
|
|
201
|
+
slots={updatedSlots}
|
|
194
202
|
{...props}
|
|
195
203
|
/>
|
|
196
204
|
);
|
|
197
205
|
},
|
|
198
206
|
);
|
|
199
207
|
|
|
200
|
-
|
|
201
|
-
|
|
208
|
+
// TODO(burdon): Remove (Just provide bundle, slots).
|
|
209
|
+
export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
|
|
210
|
+
({ readonly, placeholder, extensions = [], theme = markdownTheme, slots, ...props }, forwardedRef) => {
|
|
202
211
|
const { themeMode } = useThemeContext();
|
|
203
|
-
const
|
|
212
|
+
const updatedSlots = defaultsDeep({}, slots, defaultMarkdownSlots);
|
|
204
213
|
return (
|
|
205
214
|
<BaseTextEditor
|
|
206
215
|
ref={forwardedRef}
|
|
207
216
|
readonly={readonly}
|
|
208
|
-
extensions={[markdownBundle({ readonly, themeMode, placeholder
|
|
209
|
-
|
|
217
|
+
extensions={[markdownBundle({ readonly, themeMode, placeholder }), ...extensions]}
|
|
218
|
+
theme={theme}
|
|
219
|
+
slots={updatedSlots}
|
|
210
220
|
{...props}
|
|
211
221
|
/>
|
|
212
222
|
);
|
|
@@ -215,20 +225,14 @@ export const MarkdownEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
215
225
|
|
|
216
226
|
export const defaultSlots: TextEditorSlots = {
|
|
217
227
|
root: {
|
|
218
|
-
className: mx('p-2', inputSurface),
|
|
228
|
+
className: mx('p-2 overflow-y-auto', inputSurface),
|
|
219
229
|
},
|
|
220
230
|
};
|
|
221
231
|
|
|
222
232
|
export const defaultTextSlots: TextEditorSlots = {
|
|
223
233
|
...defaultSlots,
|
|
224
|
-
editor: {
|
|
225
|
-
theme: textTheme,
|
|
226
|
-
},
|
|
227
234
|
};
|
|
228
235
|
|
|
229
236
|
export const defaultMarkdownSlots: TextEditorSlots = {
|
|
230
237
|
...defaultSlots,
|
|
231
|
-
editor: {
|
|
232
|
-
theme: markdownTheme,
|
|
233
|
-
},
|
|
234
238
|
};
|
|
@@ -2,19 +2,29 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
5
|
+
import '@dxosTheme';
|
|
6
|
+
|
|
6
7
|
import { BroadcastChannelNetworkAdapter } from '@automerge/automerge-repo-network-broadcastchannel';
|
|
7
8
|
import { EditorView } from '@codemirror/view';
|
|
9
|
+
import '@preact/signals-react'; // Register react integration
|
|
8
10
|
import { basicSetup } from 'codemirror';
|
|
9
11
|
import get from 'lodash.get';
|
|
10
12
|
import React, { useEffect, useRef, useState } from 'react';
|
|
11
13
|
|
|
12
|
-
// TODO(burdon): Why separate imports?
|
|
13
14
|
import { type Prop } from '@dxos/automerge/automerge';
|
|
14
|
-
import { type DocHandle
|
|
15
|
+
import { Repo, type DocHandle } from '@dxos/automerge/automerge-repo';
|
|
16
|
+
import { Filter, setGlobalAutomergePreference } from '@dxos/echo-schema';
|
|
17
|
+
import { type PublicKey } from '@dxos/keys';
|
|
18
|
+
import { Expando, TextObject, useSpace } from '@dxos/react-client/echo';
|
|
19
|
+
import { useIdentity } from '@dxos/react-client/halo';
|
|
20
|
+
import { ClientRepeater } from '@dxos/react-client/testing';
|
|
21
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
15
22
|
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
23
|
+
import { MarkdownEditor } from './TextEditor';
|
|
24
|
+
import { type IDocHandle, automerge, awareness } from '../../extensions';
|
|
25
|
+
import { useTextModel } from '../../hooks';
|
|
26
|
+
|
|
27
|
+
// TODO(burdon): Move to components.
|
|
18
28
|
|
|
19
29
|
type EditorProps = {
|
|
20
30
|
handle: IDocHandle;
|
|
@@ -28,7 +38,7 @@ const Editor = ({ handle, path }: EditorProps) => {
|
|
|
28
38
|
useEffect(() => {
|
|
29
39
|
const view = (editorRoot.current = new EditorView({
|
|
30
40
|
doc: get(handle.docSync()!, path),
|
|
31
|
-
extensions: [basicSetup,
|
|
41
|
+
extensions: [basicSetup, automerge({ handle, path }), awareness()],
|
|
32
42
|
parent: containerRef.current as any,
|
|
33
43
|
}));
|
|
34
44
|
|
|
@@ -89,3 +99,53 @@ export default {
|
|
|
89
99
|
};
|
|
90
100
|
|
|
91
101
|
export const Default = {};
|
|
102
|
+
|
|
103
|
+
const EchoStory = ({ id, spaceKey }: { id: number; spaceKey: PublicKey }) => {
|
|
104
|
+
const identity = useIdentity();
|
|
105
|
+
const space = useSpace(spaceKey);
|
|
106
|
+
// TODO(dmaretskyi): useQuery doesn't work.
|
|
107
|
+
const [obj] = space?.db.query(Filter.from({ type: 'test' })).objects ?? [];
|
|
108
|
+
|
|
109
|
+
const model = useTextModel({
|
|
110
|
+
text: obj?.content,
|
|
111
|
+
identity,
|
|
112
|
+
space,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (!model) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
// <div className={mx(fixedInsetFlexLayout, groupSurface)}>
|
|
121
|
+
<div className='flex justify-center overflow-y-scroll'>
|
|
122
|
+
<div className='flex flex-col w-[800px] py-16'>
|
|
123
|
+
<MarkdownEditor model={model} />
|
|
124
|
+
<div className='flex shrink-0 h-[300px]'></div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
// </div>
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const WithEcho = {
|
|
132
|
+
render: () => {
|
|
133
|
+
setGlobalAutomergePreference(true);
|
|
134
|
+
return (
|
|
135
|
+
<ClientRepeater
|
|
136
|
+
count={2}
|
|
137
|
+
createSpace
|
|
138
|
+
onCreateSpace={async (space) => {
|
|
139
|
+
space.db.add(
|
|
140
|
+
new Expando({
|
|
141
|
+
type: 'test',
|
|
142
|
+
content: new TextObject('Hello world!'),
|
|
143
|
+
}),
|
|
144
|
+
);
|
|
145
|
+
}}
|
|
146
|
+
Component={EchoStory}
|
|
147
|
+
/>
|
|
148
|
+
);
|
|
149
|
+
},
|
|
150
|
+
decorators: [withTheme],
|
|
151
|
+
};
|
|
@@ -17,7 +17,8 @@ import { type Identity } from '@dxos/react-client/halo';
|
|
|
17
17
|
import { joinCommonSpace, TestBuilder, textGenerator } from '@dxos/react-client/testing';
|
|
18
18
|
import { YText } from '@dxos/text-model';
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { YAwarenessProvider } from '../../hooks';
|
|
21
|
+
import { cursorColor } from '../../styles';
|
|
21
22
|
import { EditorDocument, types as schema } from '../../testing';
|
|
22
23
|
|
|
23
24
|
export default {
|
|
@@ -35,7 +36,7 @@ const updateEditor = async (id: number, editor: HTMLDivElement, identity: Identi
|
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
const { awareness } = new
|
|
39
|
+
const { awareness } = new YAwarenessProvider({ space, doc, channel: `yjs.awareness.${text.id}` });
|
|
39
40
|
awareness.setLocalStateField('user', {
|
|
40
41
|
name: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
|
|
41
42
|
// TODO(wittjosiah): Pick colours from theme based on identity key.
|