@dxos/plugin-markdown 0.7.4-staging.f7e8224 → 0.7.5-feature-compute.4d9d99a
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-6OPC5MKP.mjs → MarkdownContainer-ARRY4I6S.mjs} +175 -126
- package/dist/lib/browser/MarkdownContainer-ARRY4I6S.mjs.map +7 -0
- package/dist/lib/browser/app-graph-serializer-HHWSLYIW.mjs +51 -0
- package/dist/lib/browser/app-graph-serializer-HHWSLYIW.mjs.map +7 -0
- package/dist/lib/browser/{chunk-TZN5FGB2.mjs → chunk-ADAYSA5G.mjs} +24 -13
- package/dist/lib/browser/chunk-ADAYSA5G.mjs.map +7 -0
- package/dist/lib/browser/chunk-EMIIXXVX.mjs +66 -0
- package/dist/lib/browser/chunk-EMIIXXVX.mjs.map +7 -0
- package/dist/lib/browser/chunk-FSAYVXSE.mjs +16 -0
- package/dist/lib/browser/chunk-FSAYVXSE.mjs.map +7 -0
- package/dist/lib/browser/{chunk-4X6YX3KU.mjs → chunk-YB2TJFNH.mjs} +3 -3
- package/dist/lib/browser/chunk-YB2TJFNH.mjs.map +7 -0
- package/dist/lib/browser/chunk-YPDWX3WI.mjs +47 -0
- package/dist/lib/browser/chunk-YPDWX3WI.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +99 -452
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/intent-resolver-4KWMUMND.mjs +37 -0
- package/dist/lib/browser/intent-resolver-4KWMUMND.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/react-surface-RL4CISJZ.mjs +152 -0
- package/dist/lib/browser/react-surface-RL4CISJZ.mjs.map +7 -0
- package/dist/lib/browser/settings-PTF73JDL.mjs +28 -0
- package/dist/lib/browser/settings-PTF73JDL.mjs.map +7 -0
- package/dist/lib/browser/state-DOVZP7XJ.mjs +37 -0
- package/dist/lib/browser/state-DOVZP7XJ.mjs.map +7 -0
- package/dist/lib/browser/thread-G4RS7NBZ.mjs +36 -0
- package/dist/lib/browser/thread-G4RS7NBZ.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +4 -4
- package/dist/lib/node/{MarkdownContainer-6OKJOHTZ.cjs → MarkdownContainer-TV6W64EN.cjs} +167 -120
- package/dist/lib/node/MarkdownContainer-TV6W64EN.cjs.map +7 -0
- package/dist/lib/node/app-graph-serializer-PJRST43Q.cjs +62 -0
- package/dist/lib/node/app-graph-serializer-PJRST43Q.cjs.map +7 -0
- package/dist/lib/node/chunk-34WFGSZU.cjs +86 -0
- package/dist/lib/node/chunk-34WFGSZU.cjs.map +7 -0
- package/dist/lib/node/chunk-7WZANRNS.cjs +64 -0
- package/dist/lib/node/chunk-7WZANRNS.cjs.map +7 -0
- package/dist/lib/node/{meta.cjs → chunk-G7WKHUGU.cjs} +13 -8
- package/dist/lib/node/chunk-G7WKHUGU.cjs.map +7 -0
- package/dist/lib/node/{chunk-PHHIPRJC.cjs → chunk-HTWAAR54.cjs} +7 -7
- package/dist/lib/node/chunk-HTWAAR54.cjs.map +7 -0
- package/dist/lib/node/{chunk-KEPAM4JP.cjs → chunk-UFFFUC6W.cjs} +39 -14
- package/dist/lib/node/chunk-UFFFUC6W.cjs.map +7 -0
- package/dist/lib/node/index.cjs +90 -450
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/intent-resolver-QN25AFOP.cjs +50 -0
- package/dist/lib/node/intent-resolver-QN25AFOP.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/react-surface-RZ2XV56V.cjs +165 -0
- package/dist/lib/node/react-surface-RZ2XV56V.cjs.map +7 -0
- package/dist/lib/node/settings-BO5P5R4I.cjs +42 -0
- package/dist/lib/node/settings-BO5P5R4I.cjs.map +7 -0
- package/dist/lib/node/state-F26NQP7F.cjs +51 -0
- package/dist/lib/node/state-F26NQP7F.cjs.map +7 -0
- package/dist/lib/node/thread-THWQ67WS.cjs +52 -0
- package/dist/lib/node/thread-THWQ67WS.cjs.map +7 -0
- package/dist/lib/node/types/index.cjs +7 -7
- package/dist/lib/node/types/index.cjs.map +1 -1
- package/dist/lib/node-esm/{MarkdownContainer-GBNSGROQ.mjs → MarkdownContainer-3ZHQTTMQ.mjs} +175 -126
- package/dist/lib/node-esm/MarkdownContainer-3ZHQTTMQ.mjs.map +7 -0
- package/dist/lib/node-esm/app-graph-serializer-JTHJUUS2.mjs +52 -0
- package/dist/lib/node-esm/app-graph-serializer-JTHJUUS2.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-2GHK262V.mjs +17 -0
- package/dist/lib/node-esm/chunk-2GHK262V.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-BABK7FMW.mjs → chunk-AOKWCL3O.mjs} +3 -3
- package/dist/lib/node-esm/chunk-AOKWCL3O.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-CDFNMFGT.mjs +67 -0
- package/dist/lib/node-esm/chunk-CDFNMFGT.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HLP536EW.mjs +48 -0
- package/dist/lib/node-esm/chunk-HLP536EW.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-NUMUUCYF.mjs → chunk-Y5RSQVGH.mjs} +24 -13
- package/dist/lib/node-esm/chunk-Y5RSQVGH.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +99 -452
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/intent-resolver-F7MQOTG7.mjs +38 -0
- package/dist/lib/node-esm/intent-resolver-F7MQOTG7.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/react-surface-RRXHEW4R.mjs +153 -0
- package/dist/lib/node-esm/react-surface-RRXHEW4R.mjs.map +7 -0
- package/dist/lib/node-esm/settings-E7P5FQ3F.mjs +29 -0
- package/dist/lib/node-esm/settings-E7P5FQ3F.mjs.map +7 -0
- package/dist/lib/node-esm/state-HXSOQNOV.mjs +38 -0
- package/dist/lib/node-esm/state-HXSOQNOV.mjs.map +7 -0
- package/dist/lib/node-esm/thread-CYEXBXTW.mjs +37 -0
- package/dist/lib/node-esm/thread-CYEXBXTW.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +4 -4
- package/dist/types/src/MarkdownPlugin.d.ts +1 -3
- package/dist/types/src/MarkdownPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-serializer.d.ts +4 -0
- package/dist/types/src/capabilities/app-graph-serializer.d.ts.map +1 -0
- package/dist/types/src/capabilities/capabilities.d.ts +12 -0
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +16 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/intent-resolver.d.ts +4 -0
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +4 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/settings.d.ts +4 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/capabilities/state.d.ts +11 -0
- package/dist/types/src/capabilities/state.d.ts.map +1 -0
- package/dist/types/src/capabilities/thread.d.ts +6 -0
- package/dist/types/src/capabilities/thread.d.ts.map +1 -0
- package/dist/types/src/components/MarkdownContainer.d.ts +1 -1
- package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownEditor.d.ts +15 -0
- package/dist/types/src/components/MarkdownEditor.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar.stories.d.ts +2 -2
- package/dist/types/src/components/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/extensions.d.ts +4 -4
- package/dist/types/src/extensions.d.ts.map +1 -1
- package/dist/types/src/hooks/useSelectCurrentThread.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +2 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +1 -2
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +3 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/index.d.ts +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +75 -0
- package/dist/types/src/types/schema.d.ts.map +1 -0
- package/dist/types/src/types/types.d.ts +40 -35
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +1 -5
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +33 -37
- package/src/MarkdownPlugin.tsx +84 -283
- package/src/capabilities/app-graph-serializer.ts +50 -0
- package/src/capabilities/capabilities.ts +20 -0
- package/src/capabilities/index.ts +14 -0
- package/src/capabilities/intent-resolver.ts +27 -0
- package/src/capabilities/react-surface.tsx +73 -0
- package/src/capabilities/settings.ts +25 -0
- package/src/capabilities/state.ts +31 -0
- package/src/capabilities/thread.ts +34 -0
- package/src/components/MarkdownContainer.tsx +14 -10
- package/src/components/MarkdownEditor.stories.tsx +16 -5
- package/src/components/MarkdownEditor.tsx +87 -61
- package/src/components/Toolbar.stories.tsx +16 -22
- package/src/extensions.tsx +21 -19
- package/src/hooks/useSelectCurrentThread.tsx +17 -14
- package/src/index.ts +2 -5
- package/src/meta.ts +1 -1
- package/src/translations.ts +1 -0
- package/src/types/index.ts +1 -1
- package/src/types/{document.ts → schema.ts} +4 -7
- package/src/types/types.ts +36 -58
- package/src/util.tsx +5 -11
- package/dist/lib/browser/MarkdownContainer-6OPC5MKP.mjs.map +0 -7
- package/dist/lib/browser/chunk-4X6YX3KU.mjs.map +0 -7
- package/dist/lib/browser/chunk-CMSUKMPM.mjs +0 -41
- package/dist/lib/browser/chunk-CMSUKMPM.mjs.map +0 -7
- package/dist/lib/browser/chunk-TZN5FGB2.mjs.map +0 -7
- package/dist/lib/browser/meta.mjs +0 -9
- package/dist/lib/browser/meta.mjs.map +0 -7
- package/dist/lib/node/MarkdownContainer-6OKJOHTZ.cjs.map +0 -7
- package/dist/lib/node/chunk-KEPAM4JP.cjs.map +0 -7
- package/dist/lib/node/chunk-PHHIPRJC.cjs.map +0 -7
- package/dist/lib/node/chunk-YGMWZIIJ.cjs +0 -61
- package/dist/lib/node/chunk-YGMWZIIJ.cjs.map +0 -7
- package/dist/lib/node/meta.cjs.map +0 -7
- package/dist/lib/node-esm/MarkdownContainer-GBNSGROQ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-BABK7FMW.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ERJ52KN2.mjs +0 -42
- package/dist/lib/node-esm/chunk-ERJ52KN2.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-NUMUUCYF.mjs.map +0 -7
- package/dist/lib/node-esm/meta.mjs +0 -10
- package/dist/lib/node-esm/meta.mjs.map +0 -7
- package/dist/types/src/types/document.d.ts +0 -106
- package/dist/types/src/types/document.d.ts.map +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { createSurface, contributes, Capabilities, useCapability } from '@dxos/app-framework';
|
|
8
|
+
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
9
|
+
|
|
10
|
+
import { MarkdownCapabilities } from './capabilities';
|
|
11
|
+
import { MarkdownContainer, MarkdownSettings } from '../components';
|
|
12
|
+
import { MARKDOWN_PLUGIN } from '../meta';
|
|
13
|
+
import { DocumentType, isEditorModel, type MarkdownSettingsProps } from '../types';
|
|
14
|
+
|
|
15
|
+
export default () =>
|
|
16
|
+
contributes(Capabilities.ReactSurface, [
|
|
17
|
+
createSurface({
|
|
18
|
+
id: `${MARKDOWN_PLUGIN}/document`,
|
|
19
|
+
role: ['article', 'section'],
|
|
20
|
+
filter: (data): data is { subject: DocumentType } => data.subject instanceof DocumentType,
|
|
21
|
+
component: ({ data, role }) => {
|
|
22
|
+
const settingsStore = useCapability(Capabilities.SettingsStore);
|
|
23
|
+
const settings = settingsStore.getStore<MarkdownSettingsProps>(MARKDOWN_PLUGIN)!.value;
|
|
24
|
+
const { state, editorState, getViewMode, setViewMode } = useCapability(MarkdownCapabilities.State);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<MarkdownContainer
|
|
28
|
+
id={fullyQualifiedId(data.subject)}
|
|
29
|
+
object={data.subject}
|
|
30
|
+
role={role}
|
|
31
|
+
settings={settings}
|
|
32
|
+
extensionProviders={state.extensionProviders}
|
|
33
|
+
viewMode={getViewMode(fullyQualifiedId(data.subject))}
|
|
34
|
+
editorStateStore={editorState}
|
|
35
|
+
onViewModeChange={setViewMode}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
createSurface({
|
|
41
|
+
id: `${MARKDOWN_PLUGIN}/editor`,
|
|
42
|
+
role: ['article', 'section'],
|
|
43
|
+
filter: (data): data is { subject: { id: string; text: string } } => isEditorModel(data.subject),
|
|
44
|
+
component: ({ data, role }) => {
|
|
45
|
+
const settingsStore = useCapability(Capabilities.SettingsStore);
|
|
46
|
+
const settings = settingsStore.getStore<MarkdownSettingsProps>(MARKDOWN_PLUGIN)!.value;
|
|
47
|
+
const { state, editorState, getViewMode, setViewMode } = useCapability(MarkdownCapabilities.State);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<MarkdownContainer
|
|
51
|
+
id={data.subject.id}
|
|
52
|
+
object={data.subject}
|
|
53
|
+
role={role}
|
|
54
|
+
settings={settings}
|
|
55
|
+
extensionProviders={state.extensionProviders}
|
|
56
|
+
viewMode={getViewMode(data.subject.id)}
|
|
57
|
+
editorStateStore={editorState}
|
|
58
|
+
onViewModeChange={setViewMode}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
createSurface({
|
|
64
|
+
id: `${MARKDOWN_PLUGIN}/settings`,
|
|
65
|
+
role: 'settings',
|
|
66
|
+
filter: (data): data is any => data.subject === MARKDOWN_PLUGIN,
|
|
67
|
+
component: () => {
|
|
68
|
+
const settingsStore = useCapability(Capabilities.SettingsStore);
|
|
69
|
+
const settings = settingsStore.getStore<MarkdownSettingsProps>(MARKDOWN_PLUGIN)!.value;
|
|
70
|
+
return <MarkdownSettings settings={settings} />;
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
]);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Capabilities, contributes } from '@dxos/app-framework';
|
|
6
|
+
import { create } from '@dxos/live-object';
|
|
7
|
+
|
|
8
|
+
import { MARKDOWN_PLUGIN } from '../meta';
|
|
9
|
+
import { type MarkdownSettingsProps, MarkdownSettingsSchema } from '../types';
|
|
10
|
+
|
|
11
|
+
export default () => {
|
|
12
|
+
const settings = create<MarkdownSettingsProps>({
|
|
13
|
+
defaultViewMode: 'preview',
|
|
14
|
+
toolbar: true,
|
|
15
|
+
numberedHeadings: true,
|
|
16
|
+
folding: true,
|
|
17
|
+
experimental: false,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return contributes(Capabilities.Settings, {
|
|
21
|
+
schema: MarkdownSettingsSchema,
|
|
22
|
+
prefix: MARKDOWN_PLUGIN,
|
|
23
|
+
value: settings,
|
|
24
|
+
});
|
|
25
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Capabilities, contributes, type PluginsContext } from '@dxos/app-framework';
|
|
6
|
+
import { LocalStorageStore } from '@dxos/local-storage';
|
|
7
|
+
import { type EditorViewMode, createEditorStateStore } from '@dxos/react-ui-editor';
|
|
8
|
+
|
|
9
|
+
import { MarkdownCapabilities } from './capabilities';
|
|
10
|
+
import { MARKDOWN_PLUGIN } from '../meta';
|
|
11
|
+
import { type MarkdownPluginState, type MarkdownSettingsProps } from '../types';
|
|
12
|
+
|
|
13
|
+
export default (context: PluginsContext) => {
|
|
14
|
+
const state = new LocalStorageStore<MarkdownPluginState>(MARKDOWN_PLUGIN, { extensionProviders: [], viewMode: {} });
|
|
15
|
+
|
|
16
|
+
state.prop({ key: 'viewMode', type: LocalStorageStore.json<{ [key: string]: EditorViewMode }>() });
|
|
17
|
+
|
|
18
|
+
// TODO(wittjosiah): Fold into state.
|
|
19
|
+
const editorState = createEditorStateStore(`${MARKDOWN_PLUGIN}/editor`);
|
|
20
|
+
|
|
21
|
+
const getViewMode = (id: string) => {
|
|
22
|
+
const defaultViewMode = context
|
|
23
|
+
.requestCapability(Capabilities.SettingsStore)
|
|
24
|
+
.getStore<MarkdownSettingsProps>(MARKDOWN_PLUGIN)!.value.defaultViewMode;
|
|
25
|
+
return (id && state.values.viewMode[id]) || defaultViewMode;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const setViewMode = (id: string, viewMode: EditorViewMode) => (state.values.viewMode[id] = viewMode);
|
|
29
|
+
|
|
30
|
+
return contributes(MarkdownCapabilities.State, { state: state.values, editorState, getViewMode, setViewMode });
|
|
31
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { contributes } from '@dxos/app-framework';
|
|
6
|
+
import { ThreadCapabilities } from '@dxos/plugin-space';
|
|
7
|
+
import { createDocAccessor, getRangeFromCursor } from '@dxos/react-client/echo';
|
|
8
|
+
|
|
9
|
+
import { DocumentType } from '../types';
|
|
10
|
+
|
|
11
|
+
export default () =>
|
|
12
|
+
contributes(ThreadCapabilities.Thread, {
|
|
13
|
+
predicate: (obj) => obj instanceof DocumentType,
|
|
14
|
+
createSort: (doc: DocumentType) => {
|
|
15
|
+
const accessor = doc.content.target ? createDocAccessor(doc.content.target, ['content']) : undefined;
|
|
16
|
+
if (!accessor) {
|
|
17
|
+
return (_) => 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const getStartPosition = (cursor: string | undefined) => {
|
|
21
|
+
const range = cursor ? getRangeFromCursor(accessor, cursor) : undefined;
|
|
22
|
+
return range?.start ?? Number.MAX_SAFE_INTEGER;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return (anchorA: string | undefined, anchorB: string | undefined): number => {
|
|
26
|
+
if (anchorA === undefined || anchorB === undefined) {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
const posA = getStartPosition(anchorA);
|
|
30
|
+
const posB = getStartPosition(anchorB);
|
|
31
|
+
return posA - posB;
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useEffect, useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { Capabilities, useCapabilities } from '@dxos/app-framework';
|
|
8
8
|
import { fullyQualifiedId, getSpace } from '@dxos/react-client/echo';
|
|
9
9
|
|
|
10
10
|
import { MarkdownEditor, type MarkdownEditorProps } from './MarkdownEditor';
|
|
@@ -27,7 +27,6 @@ const MarkdownContainer = ({
|
|
|
27
27
|
id,
|
|
28
28
|
role,
|
|
29
29
|
object,
|
|
30
|
-
extensionProviders,
|
|
31
30
|
settings,
|
|
32
31
|
viewMode,
|
|
33
32
|
editorStateStore,
|
|
@@ -35,7 +34,7 @@ const MarkdownContainer = ({
|
|
|
35
34
|
}: MarkdownContainerProps) => {
|
|
36
35
|
const scrollPastEnd = role === 'article';
|
|
37
36
|
const doc = object instanceof DocumentType ? object : undefined;
|
|
38
|
-
const extensions = useExtensions({
|
|
37
|
+
const extensions = useExtensions({ document: doc, settings, viewMode, editorStateStore });
|
|
39
38
|
|
|
40
39
|
if (doc) {
|
|
41
40
|
return (
|
|
@@ -77,26 +76,31 @@ export const DocumentEditor = ({ id, document: doc, settings, viewMode, ...props
|
|
|
77
76
|
|
|
78
77
|
// Migrate gradually to `fallbackName`.
|
|
79
78
|
useEffect(() => {
|
|
80
|
-
if (
|
|
81
|
-
|
|
79
|
+
if (typeof doc.fallbackName === 'string') {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const fallbackName = doc.content?.target?.content ? getFallbackName(doc.content.target.content) : undefined;
|
|
84
|
+
if (fallbackName) {
|
|
85
|
+
doc.fallbackName = fallbackName;
|
|
82
86
|
}
|
|
83
87
|
}, [doc, doc.content]);
|
|
84
88
|
|
|
85
89
|
// File dragging.
|
|
86
|
-
const
|
|
90
|
+
const [upload] = useCapabilities(Capabilities.FileUploader);
|
|
87
91
|
const handleFileUpload = useMemo(() => {
|
|
88
|
-
if (space === undefined ||
|
|
92
|
+
if (space === undefined || upload === undefined) {
|
|
89
93
|
return undefined;
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
// TODO(burdon): Re-order props: space, file.
|
|
93
|
-
return async (file: File) =>
|
|
94
|
-
}, [space,
|
|
97
|
+
return async (file: File) => upload!(file, space);
|
|
98
|
+
}, [space, upload]);
|
|
95
99
|
|
|
96
100
|
return (
|
|
97
101
|
<MarkdownEditor
|
|
98
102
|
id={id}
|
|
99
|
-
initialValue={doc.content?.content}
|
|
103
|
+
initialValue={doc.content?.target?.content}
|
|
100
104
|
viewMode={viewMode}
|
|
101
105
|
toolbar={settings.toolbar}
|
|
102
106
|
inputMode={settings.editorInputMode}
|
|
@@ -7,13 +7,18 @@ import '@dxos-theme';
|
|
|
7
7
|
import { type Meta } from '@storybook/react';
|
|
8
8
|
import React, { useMemo } from 'react';
|
|
9
9
|
|
|
10
|
+
import { IntentPlugin } from '@dxos/app-framework';
|
|
11
|
+
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
10
12
|
import { createDocAccessor, createObject } from '@dxos/react-client/echo';
|
|
11
13
|
import { Main } from '@dxos/react-ui';
|
|
12
|
-
import {
|
|
14
|
+
import { AttendableContainer } from '@dxos/react-ui-attention';
|
|
15
|
+
import { withAttention } from '@dxos/react-ui-attention/testing';
|
|
16
|
+
import { editorWithToolbarLayout, automerge, translations as editorTranslations } from '@dxos/react-ui-editor';
|
|
13
17
|
import { topbarBlockPaddingStart } from '@dxos/react-ui-theme';
|
|
14
18
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
15
19
|
|
|
16
20
|
import { MarkdownEditor, type MarkdownEditorProps } from './MarkdownEditor';
|
|
21
|
+
import translations from '../translations';
|
|
17
22
|
|
|
18
23
|
const content = Array.from({ length: 100 })
|
|
19
24
|
.map((_, i) => `Line ${i + 1}`)
|
|
@@ -27,14 +32,15 @@ type StoryProps = MarkdownEditorProps & {
|
|
|
27
32
|
const DefaultStory = ({ content = '# Test', toolbar }: StoryProps) => {
|
|
28
33
|
const doc = useMemo(() => createObject({ content }), [content]);
|
|
29
34
|
const extensions = useMemo(() => [automerge(createDocAccessor(doc, ['content']))], [doc]);
|
|
30
|
-
|
|
31
35
|
return (
|
|
32
36
|
<Main.Content
|
|
33
37
|
bounce
|
|
34
38
|
data-toolbar={toolbar ? 'enabled' : 'disabled'}
|
|
35
39
|
classNames={[topbarBlockPaddingStart, editorWithToolbarLayout]}
|
|
36
40
|
>
|
|
37
|
-
<
|
|
41
|
+
<AttendableContainer id='test'>
|
|
42
|
+
<MarkdownEditor id='test' initialValue={doc.content} extensions={extensions} toolbar={toolbar} />
|
|
43
|
+
</AttendableContainer>
|
|
38
44
|
</Main.Content>
|
|
39
45
|
);
|
|
40
46
|
};
|
|
@@ -56,8 +62,13 @@ const meta: Meta<typeof MarkdownEditor> = {
|
|
|
56
62
|
title: 'plugins/plugin-markdown/EditorMain',
|
|
57
63
|
component: MarkdownEditor,
|
|
58
64
|
render: DefaultStory,
|
|
59
|
-
decorators: [
|
|
60
|
-
|
|
65
|
+
decorators: [
|
|
66
|
+
withTheme,
|
|
67
|
+
withLayout({ tooltips: true }),
|
|
68
|
+
withAttention,
|
|
69
|
+
withPluginManager({ plugins: [IntentPlugin()] }),
|
|
70
|
+
],
|
|
71
|
+
parameters: { layout: 'fullscreen', translations: [...translations, ...editorTranslations] },
|
|
61
72
|
};
|
|
62
73
|
|
|
63
74
|
export default meta;
|
|
@@ -5,18 +5,18 @@
|
|
|
5
5
|
import { openSearchPanel } from '@codemirror/search';
|
|
6
6
|
import { type EditorView } from '@codemirror/view';
|
|
7
7
|
import React, { useMemo, useEffect, useCallback } from 'react';
|
|
8
|
+
import { useDropzone } from 'react-dropzone';
|
|
8
9
|
|
|
9
|
-
import { type FileInfo, LayoutAction, NavigationAction, useIntentDispatcher } from '@dxos/app-framework';
|
|
10
|
+
import { createIntent, type FileInfo, LayoutAction, NavigationAction, useIntentDispatcher } from '@dxos/app-framework';
|
|
10
11
|
import { useThemeContext, useTranslation } from '@dxos/react-ui';
|
|
11
|
-
import { useAttention } from '@dxos/react-ui-attention';
|
|
12
12
|
import {
|
|
13
|
-
type
|
|
13
|
+
type EditorAction,
|
|
14
14
|
type DNDOptions,
|
|
15
15
|
type EditorViewMode,
|
|
16
16
|
type EditorInputMode,
|
|
17
17
|
type EditorSelectionState,
|
|
18
18
|
type EditorStateStore,
|
|
19
|
-
|
|
19
|
+
EditorToolbar,
|
|
20
20
|
type UseTextEditorProps,
|
|
21
21
|
createBasicExtensions,
|
|
22
22
|
createMarkdownExtensions,
|
|
@@ -24,23 +24,23 @@ import {
|
|
|
24
24
|
dropFile,
|
|
25
25
|
editorContent,
|
|
26
26
|
editorGutter,
|
|
27
|
-
|
|
27
|
+
processEditorPayload,
|
|
28
28
|
useActionHandler,
|
|
29
29
|
useCommentState,
|
|
30
30
|
useCommentClickListener,
|
|
31
31
|
useFormattingState,
|
|
32
32
|
useTextEditor,
|
|
33
|
+
stackItemContentEditorClassNames,
|
|
34
|
+
useEditorToolbarState,
|
|
35
|
+
createEditorAction,
|
|
33
36
|
} from '@dxos/react-ui-editor';
|
|
34
37
|
import { StackItem } from '@dxos/react-ui-stack';
|
|
35
|
-
import { mx, textBlockWidth } from '@dxos/react-ui-theme';
|
|
36
38
|
import { isNotFalsy, nonNullable } from '@dxos/util';
|
|
37
39
|
|
|
38
40
|
import { useSelectCurrentThread } from '../hooks';
|
|
39
41
|
import { MARKDOWN_PLUGIN } from '../meta';
|
|
40
42
|
import { type MarkdownPluginState } from '../types';
|
|
41
43
|
|
|
42
|
-
const DEFAULT_VIEW_MODE: EditorViewMode = 'preview';
|
|
43
|
-
|
|
44
44
|
export type MarkdownEditorProps = {
|
|
45
45
|
id: string;
|
|
46
46
|
role?: string;
|
|
@@ -75,9 +75,9 @@ export const MarkdownEditor = ({
|
|
|
75
75
|
}: MarkdownEditorProps) => {
|
|
76
76
|
const { t } = useTranslation(MARKDOWN_PLUGIN);
|
|
77
77
|
const { themeMode } = useThemeContext();
|
|
78
|
-
const dispatch = useIntentDispatcher();
|
|
79
|
-
const
|
|
80
|
-
const
|
|
78
|
+
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
79
|
+
const toolbarState = useEditorToolbarState({ viewMode });
|
|
80
|
+
const formattingObserver = useFormattingState(toolbarState);
|
|
81
81
|
|
|
82
82
|
// Restore last selection and scroll point.
|
|
83
83
|
const { scrollTo, selection } = useMemo<EditorSelectionState>(() => editorStateStore?.getState(id) ?? {}, [id]);
|
|
@@ -89,28 +89,21 @@ export const MarkdownEditor = ({
|
|
|
89
89
|
[extensionProviders],
|
|
90
90
|
);
|
|
91
91
|
|
|
92
|
-
// TODO(Zan):
|
|
93
|
-
const
|
|
94
|
-
const onCommentClick = useCallback(() => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
action: NavigationAction.OPEN,
|
|
98
|
-
data: { activeParts: { complementary: 'comments' } },
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
action: LayoutAction.SET_LAYOUT,
|
|
102
|
-
data: { element: 'complementary', state: true },
|
|
103
|
-
},
|
|
104
|
-
]);
|
|
92
|
+
// TODO(Zan): Factor out to thread plugin.
|
|
93
|
+
const commentObserver = useCommentState(toolbarState);
|
|
94
|
+
const onCommentClick = useCallback(async () => {
|
|
95
|
+
await dispatch(createIntent(NavigationAction.Open, { activeParts: { complementary: 'comments' } }));
|
|
96
|
+
await dispatch(createIntent(LayoutAction.SetLayout, { element: 'complementary', state: true }));
|
|
105
97
|
}, [dispatch]);
|
|
106
98
|
const commentClickObserver = useCommentClickListener(onCommentClick);
|
|
107
99
|
|
|
100
|
+
// TODO(wittjosiah): Factor out to file uploader plugin.
|
|
108
101
|
// Drag files.
|
|
109
102
|
const handleDrop: DNDOptions['onDrop'] = async (view, { files }) => {
|
|
110
103
|
const file = files[0];
|
|
111
104
|
const info = file && onFileUpload ? await onFileUpload(file) : undefined;
|
|
112
105
|
if (info) {
|
|
113
|
-
|
|
106
|
+
processEditorPayload(view, { type: 'image', data: info.url });
|
|
114
107
|
}
|
|
115
108
|
};
|
|
116
109
|
|
|
@@ -156,57 +149,79 @@ export const MarkdownEditor = ({
|
|
|
156
149
|
useTest(editorView);
|
|
157
150
|
useSelectCurrentThread(editorView, id);
|
|
158
151
|
|
|
152
|
+
// https://react-dropzone.js.org/#src
|
|
153
|
+
const { acceptedFiles, getInputProps, open } = useDropzone({
|
|
154
|
+
multiple: false,
|
|
155
|
+
noDrag: true,
|
|
156
|
+
accept: {
|
|
157
|
+
'image/*': ['.jpg', '.jpeg', '.png', '.gif'],
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
if (editorView && onFileUpload && acceptedFiles.length) {
|
|
163
|
+
requestAnimationFrame(async () => {
|
|
164
|
+
// NOTE: Clone file since react-dropzone patches in a non-standard `path` property, which confuses IPFS.
|
|
165
|
+
const f = acceptedFiles[0];
|
|
166
|
+
const file = new File([f], f.name, {
|
|
167
|
+
type: f.type,
|
|
168
|
+
lastModified: f.lastModified,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const info = await onFileUpload(file);
|
|
172
|
+
if (info) {
|
|
173
|
+
processEditorPayload(editorView, { type: 'image', data: info.url });
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}, [acceptedFiles, editorView]);
|
|
178
|
+
|
|
159
179
|
// Toolbar handler.
|
|
160
180
|
const handleToolbarAction = useActionHandler(editorView);
|
|
161
|
-
const handleAction = (
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
181
|
+
const handleAction = useCallback(
|
|
182
|
+
(action: EditorAction) => {
|
|
183
|
+
switch (action.properties.type) {
|
|
184
|
+
case 'search': {
|
|
185
|
+
if (editorView) {
|
|
186
|
+
openSearchPanel(editorView);
|
|
187
|
+
}
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
case 'view-mode': {
|
|
191
|
+
onViewModeChange?.(id, action.properties.data);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
case 'image': {
|
|
195
|
+
open();
|
|
196
|
+
return;
|
|
166
197
|
}
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
case 'view-mode': {
|
|
170
|
-
onViewModeChange?.(id, action.data);
|
|
171
|
-
return;
|
|
172
198
|
}
|
|
173
|
-
}
|
|
174
199
|
|
|
175
|
-
|
|
176
|
-
|
|
200
|
+
handleToolbarAction?.(action);
|
|
201
|
+
},
|
|
202
|
+
[editorView, onViewModeChange, open],
|
|
203
|
+
);
|
|
177
204
|
|
|
178
205
|
return (
|
|
179
|
-
<StackItem.Content toolbar={toolbar}>
|
|
206
|
+
<StackItem.Content toolbar={!!toolbar}>
|
|
180
207
|
{toolbar && (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
>
|
|
188
|
-
<Toolbar.Root
|
|
189
|
-
classNames={[textBlockWidth, !hasAttention && 'opacity-20']}
|
|
190
|
-
state={formattingState && { ...formattingState, ...commentsState }}
|
|
208
|
+
<>
|
|
209
|
+
<EditorToolbar
|
|
210
|
+
attendableId={id}
|
|
211
|
+
role={role}
|
|
212
|
+
state={toolbarState}
|
|
213
|
+
customActions={onFileUpload ? createUploadAction : undefined}
|
|
191
214
|
onAction={handleAction}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
<Toolbar.Separator />
|
|
196
|
-
<Toolbar.View mode={viewMode ?? DEFAULT_VIEW_MODE} />
|
|
197
|
-
<Toolbar.Actions />
|
|
198
|
-
</Toolbar.Root>
|
|
199
|
-
</div>
|
|
215
|
+
/>
|
|
216
|
+
<input {...getInputProps()} />
|
|
217
|
+
</>
|
|
200
218
|
)}
|
|
201
219
|
<div
|
|
202
220
|
role='none'
|
|
203
221
|
ref={parentRef}
|
|
204
222
|
data-testid='composer.markdownRoot'
|
|
205
223
|
data-toolbar={toolbar ? 'enabled' : 'disabled'}
|
|
206
|
-
className={
|
|
207
|
-
'ch-focus-ring-inset data-[toolbar=disabled]:pbs-2 attention-surface',
|
|
208
|
-
role === 'article' ? 'min-bs-0' : '[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-bs-24',
|
|
209
|
-
)}
|
|
224
|
+
className={stackItemContentEditorClassNames(role)}
|
|
210
225
|
{...focusAttributes}
|
|
211
226
|
/>
|
|
212
227
|
</StackItem.Content>
|
|
@@ -223,3 +238,14 @@ const useTest = (view?: EditorView) => {
|
|
|
223
238
|
}
|
|
224
239
|
}, [view]);
|
|
225
240
|
};
|
|
241
|
+
|
|
242
|
+
export const createUploadAction = () => ({
|
|
243
|
+
nodes: [
|
|
244
|
+
createEditorAction(
|
|
245
|
+
{ type: 'image', testId: 'editor.toolbar.image' },
|
|
246
|
+
'ph--image-square--regular',
|
|
247
|
+
'upload image label',
|
|
248
|
+
),
|
|
249
|
+
],
|
|
250
|
+
edges: [{ source: 'root', target: 'image' }],
|
|
251
|
+
});
|
|
@@ -13,9 +13,8 @@ import { faker } from '@dxos/random';
|
|
|
13
13
|
import { createDocAccessor, createObject } from '@dxos/react-client/echo';
|
|
14
14
|
import { useThemeContext } from '@dxos/react-ui';
|
|
15
15
|
import {
|
|
16
|
-
type
|
|
16
|
+
type EditorAction,
|
|
17
17
|
type Comment,
|
|
18
|
-
type EditorViewMode,
|
|
19
18
|
comments,
|
|
20
19
|
createBasicExtensions,
|
|
21
20
|
createDataExtensions,
|
|
@@ -24,32 +23,33 @@ import {
|
|
|
24
23
|
decorateMarkdown,
|
|
25
24
|
editorContent,
|
|
26
25
|
formattingKeymap,
|
|
27
|
-
|
|
26
|
+
EditorToolbar,
|
|
28
27
|
translations,
|
|
29
28
|
useActionHandler,
|
|
30
29
|
useComments,
|
|
31
30
|
useFormattingState,
|
|
32
31
|
useTextEditor,
|
|
32
|
+
useEditorToolbarState,
|
|
33
33
|
} from '@dxos/react-ui-editor';
|
|
34
|
-
import {
|
|
34
|
+
import { TextType } from '@dxos/schema';
|
|
35
35
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
36
36
|
|
|
37
|
-
import { TextType } from '../types';
|
|
38
|
-
|
|
39
37
|
faker.seed(101);
|
|
40
38
|
|
|
39
|
+
const _onUpload = async (file: File) => ({ url: file.name });
|
|
40
|
+
|
|
41
41
|
const DefaultStory: FC<{ content?: string }> = ({ content = '' }) => {
|
|
42
42
|
const { themeMode } = useThemeContext();
|
|
43
43
|
const [text] = useState(createObject(create(TextType, { content })));
|
|
44
|
-
const
|
|
45
|
-
const
|
|
44
|
+
const toolbarState = useEditorToolbarState({ viewMode: 'preview' });
|
|
45
|
+
const formattingObserver = useFormattingState(toolbarState);
|
|
46
46
|
const { parentRef, view } = useTextEditor(() => {
|
|
47
47
|
return {
|
|
48
48
|
id: text.id,
|
|
49
49
|
initialValue: text.content,
|
|
50
50
|
extensions: [
|
|
51
51
|
formattingObserver,
|
|
52
|
-
createBasicExtensions({ readonly: viewMode === 'readonly' }),
|
|
52
|
+
createBasicExtensions({ readonly: toolbarState.viewMode === 'readonly' }),
|
|
53
53
|
createMarkdownExtensions({ themeMode }),
|
|
54
54
|
createThemeExtensions({ themeMode, syntaxHighlighting: true, slots: { editor: { className: editorContent } } }),
|
|
55
55
|
createDataExtensions({ id: text.id, text: createDocAccessor(text, ['content']) }),
|
|
@@ -61,15 +61,15 @@ const DefaultStory: FC<{ content?: string }> = ({ content = '' }) => {
|
|
|
61
61
|
},
|
|
62
62
|
}),
|
|
63
63
|
formattingKeymap(),
|
|
64
|
-
...(viewMode !== 'source' ? [decorateMarkdown()] : []),
|
|
64
|
+
...(toolbarState.viewMode !== 'source' ? [decorateMarkdown()] : []),
|
|
65
65
|
],
|
|
66
66
|
};
|
|
67
|
-
}, [text, formattingObserver, viewMode, themeMode]);
|
|
67
|
+
}, [text, formattingObserver, toolbarState.viewMode, themeMode]);
|
|
68
68
|
|
|
69
69
|
const handleToolbarAction = useActionHandler(view);
|
|
70
|
-
const handleAction = (action:
|
|
70
|
+
const handleAction = (action: EditorAction) => {
|
|
71
71
|
if (action.type === 'view-mode') {
|
|
72
|
-
|
|
72
|
+
toolbarState.viewMode = action.properties.data;
|
|
73
73
|
} else {
|
|
74
74
|
handleToolbarAction?.(action);
|
|
75
75
|
}
|
|
@@ -80,13 +80,7 @@ const DefaultStory: FC<{ content?: string }> = ({ content = '' }) => {
|
|
|
80
80
|
|
|
81
81
|
return (
|
|
82
82
|
<div role='none' className='fixed inset-0 flex flex-col'>
|
|
83
|
-
<
|
|
84
|
-
<Toolbar.View mode={viewMode} />
|
|
85
|
-
<Toolbar.Markdown />
|
|
86
|
-
<Toolbar.Custom onUpload={async (file) => ({ url: file.name })} />
|
|
87
|
-
<Toolbar.Separator />
|
|
88
|
-
<Toolbar.Actions />
|
|
89
|
-
</Toolbar.Root>
|
|
83
|
+
<EditorToolbar onAction={handleAction} state={toolbarState ?? {}} />
|
|
90
84
|
<div ref={parentRef} />
|
|
91
85
|
</div>
|
|
92
86
|
);
|
|
@@ -108,9 +102,9 @@ export const Default = {
|
|
|
108
102
|
},
|
|
109
103
|
};
|
|
110
104
|
|
|
111
|
-
const meta: Meta<typeof
|
|
105
|
+
const meta: Meta<typeof EditorToolbar> = {
|
|
112
106
|
title: 'plugins/plugin-markdown/Toolbar',
|
|
113
|
-
component:
|
|
107
|
+
component: EditorToolbar,
|
|
114
108
|
render: DefaultStory as any,
|
|
115
109
|
decorators: [withTheme, withLayout({ tooltips: true })],
|
|
116
110
|
parameters: { translations, layout: 'fullscreen' },
|