@dxos/react-ui-editor 0.8.4-main.9be5663bfe → 0.8.4-main.abd8ff62ef
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +548 -423
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/translations.mjs +39 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +548 -423
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/translations.mjs +41 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/types/src/components/Editor/Editor.d.ts +30 -13
- package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.stories.d.ts +4 -4
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
- package/dist/types/src/components/{EditorContent/EditorContent.d.ts → Editor/EditorView.d.ts} +5 -5
- package/dist/types/src/components/Editor/EditorView.d.ts.map +1 -0
- package/dist/types/src/components/Editor/controller.d.ts.map +1 -0
- package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/menu.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/popover.d.ts +2 -1
- package/dist/types/src/components/EditorMenuProvider/popover.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -1
- package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +2 -2
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/blocks.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/formatting.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/headings.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/index.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/lists.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/types.d.ts +6 -0
- package/dist/types/src/components/EditorToolbar/types.d.ts.map +1 -0
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts +2 -2
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/extensions/Assistant.stories.d.ts +10 -0
- package/dist/types/src/extensions/Assistant.stories.d.ts.map +1 -0
- package/dist/types/src/extensions/assistant-extension.d.ts +24 -0
- package/dist/types/src/extensions/assistant-extension.d.ts.map +1 -0
- package/dist/types/src/extensions/index.d.ts +2 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts +25 -0
- package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/stories/Automerge.stories.d.ts +24 -24
- package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Comments.stories.d.ts +1 -1
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/stories/EditorToolbar.stories.d.ts +27 -25
- package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Experimental.stories.d.ts +2 -2
- package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Markdown.stories.d.ts +1 -1
- package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Popover.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Preview.stories.d.ts +1 -1
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
- package/dist/types/src/stories/TextEditor.stories.d.ts +1 -1
- package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Theme.stories.d.ts.map +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
- package/dist/types/src/stories/components/util.d.ts +2 -1
- package/dist/types/src/stories/components/util.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +24 -24
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/react.d.ts +1 -1
- package/dist/types/src/util/react.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +50 -41
- package/src/components/Editor/Editor.stories.tsx +10 -15
- package/src/components/Editor/Editor.tsx +51 -34
- package/src/components/Editor/EditorView.tsx +103 -0
- package/src/components/EditorMenuProvider/popover.ts +2 -1
- package/src/components/EditorToolbar/EditorToolbar.tsx +9 -7
- package/src/components/EditorToolbar/blocks.ts +3 -2
- package/src/components/EditorToolbar/formatting.ts +3 -2
- package/src/components/EditorToolbar/headings.ts +3 -2
- package/src/components/EditorToolbar/image.ts +1 -1
- package/src/components/EditorToolbar/index.ts +2 -2
- package/src/components/EditorToolbar/lists.ts +3 -2
- package/src/components/EditorToolbar/search.ts +1 -1
- package/src/components/EditorToolbar/types.ts +8 -0
- package/src/components/EditorToolbar/view-mode.ts +4 -3
- package/src/components/index.ts +0 -3
- package/src/extensions/Assistant.stories.tsx +112 -0
- package/src/extensions/assistant-extension.tsx +223 -0
- package/src/extensions/index.ts +5 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useBasicMarkdownExtensions.ts +55 -0
- package/src/index.ts +1 -4
- package/src/stories/Automerge.stories.tsx +2 -1
- package/src/stories/Comments.stories.tsx +2 -1
- package/src/stories/EditorToolbar.stories.tsx +35 -78
- package/src/stories/Experimental.stories.tsx +7 -7
- package/src/stories/Theme.stories.tsx +2 -2
- package/src/stories/components/EditorStory.tsx +9 -7
- package/src/stories/components/util.tsx +1 -1
- package/src/util/react.tsx +1 -1
- package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +0 -1
- package/dist/types/src/components/EditorContent/controller.d.ts.map +0 -1
- package/dist/types/src/components/EditorContent/index.d.ts +0 -3
- package/dist/types/src/components/EditorContent/index.d.ts.map +0 -1
- package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts +0 -11
- package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts.map +0 -1
- package/src/components/EditorContent/EditorContent.tsx +0 -82
- package/src/components/EditorContent/index.ts +0 -6
- package/src/components/EditorToolbar/useEditorToolbar.ts +0 -20
- /package/dist/types/src/components/{EditorContent → Editor}/controller.d.ts +0 -0
- /package/src/components/{EditorContent → Editor}/controller.ts +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-editor",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.abd8ff62ef",
|
|
4
4
|
"description": "Text editor components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -12,18 +12,24 @@
|
|
|
12
12
|
"author": "DXOS.org",
|
|
13
13
|
"sideEffects": false,
|
|
14
14
|
"type": "module",
|
|
15
|
+
"imports": {
|
|
16
|
+
"#translations": "./src/translations.ts"
|
|
17
|
+
},
|
|
15
18
|
"exports": {
|
|
16
19
|
".": {
|
|
17
20
|
"source": "./src/index.ts",
|
|
18
21
|
"types": "./dist/types/src/index.d.ts",
|
|
19
22
|
"browser": "./dist/lib/browser/index.mjs",
|
|
20
23
|
"node": "./dist/lib/node-esm/index.mjs"
|
|
24
|
+
},
|
|
25
|
+
"./translations": {
|
|
26
|
+
"source": "./src/translations.ts",
|
|
27
|
+
"types": "./dist/types/src/translations.d.ts",
|
|
28
|
+
"browser": "./dist/lib/browser/translations.mjs",
|
|
29
|
+
"node": "./dist/lib/node-esm/translations.mjs"
|
|
21
30
|
}
|
|
22
31
|
},
|
|
23
32
|
"types": "dist/types/src/index.d.ts",
|
|
24
|
-
"typesVersions": {
|
|
25
|
-
"*": {}
|
|
26
|
-
},
|
|
27
33
|
"files": [
|
|
28
34
|
"dist",
|
|
29
35
|
"src"
|
|
@@ -63,31 +69,33 @@
|
|
|
63
69
|
"lodash.merge": "^4.6.2",
|
|
64
70
|
"lodash.sortby": "^4.7.0",
|
|
65
71
|
"style-mod": "^4.1.0",
|
|
66
|
-
"@dxos/app-graph": "0.8.4-main.
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/echo
|
|
73
|
-
"@dxos/
|
|
74
|
-
"@dxos/
|
|
75
|
-
"@dxos/
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/
|
|
78
|
-
"@dxos/
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/react-ui-mosaic": "0.8.4-main.
|
|
81
|
-
"@dxos/ui": "0.8.4-main.
|
|
82
|
-
"@dxos/ui-
|
|
83
|
-
"@dxos/
|
|
84
|
-
"@dxos/ui-
|
|
72
|
+
"@dxos/app-graph": "0.8.4-main.abd8ff62ef",
|
|
73
|
+
"@dxos/async": "0.8.4-main.abd8ff62ef",
|
|
74
|
+
"@dxos/client": "0.8.4-main.abd8ff62ef",
|
|
75
|
+
"@dxos/context": "0.8.4-main.abd8ff62ef",
|
|
76
|
+
"@dxos/debug": "0.8.4-main.abd8ff62ef",
|
|
77
|
+
"@dxos/display-name": "0.8.4-main.abd8ff62ef",
|
|
78
|
+
"@dxos/echo": "0.8.4-main.abd8ff62ef",
|
|
79
|
+
"@dxos/echo-db": "0.8.4-main.abd8ff62ef",
|
|
80
|
+
"@dxos/effect": "0.8.4-main.abd8ff62ef",
|
|
81
|
+
"@dxos/invariant": "0.8.4-main.abd8ff62ef",
|
|
82
|
+
"@dxos/log": "0.8.4-main.abd8ff62ef",
|
|
83
|
+
"@dxos/lit-ui": "0.8.4-main.abd8ff62ef",
|
|
84
|
+
"@dxos/protocols": "0.8.4-main.abd8ff62ef",
|
|
85
|
+
"@dxos/react-hooks": "0.8.4-main.abd8ff62ef",
|
|
86
|
+
"@dxos/react-ui-mosaic": "0.8.4-main.abd8ff62ef",
|
|
87
|
+
"@dxos/react-ui-menu": "0.8.4-main.abd8ff62ef",
|
|
88
|
+
"@dxos/ui-editor": "0.8.4-main.abd8ff62ef",
|
|
89
|
+
"@dxos/ui": "0.8.4-main.abd8ff62ef",
|
|
90
|
+
"@dxos/ui-theme": "0.8.4-main.abd8ff62ef",
|
|
91
|
+
"@dxos/util": "0.8.4-main.abd8ff62ef"
|
|
85
92
|
},
|
|
86
93
|
"devDependencies": {
|
|
87
94
|
"@automerge/automerge": "3.2.3",
|
|
88
95
|
"@automerge/automerge-repo": "2.5.1",
|
|
89
96
|
"@automerge/automerge-repo-network-broadcastchannel": "2.5.1",
|
|
90
97
|
"@effect-atom/atom-react": "^0.5.0",
|
|
98
|
+
"@effect/ai": "0.33.2",
|
|
91
99
|
"@effect/platform": "0.94.4",
|
|
92
100
|
"@types/chai": "^4.2.15",
|
|
93
101
|
"@types/chai-dom": "^1.11.0",
|
|
@@ -106,22 +114,23 @@
|
|
|
106
114
|
"react": "~19.2.3",
|
|
107
115
|
"react-dom": "~19.2.3",
|
|
108
116
|
"react-test-renderer": "~19.2.0",
|
|
109
|
-
"vite": "^
|
|
117
|
+
"vite": "^8.0.10",
|
|
110
118
|
"vite-plugin-top-level-await": "^1.6.0",
|
|
111
|
-
"vite-plugin-wasm": "^3.
|
|
112
|
-
"@dxos/config": "0.8.4-main.
|
|
113
|
-
"@dxos/
|
|
114
|
-
"@dxos/echo-atom": "0.8.4-main.
|
|
115
|
-
"@dxos/
|
|
116
|
-
"@dxos/
|
|
117
|
-
"@dxos/
|
|
118
|
-
"@dxos/react-
|
|
119
|
-
"@dxos/react-ui
|
|
120
|
-
"@dxos/
|
|
121
|
-
"@dxos/
|
|
122
|
-
"@dxos/
|
|
123
|
-
"@dxos/
|
|
124
|
-
"@dxos/ui-
|
|
119
|
+
"vite-plugin-wasm": "^3.6.0",
|
|
120
|
+
"@dxos/config": "0.8.4-main.abd8ff62ef",
|
|
121
|
+
"@dxos/ai": "0.8.4-main.abd8ff62ef",
|
|
122
|
+
"@dxos/echo-atom": "0.8.4-main.abd8ff62ef",
|
|
123
|
+
"@dxos/random": "0.8.4-main.abd8ff62ef",
|
|
124
|
+
"@dxos/echo": "0.8.4-main.abd8ff62ef",
|
|
125
|
+
"@dxos/keyboard": "0.8.4-main.abd8ff62ef",
|
|
126
|
+
"@dxos/react-client": "0.8.4-main.abd8ff62ef",
|
|
127
|
+
"@dxos/react-ui": "0.8.4-main.abd8ff62ef",
|
|
128
|
+
"@dxos/react-ui-attention": "0.8.4-main.abd8ff62ef",
|
|
129
|
+
"@dxos/ui-theme": "0.8.4-main.abd8ff62ef",
|
|
130
|
+
"@dxos/schema": "0.8.4-main.abd8ff62ef",
|
|
131
|
+
"@dxos/storybook-utils": "0.8.4-main.abd8ff62ef",
|
|
132
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.abd8ff62ef",
|
|
133
|
+
"@dxos/ui-types": "0.8.4-main.abd8ff62ef"
|
|
125
134
|
},
|
|
126
135
|
"peerDependencies": {
|
|
127
136
|
"@effect-atom/atom-react": "^0.5.0",
|
|
@@ -129,9 +138,9 @@
|
|
|
129
138
|
"effect": "3.20.0",
|
|
130
139
|
"react": "~19.2.3",
|
|
131
140
|
"react-dom": "~19.2.3",
|
|
132
|
-
"@dxos/react-
|
|
133
|
-
"@dxos/
|
|
134
|
-
"@dxos/
|
|
141
|
+
"@dxos/react-ui": "0.8.4-main.abd8ff62ef",
|
|
142
|
+
"@dxos/ui-theme": "0.8.4-main.abd8ff62ef",
|
|
143
|
+
"@dxos/react-client": "0.8.4-main.abd8ff62ef"
|
|
135
144
|
},
|
|
136
145
|
"publishConfig": {
|
|
137
146
|
"access": "public"
|
|
@@ -20,11 +20,7 @@ import {
|
|
|
20
20
|
} from '@dxos/ui-editor';
|
|
21
21
|
|
|
22
22
|
import { createMenuGroup } from '../EditorMenuProvider';
|
|
23
|
-
import { Editor, type
|
|
24
|
-
|
|
25
|
-
// TODO(burdon): PreviewPopoverProvider (MarkdownStream, Preview story).
|
|
26
|
-
// TODO(burdon): Adapt Markdown plugin to use new Editor (plan first to check fit).
|
|
27
|
-
// TODO(burdon): Remove redundant hooks and simplify props.
|
|
23
|
+
import { Editor, type EditorViewProps } from './Editor';
|
|
28
24
|
|
|
29
25
|
random.seed(1234);
|
|
30
26
|
|
|
@@ -32,8 +28,7 @@ const initialValue = ['# Blue Monday', '', 'How does it **feel**?', ''].join('\n
|
|
|
32
28
|
|
|
33
29
|
const items = random.helpers.multiple(random.commerce.productName, { count: 10 }).sort();
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
const withExtensions: Decorator<EditorContentProps> = (Story, { args }) => {
|
|
31
|
+
const withExtensions: Decorator<EditorViewProps> = (Story, { args }) => {
|
|
37
32
|
const { themeMode } = useThemeContext();
|
|
38
33
|
const extensions = useMemo(
|
|
39
34
|
() => [
|
|
@@ -51,7 +46,7 @@ const withExtensions: Decorator<EditorContentProps> = (Story, { args }) => {
|
|
|
51
46
|
|
|
52
47
|
const meta = {
|
|
53
48
|
title: 'ui/react-ui-editor/Editor',
|
|
54
|
-
component: Editor.
|
|
49
|
+
component: Editor.View,
|
|
55
50
|
decorators: [withExtensions, withTheme(), withLayout({ layout: 'column' }), withAttention()],
|
|
56
51
|
parameters: {
|
|
57
52
|
layout: 'fullscreen',
|
|
@@ -59,16 +54,16 @@ const meta = {
|
|
|
59
54
|
args: {
|
|
60
55
|
initialValue,
|
|
61
56
|
},
|
|
62
|
-
} satisfies Meta<typeof Editor.
|
|
57
|
+
} satisfies Meta<typeof Editor.View>;
|
|
63
58
|
|
|
64
59
|
export default meta;
|
|
65
60
|
|
|
66
|
-
type Story = StoryObj<
|
|
61
|
+
type Story = StoryObj<EditorViewProps>;
|
|
67
62
|
|
|
68
63
|
export const Default: Story = {
|
|
69
64
|
render: (args) => (
|
|
70
65
|
<Editor.Root>
|
|
71
|
-
<Editor.
|
|
66
|
+
<Editor.View {...args} />
|
|
72
67
|
</Editor.Root>
|
|
73
68
|
),
|
|
74
69
|
};
|
|
@@ -77,7 +72,7 @@ export const WithToolbar: Story = {
|
|
|
77
72
|
render: (args) => (
|
|
78
73
|
<Editor.Root>
|
|
79
74
|
<Editor.Toolbar />
|
|
80
|
-
<Editor.
|
|
75
|
+
<Editor.View {...args} />
|
|
81
76
|
</Editor.Root>
|
|
82
77
|
),
|
|
83
78
|
};
|
|
@@ -85,10 +80,10 @@ export const WithToolbar: Story = {
|
|
|
85
80
|
export const WithPopover: Story = {
|
|
86
81
|
render: (args) => (
|
|
87
82
|
<Editor.Root trigger={['@']} getMenu={({ text }) => [createMenuGroup({ items, filter: text })]}>
|
|
88
|
-
<Editor.
|
|
83
|
+
<Editor.Content>
|
|
89
84
|
<Editor.Toolbar />
|
|
90
|
-
<Editor.
|
|
91
|
-
</Editor.
|
|
85
|
+
<Editor.View {...args} />
|
|
86
|
+
</Editor.Content>
|
|
92
87
|
</Editor.Root>
|
|
93
88
|
),
|
|
94
89
|
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Extension } from '@codemirror/state';
|
|
6
|
-
import {
|
|
6
|
+
import { Atom } from '@effect-atom/atom-react';
|
|
7
7
|
import { createContext } from '@radix-ui/react-context';
|
|
8
8
|
import React, { type PropsWithChildren, forwardRef, useCallback, useImperativeHandle, useMemo, useState } from 'react';
|
|
9
9
|
|
|
@@ -13,18 +13,23 @@ import { mx } from '@dxos/ui-theme';
|
|
|
13
13
|
import { isNonNullable } from '@dxos/util';
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
type
|
|
19
|
-
|
|
20
|
-
} from '../
|
|
21
|
-
import { EditorMenuProvider, type UseEditorMenuProps, useEditorMenu } from '../EditorMenuProvider';
|
|
16
|
+
EditorMenuProvider,
|
|
17
|
+
type EditorMenuProviderProps,
|
|
18
|
+
type UseEditorMenuProps,
|
|
19
|
+
useEditorMenu,
|
|
20
|
+
} from '../EditorMenuProvider';
|
|
22
21
|
import {
|
|
23
22
|
type EditorToolbarState,
|
|
24
23
|
EditorToolbar as NaturalEditorToolbar,
|
|
25
24
|
type EditorToolbarProps as NaturalEditorToolbarProps,
|
|
26
|
-
useEditorToolbar,
|
|
27
25
|
} from '../EditorToolbar';
|
|
26
|
+
import {
|
|
27
|
+
type EditorController,
|
|
28
|
+
EditorView as NaturalEditorContent,
|
|
29
|
+
type EditorViewProps as NaturalEditorContentProps,
|
|
30
|
+
createEditorController,
|
|
31
|
+
noopController,
|
|
32
|
+
} from './EditorView';
|
|
28
33
|
|
|
29
34
|
//
|
|
30
35
|
// Context
|
|
@@ -49,7 +54,11 @@ export { useEditorContext };
|
|
|
49
54
|
//
|
|
50
55
|
|
|
51
56
|
type EditorRootProps = PropsWithChildren<
|
|
52
|
-
Pick<EditorContextValue, 'extensions'> &
|
|
57
|
+
Pick<EditorContextValue, 'extensions'> &
|
|
58
|
+
Omit<UseEditorMenuProps, 'viewRef'> &
|
|
59
|
+
Pick<EditorMenuProviderProps, 'numItems'> & {
|
|
60
|
+
viewMode?: EditorToolbarState['viewMode'];
|
|
61
|
+
}
|
|
53
62
|
>;
|
|
54
63
|
|
|
55
64
|
/**
|
|
@@ -57,11 +66,9 @@ type EditorRootProps = PropsWithChildren<
|
|
|
57
66
|
* Provides context for all child components and manages the editor controller state.
|
|
58
67
|
*/
|
|
59
68
|
const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
|
|
60
|
-
({ children, extensions: extensionsProp, viewMode, ...props }, forwardedRef) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const [controller, setController] = useState<EditorController>();
|
|
64
|
-
useImperativeHandle(forwardedRef, () => controller ?? noopController, [controller]);
|
|
69
|
+
({ children, extensions: extensionsProp, viewMode, numItems, ...props }, forwardedRef) => {
|
|
70
|
+
// TODO(wittjosiah): Including initialState in the deps causes reactivity issues.
|
|
71
|
+
const state = useMemo(() => Atom.make<EditorToolbarState>({ viewMode }), [viewMode]);
|
|
65
72
|
|
|
66
73
|
// TODO(burdon): Consider lighter-weight approach if EditorMenuProvider is not needed.
|
|
67
74
|
const { groupsRef, extension, ...menuProps } = useEditorMenu(props);
|
|
@@ -70,6 +77,10 @@ const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
|
|
|
70
77
|
[extension, extensionsProp],
|
|
71
78
|
);
|
|
72
79
|
|
|
80
|
+
// External controller.
|
|
81
|
+
const [controller, setController] = useState<EditorController>(noopController);
|
|
82
|
+
useImperativeHandle(forwardedRef, () => controller, [controller]);
|
|
83
|
+
|
|
73
84
|
return (
|
|
74
85
|
<EditorContextProvider
|
|
75
86
|
controller={controller}
|
|
@@ -77,7 +88,7 @@ const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
|
|
|
77
88
|
extensions={extensions}
|
|
78
89
|
state={state}
|
|
79
90
|
>
|
|
80
|
-
<EditorMenuProvider view={controller?.view} groups={groupsRef.current} {...menuProps}>
|
|
91
|
+
<EditorMenuProvider view={controller?.view} groups={groupsRef.current} numItems={numItems} {...menuProps}>
|
|
81
92
|
{children}
|
|
82
93
|
</EditorMenuProvider>
|
|
83
94
|
</EditorContextProvider>
|
|
@@ -88,17 +99,17 @@ const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
|
|
|
88
99
|
EditorRoot.displayName = 'Editor.Root';
|
|
89
100
|
|
|
90
101
|
//
|
|
91
|
-
//
|
|
102
|
+
// Content
|
|
92
103
|
//
|
|
93
104
|
|
|
94
|
-
const
|
|
105
|
+
const EDITOR_CONTENT_NAME = 'Editor.Content';
|
|
95
106
|
|
|
96
|
-
type
|
|
107
|
+
type EditorContentProps = ThemedClassName<PropsWithChildren<{}>>;
|
|
97
108
|
|
|
98
109
|
/**
|
|
99
|
-
*
|
|
110
|
+
* Content component that wraps the toolbar and editor view area.
|
|
100
111
|
*/
|
|
101
|
-
const
|
|
112
|
+
const EditorContent = ({ classNames, children }: EditorContentProps) => {
|
|
102
113
|
return (
|
|
103
114
|
<div role='none' className={mx('grid grid-rows-[min-content_1fr] h-full overflow-hidden', classNames)}>
|
|
104
115
|
{children}
|
|
@@ -106,23 +117,22 @@ const EditorViewport = ({ classNames, children }: EditorViewportProps) => {
|
|
|
106
117
|
);
|
|
107
118
|
};
|
|
108
119
|
|
|
109
|
-
|
|
120
|
+
EditorContent.displayName = EDITOR_CONTENT_NAME;
|
|
110
121
|
|
|
111
122
|
//
|
|
112
|
-
//
|
|
123
|
+
// View
|
|
113
124
|
//
|
|
114
125
|
|
|
115
|
-
const
|
|
126
|
+
const EDITOR_VIEW_NAME = 'Editor.View';
|
|
116
127
|
|
|
117
|
-
type
|
|
128
|
+
type EditorViewProps = Omit<NaturalEditorContentProps, 'ref'>;
|
|
118
129
|
|
|
119
130
|
/**
|
|
120
|
-
*
|
|
131
|
+
* View component that renders the actual CodeMirror editor.
|
|
121
132
|
* Automatically registers the editor controller with the context.
|
|
122
133
|
*/
|
|
123
|
-
const
|
|
124
|
-
const { extensions: additionalExtensions = [], setController } = useEditorContext(
|
|
125
|
-
|
|
134
|
+
const EditorView = ({ extensions: providedExtensions, ...props }: EditorViewProps) => {
|
|
135
|
+
const { extensions: additionalExtensions = [], setController } = useEditorContext(EDITOR_VIEW_NAME);
|
|
126
136
|
const extensions = useMemo(
|
|
127
137
|
() => [additionalExtensions, providedExtensions].filter(isNonNullable).flat(),
|
|
128
138
|
[providedExtensions, additionalExtensions],
|
|
@@ -131,7 +141,7 @@ const EditorContent = ({ extensions: providedExtensions, ...props }: EditorConte
|
|
|
131
141
|
return <NaturalEditorContent {...props} extensions={extensions} ref={setController} />;
|
|
132
142
|
};
|
|
133
143
|
|
|
134
|
-
|
|
144
|
+
EditorView.displayName = EDITOR_VIEW_NAME;
|
|
135
145
|
|
|
136
146
|
//
|
|
137
147
|
// Toolbar
|
|
@@ -147,8 +157,6 @@ type EditorToolbarProps = Omit<NaturalEditorToolbarProps, 'getView' | 'state'>;
|
|
|
147
157
|
*/
|
|
148
158
|
const EditorToolbar = (props: EditorToolbarProps) => {
|
|
149
159
|
const { controller, state } = useEditorContext(EDITOR_TOOLBAR_NAME);
|
|
150
|
-
|
|
151
|
-
// TODO(burdon): Fix invariant.
|
|
152
160
|
const getView = useCallback(() => {
|
|
153
161
|
invariant(controller?.view);
|
|
154
162
|
return controller?.view;
|
|
@@ -165,9 +173,18 @@ EditorToolbar.displayName = EDITOR_TOOLBAR_NAME;
|
|
|
165
173
|
|
|
166
174
|
export const Editor = {
|
|
167
175
|
Root: EditorRoot,
|
|
168
|
-
Viewport: EditorViewport,
|
|
169
|
-
Content: EditorContent,
|
|
170
176
|
Toolbar: EditorToolbar,
|
|
177
|
+
Content: EditorContent,
|
|
178
|
+
View: EditorView,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export type {
|
|
182
|
+
EditorController,
|
|
183
|
+
EditorRootProps,
|
|
184
|
+
EditorContentProps,
|
|
185
|
+
EditorViewProps,
|
|
186
|
+
EditorToolbarProps,
|
|
187
|
+
EditorToolbarState,
|
|
171
188
|
};
|
|
172
189
|
|
|
173
|
-
export
|
|
190
|
+
export { createEditorController };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Transaction } from '@codemirror/state';
|
|
6
|
+
import { EditorView as NaturalEditorView } from '@codemirror/view';
|
|
7
|
+
import React, { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
|
|
8
|
+
|
|
9
|
+
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
|
+
import { initialSync } from '@dxos/ui-editor';
|
|
11
|
+
import { mx } from '@dxos/ui-theme';
|
|
12
|
+
|
|
13
|
+
import { type UseTextEditorProps, useTextEditor } from '../../hooks';
|
|
14
|
+
import { type EditorController, createEditorController, noopController } from './controller';
|
|
15
|
+
|
|
16
|
+
export type EditorViewProps = ThemedClassName<
|
|
17
|
+
{
|
|
18
|
+
focusable?: boolean;
|
|
19
|
+
value?: string;
|
|
20
|
+
onChange?: (value: string) => void;
|
|
21
|
+
} & UseTextEditorProps
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Minimal text editor.
|
|
26
|
+
* NOTE: This shouold not be used with the automerge extension.
|
|
27
|
+
*/
|
|
28
|
+
// TODO(burdon): Move controller to Root component, then make composable.
|
|
29
|
+
export const EditorView = forwardRef<EditorController, EditorViewProps>(
|
|
30
|
+
({ classNames, id, extensions, selectionEnd, focusable = true, value, onChange, ...props }, forwardedRef) => {
|
|
31
|
+
// Hold the latest onChange in a ref so callers may pass an inline callback
|
|
32
|
+
// without forcing the underlying editor to be destroyed and recreated on
|
|
33
|
+
// every render — which would blur the focused input on each keystroke.
|
|
34
|
+
const onChangeRef = useRef(onChange);
|
|
35
|
+
onChangeRef.current = onChange;
|
|
36
|
+
|
|
37
|
+
const { parentRef, focusAttributes, view } = useTextEditor(
|
|
38
|
+
() => ({
|
|
39
|
+
id,
|
|
40
|
+
initialValue: value,
|
|
41
|
+
selectionEnd,
|
|
42
|
+
extensions: [
|
|
43
|
+
extensions ?? [],
|
|
44
|
+
NaturalEditorView.updateListener.of(({ view, docChanged, transactions }) => {
|
|
45
|
+
const isInitialSync = transactions.some((tr) => tr.annotation(Transaction.userEvent) === initialSync.value);
|
|
46
|
+
if (!isInitialSync && docChanged) {
|
|
47
|
+
onChangeRef.current?.(view.state.doc.toString());
|
|
48
|
+
}
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
51
|
+
...props,
|
|
52
|
+
}),
|
|
53
|
+
[id, extensions, selectionEnd],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// External controller.
|
|
57
|
+
useImperativeHandle(forwardedRef, () => {
|
|
58
|
+
return createEditorController(view);
|
|
59
|
+
}, [id, view]);
|
|
60
|
+
|
|
61
|
+
// Sync the editor doc to the controlled `value` prop, but only when they
|
|
62
|
+
// disagree. After internal typing the prop will already match the editor's
|
|
63
|
+
// doc, and dispatching anyway would race fast keystrokes — a stale rAF
|
|
64
|
+
// closure can replace doc content with an older value, dropping characters.
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (!view) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const next = value ?? '';
|
|
70
|
+
if (view.state.doc.toString() === next) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
requestAnimationFrame(() => {
|
|
74
|
+
if (view.state.doc.toString() === next) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
view.dispatch({
|
|
78
|
+
annotations: initialSync,
|
|
79
|
+
changes: [{ from: 0, to: view.state.doc.length, insert: next }],
|
|
80
|
+
selection: selectionEnd ? { anchor: next.length } : undefined,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (selectionEnd) {
|
|
84
|
+
view.focus();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}, [view, value, selectionEnd]);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div
|
|
91
|
+
role='none'
|
|
92
|
+
className={mx(
|
|
93
|
+
'w-full outline-hidden focus:border-accent-surface focus-within:border-neutral-focus-indicator',
|
|
94
|
+
classNames,
|
|
95
|
+
)}
|
|
96
|
+
{...(focusable ? focusAttributes : {})}
|
|
97
|
+
ref={parentRef}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
export { type EditorController, createEditorController, noopController };
|
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
keymap,
|
|
14
14
|
} from '@codemirror/view';
|
|
15
15
|
|
|
16
|
-
import { type PlaceholderOptions,
|
|
16
|
+
import { type PlaceholderOptions, modalStateField, placeholder } from '@dxos/ui-editor';
|
|
17
|
+
import { type Range } from '@dxos/ui-editor/types';
|
|
17
18
|
import { isNonNullable, isTruthy } from '@dxos/util';
|
|
18
19
|
|
|
19
20
|
const DELIMITERS = [' ', ':'];
|
|
@@ -9,7 +9,7 @@ import React, { memo, useMemo } from 'react';
|
|
|
9
9
|
import { type Node } from '@dxos/app-graph';
|
|
10
10
|
import { ElevationProvider, type ThemedClassName } from '@dxos/react-ui';
|
|
11
11
|
import { type ActionGraphProps, Menu, type MenuAction, MenuBuilder, useMenuActions } from '@dxos/react-ui-menu';
|
|
12
|
-
import { type EditorViewMode } from '@dxos/ui-editor';
|
|
12
|
+
import { type EditorViewMode } from '@dxos/ui-editor/types';
|
|
13
13
|
|
|
14
14
|
import { addBlocks } from './blocks';
|
|
15
15
|
import { addFormatting } from './formatting';
|
|
@@ -17,9 +17,11 @@ import { addHeadings } from './headings';
|
|
|
17
17
|
import { addImageUpload } from './image';
|
|
18
18
|
import { addLists } from './lists';
|
|
19
19
|
import { addSearch } from './search';
|
|
20
|
-
import { type EditorToolbarState } from './
|
|
20
|
+
import { type EditorToolbarState } from './types';
|
|
21
21
|
import { addViewMode } from './view-mode';
|
|
22
22
|
|
|
23
|
+
// TODO(burdon): Enable toolbar variants (e.g., markdown, code).
|
|
24
|
+
|
|
23
25
|
export type EditorToolbarFeatureFlags = Partial<{
|
|
24
26
|
showHeadings: boolean;
|
|
25
27
|
showFormatting: boolean;
|
|
@@ -49,7 +51,7 @@ export type EditorToolbarProps = ThemedClassName<
|
|
|
49
51
|
>;
|
|
50
52
|
|
|
51
53
|
export const EditorToolbar = memo(({ classNames, role, attendableId, onAction, ...props }: EditorToolbarProps) => {
|
|
52
|
-
const menuActions =
|
|
54
|
+
const menuActions = useMarkdownMenuActions(props);
|
|
53
55
|
|
|
54
56
|
return (
|
|
55
57
|
<ElevationProvider elevation={role === 'section' ? 'positioned' : 'base'}>
|
|
@@ -63,13 +65,13 @@ export const EditorToolbar = memo(({ classNames, role, attendableId, onAction, .
|
|
|
63
65
|
type ToolbarActionsProps = Pick<EditorToolbarActionGraphProps, 'state' | 'getView' | 'customActions'> &
|
|
64
66
|
EditorToolbarFeatureFlags;
|
|
65
67
|
|
|
68
|
+
// TODO(burdon): Some actions should toggle the state (e.g., toggle bullets on/off depending on the current state).
|
|
66
69
|
// TODO(wittjosiah): Toolbar re-rendering is causing this graph to be recreated and breaking reactivity in some cases.
|
|
67
70
|
// E.g. for toolbar dropdowns which use active icon, the icon is not updated when the active item changes.
|
|
68
71
|
// This is currently only happening in the markdown plugin usage and should be reproduced in an editor story.
|
|
69
|
-
|
|
70
|
-
const useEditorToolbarActionGraph = ({ state, getView, customActions, ...features }: ToolbarActionsProps) => {
|
|
72
|
+
const useMarkdownMenuActions = ({ state, getView, customActions, ...features }: ToolbarActionsProps) => {
|
|
71
73
|
const menuCreator = useMemo(
|
|
72
|
-
() =>
|
|
74
|
+
() => createMarkdownActions({ state, getView, customActions, ...features }),
|
|
73
75
|
[
|
|
74
76
|
state,
|
|
75
77
|
getView,
|
|
@@ -87,7 +89,7 @@ const useEditorToolbarActionGraph = ({ state, getView, customActions, ...feature
|
|
|
87
89
|
return useMenuActions(menuCreator);
|
|
88
90
|
};
|
|
89
91
|
|
|
90
|
-
const
|
|
92
|
+
const createMarkdownActions = ({
|
|
91
93
|
state,
|
|
92
94
|
getView,
|
|
93
95
|
customActions,
|
|
@@ -7,8 +7,9 @@ import { type EditorView } from '@codemirror/view';
|
|
|
7
7
|
import { type ActionGroupBuilderFn, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
|
|
8
8
|
import { addBlockquote, addCodeblock, insertTable, removeBlockquote, removeCodeblock } from '@dxos/ui-editor';
|
|
9
9
|
|
|
10
|
-
import { translationKey } from '
|
|
11
|
-
|
|
10
|
+
import { translationKey } from '#translations';
|
|
11
|
+
|
|
12
|
+
import { type EditorToolbarState } from './types';
|
|
12
13
|
|
|
13
14
|
const blockTypes = {
|
|
14
15
|
blockquote: 'ph--quotes--regular',
|
|
@@ -7,8 +7,9 @@ import { type EditorView } from '@codemirror/view';
|
|
|
7
7
|
import { type ActionGroupBuilderFn, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
|
|
8
8
|
import { type Formatting, Inline, addLink, removeLink, setStyle } from '@dxos/ui-editor';
|
|
9
9
|
|
|
10
|
-
import { translationKey } from '
|
|
11
|
-
|
|
10
|
+
import { translationKey } from '#translations';
|
|
11
|
+
|
|
12
|
+
import { type EditorToolbarState } from './types';
|
|
12
13
|
|
|
13
14
|
const formats = {
|
|
14
15
|
strong: 'ph--text-b--regular',
|
|
@@ -7,8 +7,9 @@ import { type EditorView } from '@codemirror/view';
|
|
|
7
7
|
import { type ActionGroupBuilderFn, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
|
|
8
8
|
import { setHeading } from '@dxos/ui-editor';
|
|
9
9
|
|
|
10
|
-
import { translationKey } from '
|
|
11
|
-
|
|
10
|
+
import { translationKey } from '#translations';
|
|
11
|
+
|
|
12
|
+
import { type EditorToolbarState } from './types';
|
|
12
13
|
|
|
13
14
|
const headingIcons: Record<string, string> = {
|
|
14
15
|
0: 'ph--paragraph--regular',
|
|
@@ -7,8 +7,9 @@ import { type EditorView } from '@codemirror/view';
|
|
|
7
7
|
import { type ActionGroupBuilderFn, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
|
|
8
8
|
import { List, addList, removeList } from '@dxos/ui-editor';
|
|
9
9
|
|
|
10
|
-
import { translationKey } from '
|
|
11
|
-
|
|
10
|
+
import { translationKey } from '#translations';
|
|
11
|
+
|
|
12
|
+
import { type EditorToolbarState } from './types';
|
|
12
13
|
|
|
13
14
|
const listStyles = {
|
|
14
15
|
bullet: 'ph--list-bullets--regular',
|
|
@@ -7,7 +7,7 @@ import { type EditorView } from '@codemirror/view';
|
|
|
7
7
|
|
|
8
8
|
import { type ActionGroupBuilderFn } from '@dxos/react-ui-menu';
|
|
9
9
|
|
|
10
|
-
import { translationKey } from '
|
|
10
|
+
import { translationKey } from '#translations';
|
|
11
11
|
|
|
12
12
|
/** Add search action to the builder. */
|
|
13
13
|
export const addSearch =
|