@dxos/plugin-markdown 0.6.13 → 0.6.14-main.1366248
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/MarkdownContainer-J5BZUIVL.mjs +472 -0
- package/dist/lib/browser/MarkdownContainer-J5BZUIVL.mjs.map +7 -0
- package/dist/lib/browser/chunk-4X6YX3KU.mjs +15 -0
- package/dist/lib/browser/chunk-4X6YX3KU.mjs.map +7 -0
- package/dist/lib/browser/chunk-PV4AWYWK.mjs +52 -0
- package/dist/lib/browser/chunk-PV4AWYWK.mjs.map +7 -0
- package/dist/lib/browser/{chunk-CQJL4G4X.mjs → chunk-VZAGHNHU.mjs} +4 -2
- package/dist/lib/browser/chunk-VZAGHNHU.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +84 -125
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/meta.mjs +1 -1
- package/dist/lib/browser/types/index.mjs +6 -4
- package/dist/lib/node/MarkdownContainer-UYBYWCRU.cjs +487 -0
- package/dist/lib/node/MarkdownContainer-UYBYWCRU.cjs.map +7 -0
- package/dist/lib/node/chunk-2A5P424C.cjs +74 -0
- package/dist/lib/node/chunk-2A5P424C.cjs.map +7 -0
- package/dist/lib/node/{chunk-VWQH4WC2.cjs → chunk-BHPFK7YI.cjs} +11 -8
- package/dist/lib/node/chunk-BHPFK7YI.cjs.map +7 -0
- package/dist/lib/node/{DocumentCard-EHJDDSRY.cjs → chunk-PHHIPRJC.cjs} +16 -10
- package/dist/lib/node/chunk-PHHIPRJC.cjs.map +7 -0
- package/dist/lib/node/index.cjs +116 -153
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.cjs +3 -3
- package/dist/lib/node/meta.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types/index.cjs +8 -6
- package/dist/lib/node/types/index.cjs.map +2 -2
- package/dist/lib/node-esm/MarkdownContainer-P3EAZ3OR.mjs +473 -0
- package/dist/lib/node-esm/MarkdownContainer-P3EAZ3OR.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-BABK7FMW.mjs +17 -0
- package/dist/lib/node-esm/chunk-BABK7FMW.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-EREAR7QS.mjs +53 -0
- package/dist/lib/node-esm/chunk-EREAR7QS.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-OEMU3XY7.mjs +42 -0
- package/dist/lib/node-esm/chunk-OEMU3XY7.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +492 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/meta.mjs +10 -0
- package/dist/lib/node-esm/types/index.mjs +15 -0
- package/dist/types/src/MarkdownPlugin.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownContainer.d.ts +15 -0
- package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -0
- package/dist/types/src/components/MarkdownEditor.d.ts +13 -8
- package/dist/types/src/components/MarkdownEditor.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownEditor.stories.d.ts +4 -14
- package/dist/types/src/components/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar.stories.d.ts +4 -2
- package/dist/types/src/components/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -11
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/extensions.d.ts +11 -14
- package/dist/types/src/extensions.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useSelectCurrentThread.d.ts +6 -0
- package/dist/types/src/hooks/useSelectCurrentThread.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +4 -9
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/types/document.d.ts +10 -1
- package/dist/types/src/types/document.d.ts.map +1 -1
- package/dist/types/src/types/types.d.ts +8 -9
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/dist/types/src/util.d.ts.map +1 -1
- package/package.json +42 -45
- package/src/MarkdownPlugin.tsx +56 -114
- package/src/components/MarkdownContainer.tsx +109 -0
- package/src/components/MarkdownEditor.stories.tsx +34 -23
- package/src/components/MarkdownEditor.tsx +48 -80
- package/src/components/MarkdownSettings.tsx +15 -15
- package/src/components/Toolbar.stories.tsx +14 -11
- package/src/components/index.ts +2 -14
- package/src/extensions.tsx +139 -66
- package/src/hooks/index.ts +5 -0
- package/src/hooks/useSelectCurrentThread.tsx +46 -0
- package/src/meta.ts +15 -0
- package/src/translations.ts +1 -1
- package/src/types/document.ts +12 -0
- package/src/types/types.ts +10 -7
- package/src/util.tsx +6 -4
- package/dist/lib/browser/DocumentCard-2P4EICBA.mjs +0 -11
- package/dist/lib/browser/DocumentEditor-GPWV3VN3.mjs +0 -11
- package/dist/lib/browser/MarkdownEditor-EKJJQEFL.mjs +0 -10
- package/dist/lib/browser/MarkdownEditor-EKJJQEFL.mjs.map +0 -7
- package/dist/lib/browser/chunk-354DCID5.mjs +0 -117
- package/dist/lib/browser/chunk-354DCID5.mjs.map +0 -7
- package/dist/lib/browser/chunk-4GGD6YJO.mjs +0 -19
- package/dist/lib/browser/chunk-4GGD6YJO.mjs.map +0 -7
- package/dist/lib/browser/chunk-7AF2JLK4.mjs +0 -164
- package/dist/lib/browser/chunk-7AF2JLK4.mjs.map +0 -7
- package/dist/lib/browser/chunk-CQJL4G4X.mjs.map +0 -7
- package/dist/lib/browser/chunk-RL7QY322.mjs +0 -86
- package/dist/lib/browser/chunk-RL7QY322.mjs.map +0 -7
- package/dist/lib/browser/chunk-VUN4QKTT.mjs +0 -208
- package/dist/lib/browser/chunk-VUN4QKTT.mjs.map +0 -7
- package/dist/lib/node/DocumentCard-EHJDDSRY.cjs.map +0 -7
- package/dist/lib/node/DocumentEditor-I5GCRBKU.cjs +0 -29
- package/dist/lib/node/DocumentEditor-I5GCRBKU.cjs.map +0 -7
- package/dist/lib/node/MarkdownEditor-UE23H75V.cjs +0 -31
- package/dist/lib/node/MarkdownEditor-UE23H75V.cjs.map +0 -7
- package/dist/lib/node/chunk-7XIBNEI7.cjs +0 -238
- package/dist/lib/node/chunk-7XIBNEI7.cjs.map +0 -7
- package/dist/lib/node/chunk-KTYIOXL5.cjs +0 -149
- package/dist/lib/node/chunk-KTYIOXL5.cjs.map +0 -7
- package/dist/lib/node/chunk-Q4ZSCBQE.cjs +0 -114
- package/dist/lib/node/chunk-Q4ZSCBQE.cjs.map +0 -7
- package/dist/lib/node/chunk-RVGN72IX.cjs +0 -189
- package/dist/lib/node/chunk-RVGN72IX.cjs.map +0 -7
- package/dist/lib/node/chunk-TGMR2CKU.cjs +0 -52
- package/dist/lib/node/chunk-TGMR2CKU.cjs.map +0 -7
- package/dist/lib/node/chunk-VWQH4WC2.cjs.map +0 -7
- package/dist/types/src/components/DocumentCard.d.ts +0 -16
- package/dist/types/src/components/DocumentCard.d.ts.map +0 -1
- package/dist/types/src/components/DocumentEditor.d.ts +0 -14
- package/dist/types/src/components/DocumentEditor.d.ts.map +0 -1
- package/dist/types/src/components/HeadingMenu.d.ts +0 -13
- package/dist/types/src/components/HeadingMenu.d.ts.map +0 -1
- package/dist/types/src/components/Layout.d.ts +0 -6
- package/dist/types/src/components/Layout.d.ts.map +0 -1
- package/src/components/DocumentCard.tsx +0 -107
- package/src/components/DocumentEditor.tsx +0 -137
- package/src/components/HeadingMenu.tsx +0 -46
- package/src/components/Layout.tsx +0 -27
- package/src/meta.tsx +0 -19
- /package/dist/lib/{browser/DocumentCard-2P4EICBA.mjs.map → node-esm/meta.mjs.map} +0 -0
- /package/dist/lib/{browser/DocumentEditor-GPWV3VN3.mjs.map → node-esm/types/index.mjs.map} +0 -0
|
@@ -3,69 +3,63 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { openSearchPanel } from '@codemirror/search';
|
|
6
|
-
import { EditorView } from '@codemirror/view';
|
|
6
|
+
import { type EditorView } from '@codemirror/view';
|
|
7
7
|
import React, { useMemo, useEffect, useCallback } from 'react';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
type FileInfo,
|
|
11
|
-
LayoutAction,
|
|
12
|
-
type LayoutCoordinate,
|
|
13
|
-
useResolvePlugin,
|
|
14
|
-
useIntentResolver,
|
|
15
|
-
parseLayoutPlugin,
|
|
16
|
-
useIntentDispatcher,
|
|
17
|
-
} from '@dxos/app-framework';
|
|
18
|
-
import { parseAttentionPlugin } from '@dxos/plugin-attention';
|
|
9
|
+
import { type FileInfo, LayoutAction, useIntentDispatcher } from '@dxos/app-framework';
|
|
19
10
|
import { useThemeContext, useTranslation } from '@dxos/react-ui';
|
|
11
|
+
import { useAttendableAttributes, useAttention } from '@dxos/react-ui-attention';
|
|
20
12
|
import {
|
|
21
13
|
type Action,
|
|
22
14
|
type DNDOptions,
|
|
23
15
|
type EditorViewMode,
|
|
24
16
|
type EditorInputMode,
|
|
25
|
-
type
|
|
17
|
+
type EditorSelectionState,
|
|
18
|
+
type EditorStateStore,
|
|
26
19
|
Toolbar,
|
|
20
|
+
type UseTextEditorProps,
|
|
27
21
|
createBasicExtensions,
|
|
28
22
|
createMarkdownExtensions,
|
|
29
23
|
createThemeExtensions,
|
|
30
24
|
dropFile,
|
|
25
|
+
editorContent,
|
|
26
|
+
editorGutter,
|
|
31
27
|
processAction,
|
|
32
28
|
useActionHandler,
|
|
33
29
|
useCommentState,
|
|
34
30
|
useCommentClickListener,
|
|
35
31
|
useFormattingState,
|
|
36
32
|
useTextEditor,
|
|
37
|
-
editorContent,
|
|
38
|
-
editorGutter,
|
|
39
|
-
Cursor,
|
|
40
|
-
setSelection,
|
|
41
33
|
} from '@dxos/react-ui-editor';
|
|
42
34
|
import { sectionToolbarLayout } from '@dxos/react-ui-stack';
|
|
43
35
|
import { textBlockWidth, focusRing, mx } from '@dxos/react-ui-theme';
|
|
44
|
-
import { nonNullable } from '@dxos/util';
|
|
36
|
+
import { isNotFalsy, nonNullable } from '@dxos/util';
|
|
45
37
|
|
|
38
|
+
import { useSelectCurrentThread } from '../hooks';
|
|
46
39
|
import { MARKDOWN_PLUGIN } from '../meta';
|
|
47
|
-
import type
|
|
48
|
-
|
|
49
|
-
const attentionFragment = mx(
|
|
50
|
-
'group-focus-within/editor:attention-surface group-[[aria-current]]/editor:attention-surface',
|
|
51
|
-
'group-focus-within/editor:border-separator',
|
|
52
|
-
);
|
|
40
|
+
import { type MarkdownPluginState } from '../types';
|
|
53
41
|
|
|
54
42
|
const DEFAULT_VIEW_MODE: EditorViewMode = 'preview';
|
|
55
43
|
|
|
56
44
|
export type MarkdownEditorProps = {
|
|
57
45
|
id: string;
|
|
58
|
-
coordinate?: LayoutCoordinate;
|
|
59
|
-
inputMode?: EditorInputMode;
|
|
60
46
|
role?: string;
|
|
47
|
+
inputMode?: EditorInputMode;
|
|
61
48
|
scrollPastEnd?: boolean;
|
|
62
49
|
toolbar?: boolean;
|
|
63
50
|
viewMode?: EditorViewMode;
|
|
51
|
+
editorStateStore?: EditorStateStore;
|
|
64
52
|
onViewModeChange?: (id: string, mode: EditorViewMode) => void;
|
|
65
53
|
onFileUpload?: (file: File) => Promise<FileInfo | undefined>;
|
|
66
|
-
} & Pick<UseTextEditorProps, 'initialValue' | '
|
|
54
|
+
} & Pick<UseTextEditorProps, 'initialValue' | 'extensions'> &
|
|
67
55
|
Partial<Pick<MarkdownPluginState, 'extensionProviders'>>;
|
|
68
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Base markdown editor component.
|
|
59
|
+
*
|
|
60
|
+
* This component provides all the features of the markdown editor that do no depend on ECHO.
|
|
61
|
+
* This allows it to be used as a common editor for markdown content on arbitrary backends (e.g. files).
|
|
62
|
+
*/
|
|
69
63
|
export const MarkdownEditor = ({
|
|
70
64
|
id,
|
|
71
65
|
role = 'article',
|
|
@@ -73,62 +67,39 @@ export const MarkdownEditor = ({
|
|
|
73
67
|
extensions,
|
|
74
68
|
extensionProviders,
|
|
75
69
|
scrollPastEnd,
|
|
76
|
-
scrollTo,
|
|
77
|
-
selection,
|
|
78
70
|
toolbar,
|
|
79
71
|
viewMode,
|
|
72
|
+
editorStateStore,
|
|
80
73
|
onFileUpload,
|
|
81
74
|
onViewModeChange,
|
|
82
75
|
}: MarkdownEditorProps) => {
|
|
83
76
|
const { t } = useTranslation(MARKDOWN_PLUGIN);
|
|
84
77
|
const { themeMode } = useThemeContext();
|
|
85
78
|
const dispatch = useIntentDispatcher();
|
|
86
|
-
const attentionPlugin = useResolvePlugin(parseAttentionPlugin);
|
|
87
|
-
const layoutPlugin = useResolvePlugin(parseLayoutPlugin);
|
|
88
|
-
const attended = Array.from(attentionPlugin?.provides.attention?.attended ?? []);
|
|
89
|
-
const isDirectlyAttended = attended.length === 1 && attended[0] === id;
|
|
90
79
|
const [formattingState, formattingObserver] = useFormattingState();
|
|
80
|
+
const attendableAttributes = useAttendableAttributes(id);
|
|
81
|
+
const { hasAttention } = useAttention(id);
|
|
82
|
+
|
|
83
|
+
// Restore last selection and scroll point.
|
|
84
|
+
const { scrollTo, selection } = useMemo<EditorSelectionState>(() => editorStateStore?.getState(id) ?? {}, [id]);
|
|
91
85
|
|
|
92
86
|
// Extensions from other plugins.
|
|
93
|
-
|
|
87
|
+
// TODO(burdon): Reconcile with DocumentEditor.useExtensions.
|
|
88
|
+
const providerExtensions = useMemo(
|
|
89
|
+
() => extensionProviders?.flatMap((provider) => provider({})).filter(nonNullable),
|
|
90
|
+
[extensionProviders],
|
|
91
|
+
);
|
|
94
92
|
|
|
95
93
|
// TODO(Zan): Move these into thread plugin as well?
|
|
96
94
|
const [commentsState, commentObserver] = useCommentState();
|
|
97
95
|
const onCommentClick = useCallback(() => {
|
|
98
|
-
void dispatch({
|
|
96
|
+
void dispatch({
|
|
97
|
+
action: LayoutAction.SET_LAYOUT,
|
|
98
|
+
data: { element: 'complementary', state: true },
|
|
99
|
+
});
|
|
99
100
|
}, [dispatch]);
|
|
100
101
|
const commentClickObserver = useCommentClickListener(onCommentClick);
|
|
101
102
|
|
|
102
|
-
// Focus the space that references the comment.
|
|
103
|
-
useIntentResolver(MARKDOWN_PLUGIN, ({ action, data }) => {
|
|
104
|
-
switch (action) {
|
|
105
|
-
// TODO(burdon): Use fully qualified ids everywhere.
|
|
106
|
-
case LayoutAction.SCROLL_INTO_VIEW: {
|
|
107
|
-
if (editorView && data?.id === id && data?.cursor) {
|
|
108
|
-
// TODO(burdon): We need typed intents.
|
|
109
|
-
const range = Cursor.getRangeFromCursor(editorView.state, data.cursor);
|
|
110
|
-
if (range) {
|
|
111
|
-
const selection = editorView.state.selection.main.from !== range.from ? { anchor: range.from } : undefined;
|
|
112
|
-
const effects = [
|
|
113
|
-
// NOTE: This does not use the DOM scrollIntoView function.
|
|
114
|
-
EditorView.scrollIntoView(range.from, { y: 'start', yMargin: 96 }),
|
|
115
|
-
];
|
|
116
|
-
if (selection) {
|
|
117
|
-
// Update the editor selection to get bi-directional highlighting.
|
|
118
|
-
effects.push(setSelection.of({ current: id }));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
editorView.dispatch({
|
|
122
|
-
effects,
|
|
123
|
-
selection: selection ? { anchor: range.from } : undefined,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
103
|
// Drag files.
|
|
133
104
|
const handleDrop: DNDOptions['onDrop'] = async (view, { files }) => {
|
|
134
105
|
const file = files[0];
|
|
@@ -161,16 +132,16 @@ export const MarkdownEditor = ({
|
|
|
161
132
|
slots: { content: { className: editorContent } },
|
|
162
133
|
}),
|
|
163
134
|
editorGutter,
|
|
164
|
-
role !== 'section' && onFileUpload
|
|
135
|
+
role !== 'section' && onFileUpload && dropFile({ onDrop: handleDrop }),
|
|
165
136
|
providerExtensions,
|
|
166
137
|
extensions,
|
|
167
|
-
].filter(
|
|
138
|
+
].filter(isNotFalsy),
|
|
168
139
|
...(role !== 'section' && {
|
|
169
140
|
id,
|
|
170
141
|
scrollTo,
|
|
171
142
|
selection,
|
|
172
143
|
// TODO(wittjosiah): Autofocus based on layout is racy.
|
|
173
|
-
autoFocus: layoutPlugin?.provides.layout ? layoutPlugin?.provides.layout.scrollIntoView === id : true,
|
|
144
|
+
// autoFocus: layoutPlugin?.provides.layout ? layoutPlugin?.provides.layout.scrollIntoView === id : true,
|
|
174
145
|
moveToEndOfLine: true,
|
|
175
146
|
}),
|
|
176
147
|
}),
|
|
@@ -178,6 +149,7 @@ export const MarkdownEditor = ({
|
|
|
178
149
|
);
|
|
179
150
|
|
|
180
151
|
useTest(editorView);
|
|
152
|
+
useSelectCurrentThread(editorView, id);
|
|
181
153
|
|
|
182
154
|
// Toolbar handler.
|
|
183
155
|
const handleToolbarAction = useActionHandler(editorView);
|
|
@@ -201,29 +173,27 @@ export const MarkdownEditor = ({
|
|
|
201
173
|
return (
|
|
202
174
|
<div
|
|
203
175
|
role='none'
|
|
204
|
-
// TODO(burdon): Move role logic out of here (see sheet, table, sketch, etc.)
|
|
205
176
|
{...(role === 'section'
|
|
206
177
|
? { className: 'flex flex-col' }
|
|
207
178
|
: {
|
|
208
|
-
className: 'contents
|
|
209
|
-
|
|
179
|
+
className: 'contents',
|
|
180
|
+
// TODO(wittjosiah): Factor out to `useAttendableAttributes`?
|
|
181
|
+
...(hasAttention && { 'aria-current': 'location' }),
|
|
182
|
+
...attendableAttributes,
|
|
210
183
|
})}
|
|
211
184
|
>
|
|
212
185
|
{toolbar && (
|
|
213
|
-
<div role='none' className=
|
|
186
|
+
<div role='none' className='flex shrink-0 justify-center overflow-x-auto attention-surface'>
|
|
214
187
|
<Toolbar.Root
|
|
215
188
|
classNames={
|
|
216
189
|
role === 'section'
|
|
217
190
|
? [
|
|
218
191
|
textBlockWidth,
|
|
219
192
|
'z-[2] group-focus-within/section:visible',
|
|
220
|
-
!
|
|
193
|
+
!hasAttention && 'invisible',
|
|
221
194
|
sectionToolbarLayout,
|
|
222
195
|
]
|
|
223
|
-
: [
|
|
224
|
-
textBlockWidth,
|
|
225
|
-
'group-focus-within/editor:border-separator group-[[aria-current]]/editor:border-separator',
|
|
226
|
-
]
|
|
196
|
+
: [textBlockWidth]
|
|
227
197
|
}
|
|
228
198
|
state={formattingState && { ...formattingState, ...commentsState }}
|
|
229
199
|
onAction={handleAction}
|
|
@@ -247,8 +217,8 @@ export const MarkdownEditor = ({
|
|
|
247
217
|
: mx(
|
|
248
218
|
'flex is-full bs-full overflow-hidden',
|
|
249
219
|
focusRing,
|
|
250
|
-
|
|
251
|
-
'
|
|
220
|
+
'focus-visible:ring-inset attention-surface',
|
|
221
|
+
'p-0.5', // TODO(burdon): Handle padding for focusRing consistently.
|
|
252
222
|
'data-[toolbar=disabled]:pbs-2 data-[toolbar=disabled]:row-span-2',
|
|
253
223
|
)
|
|
254
224
|
}
|
|
@@ -268,5 +238,3 @@ const useTest = (view?: EditorView) => {
|
|
|
268
238
|
}
|
|
269
239
|
}, [view]);
|
|
270
240
|
};
|
|
271
|
-
|
|
272
|
-
export default MarkdownEditor;
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import { SettingsValue } from '@dxos/plugin-settings';
|
|
8
7
|
import { Input, Select, useTranslation } from '@dxos/react-ui';
|
|
8
|
+
import { DeprecatedFormInput } from '@dxos/react-ui-data';
|
|
9
9
|
import { type EditorInputMode, EditorInputModes, type EditorViewMode, EditorViewModes } from '@dxos/react-ui-editor';
|
|
10
10
|
|
|
11
11
|
import { MARKDOWN_PLUGIN } from '../meta';
|
|
@@ -17,7 +17,7 @@ export const MarkdownSettings = ({ settings }: { settings: MarkdownSettingsProps
|
|
|
17
17
|
// TODO(wittjosiah): Add skill test confirmation for entering vim mode.
|
|
18
18
|
return (
|
|
19
19
|
<>
|
|
20
|
-
<
|
|
20
|
+
<DeprecatedFormInput label={t('default view mode label')}>
|
|
21
21
|
<Select.Root
|
|
22
22
|
value={settings.defaultViewMode}
|
|
23
23
|
onValueChange={(value) => {
|
|
@@ -37,9 +37,9 @@ export const MarkdownSettings = ({ settings }: { settings: MarkdownSettingsProps
|
|
|
37
37
|
</Select.Content>
|
|
38
38
|
</Select.Portal>
|
|
39
39
|
</Select.Root>
|
|
40
|
-
</
|
|
40
|
+
</DeprecatedFormInput>
|
|
41
41
|
|
|
42
|
-
<
|
|
42
|
+
<DeprecatedFormInput label={t('editor input mode label')}>
|
|
43
43
|
<Select.Root
|
|
44
44
|
value={settings.editorInputMode ?? 'default'}
|
|
45
45
|
onValueChange={(value) => {
|
|
@@ -59,31 +59,31 @@ export const MarkdownSettings = ({ settings }: { settings: MarkdownSettingsProps
|
|
|
59
59
|
</Select.Content>
|
|
60
60
|
</Select.Portal>
|
|
61
61
|
</Select.Root>
|
|
62
|
-
</
|
|
62
|
+
</DeprecatedFormInput>
|
|
63
63
|
|
|
64
|
-
<
|
|
64
|
+
<DeprecatedFormInput label={t('settings toolbar label')}>
|
|
65
65
|
<Input.Switch checked={settings.toolbar} onCheckedChange={(checked) => (settings.toolbar = !!checked)} />
|
|
66
|
-
</
|
|
66
|
+
</DeprecatedFormInput>
|
|
67
67
|
|
|
68
|
-
<
|
|
68
|
+
<DeprecatedFormInput label={t('settings numbered headings label')}>
|
|
69
69
|
<Input.Switch
|
|
70
70
|
checked={settings.numberedHeadings}
|
|
71
71
|
onCheckedChange={(checked) => (settings.numberedHeadings = !!checked)}
|
|
72
72
|
/>
|
|
73
|
-
</
|
|
73
|
+
</DeprecatedFormInput>
|
|
74
74
|
|
|
75
|
-
<
|
|
75
|
+
<DeprecatedFormInput label={t('settings folding label')}>
|
|
76
76
|
<Input.Switch checked={settings.folding} onCheckedChange={(checked) => (settings.folding = !!checked)} />
|
|
77
|
-
</
|
|
77
|
+
</DeprecatedFormInput>
|
|
78
78
|
|
|
79
|
-
<
|
|
79
|
+
<DeprecatedFormInput label={t('settings experimental label')}>
|
|
80
80
|
<Input.Switch
|
|
81
81
|
checked={settings.experimental}
|
|
82
82
|
onCheckedChange={(checked) => (settings.experimental = !!checked)}
|
|
83
83
|
/>
|
|
84
|
-
</
|
|
84
|
+
</DeprecatedFormInput>
|
|
85
85
|
|
|
86
|
-
<
|
|
86
|
+
<DeprecatedFormInput
|
|
87
87
|
label={t('settings debug label')}
|
|
88
88
|
secondary={
|
|
89
89
|
settings.debug ? (
|
|
@@ -99,7 +99,7 @@ export const MarkdownSettings = ({ settings }: { settings: MarkdownSettingsProps
|
|
|
99
99
|
}
|
|
100
100
|
>
|
|
101
101
|
<Input.Switch checked={settings.debug} onCheckedChange={(checked) => (settings.debug = !!checked)} />
|
|
102
|
-
</
|
|
102
|
+
</DeprecatedFormInput>
|
|
103
103
|
</>
|
|
104
104
|
);
|
|
105
105
|
};
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
+
import { type Meta } from '@storybook/react';
|
|
7
8
|
import React, { type FC, useState } from 'react';
|
|
8
9
|
|
|
9
10
|
import { create } from '@dxos/echo-schema';
|
|
10
11
|
import { PublicKey } from '@dxos/keys';
|
|
11
12
|
import { faker } from '@dxos/random';
|
|
12
|
-
import { createDocAccessor,
|
|
13
|
+
import { createDocAccessor, createObject } from '@dxos/react-client/echo';
|
|
13
14
|
import { useThemeContext } from '@dxos/react-ui';
|
|
14
15
|
import {
|
|
15
16
|
type Action,
|
|
@@ -37,9 +38,9 @@ import { TextType } from '../types';
|
|
|
37
38
|
|
|
38
39
|
faker.seed(101);
|
|
39
40
|
|
|
40
|
-
const
|
|
41
|
+
const DefaultStory: FC<{ content?: string }> = ({ content = '' }) => {
|
|
41
42
|
const { themeMode } = useThemeContext();
|
|
42
|
-
const [text] = useState(
|
|
43
|
+
const [text] = useState(createObject(create(TextType, { content })));
|
|
43
44
|
const [formattingState, formattingObserver] = useFormattingState();
|
|
44
45
|
const [viewMode, setViewMode] = useState<EditorViewMode>('preview');
|
|
45
46
|
const { parentRef, view } = useTextEditor(() => {
|
|
@@ -91,14 +92,6 @@ const Story: FC<{ content: string }> = ({ content }) => {
|
|
|
91
92
|
);
|
|
92
93
|
};
|
|
93
94
|
|
|
94
|
-
export default {
|
|
95
|
-
title: 'react-ui-editor/Toolbar',
|
|
96
|
-
component: Toolbar,
|
|
97
|
-
decorators: [withTheme, withLayout({ tooltips: true })],
|
|
98
|
-
parameters: { translations, layout: 'fullscreen' },
|
|
99
|
-
render: (args: any) => <Story {...args} />,
|
|
100
|
-
} as any;
|
|
101
|
-
|
|
102
95
|
const content = [
|
|
103
96
|
'# Demo',
|
|
104
97
|
'',
|
|
@@ -114,3 +107,13 @@ export const Default = {
|
|
|
114
107
|
content,
|
|
115
108
|
},
|
|
116
109
|
};
|
|
110
|
+
|
|
111
|
+
const meta: Meta<typeof Toolbar.Root> = {
|
|
112
|
+
title: 'plugins/plugin-markdown/Toolbar',
|
|
113
|
+
component: Toolbar.Root,
|
|
114
|
+
render: DefaultStory as any,
|
|
115
|
+
decorators: [withTheme, withLayout({ tooltips: true })],
|
|
116
|
+
parameters: { translations, layout: 'fullscreen' },
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export default meta;
|
package/src/components/index.ts
CHANGED
|
@@ -2,20 +2,8 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { lazy } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type DocumentEditor as DocumentEditorType } from './DocumentEditor';
|
|
8
|
-
|
|
9
|
-
export { type DocumentCardProps, type DocumentItemProps } from './DocumentCard';
|
|
10
|
-
|
|
11
|
-
export * from './DocumentCard';
|
|
12
|
-
export * from './DocumentEditor';
|
|
13
|
-
export * from './MarkdownEditor';
|
|
14
|
-
export * from './HeadingMenu';
|
|
15
|
-
export * from './Layout';
|
|
16
7
|
export * from './MarkdownSettings';
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
export const DocumentCard = React.lazy(() => import('./DocumentCard'));
|
|
20
|
-
export const DocumentEditor: LazyExoticComponent<DocumentEditorType> = React.lazy(() => import('./DocumentEditor'));
|
|
21
|
-
export const MarkdownEditor = React.lazy(() => import('./MarkdownEditor'));
|
|
9
|
+
export const MarkdownContainer = lazy(() => import('./MarkdownContainer'));
|
package/src/extensions.tsx
CHANGED
|
@@ -2,63 +2,133 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import React, { type AnchorHTMLAttributes, StrictMode } from 'react';
|
|
5
|
+
import React, { type AnchorHTMLAttributes, type ReactNode, useMemo } from 'react';
|
|
7
6
|
import { createRoot } from 'react-dom/client';
|
|
8
7
|
|
|
9
|
-
import { type IntentDispatcher, NavigationAction } from '@dxos/app-framework';
|
|
8
|
+
import { type IntentDispatcher, NavigationAction, useIntentDispatcher } from '@dxos/app-framework';
|
|
10
9
|
import { invariant } from '@dxos/invariant';
|
|
11
|
-
import { fullyQualifiedId, type Query } from '@dxos/react-client/echo';
|
|
10
|
+
import { createDocAccessor, fullyQualifiedId, getSpace, type Query } from '@dxos/react-client/echo';
|
|
11
|
+
import { useIdentity } from '@dxos/react-client/halo';
|
|
12
|
+
import { Icon, ThemeProvider } from '@dxos/react-ui';
|
|
12
13
|
import {
|
|
13
14
|
type AutocompleteResult,
|
|
14
|
-
type
|
|
15
|
+
type EditorStateStore,
|
|
15
16
|
type EditorViewMode,
|
|
17
|
+
type Extension,
|
|
18
|
+
InputModeExtensions,
|
|
19
|
+
createDataExtensions,
|
|
16
20
|
autocomplete,
|
|
17
21
|
decorateMarkdown,
|
|
22
|
+
folding,
|
|
23
|
+
formattingKeymap,
|
|
18
24
|
linkTooltip,
|
|
25
|
+
listener,
|
|
26
|
+
selectionState,
|
|
19
27
|
typewriter,
|
|
20
|
-
formattingKeymap,
|
|
21
|
-
InputModeExtensions,
|
|
22
|
-
folding,
|
|
23
28
|
} from '@dxos/react-ui-editor';
|
|
24
|
-
import {
|
|
25
|
-
import { isNotFalsy
|
|
29
|
+
import { defaultTx } from '@dxos/react-ui-theme';
|
|
30
|
+
import { isNotFalsy } from '@dxos/util';
|
|
26
31
|
|
|
27
|
-
import { type DocumentType, type MarkdownSettingsProps } from './types';
|
|
32
|
+
import { type DocumentType, type MarkdownPluginState, type MarkdownSettingsProps } from './types';
|
|
33
|
+
import { setFallbackName } from './util';
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
viewMode?: EditorViewMode;
|
|
31
|
-
settings?: MarkdownSettingsProps;
|
|
35
|
+
type ExtensionsOptions = {
|
|
32
36
|
document?: DocumentType;
|
|
33
|
-
debug?: boolean;
|
|
34
|
-
experimental?: boolean;
|
|
35
|
-
numberedHeadings?: boolean;
|
|
36
|
-
folding?: boolean;
|
|
37
|
-
query?: Query<DocumentType>;
|
|
38
37
|
dispatch?: IntentDispatcher;
|
|
38
|
+
query?: Query<DocumentType>;
|
|
39
|
+
settings: MarkdownSettingsProps;
|
|
40
|
+
viewMode?: EditorViewMode;
|
|
41
|
+
editorStateStore?: EditorStateStore;
|
|
39
42
|
};
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
*/
|
|
44
|
-
export const createBaseExtensions = ({
|
|
45
|
-
viewMode,
|
|
46
|
-
settings,
|
|
44
|
+
// TODO(burdon): Merge with createBaseExtensions below.
|
|
45
|
+
export const useExtensions = ({
|
|
47
46
|
document,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
settings,
|
|
48
|
+
viewMode,
|
|
49
|
+
editorStateStore,
|
|
50
|
+
extensionProviders,
|
|
51
|
+
}: ExtensionsOptions & Pick<MarkdownPluginState, 'extensionProviders'>): Extension[] => {
|
|
52
|
+
const dispatch = useIntentDispatcher();
|
|
53
|
+
const identity = useIdentity();
|
|
54
|
+
const space = getSpace(document);
|
|
55
|
+
|
|
56
|
+
// TODO(wittjosiah): Autocomplete is not working and this query is causing performance issues.
|
|
57
|
+
// TODO(burdon): Unsubscribe.
|
|
58
|
+
// const query = space?.db.query(Filter.schema(DocumentType));
|
|
59
|
+
// query?.subscribe();
|
|
60
|
+
const baseExtensions = useMemo(
|
|
61
|
+
() =>
|
|
62
|
+
createBaseExtensions({
|
|
63
|
+
document,
|
|
64
|
+
settings,
|
|
65
|
+
viewMode,
|
|
66
|
+
dispatch,
|
|
67
|
+
// query,
|
|
68
|
+
}),
|
|
69
|
+
[
|
|
70
|
+
document,
|
|
71
|
+
viewMode,
|
|
72
|
+
dispatch,
|
|
73
|
+
settings,
|
|
74
|
+
settings.editorInputMode,
|
|
75
|
+
settings.folding,
|
|
76
|
+
settings.numberedHeadings,
|
|
77
|
+
settings.debug,
|
|
78
|
+
settings.typewriter,
|
|
79
|
+
],
|
|
80
|
+
);
|
|
52
81
|
|
|
53
82
|
//
|
|
54
|
-
//
|
|
83
|
+
// External extensions from other plugins.
|
|
55
84
|
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
85
|
+
const pluginExtensions = useMemo<Extension[] | undefined>(
|
|
86
|
+
() =>
|
|
87
|
+
extensionProviders?.reduce((acc: Extension[], provider) => {
|
|
88
|
+
const extension = typeof provider === 'function' ? provider({ document }) : provider;
|
|
89
|
+
if (extension) {
|
|
90
|
+
acc.push(extension);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return acc;
|
|
94
|
+
}, []),
|
|
95
|
+
[extensionProviders],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
//
|
|
99
|
+
// Basic plugins.
|
|
100
|
+
//
|
|
101
|
+
return useMemo<Extension[]>(
|
|
102
|
+
() =>
|
|
103
|
+
[
|
|
104
|
+
// NOTE: Data extensions must be first so that automerge is updated before other extensions compute their state.
|
|
105
|
+
document &&
|
|
106
|
+
createDataExtensions({
|
|
107
|
+
id: document.id,
|
|
108
|
+
text: document.content && createDocAccessor(document.content, ['content']),
|
|
109
|
+
space,
|
|
110
|
+
identity,
|
|
111
|
+
}),
|
|
112
|
+
selectionState(editorStateStore),
|
|
113
|
+
document &&
|
|
114
|
+
listener({
|
|
115
|
+
onChange: (text) => setFallbackName(document, text),
|
|
116
|
+
}),
|
|
117
|
+
baseExtensions,
|
|
118
|
+
pluginExtensions,
|
|
119
|
+
].filter(isNotFalsy),
|
|
120
|
+
[baseExtensions, pluginExtensions, document, document?.content, space, identity],
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Create extension instances for editor.
|
|
126
|
+
*/
|
|
127
|
+
const createBaseExtensions = ({ document, dispatch, settings, query, viewMode }: ExtensionsOptions): Extension[] => {
|
|
128
|
+
const extensions: Extension[] = [
|
|
129
|
+
settings.editorInputMode && InputModeExtensions[settings.editorInputMode],
|
|
130
|
+
settings.folding && folding(),
|
|
131
|
+
].filter(isNotFalsy);
|
|
62
132
|
|
|
63
133
|
//
|
|
64
134
|
// Markdown
|
|
@@ -69,7 +139,7 @@ export const createBaseExtensions = ({
|
|
|
69
139
|
formattingKeymap(),
|
|
70
140
|
decorateMarkdown({
|
|
71
141
|
selectionChangeDelay: 100,
|
|
72
|
-
numberedHeadings: settings
|
|
142
|
+
numberedHeadings: settings.numberedHeadings ? { from: 2 } : undefined,
|
|
73
143
|
// TODO(wittjosiah): For internal links, consider ignoring the link text and rendering the label of the object being linked to.
|
|
74
144
|
renderLinkButton:
|
|
75
145
|
dispatch && document
|
|
@@ -98,7 +168,6 @@ export const createBaseExtensions = ({
|
|
|
98
168
|
extensions.push(
|
|
99
169
|
autocomplete({
|
|
100
170
|
onSearch: (text: string) => {
|
|
101
|
-
// TODO query
|
|
102
171
|
// TODO(burdon): Specify filter (e.g., stack).
|
|
103
172
|
return query.objects
|
|
104
173
|
.map<AutocompleteResult | undefined>((object) =>
|
|
@@ -110,29 +179,27 @@ export const createBaseExtensions = ({
|
|
|
110
179
|
}
|
|
111
180
|
: undefined,
|
|
112
181
|
)
|
|
113
|
-
.filter(
|
|
182
|
+
.filter(isNotFalsy);
|
|
114
183
|
},
|
|
115
184
|
}),
|
|
116
185
|
);
|
|
117
186
|
}
|
|
118
187
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
if (settings?.debug) {
|
|
127
|
-
const items = settings.typewriter ?? '';
|
|
128
|
-
extensions.push(...[items ? typewriter({ items: items.split(/[,\n]/) }) : undefined].filter(nonNullable));
|
|
188
|
+
if (settings.debug) {
|
|
189
|
+
const items = settings.typewriter?.split(/[,\n]/) ?? '';
|
|
190
|
+
if (items) {
|
|
191
|
+
extensions.push(typewriter({ items }));
|
|
192
|
+
}
|
|
129
193
|
}
|
|
130
194
|
|
|
131
195
|
return extensions;
|
|
132
196
|
};
|
|
133
197
|
|
|
134
|
-
// TODO(burdon): Factor out
|
|
135
|
-
const
|
|
198
|
+
// TODO(burdon): Factor out styles.
|
|
199
|
+
const style = {
|
|
200
|
+
hover: 'rounded-sm text-primary-500 hover:text-primary-600 dark:text-primary-500 hover:dark:text-primary-400',
|
|
201
|
+
icon: 'inline-block leading-none mis-1 cursor-pointer',
|
|
202
|
+
};
|
|
136
203
|
|
|
137
204
|
const onRenderLink = (onSelectObject: (id: string) => void) => (el: Element, url: string) => {
|
|
138
205
|
// TODO(burdon): Formalize/document internal link format.
|
|
@@ -155,25 +222,31 @@ const onRenderLink = (onSelectObject: (id: string) => void) => (el: Element, url
|
|
|
155
222
|
target: '_blank',
|
|
156
223
|
};
|
|
157
224
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
225
|
+
renderRoot(
|
|
226
|
+
el,
|
|
227
|
+
<a {...options} className={style.hover}>
|
|
228
|
+
<Icon
|
|
229
|
+
icon={isInternal ? 'ph--arrow-square-down--bold' : 'ph--arrow-square-out--bold'}
|
|
230
|
+
size={4}
|
|
231
|
+
classNames={style.icon}
|
|
232
|
+
/>
|
|
233
|
+
</a>,
|
|
166
234
|
);
|
|
167
235
|
};
|
|
168
236
|
|
|
169
237
|
const renderLinkTooltip = (el: Element, url: string) => {
|
|
170
238
|
const web = new URL(url);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
</StrictMode>,
|
|
239
|
+
renderRoot(
|
|
240
|
+
el,
|
|
241
|
+
<a href={url} rel='noreferrer' target='_blank' className={style.hover}>
|
|
242
|
+
{web.origin}
|
|
243
|
+
<Icon icon='ph--arrow-square-out--bold' size={4} classNames={style.icon} />
|
|
244
|
+
</a>,
|
|
178
245
|
);
|
|
179
246
|
};
|
|
247
|
+
|
|
248
|
+
// TODO(burdon): Remove react rendering; use DOM directly.
|
|
249
|
+
export const renderRoot = <T extends Element>(root: T, node: ReactNode): T => {
|
|
250
|
+
createRoot(root).render(<ThemeProvider tx={defaultTx}>{node}</ThemeProvider>);
|
|
251
|
+
return root;
|
|
252
|
+
};
|