@dxos/react-ui-editor 0.8.4-main.9735255 → 0.8.4-main.9be5663bfe

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 (94) hide show
  1. package/dist/lib/browser/index.mjs +310 -388
  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 +310 -388
  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 +9 -15
  8. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  9. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +1 -1
  11. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts +1 -3
  12. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.map +1 -1
  13. package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts.map +1 -1
  14. package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -1
  15. package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -1
  16. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  17. package/dist/types/src/components/EditorToolbar/blocks.d.ts +3 -17
  18. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  19. package/dist/types/src/components/EditorToolbar/formatting.d.ts +3 -17
  20. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  21. package/dist/types/src/components/EditorToolbar/headings.d.ts +3 -17
  22. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  23. package/dist/types/src/components/EditorToolbar/image.d.ts +3 -8
  24. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  25. package/dist/types/src/components/EditorToolbar/index.d.ts +0 -1
  26. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  27. package/dist/types/src/components/EditorToolbar/lists.d.ts +6 -0
  28. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -0
  29. package/dist/types/src/components/EditorToolbar/search.d.ts +3 -8
  30. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  31. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +3 -17
  32. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  33. package/dist/types/src/stories/Automerge.stories.d.ts +25 -24
  34. package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
  35. package/dist/types/src/stories/Comments.stories.d.ts +1 -1
  36. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  37. package/dist/types/src/stories/EditorToolbar.stories.d.ts +26 -26
  38. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  39. package/dist/types/src/stories/Experimental.stories.d.ts +1 -1
  40. package/dist/types/src/stories/Markdown.stories.d.ts +1 -1
  41. package/dist/types/src/stories/Outliner.stories.d.ts +2 -2
  42. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  43. package/dist/types/src/stories/Popover.stories.d.ts +2 -2
  44. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -1
  45. package/dist/types/src/stories/Preview.stories.d.ts +1 -1
  46. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  47. package/dist/types/src/stories/TextEditor.stories.d.ts +1 -1
  48. package/dist/types/src/stories/components/EditorStory.d.ts +3 -3
  49. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  50. package/dist/types/src/stories/components/util.d.ts +3 -3
  51. package/dist/types/src/stories/components/util.d.ts.map +1 -1
  52. package/dist/types/src/translations.d.ts +24 -24
  53. package/dist/types/src/translations.d.ts.map +1 -1
  54. package/dist/types/src/util/react.d.ts +1 -4
  55. package/dist/types/src/util/react.d.ts.map +1 -1
  56. package/dist/types/tsconfig.tsbuildinfo +1 -1
  57. package/package.json +45 -45
  58. package/src/components/Editor/Editor.stories.tsx +6 -7
  59. package/src/components/Editor/Editor.tsx +21 -27
  60. package/src/components/EditorContent/EditorContent.tsx +1 -2
  61. package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +21 -28
  62. package/src/components/EditorMenuProvider/menu-presets.ts +1 -0
  63. package/src/components/EditorMenuProvider/useEditorMenu.ts +8 -1
  64. package/src/components/EditorPreviewProvider/EditorPreviewProvider.tsx +5 -7
  65. package/src/components/EditorToolbar/EditorToolbar.tsx +24 -61
  66. package/src/components/EditorToolbar/blocks.ts +53 -46
  67. package/src/components/EditorToolbar/formatting.ts +44 -46
  68. package/src/components/EditorToolbar/headings.ts +42 -49
  69. package/src/components/EditorToolbar/image.ts +16 -21
  70. package/src/components/EditorToolbar/index.ts +0 -1
  71. package/src/components/EditorToolbar/lists.ts +57 -0
  72. package/src/components/EditorToolbar/search.ts +16 -21
  73. package/src/components/EditorToolbar/view-mode.ts +34 -41
  74. package/src/stories/Automerge.stories.tsx +10 -12
  75. package/src/stories/Comments.stories.tsx +4 -5
  76. package/src/stories/EditorToolbar.stories.tsx +32 -17
  77. package/src/stories/Experimental.stories.tsx +6 -6
  78. package/src/stories/Markdown.stories.tsx +2 -2
  79. package/src/stories/Outliner.stories.tsx +4 -5
  80. package/src/stories/Popover.stories.tsx +9 -10
  81. package/src/stories/Preview.stories.tsx +60 -51
  82. package/src/stories/Tags.stories.tsx +5 -5
  83. package/src/stories/TextEditor.stories.tsx +2 -2
  84. package/src/stories/Theme.stories.tsx +2 -2
  85. package/src/stories/components/EditorStory.tsx +17 -12
  86. package/src/stories/components/util.tsx +49 -50
  87. package/src/translations.ts +29 -24
  88. package/src/util/react.tsx +2 -11
  89. package/dist/types/src/components/EditorToolbar/actions.d.ts +0 -24
  90. package/dist/types/src/components/EditorToolbar/actions.d.ts.map +0 -1
  91. package/dist/types/src/stories/CommandDialog.stories.d.ts +0 -14
  92. package/dist/types/src/stories/CommandDialog.stories.d.ts.map +0 -1
  93. package/src/components/EditorToolbar/actions.ts +0 -87
  94. package/src/stories/CommandDialog.stories.tsx +0 -81
@@ -9,18 +9,17 @@ 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
 
16
16
  import { createRenderer, str } from '../util';
17
-
18
17
  import { EditorStory, content, longText } from './components';
19
18
 
20
19
  const meta = {
21
20
  title: 'ui/react-ui-editor/Comments',
22
21
  component: EditorStory,
23
- decorators: [withRegistry, withTheme],
22
+ decorators: [withRegistry, withTheme(), withLayout({ layout: 'fullscreen' })],
24
23
  parameters: {
25
24
  layout: 'fullscreen',
26
25
  },
@@ -77,14 +76,14 @@ export const Comments: Story = {
77
76
  };
78
77
 
79
78
  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'>
79
+ <span className='flex justify-center items-center w-[24px] h-[24px] rounded-sm text-xs bg-neutral-200 text-black'>
81
80
  {char}
82
81
  </span>
83
82
  );
84
83
 
85
84
  const CommentTooltip: FC<{ shortcut: string }> = ({ shortcut }) => {
86
85
  return (
87
- <div className='flex items-center gap-2 pli-2 plb-2 bg-neutral-700 text-white text-xs rounded'>
86
+ <div className='flex items-center gap-2 px-2 py-2 bg-neutral-700 text-white text-xs rounded-sm'>
88
87
  <div>Create comment</div>
89
88
  <div className='flex gap-1'>
90
89
  {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,
@@ -7,8 +7,8 @@ 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';
10
+ import { random } from '@dxos/random';
11
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
12
12
  import { blast, defaultOptions, dropFile, join, typewriter } from '@dxos/ui-editor';
13
13
 
14
14
  import { EditorStory, content } from './components';
@@ -16,7 +16,7 @@ import { EditorStory, content } from './components';
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
  },
@@ -30,7 +30,7 @@ type Story = StoryObj<typeof meta>;
30
30
  // Typewriter
31
31
  //
32
32
 
33
- const typewriterItems = localStorage.getItem('dxos.org/testing/typewriter')?.split(',');
33
+ const typewriterItems = localStorage.getItem('org.dxos.testing.typewriter')?.split(',');
34
34
 
35
35
  export const Typewriter: Story = {
36
36
  render: () => (
@@ -57,8 +57,8 @@ export const Blast: Story = {
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']>>(
@@ -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
- const handlePreviewLookup = async ({ label, ref }: PreviewLinkRef): Promise<PreviewLinkTarget> => {
30
+ const handlePreviewLookup = async ({ dxn, label }: PreviewLinkRef): Promise<PreviewLinkTarget> => {
32
31
  // Random text.
33
- faker.seed(ref.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,
@@ -51,24 +50,25 @@ const useRefTarget = (link: PreviewLinkRef): PreviewLinkTarget | undefined => {
51
50
 
52
51
  const PreviewCard = () => {
53
52
  const { target } = useEditorPreview('PreviewCard');
53
+ if (!target) {
54
+ return null;
55
+ }
54
56
 
55
57
  return (
56
58
  <Popover.Portal>
57
59
  <Popover.Content onOpenAutoFocus={(event) => event.preventDefault()}>
58
- <Popover.Viewport classNames='popover-card-width'>
60
+ <Popover.Viewport classNames='dx-card-popover-width'>
59
61
  <Card.Root border={false}>
60
62
  <Card.Toolbar>
61
- <Card.Icon toolbar icon='ph--file-text--regular' />
62
- <Card.Title>{target?.label}</Card.Title>
63
+ <Card.Icon icon='ph--file-text--regular' />
64
+ <Card.Title>{target.label}</Card.Title>
63
65
  <Popover.Close asChild>
64
- <Card.Close />
66
+ <Card.CloseIconButton />
65
67
  </Popover.Close>
66
68
  </Card.Toolbar>
67
- {target && (
68
- <Card.Row>
69
- <Card.Text variant='description'>{target.text}</Card.Text>
70
- </Card.Row>
71
- )}
69
+ <Card.Row>
70
+ <Card.Text variant='description'>{target.text}</Card.Text>
71
+ </Card.Row>
72
72
  </Card.Root>
73
73
  </Popover.Viewport>
74
74
  <Popover.Arrow />
@@ -101,7 +101,7 @@ const PreviewBlockComponent = ({ link, el, view }: { link: PreviewLinkRef; el: H
101
101
  }
102
102
 
103
103
  const link = getLinkRef(view.state, node);
104
- if (link?.ref !== action.link.ref) {
104
+ if (link?.dxn !== action.link.dxn) {
105
105
  return;
106
106
  }
107
107
 
@@ -143,36 +143,45 @@ const PreviewBlockComponent = ({ link, el, view }: { link: PreviewLinkRef; el: H
143
143
  }
144
144
  }, [handleAction, link, target]);
145
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
+
146
164
  return createPortal(
147
- <Card.Root classNames={hoverableControls}>
148
- {!view?.state.readOnly && (
149
- <Card.Toolbar>
150
- <Card.Icon toolbar icon='ph--bookmark--regular' />
151
- <Card.Title>{link.label}</Card.Title>
152
- <Card.Menu
153
- items={[
154
- {
155
- id: 'delete',
156
- label: link.suggest ? 'Discard' : 'Delete',
157
- icon: 'ph--x--regular',
158
- onClick: handleDelete,
159
- },
160
- target && {
161
- id: 'apply',
162
- label: 'Apply',
163
- icon: 'ph--check--regular',
164
- onClick: handleInsert,
165
- },
166
- ].filter(isTruthy)}
167
- />
168
- </Card.Toolbar>
169
- )}
170
- {target && (
171
- <Card.Row>
172
- <Card.Text className='text-description'>{target.text}</Card.Text>
173
- </Card.Row>
174
- )}
175
- </Card.Root>,
165
+ <Menu.Root>
166
+ <Card.Root classNames={hoverableControls}>
167
+ {!view?.state.readOnly && (
168
+ <Card.Toolbar>
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.Toolbar>
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>,
176
185
  el,
177
186
  );
178
187
  };
@@ -180,7 +189,7 @@ const PreviewBlockComponent = ({ link, el, view }: { link: PreviewLinkRef; el: H
180
189
  const meta = {
181
190
  title: 'ui/react-ui-editor/Preview',
182
191
  component: EditorStory,
183
- decorators: [withTheme],
192
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
184
193
  parameters: {
185
194
  layout: 'fullscreen',
186
195
  },
@@ -217,7 +226,7 @@ export const Default: Story = {
217
226
  setPreviewBlocks((prev) => [...prev, block]);
218
227
  },
219
228
  removeBlockContainer: (block) => {
220
- setPreviewBlocks((prev) => prev.filter(({ link: prevLink }) => prevLink.ref !== block.link.ref));
229
+ setPreviewBlocks((prev) => prev.filter(({ link: prevLink }) => prevLink.dxn !== block.link.dxn));
221
230
  },
222
231
  }),
223
232
  ];
@@ -230,7 +239,7 @@ export const Default: Story = {
230
239
  <PreviewCard />
231
240
  {controller?.view &&
232
241
  previewBlocks.map(({ link, el }) => (
233
- <PreviewBlockComponent key={link.ref} link={link} el={el} view={controller.view!} />
242
+ <PreviewBlockComponent key={link.dxn} link={link} el={el} view={controller.view!} />
234
243
  ))}
235
244
  </EditorPreviewProvider>
236
245
  );
@@ -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
  },
@@ -8,7 +8,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import React from 'react';
9
9
 
10
10
  import { log } from '@dxos/log';
11
- import { withTheme } from '@dxos/react-ui/testing';
11
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
12
12
  import {
13
13
  InputModeExtensions,
14
14
  decorateMarkdown,
@@ -37,7 +37,7 @@ import {
37
37
  const meta = {
38
38
  title: 'ui/react-ui-editor/TextEditor',
39
39
  component: EditorStory,
40
- decorators: [withTheme],
40
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
41
41
  parameters: {
42
42
  layout: 'fullscreen',
43
43
  controls: { disable: true },
@@ -32,7 +32,7 @@ const DefaultStory = () => {
32
32
  );
33
33
 
34
34
  return (
35
- <div className='is-full grid grid-cols-2 gap-2'>
35
+ <div className='w-full grid grid-cols-2 gap-2'>
36
36
  <Editor.Root>
37
37
  <Editor.Content classNames='p-2' extensions={ext1} initialValue={createText(false)} />
38
38
  </Editor.Root>
@@ -46,7 +46,7 @@ const DefaultStory = () => {
46
46
  const meta: Meta<typeof DefaultStory> = {
47
47
  title: 'ui/react-ui-editor/Theme',
48
48
  component: DefaultStory,
49
- decorators: [withTheme, withLayout()],
49
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
50
50
  parameters: {
51
51
  layout: 'fullscreen',
52
52
  },
@@ -12,7 +12,7 @@ import { PublicKey } from '@dxos/keys';
12
12
  import { log } from '@dxos/log';
13
13
  import { useMergeRefs, useThemeContext } from '@dxos/react-ui';
14
14
  import { useAttentionAttributes } from '@dxos/react-ui-attention';
15
- import { JsonFilter } from '@dxos/react-ui-syntax-highlighter';
15
+ import { Json } from '@dxos/react-ui-syntax-highlighter';
16
16
  import {
17
17
  type DebugNode,
18
18
  type ThemeExtensionsOptions,
@@ -20,8 +20,7 @@ import {
20
20
  createMarkdownExtensions,
21
21
  createThemeExtensions,
22
22
  debugTree,
23
- decorateMarkdown,
24
- editorSlots,
23
+ documentSlots,
25
24
  } from '@dxos/ui-editor';
26
25
  import { mx } from '@dxos/ui-theme';
27
26
  import { isNonNullable } from '@dxos/util';
@@ -34,12 +33,12 @@ export type DebugMode = 'raw' | 'tree' | 'raw+tree';
34
33
 
35
34
  const defaultId = 'editor-' + PublicKey.random().toHex().slice(0, 8);
36
35
 
37
- export type StoryProps = Pick<UseTextEditorProps, 'id' | 'scrollTo' | 'selection' | 'extensions'> &
36
+ export type EditorStoryProps = Pick<UseTextEditorProps, 'id' | 'scrollTo' | 'selection' | 'extensions'> &
38
37
  Pick<ThemeExtensionsOptions, 'slots'> & {
39
38
  debug?: DebugMode;
40
39
  debugCustom?: (view: EditorView) => ReactNode;
41
40
  text?: string;
42
- object?: Obj.Obj<TestSchema.Expando>;
41
+ object?: Obj.OfShape<TestSchema.Expando>;
43
42
  readOnly?: boolean;
44
43
  placeholder?: string;
45
44
  lineNumbers?: boolean;
@@ -47,7 +46,7 @@ export type StoryProps = Pick<UseTextEditorProps, 'id' | 'scrollTo' | 'selection
47
46
  onReady?: (view: EditorView) => void;
48
47
  };
49
48
 
50
- export const EditorStory = forwardRef<EditorController, StoryProps>(
49
+ export const EditorStory = forwardRef<EditorController, EditorStoryProps>(
51
50
  ({ debug, debugCustom, text, extensions: extensionsProp, ...props }, forwardedRef) => {
52
51
  const controllerRef = useRef<EditorController>(null);
53
52
  const mergedRef = useMergeRefs([controllerRef, forwardedRef]);
@@ -63,12 +62,12 @@ export const EditorStory = forwardRef<EditorController, StoryProps>(
63
62
 
64
63
  const view = controllerRef.current?.view;
65
64
  return (
66
- <div className={mx('is-full bs-full grid overflow-hidden', debug && 'grid-cols-2 lg:grid-cols-[1fr_600px]')}>
65
+ <div className={mx('dx-container grid', debug && 'grid-cols-2 lg:grid-cols-[1fr_600px]')}>
67
66
  <EditorComponent ref={mergedRef} object={object} text={text} extensions={extensions} {...props} />
68
67
 
69
68
  {debug && (
70
69
  <div
71
- className='grid bs-full auto-rows-fr border-l border-separator divide-y divide-separator overflow-hidden'
70
+ className='grid h-full auto-rows-fr border-l border-separator divide-y divide-separator overflow-hidden'
72
71
  {...attentionAttrs}
73
72
  >
74
73
  {view && debugCustom?.(view)}
@@ -77,7 +76,14 @@ export const EditorStory = forwardRef<EditorController, StoryProps>(
77
76
  {view?.state.doc.toString()}
78
77
  </pre>
79
78
  )}
80
- {(debug === 'tree' || debug === 'raw+tree') && <JsonFilter data={tree} classNames='p-1 text-xs' />}
79
+ {(debug === 'tree' || debug === 'raw+tree') && (
80
+ <Json.Root data={tree}>
81
+ <Json.Content>
82
+ <Json.Filter />
83
+ <Json.Data classNames='p-1 text-xs' />
84
+ </Json.Content>
85
+ </Json.Root>
86
+ )}
81
87
  </div>
82
88
  )}
83
89
  </div>
@@ -88,7 +94,7 @@ export const EditorStory = forwardRef<EditorController, StoryProps>(
88
94
  /**
89
95
  * Default story component.
90
96
  */
91
- const EditorComponent = forwardRef<EditorController, StoryProps>(
97
+ const EditorComponent = forwardRef<EditorController, EditorStoryProps>(
92
98
  (
93
99
  {
94
100
  id = defaultId,
@@ -101,7 +107,7 @@ const EditorComponent = forwardRef<EditorController, StoryProps>(
101
107
  scrollTo,
102
108
  selection,
103
109
  extensions,
104
- slots = editorSlots,
110
+ slots = documentSlots,
105
111
  onReady,
106
112
  },
107
113
  forwardedRef,
@@ -119,7 +125,6 @@ const EditorComponent = forwardRef<EditorController, StoryProps>(
119
125
  createBasicExtensions({ readOnly, placeholder, lineNumbers, scrollPastEnd: true, search: true }),
120
126
  createThemeExtensions({ monospace, themeMode, syntaxHighlighting: true, slots }),
121
127
  createMarkdownExtensions(),
122
- decorateMarkdown(),
123
128
  extensions || [],
124
129
  ],
125
130
  }),