@dxos/react-ui-editor 0.8.4-main.bc674ce → 0.8.4-main.bcb3aa67d6

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 (90) hide show
  1. package/dist/lib/browser/index.mjs +298 -377
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +298 -377
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  8. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts +1 -3
  9. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.map +1 -1
  10. package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts.map +1 -1
  11. package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -1
  12. package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -1
  13. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  14. package/dist/types/src/components/EditorToolbar/blocks.d.ts +3 -17
  15. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  16. package/dist/types/src/components/EditorToolbar/formatting.d.ts +3 -17
  17. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  18. package/dist/types/src/components/EditorToolbar/headings.d.ts +3 -17
  19. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorToolbar/image.d.ts +3 -8
  21. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorToolbar/index.d.ts +0 -1
  23. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  24. package/dist/types/src/components/EditorToolbar/lists.d.ts +6 -0
  25. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -0
  26. package/dist/types/src/components/EditorToolbar/search.d.ts +3 -8
  27. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  28. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +3 -17
  29. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  30. package/dist/types/src/stories/Automerge.stories.d.ts +25 -24
  31. package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
  32. package/dist/types/src/stories/Comments.stories.d.ts +1 -1
  33. package/dist/types/src/stories/EditorToolbar.stories.d.ts +26 -26
  34. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  35. package/dist/types/src/stories/Experimental.stories.d.ts +1 -1
  36. package/dist/types/src/stories/Markdown.stories.d.ts +1 -1
  37. package/dist/types/src/stories/Outliner.stories.d.ts +2 -2
  38. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  39. package/dist/types/src/stories/Popover.stories.d.ts +2 -2
  40. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -1
  41. package/dist/types/src/stories/Preview.stories.d.ts +1 -1
  42. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  43. package/dist/types/src/stories/TextEditor.stories.d.ts +1 -1
  44. package/dist/types/src/stories/components/EditorStory.d.ts +3 -3
  45. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  46. package/dist/types/src/stories/components/util.d.ts +3 -3
  47. package/dist/types/src/stories/components/util.d.ts.map +1 -1
  48. package/dist/types/src/translations.d.ts +24 -24
  49. package/dist/types/src/translations.d.ts.map +1 -1
  50. package/dist/types/src/util/react.d.ts +1 -4
  51. package/dist/types/src/util/react.d.ts.map +1 -1
  52. package/dist/types/tsconfig.tsbuildinfo +1 -1
  53. package/package.json +45 -45
  54. package/src/components/Editor/Editor.stories.tsx +2 -2
  55. package/src/components/Editor/Editor.tsx +12 -6
  56. package/src/components/EditorContent/EditorContent.tsx +1 -1
  57. package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +21 -28
  58. package/src/components/EditorMenuProvider/menu-presets.ts +1 -0
  59. package/src/components/EditorMenuProvider/useEditorMenu.ts +8 -1
  60. package/src/components/EditorPreviewProvider/EditorPreviewProvider.tsx +5 -7
  61. package/src/components/EditorToolbar/EditorToolbar.tsx +24 -61
  62. package/src/components/EditorToolbar/blocks.ts +54 -46
  63. package/src/components/EditorToolbar/formatting.ts +43 -44
  64. package/src/components/EditorToolbar/headings.ts +42 -48
  65. package/src/components/EditorToolbar/image.ts +16 -21
  66. package/src/components/EditorToolbar/index.ts +0 -1
  67. package/src/components/EditorToolbar/lists.ts +57 -0
  68. package/src/components/EditorToolbar/search.ts +16 -21
  69. package/src/components/EditorToolbar/view-mode.ts +34 -40
  70. package/src/stories/Automerge.stories.tsx +8 -10
  71. package/src/stories/Comments.stories.tsx +4 -4
  72. package/src/stories/EditorToolbar.stories.tsx +32 -17
  73. package/src/stories/Experimental.stories.tsx +3 -3
  74. package/src/stories/Markdown.stories.tsx +2 -2
  75. package/src/stories/Outliner.stories.tsx +4 -4
  76. package/src/stories/Popover.stories.tsx +6 -6
  77. package/src/stories/Preview.stories.tsx +58 -48
  78. package/src/stories/Tags.stories.tsx +5 -5
  79. package/src/stories/TextEditor.stories.tsx +2 -2
  80. package/src/stories/Theme.stories.tsx +2 -2
  81. package/src/stories/components/EditorStory.tsx +17 -12
  82. package/src/stories/components/util.tsx +19 -22
  83. package/src/translations.ts +29 -24
  84. package/src/util/react.tsx +2 -11
  85. package/dist/types/src/components/EditorToolbar/actions.d.ts +0 -24
  86. package/dist/types/src/components/EditorToolbar/actions.d.ts.map +0 -1
  87. package/dist/types/src/stories/CommandDialog.stories.d.ts +0 -14
  88. package/dist/types/src/stories/CommandDialog.stories.d.ts.map +0 -1
  89. package/src/components/EditorToolbar/actions.ts +0 -87
  90. package/src/stories/CommandDialog.stories.tsx +0 -81
@@ -4,11 +4,11 @@
4
4
 
5
5
  import { type EditorView } from '@codemirror/view';
6
6
 
7
- import { type Node } from '@dxos/app-graph';
8
- import { type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
7
+ import { type ActionGroupBuilderFn, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
9
8
  import { type Formatting, Inline, addLink, removeLink, setStyle } from '@dxos/ui-editor';
10
9
 
11
- import { createEditorAction, createEditorActionGroup } from './actions';
10
+ import { translationKey } from '../../translations';
11
+
12
12
  import { type EditorToolbarState } from './useEditorToolbar';
13
13
 
14
14
  const formats = {
@@ -19,47 +19,46 @@ const formats = {
19
19
  link: 'ph--link--regular',
20
20
  };
21
21
 
22
- const createFormattingGroup = (formatting: Formatting) =>
23
- createEditorActionGroup('formatting', {
24
- variant: 'toggleGroup',
25
- selectCardinality: 'multiple',
26
- value: Object.keys(formats).filter((key) => !!formatting[key as keyof Formatting]),
27
- } as ToolbarMenuActionGroupProperties);
28
-
29
- const createFormattingActions = (formatting: Formatting, getView: () => EditorView) =>
30
- Object.entries(formats).map(([type, icon]) => {
31
- const checked = !!formatting[type as keyof Formatting];
32
- return createEditorAction(type, { checked, icon }, () => {
33
- const view = getView();
34
- if (!view) {
35
- return;
36
- }
22
+ /** Add formatting actions to the builder. */
23
+ export const addFormatting =
24
+ (state: EditorToolbarState, getView: () => EditorView): ActionGroupBuilderFn =>
25
+ (builder) => {
26
+ const formatting: Formatting = state;
27
+ builder.group(
28
+ 'formatting',
29
+ {
30
+ label: ['formatting.label', { ns: translationKey }],
31
+ iconOnly: true,
32
+ variant: 'toggleGroup',
33
+ selectCardinality: 'multiple',
34
+ value: Object.keys(formats).filter((key) => !!formatting[key as keyof Formatting]),
35
+ } as ToolbarMenuActionGroupProperties,
36
+ (group) => {
37
+ for (const [type, icon] of Object.entries(formats)) {
38
+ const checked = !!formatting[type as keyof Formatting];
39
+ group.action(type, { label: [`formatting.${type}.label`, { ns: translationKey }], checked, icon }, () => {
40
+ const view = getView();
41
+ if (!view) {
42
+ return;
43
+ }
37
44
 
38
- if (type === 'link') {
39
- checked ? removeLink(view) : addLink()(view);
40
- return;
41
- }
45
+ if (type === 'link') {
46
+ checked ? removeLink(view) : addLink()(view);
47
+ return;
48
+ }
42
49
 
43
- const inlineType =
44
- type === 'strong'
45
- ? Inline.Strong
46
- : type === 'emphasis'
47
- ? Inline.Emphasis
48
- : type === 'strikethrough'
49
- ? Inline.Strikethrough
50
- : Inline.Code;
51
- setStyle(inlineType, !checked)(view);
52
- });
53
- });
54
-
55
- export const createFormatting = (state: EditorToolbarState, getView: () => EditorView) => {
56
- const formattingGroupAction = createFormattingGroup(state);
57
- const formattingActions = createFormattingActions(state, getView);
58
- return {
59
- nodes: [formattingGroupAction as Node.NodeArg<any>, ...formattingActions],
60
- edges: [
61
- { source: 'root', target: 'formatting' },
62
- ...formattingActions.map(({ id }) => ({ source: formattingGroupAction.id, target: id })),
63
- ],
50
+ setStyle(
51
+ type === 'strong'
52
+ ? Inline.Strong
53
+ : type === 'emphasis'
54
+ ? Inline.Emphasis
55
+ : type === 'strikethrough'
56
+ ? Inline.Strikethrough
57
+ : Inline.Code,
58
+ !checked,
59
+ )(view);
60
+ });
61
+ }
62
+ },
63
+ );
64
64
  };
65
- };
@@ -4,49 +4,22 @@
4
4
 
5
5
  import { type EditorView } from '@codemirror/view';
6
6
 
7
- import { type Node } from '@dxos/app-graph';
8
- import { type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
7
+ import { type ActionGroupBuilderFn, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
9
8
  import { setHeading } from '@dxos/ui-editor';
10
9
 
11
10
  import { translationKey } from '../../translations';
12
11
 
13
- import { createEditorAction, createEditorActionGroup } from './actions';
14
12
  import { type EditorToolbarState } from './useEditorToolbar';
15
13
 
16
- const createHeadingGroupAction = (value: string) =>
17
- createEditorActionGroup(
18
- 'heading',
19
- {
20
- variant: 'dropdownMenu',
21
- applyActive: true,
22
- selectCardinality: 'single',
23
- // TODO(wittjosiah): Remove? Not sure this does anything.
24
- value,
25
- } as ToolbarMenuActionGroupProperties,
26
- 'ph--text-h--regular',
27
- );
28
-
29
- const createHeadingActions = (currentLevel: string, getView: () => EditorView) =>
30
- Object.entries({
31
- '0': 'ph--paragraph--regular',
32
- '1': 'ph--text-h-one--regular',
33
- '2': 'ph--text-h-two--regular',
34
- '3': 'ph--text-h-three--regular',
35
- '4': 'ph--text-h-four--regular',
36
- '5': 'ph--text-h-five--regular',
37
- '6': 'ph--text-h-six--regular',
38
- }).map(([levelStr, icon]) => {
39
- const level = parseInt(levelStr);
40
- return createEditorAction(
41
- `heading--${levelStr}`,
42
- {
43
- label: ['heading level label', { count: level, ns: translationKey }],
44
- icon,
45
- checked: levelStr === currentLevel,
46
- },
47
- () => setHeading(level)(getView()),
48
- );
49
- });
14
+ const headingIcons: Record<string, string> = {
15
+ 0: 'ph--paragraph--regular',
16
+ 1: 'ph--text-h-one--regular',
17
+ 2: 'ph--text-h-two--regular',
18
+ 3: 'ph--text-h-three--regular',
19
+ 4: 'ph--text-h-four--regular',
20
+ 5: 'ph--text-h-five--regular',
21
+ 6: 'ph--text-h-six--regular',
22
+ };
50
23
 
51
24
  const computeHeadingValue = (state: EditorToolbarState) => {
52
25
  const blockType = state ? state.blockType : 'paragraph';
@@ -54,15 +27,36 @@ const computeHeadingValue = (state: EditorToolbarState) => {
54
27
  return heading ? heading[1] : blockType === 'paragraph' || !blockType ? '0' : '';
55
28
  };
56
29
 
57
- export const createHeadings = (state: EditorToolbarState, getView: () => EditorView) => {
58
- const headingValue = computeHeadingValue(state);
59
- const headingGroupAction = createHeadingGroupAction(headingValue);
60
- const headingActions = createHeadingActions(headingValue, getView);
61
- return {
62
- nodes: [headingGroupAction as Node.NodeArg<any>, ...headingActions],
63
- edges: [
64
- { source: 'root', target: 'heading' },
65
- ...headingActions.map(({ id }) => ({ source: headingGroupAction.id, target: id })),
66
- ],
30
+ /** Add heading actions to the builder. */
31
+ export const addHeadings =
32
+ (state: EditorToolbarState, getView: () => EditorView): ActionGroupBuilderFn =>
33
+ (builder) => {
34
+ const headingValue = computeHeadingValue(state);
35
+ builder.group(
36
+ 'heading',
37
+ {
38
+ label: ['heading.label', { ns: translationKey }],
39
+ icon: 'ph--text-h--regular',
40
+ iconOnly: true,
41
+ variant: 'dropdownMenu',
42
+ applyActive: true,
43
+ selectCardinality: 'single',
44
+ // TODO(wittjosiah): Remove? Not sure this does anything.
45
+ value: headingValue,
46
+ } as ToolbarMenuActionGroupProperties,
47
+ (group) => {
48
+ for (const [levelStr, icon] of Object.entries(headingIcons)) {
49
+ const level = parseInt(levelStr);
50
+ group.action(
51
+ `heading--${levelStr}`,
52
+ {
53
+ label: ['heading-level.label', { count: level, ns: translationKey }],
54
+ icon,
55
+ checked: levelStr === headingValue,
56
+ },
57
+ () => setHeading(level)(getView()),
58
+ );
59
+ }
60
+ },
61
+ );
67
62
  };
68
- };
@@ -2,26 +2,21 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type Node } from '@dxos/app-graph';
5
+ import { type ActionGroupBuilderFn } from '@dxos/react-ui-menu';
6
6
 
7
- import { createEditorAction } from './actions';
7
+ import { translationKey } from '../../translations';
8
8
 
9
- const createImageUploadAction = (onImageUpload: () => void) =>
10
- createEditorAction(
11
- 'image',
12
- {
13
- testId: 'editor.toolbar.image',
14
- icon: 'ph--image-square--regular',
15
- },
16
- onImageUpload,
17
- );
18
-
19
- export const createImageUpload = (
20
- onImageUpload: () => void,
21
- ): {
22
- nodes: Node.NodeArg<any>[];
23
- edges: Array<{ source: string; target: string }>;
24
- } => ({
25
- nodes: [createImageUploadAction(onImageUpload)],
26
- edges: [{ source: 'root', target: 'image' }],
27
- });
9
+ /** Add image upload action to the builder. */
10
+ export const addImageUpload =
11
+ (onImageUpload: () => void): ActionGroupBuilderFn =>
12
+ (builder) => {
13
+ builder.action(
14
+ 'image',
15
+ {
16
+ label: ['image.label', { ns: translationKey }],
17
+ testId: 'editor.toolbar.image',
18
+ icon: 'ph--image-square--regular',
19
+ },
20
+ onImageUpload,
21
+ );
22
+ };
@@ -4,5 +4,4 @@
4
4
 
5
5
  export * from './EditorToolbar';
6
6
 
7
- export { createEditorAction, createEditorActionGroup } from './actions';
8
7
  export { type EditorToolbarState, useEditorToolbar } from './useEditorToolbar';
@@ -0,0 +1,57 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type EditorView } from '@codemirror/view';
6
+
7
+ import { type ActionGroupBuilderFn, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
8
+ import { List, addList, removeList } from '@dxos/ui-editor';
9
+
10
+ import { translationKey } from '../../translations';
11
+ import { type EditorToolbarState } from './useEditorToolbar';
12
+
13
+ const listStyles = {
14
+ bullet: 'ph--list-bullets--regular',
15
+ ordered: 'ph--list-numbers--regular',
16
+ task: 'ph--list-checks--regular',
17
+ };
18
+
19
+ /** Add list actions to the builder. */
20
+ export const addLists =
21
+ (state: EditorToolbarState, getView: () => EditorView): ActionGroupBuilderFn =>
22
+ (builder) => {
23
+ const value = state.listStyle ?? '';
24
+ builder.group(
25
+ 'list',
26
+ {
27
+ label: ['list.label', { ns: translationKey }],
28
+ iconOnly: true,
29
+ variant: 'toggleGroup',
30
+ selectCardinality: 'single',
31
+ value,
32
+ } as ToolbarMenuActionGroupProperties,
33
+ (group) => {
34
+ for (const [listStyle, icon] of Object.entries(listStyles)) {
35
+ const checked = value === listStyle;
36
+ group.action(
37
+ `list-${listStyle}`,
38
+ { label: [`list.${listStyle}.label`, { ns: translationKey }], checked, icon },
39
+ () => {
40
+ const view = getView();
41
+ if (!view) {
42
+ return;
43
+ }
44
+
45
+ const listType =
46
+ listStyle === 'ordered' ? List.Ordered : listStyle === 'bullet' ? List.Bullet : List.Task;
47
+ if (checked) {
48
+ removeList(listType)(view);
49
+ } else {
50
+ addList(listType)(view);
51
+ }
52
+ },
53
+ );
54
+ }
55
+ },
56
+ );
57
+ };
@@ -5,26 +5,21 @@
5
5
  import { openSearchPanel } from '@codemirror/search';
6
6
  import { type EditorView } from '@codemirror/view';
7
7
 
8
- import { type Node } from '@dxos/app-graph';
8
+ import { type ActionGroupBuilderFn } from '@dxos/react-ui-menu';
9
9
 
10
- import { createEditorAction } from './actions';
10
+ import { translationKey } from '../../translations';
11
11
 
12
- const createSearchAction = (getView: () => EditorView) =>
13
- createEditorAction(
14
- 'search',
15
- {
16
- testId: 'editor.toolbar.search',
17
- icon: 'ph--magnifying-glass--regular',
18
- },
19
- () => openSearchPanel(getView()),
20
- );
21
-
22
- export const createSearch = (
23
- getView: () => EditorView,
24
- ): {
25
- nodes: Node.NodeArg<any>[];
26
- edges: Array<{ source: string; target: string }>;
27
- } => ({
28
- nodes: [createSearchAction(getView)],
29
- edges: [{ source: 'root', target: 'search' }],
30
- });
12
+ /** Add search action to the builder. */
13
+ export const addSearch =
14
+ (getView: () => EditorView): ActionGroupBuilderFn =>
15
+ (builder) => {
16
+ builder.action(
17
+ 'search',
18
+ {
19
+ label: ['search.label', { ns: translationKey }],
20
+ testId: 'editor.toolbar.search',
21
+ icon: 'ph--magnifying-glass--regular',
22
+ },
23
+ () => openSearchPanel(getView()),
24
+ );
25
+ };
@@ -2,54 +2,48 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type Node } from '@dxos/app-graph';
6
- import { type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
5
+ import { type ActionGroupBuilderFn, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
7
6
  import { type EditorViewMode } from '@dxos/ui-editor';
8
7
 
9
8
  import { translationKey } from '../../translations';
10
9
 
11
- import { createEditorAction, createEditorActionGroup } from './actions';
12
10
  import { type EditorToolbarState } from './useEditorToolbar';
13
11
 
14
- const createViewModeGroupAction = (value: string) =>
15
- createEditorActionGroup(
16
- 'viewMode',
17
- {
18
- variant: 'dropdownMenu',
19
- applyActive: true,
20
- selectCardinality: 'single',
21
- value,
22
- } as ToolbarMenuActionGroupProperties,
23
- 'ph--eye--regular',
24
- );
12
+ const viewModes = {
13
+ preview: 'ph--eye--regular',
14
+ source: 'ph--pencil-simple--regular',
15
+ readonly: 'ph--pencil-slash--regular',
16
+ };
25
17
 
26
- const createViewModeActions = (value: string, onViewModeChange: (mode: EditorViewMode) => void) =>
27
- Object.entries({
28
- preview: 'ph--eye--regular',
29
- source: 'ph--pencil-simple--regular',
30
- readonly: 'ph--pencil-slash--regular',
31
- }).map(([viewMode, icon]) => {
32
- const checked = viewMode === value;
33
- return createEditorAction(
34
- `view-mode--${viewMode}`,
18
+ /** Add view mode actions to the builder. */
19
+ export const addViewMode =
20
+ (state: EditorToolbarState, onViewModeChange: (mode: EditorViewMode) => void): ActionGroupBuilderFn =>
21
+ (builder) => {
22
+ const value = state.viewMode ?? 'source';
23
+ builder.group(
24
+ 'viewMode',
35
25
  {
36
- label: [`${viewMode} mode label`, { ns: translationKey }],
37
- checked,
38
- icon,
26
+ label: ['view-mode.label', { ns: translationKey }],
27
+ icon: 'ph--eye--regular',
28
+ iconOnly: true,
29
+ variant: 'dropdownMenu',
30
+ applyActive: true,
31
+ selectCardinality: 'single',
32
+ value,
33
+ } as ToolbarMenuActionGroupProperties,
34
+ (group) => {
35
+ for (const [viewMode, icon] of Object.entries(viewModes)) {
36
+ const checked = viewMode === value;
37
+ group.action(
38
+ `view-mode--${viewMode}`,
39
+ {
40
+ label: [`view-mode.${viewMode}.label`, { ns: translationKey }],
41
+ checked,
42
+ icon,
43
+ },
44
+ () => onViewModeChange(viewMode as EditorViewMode),
45
+ );
46
+ }
39
47
  },
40
- () => onViewModeChange(viewMode as EditorViewMode),
41
48
  );
42
- });
43
-
44
- export const createViewMode = (state: EditorToolbarState, onViewModeChange: (mode: EditorViewMode) => void) => {
45
- const value = state.viewMode ?? 'source';
46
- const viewModeGroupAction = createViewModeGroupAction(value);
47
- const viewModeActions = createViewModeActions(value, onViewModeChange);
48
- return {
49
- nodes: [viewModeGroupAction as Node.NodeArg<any>, ...viewModeActions],
50
- edges: [
51
- { source: 'root', target: 'viewMode' },
52
- ...viewModeActions.map(({ id }) => ({ source: viewModeGroupAction.id, target: id })),
53
- ],
54
49
  };
55
- };
@@ -16,8 +16,7 @@ import { Query, 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
 
23
22
  import { useTextEditor } from '../hooks';
@@ -51,7 +50,7 @@ const Editor = ({ source, messenger, identity, autoFocus }: EditorProps) => {
51
50
  [source, themeMode],
52
51
  );
53
52
 
54
- return <div ref={parentRef} className='flex is-full' />;
53
+ return <div ref={parentRef} className='flex w-full' />;
55
54
  };
56
55
 
57
56
  const DefaultStory = () => {
@@ -77,11 +76,11 @@ const DefaultStory = () => {
77
76
  }, []);
78
77
 
79
78
  if (!object1 || !object2) {
80
- return null;
79
+ return <Loading data={{ object1: !!object1, object2: !!object2 }} />;
81
80
  }
82
81
 
83
82
  return (
84
- <div className='bs-full is-full grid grid-cols-2 gap-4'>
83
+ <div className='h-full w-full grid grid-cols-2 gap-4'>
85
84
  <Editor source={object1} autoFocus />
86
85
  <Editor source={object2} />
87
86
  </div>
@@ -117,7 +116,7 @@ const EchoStory = () => {
117
116
  }, [objects, source]);
118
117
 
119
118
  return (
120
- <div className='bs-full is-full flex flex-col overflow-hidden'>
119
+ <div className='h-full w-full flex flex-col overflow-hidden'>
121
120
  <pre className='p-2 text-xs text-subdued'>
122
121
  {JSON.stringify({ index, identity: identity?.identityKey.truncate(), spaceId, objects }, null, 2)}
123
122
  </pre>
@@ -137,6 +136,7 @@ const EchoStory = () => {
137
136
  const meta = {
138
137
  title: 'ui/react-ui-editor/Automerge',
139
138
  component: Editor as any,
139
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
140
140
  parameters: {
141
141
  layout: 'fullscreen',
142
142
  translations,
@@ -149,14 +149,12 @@ type Story = StoryObj<typeof meta>;
149
149
 
150
150
  // TODO(burdon): ERROR: factories.ts:126 Error: Non-base58 character
151
151
  export const Default: Story = {
152
- decorators: [withTheme],
153
- render: render(DefaultStory),
152
+ render: DefaultStory,
154
153
  };
155
154
 
156
155
  // TODO(burdon): Failing (doesn't sync)
157
156
  export const WithEcho: Story = {
158
157
  decorators: [
159
- withTheme,
160
158
  withMultiClientProvider({
161
159
  numClients: 2,
162
160
  createIdentity: true,
@@ -171,5 +169,5 @@ export const WithEcho: Story = {
171
169
  },
172
170
  }),
173
171
  ],
174
- render: render(EchoStory),
172
+ render: EchoStory,
175
173
  };
@@ -9,7 +9,7 @@ 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
14
  import { type Comment, annotations, comments, createExternalCommentSync } from '@dxos/ui-editor';
15
15
 
@@ -20,7 +20,7 @@ import { EditorStory, content, longText } from './components';
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) => (
@@ -7,8 +7,8 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
7
  import React, { useCallback, useContext, useState } from 'react';
8
8
 
9
9
  import { invariant } from '@dxos/invariant';
10
- import { useThemeContext } from '@dxos/react-ui';
11
- import { withTheme } from '@dxos/react-ui/testing';
10
+ import { Panel, useThemeContext } from '@dxos/react-ui';
11
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
12
12
  import { withRegistry } from '@dxos/storybook-utils';
13
13
  import {
14
14
  type EditorInputMode,
@@ -18,19 +18,18 @@ import {
18
18
  createMarkdownExtensions,
19
19
  createThemeExtensions,
20
20
  decorateMarkdown,
21
- editorWidth,
21
+ documentSlots,
22
22
  formattingKeymap,
23
23
  formattingListener,
24
24
  } from '@dxos/ui-editor';
25
- import { attentionSurface, mx } from '@dxos/ui-theme';
26
25
 
27
26
  import { EditorToolbar, type EditorToolbarState, useEditorToolbar } from '../components';
28
27
  import { type UseTextEditorProps, useTextEditor } from '../hooks';
29
28
  import { translations } from '../translations';
30
29
 
31
- type StoryProps = { placeholder?: string } & UseTextEditorProps;
30
+ type DefaultStoryProps = { placeholder?: string } & UseTextEditorProps;
32
31
 
33
- const DefaultStory = ({ autoFocus, initialValue, placeholder }: StoryProps) => {
32
+ const DefaultStory = ({ autoFocus, initialValue, placeholder }: DefaultStoryProps) => {
34
33
  const { themeMode } = useThemeContext();
35
34
  const registry = useContext(RegistryContext);
36
35
 
@@ -53,9 +52,18 @@ const DefaultStory = ({ autoFocus, initialValue, placeholder }: StoryProps) => {
53
52
  selectionEnd: true,
54
53
  extensions: [
55
54
  editorInputMode ? InputModeExtensions[editorInputMode] : [],
56
- createBasicExtensions({ placeholder, lineWrapping: true, readOnly: viewMode === 'readonly', search: true }),
55
+ createBasicExtensions({
56
+ placeholder,
57
+ lineWrapping: true,
58
+ readOnly: viewMode === 'readonly',
59
+ search: true,
60
+ }),
61
+ createThemeExtensions({
62
+ themeMode,
63
+ syntaxHighlighting: true,
64
+ slots: documentSlots,
65
+ }),
57
66
  createMarkdownExtensions(),
58
- createThemeExtensions({ themeMode, syntaxHighlighting: true }),
59
67
  viewMode === 'source' ? [] : decorateMarkdown(),
60
68
  formattingKeymap(),
61
69
  formattingListener(updateToolbarState),
@@ -76,22 +84,29 @@ const DefaultStory = ({ autoFocus, initialValue, placeholder }: StoryProps) => {
76
84
  [registry, toolbarState],
77
85
  );
78
86
 
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
87
  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>
88
+ <Panel.Root>
89
+ {toolbarState && (
90
+ <Panel.Toolbar>
91
+ <EditorToolbar
92
+ classNames='dx-document'
93
+ state={toolbarState}
94
+ getView={getView}
95
+ onViewModeChange={handleViewModeChange}
96
+ />
97
+ </Panel.Toolbar>
98
+ )}
99
+ <Panel.Content>
100
+ <div role='none' className='dx-container dx-document bg-base-surface' ref={parentRef} />
101
+ </Panel.Content>
102
+ </Panel.Root>
88
103
  );
89
104
  };
90
105
 
91
106
  const meta = {
92
107
  title: 'ui/react-ui-editor/EditorToolbar',
93
108
  render: DefaultStory,
94
- decorators: [withRegistry, withTheme],
109
+ decorators: [withRegistry, withTheme(), withLayout({ layout: 'fullscreen', classNames: 'bg-sidebar-surface' })],
95
110
  parameters: {
96
111
  layout: 'fullscreen',
97
112
  translations,