@dxos/react-ui-editor 0.4.2-main.ec5936e → 0.4.2

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 (41) hide show
  1. package/dist/lib/browser/index.mjs +185 -233
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +0 -20
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +0 -20
  7. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/automerge.stories.d.ts +2 -22
  9. package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/hooks.stories.d.ts +0 -20
  11. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/Toolbar/Toolbar.d.ts +10 -7
  13. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  14. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +6 -23
  15. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  16. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  17. package/dist/types/src/extensions/automerge/{sync.d.ts → semaphore.d.ts} +5 -6
  18. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -0
  19. package/dist/types/src/index.d.ts +1 -1
  20. package/dist/types/src/index.d.ts.map +1 -1
  21. package/dist/types/src/styles/index.d.ts +0 -1
  22. package/dist/types/src/styles/index.d.ts.map +1 -1
  23. package/package.json +24 -24
  24. package/src/components/TextEditor/MarkdownEditor.stories.tsx +16 -13
  25. package/src/components/TextEditor/TextEditor.stories.tsx +14 -11
  26. package/src/components/TextEditor/TextEditor.tsx +3 -3
  27. package/src/components/TextEditor/automerge.stories.tsx +44 -38
  28. package/src/components/TextEditor/hooks.stories.tsx +16 -15
  29. package/src/components/Toolbar/Toolbar.stories.tsx +12 -16
  30. package/src/components/Toolbar/Toolbar.tsx +129 -156
  31. package/src/extensions/automerge/automerge.ts +21 -5
  32. package/src/extensions/automerge/{sync.ts → semaphore.ts} +34 -32
  33. package/src/index.ts +1 -1
  34. package/src/styles/index.ts +0 -1
  35. package/dist/types/src/extensions/automerge/sync.d.ts.map +0 -1
  36. package/dist/types/src/styles/layout.d.ts +0 -2
  37. package/dist/types/src/styles/layout.d.ts.map +0 -1
  38. package/dist/types/src/translations.d.ts +0 -20
  39. package/dist/types/src/translations.d.ts.map +0 -1
  40. package/src/styles/layout.ts +0 -6
  41. package/src/translations.ts +0 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-editor",
3
- "version": "0.4.2-main.ec5936e",
3
+ "version": "0.4.2",
4
4
  "description": "Document editing experience within a DXOS shell.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -36,20 +36,20 @@
36
36
  "lodash.pick": "^4.4.0",
37
37
  "lodash.sortby": "^4.7.0",
38
38
  "style-mod": "^4.1.0",
39
- "@dxos/async": "0.4.2-main.ec5936e",
40
- "@dxos/display-name": "0.4.2-main.ec5936e",
41
- "@dxos/context": "0.4.2-main.ec5936e",
42
- "@dxos/debug": "0.4.2-main.ec5936e",
43
- "@dxos/automerge": "0.4.2-main.ec5936e",
44
- "@dxos/echo-schema": "0.4.2-main.ec5936e",
45
- "@dxos/invariant": "0.4.2-main.ec5936e",
46
- "@dxos/protocols": "0.4.2-main.ec5936e",
47
- "@dxos/log": "0.4.2-main.ec5936e",
48
- "@dxos/react-async": "0.4.2-main.ec5936e",
49
- "@dxos/react-ui-theme": "0.4.2-main.ec5936e",
50
- "@dxos/text-model": "0.4.2-main.ec5936e",
51
- "@dxos/util": "0.4.2-main.ec5936e",
52
- "@dxos/react-ui": "0.4.2-main.ec5936e"
39
+ "@dxos/async": "0.4.2",
40
+ "@dxos/context": "0.4.2",
41
+ "@dxos/display-name": "0.4.2",
42
+ "@dxos/debug": "0.4.2",
43
+ "@dxos/echo-schema": "0.4.2",
44
+ "@dxos/log": "0.4.2",
45
+ "@dxos/invariant": "0.4.2",
46
+ "@dxos/protocols": "0.4.2",
47
+ "@dxos/react-ui": "0.4.2",
48
+ "@dxos/react-async": "0.4.2",
49
+ "@dxos/automerge": "0.4.2",
50
+ "@dxos/react-ui-theme": "0.4.2",
51
+ "@dxos/text-model": "0.4.2",
52
+ "@dxos/util": "0.4.2"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@automerge/automerge-repo-network-broadcastchannel": "^1.1.0",
@@ -76,20 +76,20 @@
76
76
  "vite-plugin-top-level-await": "^1.4.1",
77
77
  "vite-plugin-wasm": "^3.3.0",
78
78
  "vitest": "^1.2.1",
79
- "@dxos/config": "0.4.2-main.ec5936e",
80
- "@dxos/echo-signals": "0.4.2-main.ec5936e",
81
- "@dxos/echo-typegen": "0.4.2-main.ec5936e",
82
- "@dxos/keyboard": "0.4.2-main.ec5936e",
83
- "@dxos/random": "0.4.2-main.ec5936e",
84
- "@dxos/react-client": "0.4.2-main.ec5936e",
85
- "@dxos/react-ui": "0.4.2-main.ec5936e",
86
- "@dxos/storybook-utils": "0.4.2-main.ec5936e"
79
+ "@dxos/config": "0.4.2",
80
+ "@dxos/echo-typegen": "0.4.2",
81
+ "@dxos/echo-signals": "0.4.2",
82
+ "@dxos/random": "0.4.2",
83
+ "@dxos/keyboard": "0.4.2",
84
+ "@dxos/react-client": "0.4.2",
85
+ "@dxos/react-ui": "0.4.2",
86
+ "@dxos/storybook-utils": "0.4.2"
87
87
  },
88
88
  "peerDependencies": {
89
89
  "@phosphor-icons/react": "^2.0.5",
90
90
  "react": "^18.2.0",
91
91
  "react-dom": "^18.2.0",
92
- "@dxos/react-client": "0.4.2-main.ec5936e"
92
+ "@dxos/react-client": "0.4.2"
93
93
  },
94
94
  "publishConfig": {
95
95
  "access": "public"
@@ -14,7 +14,7 @@ import { TextObject } from '@dxos/echo-schema';
14
14
  import { keySymbols, parseShortcut } from '@dxos/keyboard';
15
15
  import { PublicKey } from '@dxos/keys';
16
16
  import { faker } from '@dxos/random';
17
- import { getSize, mx, textBlockWidth } from '@dxos/react-ui-theme';
17
+ import { fixedInsetFlexLayout, getSize, groupSurface, mx } from '@dxos/react-ui-theme';
18
18
  import { withTheme } from '@dxos/storybook-utils';
19
19
 
20
20
  import { MarkdownEditor, type TextEditorProps } from './TextEditor';
@@ -39,7 +39,6 @@ import {
39
39
  annotations,
40
40
  } from '../../extensions';
41
41
  import { type Comment, useTextModel } from '../../hooks';
42
- import translations from '../../translations';
43
42
 
44
43
  faker.seed(101);
45
44
 
@@ -225,16 +224,21 @@ const Story = ({ text, comments, placeholder = 'New document.', ...props }: Stor
225
224
  }
226
225
 
227
226
  return (
228
- <MarkdownEditor
229
- ref={view}
230
- model={model}
231
- placeholder={placeholder}
232
- slots={{
233
- root: { className: mx(textBlockWidth, 'min-bs-dvh') },
234
- editor: { className: 'min-bs-dvh p-2 bg-white dark:bg-black' },
235
- }}
236
- {...props}
237
- />
227
+ <div className={mx(fixedInsetFlexLayout, groupSurface)}>
228
+ <div className='flex h-full justify-center'>
229
+ <div className='flex flex-col h-full w-[800px] overflow-y-auto'>
230
+ <MarkdownEditor
231
+ ref={view}
232
+ model={model}
233
+ placeholder={placeholder}
234
+ slots={{
235
+ editor: { className: 'bs-full p-2 bg-white dark:bg-black' },
236
+ }}
237
+ {...props}
238
+ />
239
+ </div>
240
+ </div>
241
+ </div>
238
242
  );
239
243
  };
240
244
 
@@ -243,7 +247,6 @@ export default {
243
247
  component: MarkdownEditor,
244
248
  decorators: [withTheme],
245
249
  render: Story,
246
- parameters: { translations, layout: 'fullscreen' },
247
250
  };
248
251
 
249
252
  const defaults = [
@@ -7,13 +7,12 @@ import '@dxosTheme';
7
7
  import React, { useState } from 'react';
8
8
 
9
9
  import { TextObject } from '@dxos/echo-schema';
10
- import { mx, textBlockWidth } from '@dxos/react-ui-theme';
10
+ import { fixedInsetFlexLayout, groupSurface, mx } from '@dxos/react-ui-theme';
11
11
  import { withTheme } from '@dxos/storybook-utils';
12
12
 
13
13
  import { TextEditor, type TextEditorProps } from './TextEditor';
14
14
  import { listener } from '../../extensions';
15
15
  import { useTextModel } from '../../hooks';
16
- import translations from '../../translations';
17
16
 
18
17
  const initialText = [
19
18
  '# TextEditor',
@@ -37,14 +36,19 @@ const Story = ({
37
36
  }
38
37
 
39
38
  return (
40
- <TextEditor
41
- model={model}
42
- slots={{
43
- root: { className: mx(textBlockWidth, 'min-bs-dvh') },
44
- editor: { className: 'min-bs-dvh p-2 bg-white dark:bg-black' },
45
- }}
46
- {...props}
47
- />
39
+ <div className={mx(fixedInsetFlexLayout, groupSurface)}>
40
+ <div className='flex justify-center'>
41
+ <div className='flex flex-col w-[800px] overflow-y-auto'>
42
+ <TextEditor
43
+ model={model}
44
+ slots={{
45
+ editor: { className: 'bs-full p-2 bg-white dark:bg-black' },
46
+ }}
47
+ {...props}
48
+ />
49
+ </div>
50
+ </div>
51
+ </div>
48
52
  );
49
53
  };
50
54
 
@@ -53,7 +57,6 @@ export default {
53
57
  component: TextEditor,
54
58
  decorators: [withTheme],
55
59
  render: Story,
56
- parameters: { translations, layout: 'fullscreen' },
57
60
  };
58
61
 
59
62
  export const Default = {
@@ -20,7 +20,7 @@ import React, {
20
20
 
21
21
  import { log } from '@dxos/log';
22
22
  import { useThemeContext } from '@dxos/react-ui';
23
- import { focusRing } from '@dxos/react-ui-theme';
23
+ import { focusRing, mx } from '@dxos/react-ui-theme';
24
24
  import { isNotFalsy } from '@dxos/util';
25
25
 
26
26
  import { createBasicBundle, createMarkdownExtensions } from '../../extensions';
@@ -259,10 +259,10 @@ export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
259
259
 
260
260
  export const defaultSlots: TextEditorSlots = {
261
261
  root: {
262
- className: focusRing,
262
+ className: mx('grow', focusRing),
263
263
  },
264
264
  editor: {
265
- className: 'min-bs-full',
265
+ className: 'bs-full',
266
266
  },
267
267
  };
268
268
 
@@ -5,10 +5,11 @@
5
5
  import '@dxosTheme';
6
6
 
7
7
  import { BroadcastChannelNetworkAdapter } from '@automerge/automerge-repo-network-broadcastchannel';
8
- import '@preact/signals-react';
9
- import { EditorView } from '@codemirror/view'; // Register react integration.
8
+ import { EditorView } from '@codemirror/view';
9
+ import '@preact/signals-react'; // Register react integration
10
+ import { basicSetup } from 'codemirror';
10
11
  import get from 'lodash.get';
11
- import React, { useEffect, useState } from 'react';
12
+ import React, { useEffect, useRef, useState } from 'react';
12
13
 
13
14
  import { type Prop } from '@dxos/automerge/automerge';
14
15
  import { Repo, type DocHandle } from '@dxos/automerge/automerge-repo';
@@ -20,42 +21,38 @@ import { ClientRepeater } from '@dxos/react-client/testing';
20
21
  import { withTheme } from '@dxos/storybook-utils';
21
22
 
22
23
  import { MarkdownEditor } from './TextEditor';
23
- import { type IDocHandle, automerge, awareness, createBasicBundle } from '../../extensions';
24
- import { useTextEditor, useTextModel } from '../../hooks';
25
- import { defaultTheme } from '../../themes';
26
- import translations from '../../translations';
24
+ import { type IDocHandle, automerge, awareness } from '../../extensions';
25
+ import { useTextModel } from '../../hooks';
27
26
 
28
- const initialContent = 'Hello world!';
29
-
30
- type TestObject = {
31
- text: string;
32
- };
27
+ // TODO(burdon): Move to components.
33
28
 
34
29
  type EditorProps = {
35
30
  handle: IDocHandle;
36
- path?: Prop[];
31
+ path: Prop[];
37
32
  };
38
33
 
39
- const Editor = ({ handle, path = ['text'] }: EditorProps) => {
40
- const { parentRef } = useTextEditor({
41
- autoFocus: true,
42
- doc: get(handle.docSync()!, path),
43
- extensions: [
44
- //
45
- EditorView.baseTheme(defaultTheme),
46
- EditorView.editorAttributes.of({ class: 'p-2 bg-white' }),
47
- createBasicBundle({ placeholder: 'Type here...' }),
48
- automerge({ handle, path }),
49
- awareness(),
50
- ],
51
- });
34
+ const Editor = ({ handle, path }: EditorProps) => {
35
+ const containerRef = useRef<HTMLDivElement | null>(null);
36
+ const editorRoot = useRef<EditorView>();
37
+
38
+ useEffect(() => {
39
+ const view = (editorRoot.current = new EditorView({
40
+ doc: get(handle.docSync()!, path),
41
+ extensions: [basicSetup, automerge({ handle, path }), awareness()],
42
+ parent: containerRef.current as any,
43
+ }));
44
+
45
+ return () => {
46
+ view.destroy();
47
+ };
48
+ }, []);
52
49
 
53
- return <div ref={parentRef} />;
50
+ return <div className='codemirror-editor' ref={containerRef} onKeyDown={(evt) => evt.stopPropagation()} />;
54
51
  };
55
52
 
56
53
  const Story = () => {
57
- const [object1, setObject1] = useState<DocHandle<TestObject>>();
58
- const [object2, setObject2] = useState<DocHandle<TestObject>>();
54
+ const [object1, setObject1] = useState<DocHandle<any> | null>(null);
55
+ const [object2, setObject2] = useState<DocHandle<any> | null>(null);
59
56
 
60
57
  useEffect(() => {
61
58
  queueMicrotask(async () => {
@@ -67,8 +64,8 @@ const Story = () => {
67
64
  });
68
65
 
69
66
  const object1 = repo1.create();
70
- object1.change((doc: TestObject) => {
71
- doc.text = initialContent;
67
+ object1.change((doc: any) => {
68
+ doc.text = 'Hello world!';
72
69
  });
73
70
 
74
71
  const object2 = repo2.find(object1.url);
@@ -84,9 +81,13 @@ const Story = () => {
84
81
  }
85
82
 
86
83
  return (
87
- <div role='none' className='grid grid-cols-2 bs-full is-full gap-2'>
88
- <Editor handle={object1} path={['text']} />
89
- <Editor handle={object2} path={['text']} />
84
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', width: '100vw' }}>
85
+ <div>
86
+ <Editor handle={object1} path={['text']} />
87
+ </div>
88
+ <div>
89
+ <Editor handle={object2} path={['text']} />
90
+ </div>
90
91
  </div>
91
92
  );
92
93
  };
@@ -95,7 +96,6 @@ export default {
95
96
  title: 'react-ui-editor/Automerge',
96
97
  component: Editor,
97
98
  render: () => <Story />,
98
- parameters: { translations, layout: 'fullscreen' },
99
99
  };
100
100
 
101
101
  export const Default = {};
@@ -115,11 +115,16 @@ const EchoStory = ({ spaceKey }: { spaceKey: PublicKey }) => {
115
115
  return null;
116
116
  }
117
117
 
118
- return <MarkdownEditor model={model} />;
118
+ return (
119
+ <div className='flex overflow-hidden'>
120
+ <div className='flex flex-col w-[800px] overflow-y-scroll'>
121
+ <MarkdownEditor model={model} />
122
+ </div>
123
+ </div>
124
+ );
119
125
  };
120
126
 
121
127
  export const WithEcho = {
122
- decorators: [withTheme],
123
128
  render: () => {
124
129
  return (
125
130
  <ClientRepeater
@@ -129,7 +134,7 @@ export const WithEcho = {
129
134
  space.db.add(
130
135
  new Expando({
131
136
  type: 'test',
132
- content: new TextObject(initialContent),
137
+ content: new TextObject('Hello world!'),
133
138
  }),
134
139
  );
135
140
  }}
@@ -137,4 +142,5 @@ export const WithEcho = {
137
142
  />
138
143
  );
139
144
  },
145
+ decorators: [withTheme],
140
146
  };
@@ -7,7 +7,7 @@ import '@dxosTheme';
7
7
  import React from 'react';
8
8
 
9
9
  import { useThemeContext } from '@dxos/react-ui';
10
- import { mx, textBlockWidth } from '@dxos/react-ui-theme';
10
+ import { fixedInsetFlexLayout, groupSurface, mx } from '@dxos/react-ui-theme';
11
11
  import { withTheme } from '@dxos/storybook-utils';
12
12
 
13
13
  import { TextEditor } from './TextEditor';
@@ -24,9 +24,7 @@ import {
24
24
  useFormattingState,
25
25
  } from '../../extensions';
26
26
  import { createDataExtensions, createThemeExtensions, useActionHandler, useTextEditor } from '../../hooks';
27
- import { editorWithToolbarLayout } from '../../styles/layout';
28
27
  import { markdownTheme } from '../../themes';
29
- import translations from '../../translations';
30
28
  import { Toolbar } from '../Toolbar';
31
29
 
32
30
  // TODO(burdon): Demo toolbar with hooks.
@@ -55,7 +53,11 @@ const Story = ({ autoFocus, placeholder, doc, readonly }: StoryProps) => {
55
53
  themeMode,
56
54
  theme: markdownTheme,
57
55
  slots: {
58
- editor: { className: 'min-bs-full' },
56
+ // TODO(burdon): Document classes re base theme.
57
+ // Semantic tokens (e.g., replacement of input surface).
58
+ editor: {
59
+ className: 'h-full p-4 bg-white text-black dark:bg-black dark:text-white',
60
+ },
59
61
  },
60
62
  }),
61
63
  // TODO(burdon): Move lineWrapping.
@@ -78,16 +80,16 @@ const Story = ({ autoFocus, placeholder, doc, readonly }: StoryProps) => {
78
80
  // FIXME This doesn't update the state on view changes. Also not
79
81
  // sure if view is even guaranteed to exist at this point.
80
82
  return (
81
- <div role='none' className={mx('fixed inset-0', editorWithToolbarLayout)}>
82
- <Toolbar.Root onAction={handleAction} state={formattingState} classNames={textBlockWidth}>
83
- <Toolbar.Markdown />
84
- </Toolbar.Root>
85
- <div role='none' className='overflow-y-auto'>
86
- <div
87
- role='textbox'
88
- className={mx(textBlockWidth, 'min-bs-full p-4 surface-attention fg-base')}
89
- ref={parentRef}
90
- />
83
+ <div className={mx(fixedInsetFlexLayout, groupSurface)}>
84
+ <div className='flex h-full justify-center'>
85
+ <div className='flex flex-col h-full w-[800px]'>
86
+ <Toolbar.Root onAction={handleAction} state={formattingState}>
87
+ <Toolbar.Markdown />
88
+ </Toolbar.Root>
89
+
90
+ {/* TODO(burdon): Handle scrolling in component wrapper (like this). */}
91
+ <div role='none' className='h-full overflow-y-auto' ref={parentRef} />
92
+ </div>
91
93
  </div>
92
94
  </div>
93
95
  );
@@ -98,7 +100,6 @@ export default {
98
100
  component: TextEditor,
99
101
  decorators: [withTheme],
100
102
  render: (args: StoryProps) => <Story {...args} />,
101
- parameters: { translations, layout: 'fullscreen' },
102
103
  };
103
104
 
104
105
  export const Default = {
@@ -9,7 +9,7 @@ import React, { type FC, useMemo, useState } from 'react';
9
9
  import { TextObject } from '@dxos/echo-schema';
10
10
  import { PublicKey } from '@dxos/keys';
11
11
  import { faker } from '@dxos/random';
12
- import { mx, textBlockWidth } from '@dxos/react-ui-theme';
12
+ import { fixedInsetFlexLayout, groupSurface, mx } from '@dxos/react-ui-theme';
13
13
  import { withTheme } from '@dxos/storybook-utils';
14
14
 
15
15
  import { Toolbar } from './Toolbar';
@@ -25,8 +25,6 @@ import {
25
25
  useFormattingState,
26
26
  } from '../../extensions';
27
27
  import { type Comment, useActionHandler, useEditorView, useTextModel } from '../../hooks';
28
- import { editorWithToolbarLayout } from '../../styles';
29
- import translations from '../../translations';
30
28
  import { MarkdownEditor } from '../TextEditor';
31
29
 
32
30
  faker.seed(101);
@@ -65,18 +63,17 @@ const Story: FC<{ content: string }> = ({ content }) => {
65
63
  }
66
64
 
67
65
  return (
68
- <div role='none' className={mx('fixed inset-0', editorWithToolbarLayout)}>
69
- <Toolbar.Root onAction={handleAction} state={formattingState} classNames={textBlockWidth}>
70
- <Toolbar.Markdown />
71
- <Toolbar.Separator />
72
- <Toolbar.Extended />
73
- </Toolbar.Root>
74
- <MarkdownEditor
75
- ref={editorRef}
76
- model={model}
77
- extensions={extensions}
78
- slots={{ root: { className: mx(textBlockWidth, 'pli-2') } }}
79
- />
66
+ <div className={mx(fixedInsetFlexLayout, groupSurface)}>
67
+ <div className='flex h-full justify-center'>
68
+ <div className='flex flex-col h-full w-[800px]'>
69
+ <Toolbar.Root onAction={handleAction} state={formattingState}>
70
+ <Toolbar.Markdown />
71
+ <Toolbar.Separator />
72
+ <Toolbar.Extended />
73
+ </Toolbar.Root>
74
+ <MarkdownEditor ref={editorRef} model={model} extensions={extensions} />
75
+ </div>
76
+ </div>
80
77
  </div>
81
78
  );
82
79
  };
@@ -86,7 +83,6 @@ export default {
86
83
  component: Toolbar,
87
84
  render: (args: any) => <Story {...args} />,
88
85
  decorators: [withTheme],
89
- parameters: { translations, layout: 'fullscreen' },
90
86
  };
91
87
 
92
88
  const content = [