@dxos/react-ui-editor 0.8.4-main.ef1bc66f44 → 0.8.4-main.effb148878

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 (149) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/index.mjs +796 -755
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/translations.mjs +39 -0
  7. package/dist/lib/browser/translations.mjs.map +7 -0
  8. package/dist/lib/node-esm/index.mjs +796 -755
  9. package/dist/lib/node-esm/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/meta.json +1 -1
  11. package/dist/lib/node-esm/translations.mjs +41 -0
  12. package/dist/lib/node-esm/translations.mjs.map +7 -0
  13. package/dist/types/src/components/Editor/Editor.d.ts +36 -25
  14. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  15. package/dist/types/src/components/Editor/Editor.stories.d.ts +4 -4
  16. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/{EditorContent/EditorContent.d.ts → Editor/EditorView.d.ts} +5 -5
  18. package/dist/types/src/components/Editor/EditorView.d.ts.map +1 -0
  19. package/dist/types/src/components/Editor/controller.d.ts.map +1 -0
  20. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts +1 -3
  21. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts.map +1 -1
  23. package/dist/types/src/components/EditorMenuProvider/menu.d.ts.map +1 -1
  24. package/dist/types/src/components/EditorMenuProvider/popover.d.ts +2 -1
  25. package/dist/types/src/components/EditorMenuProvider/popover.d.ts.map +1 -1
  26. package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -1
  27. package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -1
  28. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +2 -2
  29. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  30. package/dist/types/src/components/EditorToolbar/blocks.d.ts +4 -18
  31. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  32. package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -18
  33. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  34. package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -18
  35. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  36. package/dist/types/src/components/EditorToolbar/image.d.ts +3 -8
  37. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  38. package/dist/types/src/components/EditorToolbar/index.d.ts +1 -2
  39. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  40. package/dist/types/src/components/EditorToolbar/lists.d.ts +6 -0
  41. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -0
  42. package/dist/types/src/components/EditorToolbar/search.d.ts +3 -8
  43. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  44. package/dist/types/src/components/EditorToolbar/types.d.ts +6 -0
  45. package/dist/types/src/components/EditorToolbar/types.d.ts.map +1 -0
  46. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +5 -19
  47. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  48. package/dist/types/src/components/index.d.ts +0 -2
  49. package/dist/types/src/components/index.d.ts.map +1 -1
  50. package/dist/types/src/extensions/Assistant.stories.d.ts +10 -0
  51. package/dist/types/src/extensions/Assistant.stories.d.ts.map +1 -0
  52. package/dist/types/src/extensions/assistant-extension.d.ts +24 -0
  53. package/dist/types/src/extensions/assistant-extension.d.ts.map +1 -0
  54. package/dist/types/src/extensions/index.d.ts +2 -0
  55. package/dist/types/src/extensions/index.d.ts.map +1 -0
  56. package/dist/types/src/hooks/index.d.ts +1 -0
  57. package/dist/types/src/hooks/index.d.ts.map +1 -1
  58. package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts +25 -0
  59. package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts.map +1 -0
  60. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  61. package/dist/types/src/index.d.ts +1 -2
  62. package/dist/types/src/index.d.ts.map +1 -1
  63. package/dist/types/src/stories/Automerge.stories.d.ts +25 -24
  64. package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
  65. package/dist/types/src/stories/Comments.stories.d.ts +2 -2
  66. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  67. package/dist/types/src/stories/EditorToolbar.stories.d.ts +28 -26
  68. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  69. package/dist/types/src/stories/Experimental.stories.d.ts +3 -3
  70. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  71. package/dist/types/src/stories/Markdown.stories.d.ts +2 -2
  72. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  73. package/dist/types/src/stories/Outliner.stories.d.ts +2 -2
  74. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  75. package/dist/types/src/stories/Popover.stories.d.ts +2 -2
  76. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -1
  77. package/dist/types/src/stories/Preview.stories.d.ts +2 -2
  78. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  79. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
  80. package/dist/types/src/stories/TextEditor.stories.d.ts +2 -2
  81. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  82. package/dist/types/src/stories/Theme.stories.d.ts.map +1 -1
  83. package/dist/types/src/stories/components/EditorStory.d.ts +4 -4
  84. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  85. package/dist/types/src/stories/components/util.d.ts +3 -2
  86. package/dist/types/src/stories/components/util.d.ts.map +1 -1
  87. package/dist/types/src/translations.d.ts +24 -24
  88. package/dist/types/src/translations.d.ts.map +1 -1
  89. package/dist/types/src/util/react.d.ts +2 -5
  90. package/dist/types/src/util/react.d.ts.map +1 -1
  91. package/dist/types/tsconfig.tsbuildinfo +1 -1
  92. package/package.json +77 -68
  93. package/src/components/Editor/Editor.stories.tsx +15 -21
  94. package/src/components/Editor/Editor.tsx +54 -53
  95. package/src/components/Editor/EditorView.tsx +102 -0
  96. package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +19 -24
  97. package/src/components/EditorMenuProvider/menu-presets.ts +1 -0
  98. package/src/components/EditorMenuProvider/popover.ts +3 -1
  99. package/src/components/EditorMenuProvider/useEditorMenu.ts +8 -1
  100. package/src/components/EditorPreviewProvider/EditorPreviewProvider.tsx +1 -1
  101. package/src/components/EditorToolbar/EditorToolbar.tsx +31 -65
  102. package/src/components/EditorToolbar/blocks.ts +54 -46
  103. package/src/components/EditorToolbar/formatting.ts +44 -45
  104. package/src/components/EditorToolbar/headings.ts +44 -50
  105. package/src/components/EditorToolbar/image.ts +16 -21
  106. package/src/components/EditorToolbar/index.ts +2 -3
  107. package/src/components/EditorToolbar/lists.ts +58 -0
  108. package/src/components/EditorToolbar/search.ts +16 -21
  109. package/src/components/EditorToolbar/types.ts +8 -0
  110. package/src/components/EditorToolbar/view-mode.ts +37 -43
  111. package/src/components/index.ts +0 -3
  112. package/src/extensions/Assistant.stories.tsx +112 -0
  113. package/src/extensions/assistant-extension.tsx +223 -0
  114. package/src/extensions/index.ts +5 -0
  115. package/src/hooks/index.ts +1 -0
  116. package/src/hooks/useBasicMarkdownExtensions.ts +55 -0
  117. package/src/index.ts +1 -4
  118. package/src/stories/Automerge.stories.tsx +18 -16
  119. package/src/stories/Comments.stories.tsx +6 -6
  120. package/src/stories/EditorToolbar.stories.tsx +37 -65
  121. package/src/stories/Experimental.stories.tsx +12 -12
  122. package/src/stories/Markdown.stories.tsx +2 -2
  123. package/src/stories/Outliner.stories.tsx +4 -5
  124. package/src/stories/Popover.stories.tsx +10 -11
  125. package/src/stories/Preview.stories.tsx +51 -43
  126. package/src/stories/Tags.stories.tsx +5 -5
  127. package/src/stories/TextEditor.stories.tsx +2 -2
  128. package/src/stories/Theme.stories.tsx +4 -4
  129. package/src/stories/components/EditorStory.tsx +19 -12
  130. package/src/stories/components/util.tsx +49 -50
  131. package/src/translations.ts +29 -24
  132. package/src/util/react.tsx +4 -13
  133. package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +0 -1
  134. package/dist/types/src/components/EditorContent/controller.d.ts.map +0 -1
  135. package/dist/types/src/components/EditorContent/index.d.ts +0 -3
  136. package/dist/types/src/components/EditorContent/index.d.ts.map +0 -1
  137. package/dist/types/src/components/EditorToolbar/actions.d.ts +0 -24
  138. package/dist/types/src/components/EditorToolbar/actions.d.ts.map +0 -1
  139. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts +0 -11
  140. package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts.map +0 -1
  141. package/dist/types/src/stories/CommandDialog.stories.d.ts +0 -14
  142. package/dist/types/src/stories/CommandDialog.stories.d.ts.map +0 -1
  143. package/src/components/EditorContent/EditorContent.tsx +0 -83
  144. package/src/components/EditorContent/index.ts +0 -6
  145. package/src/components/EditorToolbar/actions.ts +0 -87
  146. package/src/components/EditorToolbar/useEditorToolbar.ts +0 -20
  147. package/src/stories/CommandDialog.stories.tsx +0 -81
  148. /package/dist/types/src/components/{EditorContent → Editor}/controller.d.ts +0 -0
  149. /package/src/components/{EditorContent → Editor}/controller.ts +0 -0
@@ -2,26 +2,26 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Repo } from '@automerge/automerge-repo';
5
+ import { Repo, initSubduction } from '@automerge/automerge-repo';
6
6
  import { BroadcastChannelNetworkAdapter } from '@automerge/automerge-repo-network-broadcastchannel';
7
7
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import React, { useCallback, useEffect, useState } from 'react';
9
9
 
10
- import { Obj, Ref } from '@dxos/echo';
11
- import { TestSchema } from '@dxos/echo/testing';
10
+ import { Obj, Query, Ref } from '@dxos/echo';
12
11
  import { DocAccessor, createDocAccessor } from '@dxos/echo-db';
12
+ import { TestSchema } from '@dxos/echo/testing';
13
13
  import { log } from '@dxos/log';
14
14
  import { type Messenger } from '@dxos/protocols';
15
- import { Query, useQuery, useSpace } from '@dxos/react-client/echo';
15
+ import { useQuery, useSpace } from '@dxos/react-client/echo';
16
16
  import { type Identity, useIdentity } from '@dxos/react-client/halo';
17
17
  import { useClientStory, withMultiClientProvider } from '@dxos/react-client/testing';
18
18
  import { Button, useThemeContext } from '@dxos/react-ui';
19
- import { withTheme } from '@dxos/react-ui/testing';
20
- import { render } from '@dxos/storybook-utils';
19
+ import { withLayout, withTheme, Loading } from '@dxos/react-ui/testing';
21
20
  import { createBasicExtensions, createDataExtensions, createThemeExtensions } from '@dxos/ui-editor';
22
21
 
22
+ import { translations } from '#translations';
23
+
23
24
  import { useTextEditor } from '../hooks';
24
- import { translations } from '../translations';
25
25
 
26
26
  const initialContent = 'Hello world!';
27
27
 
@@ -44,14 +44,14 @@ const Editor = ({ source, messenger, identity, autoFocus }: EditorProps) => {
44
44
  initialValue: DocAccessor.getValue(source),
45
45
  extensions: [
46
46
  createBasicExtensions({ placeholder: 'Type here...', search: true }),
47
- createThemeExtensions({ themeMode, slots: { scroll: { className: 'p-2' } } }),
47
+ createThemeExtensions({ themeMode, slots: { scroller: { className: 'p-2' } } }),
48
48
  createDataExtensions({ id: 'test', text: source, messenger, identity }),
49
49
  ],
50
50
  }),
51
51
  [source, themeMode],
52
52
  );
53
53
 
54
- return <div ref={parentRef} className='flex is-full' />;
54
+ return <div ref={parentRef} className='flex w-full' />;
55
55
  };
56
56
 
57
57
  const DefaultStory = () => {
@@ -60,6 +60,9 @@ const DefaultStory = () => {
60
60
 
61
61
  useEffect(() => {
62
62
  queueMicrotask(async () => {
63
+ // Subduction-fork `Repo` constructs a `MemorySigner` internally; WASM must be
64
+ // initialized first or the constructor throws `'set_subduction_logger' of undefined`.
65
+ await initSubduction();
63
66
  const repo1 = new Repo({ network: [new BroadcastChannelNetworkAdapter()] });
64
67
  const repo2 = new Repo({ network: [new BroadcastChannelNetworkAdapter()] });
65
68
 
@@ -77,11 +80,11 @@ const DefaultStory = () => {
77
80
  }, []);
78
81
 
79
82
  if (!object1 || !object2) {
80
- return null;
83
+ return <Loading data={{ object1: !!object1, object2: !!object2 }} />;
81
84
  }
82
85
 
83
86
  return (
84
- <div className='bs-full is-full grid grid-cols-2 gap-4'>
87
+ <div className='h-full w-full grid grid-cols-2 gap-4'>
85
88
  <Editor source={object1} autoFocus />
86
89
  <Editor source={object2} />
87
90
  </div>
@@ -117,7 +120,7 @@ const EchoStory = () => {
117
120
  }, [objects, source]);
118
121
 
119
122
  return (
120
- <div className='bs-full is-full flex flex-col overflow-hidden'>
123
+ <div className='h-full w-full flex flex-col overflow-hidden'>
121
124
  <pre className='p-2 text-xs text-subdued'>
122
125
  {JSON.stringify({ index, identity: identity?.identityKey.truncate(), spaceId, objects }, null, 2)}
123
126
  </pre>
@@ -137,6 +140,7 @@ const EchoStory = () => {
137
140
  const meta = {
138
141
  title: 'ui/react-ui-editor/Automerge',
139
142
  component: Editor as any,
143
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
140
144
  parameters: {
141
145
  layout: 'fullscreen',
142
146
  translations,
@@ -149,14 +153,12 @@ type Story = StoryObj<typeof meta>;
149
153
 
150
154
  // TODO(burdon): ERROR: factories.ts:126 Error: Non-base58 character
151
155
  export const Default: Story = {
152
- decorators: [withTheme()],
153
- render: render(DefaultStory),
156
+ render: DefaultStory,
154
157
  };
155
158
 
156
159
  // TODO(burdon): Failing (doesn't sync)
157
160
  export const WithEcho: Story = {
158
161
  decorators: [
159
- withTheme(),
160
162
  withMultiClientProvider({
161
163
  numClients: 2,
162
164
  createIdentity: true,
@@ -171,5 +173,5 @@ export const WithEcho: Story = {
171
173
  },
172
174
  }),
173
175
  ],
174
- render: render(EchoStory),
176
+ render: EchoStory,
175
177
  };
@@ -9,18 +9,18 @@ import React, { type FC, useContext, useMemo } from 'react';
9
9
  import { keySymbols, parseShortcut } from '@dxos/keyboard';
10
10
  import { PublicKey } from '@dxos/keys';
11
11
  import { log } from '@dxos/log';
12
- import { withTheme } from '@dxos/react-ui/testing';
12
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
13
13
  import { withRegistry } from '@dxos/storybook-utils';
14
- import { type Comment, annotations, comments, createExternalCommentSync } from '@dxos/ui-editor';
14
+ import { annotations, comments, createExternalCommentSync } from '@dxos/ui-editor';
15
+ import { type Comment } from '@dxos/ui-editor/types';
15
16
 
16
17
  import { createRenderer, str } from '../util';
17
-
18
18
  import { EditorStory, content, longText } from './components';
19
19
 
20
20
  const meta = {
21
21
  title: 'ui/react-ui-editor/Comments',
22
22
  component: EditorStory,
23
- decorators: [withRegistry, withTheme()],
23
+ decorators: [withRegistry, withTheme(), withLayout({ layout: 'fullscreen' })],
24
24
  parameters: {
25
25
  layout: 'fullscreen',
26
26
  },
@@ -77,14 +77,14 @@ export const Comments: Story = {
77
77
  };
78
78
 
79
79
  const Key: FC<{ char: string }> = ({ char }) => (
80
- <span className='flex justify-center items-center is-[24px] bs-[24px] rounded text-xs bg-neutral-200 text-black'>
80
+ <span className='flex justify-center items-center w-[24px] h-[24px] rounded-sm text-xs bg-neutral-200 text-black'>
81
81
  {char}
82
82
  </span>
83
83
  );
84
84
 
85
85
  const CommentTooltip: FC<{ shortcut: string }> = ({ shortcut }) => {
86
86
  return (
87
- <div className='flex items-center gap-2 pli-2 plb-2 bg-neutral-700 text-white text-xs rounded'>
87
+ <div className='flex items-center gap-2 px-2 py-2 bg-neutral-700 text-white text-xs rounded-sm'>
88
88
  <div>Create comment</div>
89
89
  <div className='flex gap-1'>
90
90
  {keySymbols(parseShortcut(shortcut)).map((char) => (
@@ -2,96 +2,68 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { RegistryContext, useAtomValue } from '@effect-atom/atom-react';
6
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
7
- import React, { useCallback, useContext, useState } from 'react';
6
+ import React, { useMemo } from 'react';
8
7
 
9
- import { invariant } from '@dxos/invariant';
10
8
  import { useThemeContext } from '@dxos/react-ui';
11
- import { withTheme } from '@dxos/react-ui/testing';
9
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
12
10
  import { withRegistry } from '@dxos/storybook-utils';
13
11
  import {
14
- type EditorInputMode,
15
- type EditorViewMode,
16
- InputModeExtensions,
17
12
  createBasicExtensions,
18
13
  createMarkdownExtensions,
19
14
  createThemeExtensions,
20
15
  decorateMarkdown,
21
- editorWidth,
16
+ documentSlots,
22
17
  formattingKeymap,
23
- formattingListener,
24
18
  } from '@dxos/ui-editor';
25
- import { attentionSurface, mx } from '@dxos/ui-theme';
19
+ import { type EditorViewMode } from '@dxos/ui-editor/types';
26
20
 
27
- import { EditorToolbar, type EditorToolbarState, useEditorToolbar } from '../components';
28
- import { type UseTextEditorProps, useTextEditor } from '../hooks';
29
- import { translations } from '../translations';
21
+ import { translations } from '#translations';
30
22
 
31
- type StoryProps = { placeholder?: string } & UseTextEditorProps;
23
+ import { Editor } from '../components';
24
+ import { type UseTextEditorProps } from '../hooks';
32
25
 
33
- const DefaultStory = ({ autoFocus, initialValue, placeholder }: StoryProps) => {
34
- const { themeMode } = useThemeContext();
35
- const registry = useContext(RegistryContext);
36
-
37
- const toolbarState = useEditorToolbar({ viewMode: 'source' });
38
- const { viewMode } = useAtomValue(toolbarState);
39
-
40
- const updateToolbarState = useCallback(
41
- (formatting: EditorToolbarState) => {
42
- registry.update(toolbarState, (state) => ({ ...state, ...formatting }));
43
- },
44
- [registry, toolbarState],
45
- );
26
+ type DefaultStoryProps = { placeholder?: string; viewMode?: EditorViewMode } & UseTextEditorProps;
46
27
 
47
- // TODO(wittjosiah): Provide way to change the input mode.
48
- const [editorInputMode, _setEditorInputMode] = useState<EditorInputMode>('default');
49
- const { parentRef, view } = useTextEditor(
50
- () => ({
51
- autoFocus,
52
- initialValue,
53
- selectionEnd: true,
54
- extensions: [
55
- editorInputMode ? InputModeExtensions[editorInputMode] : [],
56
- createBasicExtensions({ placeholder, lineWrapping: true, readOnly: viewMode === 'readonly', search: true }),
57
- createMarkdownExtensions(),
58
- createThemeExtensions({ themeMode, syntaxHighlighting: true }),
59
- viewMode === 'source' ? [] : decorateMarkdown(),
60
- formattingKeymap(),
61
- formattingListener(updateToolbarState),
62
- ],
63
- }),
64
- [editorInputMode, viewMode, themeMode, placeholder],
65
- );
66
-
67
- const getView = useCallback(() => {
68
- invariant(view);
69
- return view;
70
- }, [view]);
28
+ const DefaultStory = ({ autoFocus, initialValue, placeholder, viewMode = 'source' }: DefaultStoryProps) => {
29
+ const { themeMode } = useThemeContext();
71
30
 
72
- const handleViewModeChange = useCallback(
73
- (mode: EditorViewMode) => {
74
- registry.update(toolbarState, (state) => ({ ...state, viewMode: mode }));
75
- },
76
- [registry, toolbarState],
31
+ const extensions = useMemo(
32
+ () => [
33
+ createBasicExtensions({
34
+ placeholder,
35
+ lineWrapping: true,
36
+ readOnly: viewMode === 'readonly',
37
+ search: true,
38
+ }),
39
+ createThemeExtensions({
40
+ themeMode,
41
+ syntaxHighlighting: true,
42
+ slots: documentSlots,
43
+ }),
44
+ createMarkdownExtensions(),
45
+ viewMode === 'source' ? [] : decorateMarkdown(),
46
+ formattingKeymap(),
47
+ ],
48
+ [viewMode, themeMode, placeholder],
77
49
  );
78
50
 
79
- // TODO(marijn): This doesn't update the state on view changes.
80
- // Also not sure if view is even guaranteed to exist at this point.
81
51
  return (
82
- <div role='none' className={mx('fixed inset-0 flex flex-col')}>
83
- {toolbarState && <EditorToolbar state={toolbarState} getView={getView} onViewModeChange={handleViewModeChange} />}
84
- <div role='none' className={mx('grow overflow-hidden', attentionSurface)}>
85
- <div className={mx(editorWidth)} ref={parentRef} />
86
- </div>
87
- </div>
52
+ <Editor.Root extensions={extensions} viewMode={viewMode}>
53
+ <Editor.Content>
54
+ <Editor.Toolbar classNames='dx-document' />
55
+ <div className='dx-container dx-document bg-base-surface'>
56
+ <Editor.View autoFocus={autoFocus} initialValue={initialValue} selectionEnd />
57
+ </div>
58
+ </Editor.Content>
59
+ </Editor.Root>
88
60
  );
89
61
  };
90
62
 
91
63
  const meta = {
92
64
  title: 'ui/react-ui-editor/EditorToolbar',
93
65
  render: DefaultStory,
94
- decorators: [withRegistry, withTheme()],
66
+ decorators: [withRegistry, withTheme(), withLayout({ layout: 'fullscreen', classNames: 'bg-sidebar-surface' })],
95
67
  parameters: {
96
68
  layout: 'fullscreen',
97
69
  translations,
@@ -7,16 +7,16 @@ import defaultsDeep from 'lodash.defaultsdeep';
7
7
  import React from 'react';
8
8
 
9
9
  import { log } from '@dxos/log';
10
- import { faker } from '@dxos/random';
11
- import { withTheme } from '@dxos/react-ui/testing';
12
- import { blast, defaultOptions, dropFile, join, typewriter } from '@dxos/ui-editor';
10
+ import { random } from '@dxos/random';
11
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
12
+ import { blast, defaultOptions, dropFile, join, snippets } from '@dxos/ui-editor';
13
13
 
14
14
  import { EditorStory, content } from './components';
15
15
 
16
16
  const meta = {
17
17
  title: 'ui/react-ui-editor/Experimental',
18
18
  component: EditorStory,
19
- decorators: [withTheme()],
19
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
20
20
  parameters: {
21
21
  layout: 'fullscreen',
22
22
  },
@@ -27,16 +27,16 @@ export default meta;
27
27
  type Story = StoryObj<typeof meta>;
28
28
 
29
29
  //
30
- // Typewriter
30
+ // Snippets
31
31
  //
32
32
 
33
- const typewriterItems = localStorage.getItem('dxos.org/testing/typewriter')?.split(',');
33
+ const snippetItems = localStorage.getItem('org.dxos.testing.snippets')?.split(',');
34
34
 
35
- export const Typewriter: Story = {
35
+ export const Snippets: Story = {
36
36
  render: () => (
37
37
  <EditorStory
38
- text={join('# Typewriter', '', content.paragraphs, content.footer)}
39
- extensions={[typewriter({ items: typewriterItems })]}
38
+ text={join('# Snippets', '', content.paragraphs, content.footer)}
39
+ extensions={[snippets({ items: snippetItems })]}
40
40
  />
41
41
  ),
42
42
  };
@@ -50,15 +50,15 @@ export const Blast: Story = {
50
50
  <EditorStory
51
51
  text={join('# Blast', '', content.paragraphs, content.codeblocks, content.paragraphs)}
52
52
  extensions={[
53
- typewriter({ items: typewriterItems }),
53
+ snippets({ items: snippetItems }),
54
54
  blast(
55
55
  defaultsDeep(
56
56
  {
57
57
  effect: 2,
58
58
  particleGravity: 0.2,
59
59
  particleShrinkRate: 0.995,
60
- color: () => [faker.number.int({ min: 100, max: 200 }), 0, 0],
61
- // color: () => [faker.number.int(256), faker.number.int(256), faker.number.int(256)],
60
+ color: () => [random.number.int({ min: 100, max: 200 }), 0, 0],
61
+ // color: () => [random.number.int(256), random.number.int(256), random.number.int(256)],
62
62
  },
63
63
  defaultOptions,
64
64
  ),
@@ -6,7 +6,7 @@ import { markdown } from '@codemirror/lang-markdown';
6
6
  import { type Meta, type StoryObj } from '@storybook/react-vite';
7
7
  import React from 'react';
8
8
 
9
- import { withTheme } from '@dxos/react-ui/testing';
9
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
10
10
  import { decorateMarkdown, image, join, linkTooltip, table } from '@dxos/ui-editor';
11
11
 
12
12
  import { EditorStory, content, defaultExtensions, headings, renderLinkTooltip, text } from './components';
@@ -14,7 +14,7 @@ import { EditorStory, content, defaultExtensions, headings, renderLinkTooltip, t
14
14
  const meta = {
15
15
  title: 'ui/react-ui-editor/Markdown',
16
16
  component: EditorStory,
17
- decorators: [withTheme()],
17
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
18
18
  parameters: {
19
19
  layout: 'fullscreen',
20
20
  },
@@ -5,19 +5,18 @@
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { useMemo, useState } from 'react';
7
7
 
8
- import { withTheme } from '@dxos/react-ui/testing';
9
8
  import { withAttention } from '@dxos/react-ui-attention/testing';
9
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
10
10
  import { deleteItem, hashtag, join, listItemToString, outliner, treeFacet } from '@dxos/ui-editor';
11
11
 
12
12
  import { type EditorController, type EditorMenuGroup, EditorMenuProvider } from '../components';
13
-
14
13
  import { EditorStory } from './components';
15
14
 
16
- type StoryProps = {
15
+ type DefaultStoryProps = {
17
16
  text?: string;
18
17
  };
19
18
 
20
- const DefaultStory = ({ text }: StoryProps) => {
19
+ const DefaultStory = ({ text }: DefaultStoryProps) => {
21
20
  const [controller, setController] = useState<EditorController | null>(null);
22
21
 
23
22
  const extensions = useMemo(() => [outliner(), hashtag()], []);
@@ -68,7 +67,7 @@ const DefaultStory = ({ text }: StoryProps) => {
68
67
  const meta = {
69
68
  title: 'ui/react-ui-editor/Outliner',
70
69
  render: DefaultStory,
71
- decorators: [withTheme(), withAttention()],
70
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' }), withAttention()],
72
71
  parameters: {
73
72
  layout: 'fullscreen',
74
73
  },
@@ -6,9 +6,9 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { useCallback, useState } from 'react';
7
7
 
8
8
  import { Obj, Query } from '@dxos/echo';
9
- import { faker } from '@dxos/random';
9
+ import { random } from '@dxos/random';
10
10
  import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
11
- import { withTheme } from '@dxos/react-ui/testing';
11
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
12
12
  import { TestSchema, type ValueGenerator, createObjectFactory } from '@dxos/schema/testing';
13
13
  import { Domino, mx } from '@dxos/ui';
14
14
  import { insertAtCursor, insertAtLineStart, join } from '@dxos/ui-editor';
@@ -25,10 +25,9 @@ import {
25
25
  linkSlashCommands,
26
26
  useEditorMenu,
27
27
  } from '../components';
28
-
29
28
  import { EditorStory } from './components';
30
29
 
31
- const generator: ValueGenerator = faker as any;
30
+ const generator: ValueGenerator = random as any;
32
31
 
33
32
  const customCompletions: EditorMenuGroup = createMenuGroup({
34
33
  id: 'test',
@@ -38,15 +37,15 @@ const customCompletions: EditorMenuGroup = createMenuGroup({
38
37
  const placeholder = (trigger: string[]) => {
39
38
  const pressEl = Domino.of('span').text('Press');
40
39
  const triggerEls = trigger.map((trigger) =>
41
- Domino.of('span').classNames(mx('border border-separator rounded-sm mx-1 pli-1 pbs-[2px] pbe-[3px]')).text(trigger),
40
+ Domino.of('span').classNames(mx('border border-separator rounded-xs mx-1 px-1 py-[2px] pb-[3px]')).text(trigger),
42
41
  );
43
42
  const forCommandsEl = Domino.of('span').text('for commands');
44
- return Domino.of('div').children(pressEl, ...triggerEls, forCommandsEl).root;
43
+ return Domino.of('div').append(pressEl, ...triggerEls, forCommandsEl).root;
45
44
  };
46
45
 
47
- type StoryProps = Omit<UseEditorMenuProps, 'viewRef'> & { text: string };
46
+ type DefaultStoryProps = Omit<UseEditorMenuProps, 'viewRef'> & { text: string };
48
47
 
49
- const DefaultStory = ({ text, ...props }: StoryProps) => {
48
+ const DefaultStory = ({ text, ...props }: DefaultStoryProps) => {
50
49
  const [controller, setController] = useState<EditorController | null>(null);
51
50
  const { groupsRef, extension, ...menuProps } = useEditorMenu(props);
52
51
 
@@ -57,7 +56,7 @@ const DefaultStory = ({ text, ...props }: StoryProps) => {
57
56
  );
58
57
  };
59
58
 
60
- const LinkStory = (args: StoryProps) => {
59
+ const LinkStory = (args: DefaultStoryProps) => {
61
60
  const { space } = useClientStory();
62
61
 
63
62
  const getMenu = useCallback<NonNullable<UseEditorMenuProps['getMenu']>>(
@@ -82,7 +81,7 @@ const LinkStory = (args: StoryProps) => {
82
81
  label: object.name,
83
82
  icon: 'ph--user--regular',
84
83
  onSelect: ({ view, head }) => {
85
- const link = `[${object.name}](${Obj.getDXN(object)})`;
84
+ const link = `[${object.name}](${Obj.getURI(object)})`;
86
85
  if (text?.startsWith('@')) {
87
86
  insertAtLineStart(view, head, `!${link}\n`);
88
87
  } else {
@@ -103,7 +102,7 @@ const LinkStory = (args: StoryProps) => {
103
102
  const meta = {
104
103
  title: 'ui/react-ui-editor/Popover',
105
104
  render: DefaultStory,
106
- decorators: [withTheme()],
105
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
107
106
  parameters: {
108
107
  layout: 'fullscreen',
109
108
  },
@@ -9,10 +9,10 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
9
9
  import { createPortal } from 'react-dom';
10
10
 
11
11
  import { invariant } from '@dxos/invariant';
12
- import { faker } from '@dxos/random';
13
- import { Popover } from '@dxos/react-ui';
14
- import { withTheme } from '@dxos/react-ui/testing';
15
- import { Card } from '@dxos/react-ui-mosaic';
12
+ import { random } from '@dxos/random';
13
+ import { Card, Popover, Toolbar } from '@dxos/react-ui';
14
+ import { Menu, createMenuAction } from '@dxos/react-ui-menu';
15
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
16
16
  import {
17
17
  type PreviewBlock,
18
18
  type PreviewLinkRef,
@@ -22,16 +22,15 @@ import {
22
22
  preview,
23
23
  } from '@dxos/ui-editor';
24
24
  import { hoverableControls } from '@dxos/ui-theme';
25
- import { isTruthy, trim } from '@dxos/util';
25
+ import { trim } from '@dxos/util';
26
26
 
27
27
  import { type EditorController, EditorPreviewProvider, useEditorPreview } from '../components';
28
-
29
28
  import { EditorStory } from './components';
30
29
 
31
30
  const handlePreviewLookup = async ({ dxn, label }: PreviewLinkRef): Promise<PreviewLinkTarget> => {
32
31
  // Random text.
33
- faker.seed(dxn.split('').reduce((acc: number, char: string) => acc + char.charCodeAt(0), 1));
34
- const text = Array.from({ length: 2 }, () => faker.lorem.paragraphs()).join('\n\n');
32
+ random.seed(dxn.split('').reduce((acc: number, char: string) => acc + char.charCodeAt(0), 1));
33
+ const text = Array.from({ length: 2 }, () => random.lorem.paragraphs()).join('\n\n');
35
34
  return {
36
35
  label,
37
36
  text,
@@ -58,15 +57,15 @@ const PreviewCard = () => {
58
57
  return (
59
58
  <Popover.Portal>
60
59
  <Popover.Content onOpenAutoFocus={(event) => event.preventDefault()}>
61
- <Popover.Viewport classNames='popover-card-width'>
60
+ <Popover.Viewport classNames='dx-card-popover-width'>
62
61
  <Card.Root border={false}>
63
- <Card.Toolbar>
64
- <Card.Icon toolbar icon='ph--file-text--regular' />
62
+ <Card.Header>
63
+ <Card.Icon icon='ph--file-text--regular' />
65
64
  <Card.Title>{target.label}</Card.Title>
66
65
  <Popover.Close asChild>
67
- <Card.Close />
66
+ <Card.ActionIconButton action='close' />
68
67
  </Popover.Close>
69
- </Card.Toolbar>
68
+ </Card.Header>
70
69
  <Card.Row>
71
70
  <Card.Text variant='description'>{target.text}</Card.Text>
72
71
  </Card.Row>
@@ -144,36 +143,45 @@ const PreviewBlockComponent = ({ link, el, view }: { link: PreviewLinkRef; el: H
144
143
  }
145
144
  }, [handleAction, link, target]);
146
145
 
146
+ const menuItems = useMemo(
147
+ () => [
148
+ createMenuAction('delete', handleDelete, {
149
+ label: link.suggest ? 'Discard' : 'Delete',
150
+ icon: 'ph--x--regular',
151
+ }),
152
+ ...(target
153
+ ? [
154
+ createMenuAction('apply', handleInsert, {
155
+ label: 'Apply',
156
+ icon: 'ph--check--regular',
157
+ }),
158
+ ]
159
+ : []),
160
+ ],
161
+ [handleDelete, handleInsert, link.suggest, target],
162
+ );
163
+
147
164
  return createPortal(
148
- <Card.Root classNames={hoverableControls}>
149
- {!view?.state.readOnly && (
150
- <Card.Toolbar>
151
- <Card.Icon toolbar icon='ph--bookmark--regular' />
152
- <Card.Title>{link.label}</Card.Title>
153
- <Card.Menu
154
- items={[
155
- {
156
- id: 'delete',
157
- label: link.suggest ? 'Discard' : 'Delete',
158
- icon: 'ph--x--regular',
159
- onClick: handleDelete,
160
- },
161
- target && {
162
- id: 'apply',
163
- label: 'Apply',
164
- icon: 'ph--check--regular',
165
- onClick: handleInsert,
166
- },
167
- ].filter(isTruthy)}
168
- />
169
- </Card.Toolbar>
170
- )}
171
- {target && (
172
- <Card.Row>
173
- <Card.Text className='text-description'>{target.text}</Card.Text>
174
- </Card.Row>
175
- )}
176
- </Card.Root>,
165
+ <Menu.Root>
166
+ <Card.Root classNames={hoverableControls}>
167
+ {!view?.state.readOnly && (
168
+ <Card.Header>
169
+ <Card.Icon icon='ph--bookmark--regular' />
170
+ <Card.Title>{link.label}</Card.Title>
171
+ {/* TODO(wittjosiah): Reconcile with Card.Menu. */}
172
+ <Menu.Trigger asChild disabled={!menuItems?.length}>
173
+ <Toolbar.IconButton iconOnly variant='ghost' icon='ph--dots-three-vertical--regular' label='Menu' />
174
+ </Menu.Trigger>
175
+ <Menu.Content items={menuItems} />
176
+ </Card.Header>
177
+ )}
178
+ {target && (
179
+ <Card.Row>
180
+ <Card.Text className='text-description'>{target.text}</Card.Text>
181
+ </Card.Row>
182
+ )}
183
+ </Card.Root>
184
+ </Menu.Root>,
177
185
  el,
178
186
  );
179
187
  };
@@ -181,7 +189,7 @@ const PreviewBlockComponent = ({ link, el, view }: { link: PreviewLinkRef; el: H
181
189
  const meta = {
182
190
  title: 'ui/react-ui-editor/Preview',
183
191
  component: EditorStory,
184
- decorators: [withTheme()],
192
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
185
193
  parameters: {
186
194
  layout: 'fullscreen',
187
195
  },
@@ -7,7 +7,7 @@ import React, { useEffect, useState } from 'react';
7
7
  import { createPortal } from 'react-dom';
8
8
 
9
9
  import { useThemeContext } from '@dxos/react-ui';
10
- import { withTheme } from '@dxos/react-ui/testing';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
11
  import {
12
12
  type XmlWidgetRegistry,
13
13
  type XmlWidgetState,
@@ -25,7 +25,7 @@ const registry = {
25
25
  /**
26
26
  * Custom tag: <test/>
27
27
  */
28
- ['test' as const]: {
28
+ test: {
29
29
  block: true,
30
30
  Component: ({ start = '0' }) => {
31
31
  const [count, setCount] = useState<number>(safeParseInt(start, 0));
@@ -43,7 +43,7 @@ const registry = {
43
43
  return () => clearInterval(interval);
44
44
  }, []);
45
45
 
46
- return <div className='p-2 border border-separator rounded'>Test {count}</div>;
46
+ return <div className='p-2 border border-separator rounded-sm'>Test {count}</div>;
47
47
  },
48
48
  },
49
49
  } satisfies XmlWidgetRegistry;
@@ -64,7 +64,7 @@ const DefaultStory = ({ text }: { text?: string }) => {
64
64
 
65
65
  return (
66
66
  <>
67
- <div ref={parentRef} className='is-full p-4' />
67
+ <div ref={parentRef} className='w-full p-4' />
68
68
  {widgets.map(({ id, root, Component, props }) => (
69
69
  <div key={id}>{createPortal(<Component {...props} />, root)}</div>
70
70
  ))}
@@ -87,7 +87,7 @@ const text = trim`
87
87
  const meta = {
88
88
  title: 'ui/react-ui-editor/Tags',
89
89
  render: DefaultStory,
90
- decorators: [withTheme()],
90
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
91
91
  parameters: {
92
92
  layout: 'fullscreen',
93
93
  },