@dxos/plugin-markdown 0.6.13 → 0.6.14-main.7bd9c89
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-X53JPAW5.mjs +469 -0
- package/dist/lib/browser/MarkdownContainer-X53JPAW5.mjs.map +7 -0
- package/dist/lib/browser/chunk-45N5MEOV.mjs +50 -0
- package/dist/lib/browser/chunk-45N5MEOV.mjs.map +7 -0
- package/dist/lib/browser/chunk-DRJ3FPYF.mjs +15 -0
- package/dist/lib/browser/chunk-DRJ3FPYF.mjs.map +7 -0
- package/dist/lib/browser/{chunk-CQJL4G4X.mjs → chunk-US5O2P3R.mjs} +4 -2
- package/dist/lib/browser/chunk-US5O2P3R.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +80 -119
- 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-GZ6EAOPC.cjs +484 -0
- package/dist/lib/node/MarkdownContainer-GZ6EAOPC.cjs.map +7 -0
- package/dist/lib/node/{DocumentCard-EHJDDSRY.cjs → chunk-P7YU53RP.cjs} +16 -10
- package/dist/lib/node/chunk-P7YU53RP.cjs.map +7 -0
- package/dist/lib/node/{chunk-VWQH4WC2.cjs → chunk-UJMOZCIA.cjs} +11 -8
- package/dist/lib/node/chunk-UJMOZCIA.cjs.map +7 -0
- package/dist/lib/node/chunk-W2YJVZ3N.cjs +72 -0
- package/dist/lib/node/chunk-W2YJVZ3N.cjs.map +7 -0
- package/dist/lib/node/index.cjs +114 -149
- 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-FGCDNB4S.mjs +470 -0
- package/dist/lib/node-esm/MarkdownContainer-FGCDNB4S.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-MIDCCMIX.mjs +42 -0
- package/dist/lib/node-esm/chunk-MIDCCMIX.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-NEVN5WR6.mjs +17 -0
- package/dist/lib/node-esm/chunk-NEVN5WR6.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-UCNOGIBC.mjs +51 -0
- package/dist/lib/node-esm/chunk-UCNOGIBC.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +494 -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 +8 -3
- 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 -15
- 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/package.json +41 -36
- package/src/MarkdownPlugin.tsx +51 -98
- package/src/components/MarkdownContainer.tsx +111 -0
- package/src/components/MarkdownEditor.stories.tsx +34 -23
- package/src/components/MarkdownEditor.tsx +40 -76
- package/src/components/Toolbar.stories.tsx +14 -11
- package/src/components/index.ts +2 -14
- package/src/extensions.tsx +124 -67
- package/src/hooks/index.ts +5 -0
- package/src/hooks/useSelectCurrentThread.tsx +46 -0
- package/src/meta.ts +15 -0
- package/src/types/document.ts +12 -0
- package/src/types/types.ts +10 -7
- package/src/util.tsx +2 -2
- 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
|
@@ -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,117 @@
|
|
|
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';
|
|
13
|
+
import { createDataExtensions, listener, localStorageStateStoreAdapter, state } from '@dxos/react-ui-editor';
|
|
12
14
|
import {
|
|
13
15
|
type AutocompleteResult,
|
|
14
|
-
type Extension,
|
|
15
16
|
type EditorViewMode,
|
|
17
|
+
type Extension,
|
|
18
|
+
InputModeExtensions,
|
|
16
19
|
autocomplete,
|
|
17
20
|
decorateMarkdown,
|
|
21
|
+
folding,
|
|
22
|
+
formattingKeymap,
|
|
18
23
|
linkTooltip,
|
|
19
24
|
typewriter,
|
|
20
|
-
formattingKeymap,
|
|
21
|
-
InputModeExtensions,
|
|
22
|
-
folding,
|
|
23
25
|
} from '@dxos/react-ui-editor';
|
|
24
|
-
import {
|
|
25
|
-
import { isNotFalsy
|
|
26
|
+
import { defaultTx } from '@dxos/react-ui-theme';
|
|
27
|
+
import { isNotFalsy } from '@dxos/util';
|
|
26
28
|
|
|
27
|
-
import { type DocumentType, type MarkdownSettingsProps } from './types';
|
|
29
|
+
import { type DocumentType, type MarkdownPluginState, type MarkdownSettingsProps } from './types';
|
|
30
|
+
import { setFallbackName } from './util';
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
settings?: MarkdownSettingsProps;
|
|
32
|
-
document?: DocumentType;
|
|
33
|
-
debug?: boolean;
|
|
34
|
-
experimental?: boolean;
|
|
35
|
-
numberedHeadings?: boolean;
|
|
36
|
-
folding?: boolean;
|
|
37
|
-
query?: Query<DocumentType>;
|
|
32
|
+
type ExtensionsOptions = {
|
|
33
|
+
document: DocumentType;
|
|
38
34
|
dispatch?: IntentDispatcher;
|
|
35
|
+
query?: Query<DocumentType>;
|
|
36
|
+
settings: MarkdownSettingsProps;
|
|
37
|
+
viewMode?: EditorViewMode;
|
|
39
38
|
};
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
export const createBaseExtensions = ({
|
|
45
|
-
viewMode,
|
|
46
|
-
settings,
|
|
40
|
+
// TODO(burdon): Merge with createBaseExtensions above.
|
|
41
|
+
export const useExtensions = ({
|
|
42
|
+
extensionProviders,
|
|
47
43
|
document,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}: ExtensionsOptions
|
|
51
|
-
|
|
44
|
+
settings,
|
|
45
|
+
viewMode,
|
|
46
|
+
}: Pick<ExtensionsOptions, 'document' | 'settings' | 'viewMode'> &
|
|
47
|
+
Pick<MarkdownPluginState, 'extensionProviders'>): Extension[] => {
|
|
48
|
+
const dispatch = useIntentDispatcher();
|
|
49
|
+
const identity = useIdentity();
|
|
50
|
+
const space = getSpace(document);
|
|
51
|
+
|
|
52
|
+
// TODO(wittjosiah): Autocomplete is not working and this query is causing performance issues.
|
|
53
|
+
// TODO(burdon): Unsubscribe.
|
|
54
|
+
// const query = space?.db.query(Filter.schema(DocumentType));
|
|
55
|
+
// query?.subscribe();
|
|
56
|
+
const baseExtensions = useMemo(
|
|
57
|
+
() =>
|
|
58
|
+
createBaseExtensions({
|
|
59
|
+
document,
|
|
60
|
+
settings,
|
|
61
|
+
viewMode,
|
|
62
|
+
dispatch,
|
|
63
|
+
// query,
|
|
64
|
+
}),
|
|
65
|
+
[document, viewMode, dispatch, settings, settings.folding, settings.numberedHeadings],
|
|
66
|
+
);
|
|
52
67
|
|
|
53
68
|
//
|
|
54
|
-
//
|
|
69
|
+
// External extensions from other plugins.
|
|
55
70
|
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
71
|
+
const pluginExtensions = useMemo<Extension[] | undefined>(
|
|
72
|
+
() =>
|
|
73
|
+
extensionProviders?.reduce((acc: Extension[], provider) => {
|
|
74
|
+
const extension = typeof provider === 'function' ? provider({ document }) : provider;
|
|
75
|
+
if (extension) {
|
|
76
|
+
acc.push(extension);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return acc;
|
|
80
|
+
}, []),
|
|
81
|
+
[extensionProviders],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
//
|
|
85
|
+
// Basic plugins.
|
|
86
|
+
//
|
|
87
|
+
return useMemo<Extension[]>(
|
|
88
|
+
() =>
|
|
89
|
+
[
|
|
90
|
+
// NOTE: Data extensions must be first so that automerge is updated before other extensions compute their state.
|
|
91
|
+
createDataExtensions({
|
|
92
|
+
id: document.id,
|
|
93
|
+
text: document.content && createDocAccessor(document.content, ['content']),
|
|
94
|
+
space,
|
|
95
|
+
identity,
|
|
96
|
+
}),
|
|
97
|
+
state(localStorageStateStoreAdapter),
|
|
98
|
+
listener({
|
|
99
|
+
onChange: (text) => setFallbackName(document, text),
|
|
100
|
+
}),
|
|
101
|
+
baseExtensions,
|
|
102
|
+
pluginExtensions,
|
|
103
|
+
].filter(isNotFalsy),
|
|
104
|
+
[baseExtensions, pluginExtensions, document, document.content, space, identity],
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Create extension instances for editor.
|
|
110
|
+
*/
|
|
111
|
+
const createBaseExtensions = ({ document, dispatch, settings, query, viewMode }: ExtensionsOptions): Extension[] => {
|
|
112
|
+
const extensions: Extension[] = [
|
|
113
|
+
settings.editorInputMode && InputModeExtensions[settings.editorInputMode],
|
|
114
|
+
settings.folding && folding(),
|
|
115
|
+
].filter(isNotFalsy);
|
|
62
116
|
|
|
63
117
|
//
|
|
64
118
|
// Markdown
|
|
@@ -69,7 +123,7 @@ export const createBaseExtensions = ({
|
|
|
69
123
|
formattingKeymap(),
|
|
70
124
|
decorateMarkdown({
|
|
71
125
|
selectionChangeDelay: 100,
|
|
72
|
-
numberedHeadings: settings
|
|
126
|
+
numberedHeadings: settings.numberedHeadings ? { from: 2 } : undefined,
|
|
73
127
|
// TODO(wittjosiah): For internal links, consider ignoring the link text and rendering the label of the object being linked to.
|
|
74
128
|
renderLinkButton:
|
|
75
129
|
dispatch && document
|
|
@@ -98,7 +152,6 @@ export const createBaseExtensions = ({
|
|
|
98
152
|
extensions.push(
|
|
99
153
|
autocomplete({
|
|
100
154
|
onSearch: (text: string) => {
|
|
101
|
-
// TODO query
|
|
102
155
|
// TODO(burdon): Specify filter (e.g., stack).
|
|
103
156
|
return query.objects
|
|
104
157
|
.map<AutocompleteResult | undefined>((object) =>
|
|
@@ -110,29 +163,27 @@ export const createBaseExtensions = ({
|
|
|
110
163
|
}
|
|
111
164
|
: undefined,
|
|
112
165
|
)
|
|
113
|
-
.filter(
|
|
166
|
+
.filter(isNotFalsy);
|
|
114
167
|
},
|
|
115
168
|
}),
|
|
116
169
|
);
|
|
117
170
|
}
|
|
118
171
|
|
|
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));
|
|
172
|
+
if (settings.debug) {
|
|
173
|
+
const items = settings.typewriter?.split(/[,\n]/) ?? '';
|
|
174
|
+
if (items) {
|
|
175
|
+
extensions.push(typewriter({ items }));
|
|
176
|
+
}
|
|
129
177
|
}
|
|
130
178
|
|
|
131
179
|
return extensions;
|
|
132
180
|
};
|
|
133
181
|
|
|
134
|
-
// TODO(burdon): Factor out
|
|
135
|
-
const
|
|
182
|
+
// TODO(burdon): Factor out styles.
|
|
183
|
+
const style = {
|
|
184
|
+
hover: 'rounded-sm text-primary-500 hover:text-primary-600 dark:text-primary-500 hover:dark:text-primary-400',
|
|
185
|
+
icon: 'inline-block leading-none mis-1 cursor-pointer',
|
|
186
|
+
};
|
|
136
187
|
|
|
137
188
|
const onRenderLink = (onSelectObject: (id: string) => void) => (el: Element, url: string) => {
|
|
138
189
|
// TODO(burdon): Formalize/document internal link format.
|
|
@@ -155,25 +206,31 @@ const onRenderLink = (onSelectObject: (id: string) => void) => (el: Element, url
|
|
|
155
206
|
target: '_blank',
|
|
156
207
|
};
|
|
157
208
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
209
|
+
renderRoot(
|
|
210
|
+
el,
|
|
211
|
+
<a {...options} className={style.hover}>
|
|
212
|
+
<Icon
|
|
213
|
+
icon={isInternal ? 'ph--arrow-square-down--bold' : 'ph--arrow-square-out--bold'}
|
|
214
|
+
size={4}
|
|
215
|
+
classNames={style.icon}
|
|
216
|
+
/>
|
|
217
|
+
</a>,
|
|
166
218
|
);
|
|
167
219
|
};
|
|
168
220
|
|
|
169
221
|
const renderLinkTooltip = (el: Element, url: string) => {
|
|
170
222
|
const web = new URL(url);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
</StrictMode>,
|
|
223
|
+
renderRoot(
|
|
224
|
+
el,
|
|
225
|
+
<a href={url} rel='noreferrer' target='_blank' className={style.hover}>
|
|
226
|
+
{web.origin}
|
|
227
|
+
<Icon icon='ph--arrow-square-out--bold' size={4} classNames={style.icon} />
|
|
228
|
+
</a>,
|
|
178
229
|
);
|
|
179
230
|
};
|
|
231
|
+
|
|
232
|
+
// TODO(burdon): Remove react rendering; use DOM directly.
|
|
233
|
+
export const renderRoot = <T extends Element>(root: T, node: ReactNode): T => {
|
|
234
|
+
createRoot(root).render(<ThemeProvider tx={defaultTx}>{node}</ThemeProvider>);
|
|
235
|
+
return root;
|
|
236
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { EditorView } from '@codemirror/view';
|
|
6
|
+
import { useCallback } from 'react';
|
|
7
|
+
|
|
8
|
+
import { LayoutAction, useIntentResolver } from '@dxos/app-framework';
|
|
9
|
+
import { Cursor, setSelection } from '@dxos/react-ui-editor';
|
|
10
|
+
|
|
11
|
+
import { MARKDOWN_PLUGIN } from '../meta';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Handle scrolling and selection of the current thread in a markdown editor.
|
|
15
|
+
*/
|
|
16
|
+
export const useSelectCurrentThread = (editorView: EditorView | undefined, documentId: string) => {
|
|
17
|
+
const handleScrollIntoView = useCallback(
|
|
18
|
+
({ action, data }: { action: string; data?: any }) => {
|
|
19
|
+
if (action === LayoutAction.SCROLL_INTO_VIEW) {
|
|
20
|
+
if (editorView && data?.id === documentId && data?.cursor) {
|
|
21
|
+
// TODO(burdon): We need typed intents.
|
|
22
|
+
const range = Cursor.getRangeFromCursor(editorView.state, data.cursor);
|
|
23
|
+
if (range) {
|
|
24
|
+
const selection = editorView.state.selection.main.from !== range.from ? { anchor: range.from } : undefined;
|
|
25
|
+
const effects = [
|
|
26
|
+
// NOTE: This does not use the DOM scrollIntoView function.
|
|
27
|
+
EditorView.scrollIntoView(range.from, { y: 'start', yMargin: 96 }),
|
|
28
|
+
];
|
|
29
|
+
if (selection) {
|
|
30
|
+
// Update the editor selection to get bi-directional highlighting.
|
|
31
|
+
effects.push(setSelection.of({ current: documentId }));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
editorView.dispatch({
|
|
35
|
+
effects,
|
|
36
|
+
selection: selection ? { anchor: range.from } : undefined,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
[documentId, editorView],
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
useIntentResolver(MARKDOWN_PLUGIN, handleScrollIntoView);
|
|
46
|
+
};
|
package/src/meta.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type PluginMeta } from '@dxos/app-framework';
|
|
6
|
+
|
|
7
|
+
export const MARKDOWN_PLUGIN = 'dxos.org/plugin/markdown';
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
id: MARKDOWN_PLUGIN,
|
|
11
|
+
name: 'Markdown Editor',
|
|
12
|
+
description: 'Text editor supporting extended Markdown.',
|
|
13
|
+
homePage: 'https://github.com/dxos/dxos/tree/main/packages/apps/plugins/plugin-markdown',
|
|
14
|
+
icon: 'ph--text-aa--regular',
|
|
15
|
+
} satisfies PluginMeta;
|
package/src/types/document.ts
CHANGED
|
@@ -15,3 +15,15 @@ export class DocumentType extends TypedObject({ typename: 'dxos.org/type/Documen
|
|
|
15
15
|
content: ref(TextType),
|
|
16
16
|
threads: S.mutable(S.Array(ref(ThreadType))),
|
|
17
17
|
}) {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Checks if an object conforms to the interface needed to render an editor.
|
|
21
|
+
*/
|
|
22
|
+
// TODO(burdon): Normalize types (from FilesPlugin).
|
|
23
|
+
export const isEditorModel = (data: any): data is { id: string; text: string } =>
|
|
24
|
+
data &&
|
|
25
|
+
typeof data === 'object' &&
|
|
26
|
+
'id' in data &&
|
|
27
|
+
typeof data.id === 'string' &&
|
|
28
|
+
'text' in data &&
|
|
29
|
+
typeof data.text === 'string';
|
package/src/types/types.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
TranslationsProvides,
|
|
13
13
|
} from '@dxos/app-framework';
|
|
14
14
|
import { type SchemaProvides } from '@dxos/plugin-client';
|
|
15
|
+
import { type SpaceInitProvides } from '@dxos/plugin-space';
|
|
15
16
|
import { type Extension, type EditorInputMode, type EditorViewMode } from '@dxos/react-ui-editor';
|
|
16
17
|
|
|
17
18
|
import { type DocumentType } from './document';
|
|
@@ -26,13 +27,15 @@ export enum MarkdownAction {
|
|
|
26
27
|
|
|
27
28
|
export type MarkdownProperties = Record<string, any>;
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
// TODO(burdon): Async.
|
|
31
|
+
export type MarkdownExtensionProvider = (props: { document?: DocumentType }) => Extension | undefined;
|
|
30
32
|
|
|
31
33
|
export type OnChange = (text: string) => void;
|
|
32
34
|
|
|
33
35
|
export type MarkdownExtensionProvides = {
|
|
36
|
+
// TODO(burdon): Rename.
|
|
34
37
|
markdown: {
|
|
35
|
-
extensions:
|
|
38
|
+
extensions: MarkdownExtensionProvider;
|
|
36
39
|
};
|
|
37
40
|
};
|
|
38
41
|
|
|
@@ -45,11 +48,11 @@ type StackProvides = {
|
|
|
45
48
|
|
|
46
49
|
export type MarkdownPluginState = {
|
|
47
50
|
// Codemirror extensions provided by other plugins.
|
|
48
|
-
extensionProviders
|
|
51
|
+
extensionProviders?: MarkdownExtensionProvider[];
|
|
49
52
|
|
|
50
53
|
// TODO(burdon): Extend view mode per document to include scroll position, etc.
|
|
51
54
|
// View mode per document.
|
|
52
|
-
viewMode:
|
|
55
|
+
viewMode: Record<string, EditorViewMode>;
|
|
53
56
|
};
|
|
54
57
|
|
|
55
58
|
export type MarkdownSettingsProps = {
|
|
@@ -64,12 +67,11 @@ export type MarkdownSettingsProps = {
|
|
|
64
67
|
folding?: boolean;
|
|
65
68
|
};
|
|
66
69
|
|
|
67
|
-
// TODO(Zan): Move this to the plugin-space plugin or another common location
|
|
68
|
-
// when we implement comments in sheets.
|
|
70
|
+
// TODO(Zan): Move this to the plugin-space plugin or another common location when we implement comments in sheets.
|
|
69
71
|
type ThreadProvides<T> = {
|
|
70
72
|
thread: {
|
|
71
73
|
predicate: (obj: any) => obj is T;
|
|
72
|
-
createSort: (obj: T) => (anchorA: string, anchorB: string) => number;
|
|
74
|
+
createSort: (obj: T) => (anchorA: string | undefined, anchorB: string | undefined) => number;
|
|
73
75
|
};
|
|
74
76
|
};
|
|
75
77
|
|
|
@@ -81,5 +83,6 @@ export type MarkdownPluginProvides = SurfaceProvides &
|
|
|
81
83
|
SettingsProvides<MarkdownSettingsProps> &
|
|
82
84
|
TranslationsProvides &
|
|
83
85
|
SchemaProvides &
|
|
86
|
+
SpaceInitProvides &
|
|
84
87
|
StackProvides &
|
|
85
88
|
ThreadProvides<DocumentType>;
|
package/src/util.tsx
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { type Plugin } from '@dxos/app-framework';
|
|
6
6
|
import { debounce } from '@dxos/async';
|
|
7
7
|
import { type TypedObjectSerializer } from '@dxos/plugin-space/types';
|
|
8
|
-
import { create,
|
|
8
|
+
import { create, createObject, isEchoObject, loadObjectReferences } from '@dxos/react-client/echo';
|
|
9
9
|
|
|
10
10
|
import { DocumentType, type MarkdownProperties, type MarkdownExtensionProvides, TextType } from './types';
|
|
11
11
|
|
|
@@ -43,6 +43,6 @@ export const serializer: TypedObjectSerializer<DocumentType> = {
|
|
|
43
43
|
|
|
44
44
|
deserialize: async ({ content: serialized }) => {
|
|
45
45
|
const { name, content } = JSON.parse(serialized);
|
|
46
|
-
return
|
|
46
|
+
return createObject(create(DocumentType, { name, content: create(TextType, { content }), threads: [] }));
|
|
47
47
|
},
|
|
48
48
|
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DocumentCard,
|
|
3
|
-
DocumentCard_default
|
|
4
|
-
} from "./chunk-RL7QY322.mjs";
|
|
5
|
-
import "./chunk-354DCID5.mjs";
|
|
6
|
-
import "./chunk-4GGD6YJO.mjs";
|
|
7
|
-
export {
|
|
8
|
-
DocumentCard,
|
|
9
|
-
DocumentCard_default as default
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=DocumentCard-2P4EICBA.mjs.map
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DocumentEditor_default
|
|
3
|
-
} from "./chunk-7AF2JLK4.mjs";
|
|
4
|
-
import "./chunk-CQJL4G4X.mjs";
|
|
5
|
-
import "./chunk-354DCID5.mjs";
|
|
6
|
-
import "./chunk-VUN4QKTT.mjs";
|
|
7
|
-
import "./chunk-4GGD6YJO.mjs";
|
|
8
|
-
export {
|
|
9
|
-
DocumentEditor_default as default
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=DocumentEditor-GPWV3VN3.mjs.map
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
// packages/plugins/plugin-markdown/src/extensions.tsx
|
|
2
|
-
import { ArrowSquareDown, ArrowSquareOut } from "@phosphor-icons/react";
|
|
3
|
-
import React, { StrictMode } from "react";
|
|
4
|
-
import { createRoot } from "react-dom/client";
|
|
5
|
-
import { NavigationAction } from "@dxos/app-framework";
|
|
6
|
-
import { invariant } from "@dxos/invariant";
|
|
7
|
-
import { fullyQualifiedId } from "@dxos/react-client/echo";
|
|
8
|
-
import { autocomplete, decorateMarkdown, linkTooltip, typewriter, formattingKeymap, InputModeExtensions, folding } from "@dxos/react-ui-editor";
|
|
9
|
-
import { getSize, mx } from "@dxos/react-ui-theme";
|
|
10
|
-
import { isNotFalsy, nonNullable } from "@dxos/util";
|
|
11
|
-
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/plugins/plugin-markdown/src/extensions.tsx";
|
|
12
|
-
var createBaseExtensions = ({ viewMode, settings, document, query, dispatch }) => {
|
|
13
|
-
const extensions = [];
|
|
14
|
-
if (settings?.editorInputMode) {
|
|
15
|
-
const extension = InputModeExtensions[settings.editorInputMode];
|
|
16
|
-
if (extension) {
|
|
17
|
-
extensions.push(extension);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
if (viewMode !== "source") {
|
|
21
|
-
extensions.push(...[
|
|
22
|
-
formattingKeymap(),
|
|
23
|
-
decorateMarkdown({
|
|
24
|
-
selectionChangeDelay: 100,
|
|
25
|
-
numberedHeadings: settings?.numberedHeadings ? {
|
|
26
|
-
from: 2
|
|
27
|
-
} : void 0,
|
|
28
|
-
// TODO(wittjosiah): For internal links, consider ignoring the link text and rendering the label of the object being linked to.
|
|
29
|
-
renderLinkButton: dispatch && document ? onRenderLink((id) => {
|
|
30
|
-
void dispatch({
|
|
31
|
-
action: NavigationAction.ADD_TO_ACTIVE,
|
|
32
|
-
data: {
|
|
33
|
-
id,
|
|
34
|
-
part: "main",
|
|
35
|
-
pivotId: fullyQualifiedId(document),
|
|
36
|
-
scrollIntoView: true
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
}) : void 0
|
|
40
|
-
}),
|
|
41
|
-
linkTooltip(renderLinkTooltip)
|
|
42
|
-
]);
|
|
43
|
-
}
|
|
44
|
-
if (query) {
|
|
45
|
-
extensions.push(autocomplete({
|
|
46
|
-
onSearch: (text) => {
|
|
47
|
-
return query.objects.map((object) => object.name?.length && object.id !== document?.id ? {
|
|
48
|
-
label: object.name,
|
|
49
|
-
// TODO(burdon): Factor out URL builder.
|
|
50
|
-
apply: `[${object.name}](/${fullyQualifiedId(object)})`
|
|
51
|
-
} : void 0).filter(nonNullable);
|
|
52
|
-
}
|
|
53
|
-
}));
|
|
54
|
-
}
|
|
55
|
-
extensions.push(...[
|
|
56
|
-
//
|
|
57
|
-
settings?.folding && folding()
|
|
58
|
-
].filter(isNotFalsy));
|
|
59
|
-
if (settings?.debug) {
|
|
60
|
-
const items = settings.typewriter ?? "";
|
|
61
|
-
extensions.push(...[
|
|
62
|
-
items ? typewriter({
|
|
63
|
-
items: items.split(/[,\n]/)
|
|
64
|
-
}) : void 0
|
|
65
|
-
].filter(nonNullable));
|
|
66
|
-
}
|
|
67
|
-
return extensions;
|
|
68
|
-
};
|
|
69
|
-
var hover = "rounded-sm text-primary-600 hover:text-primary-500 dark:text-primary-300 hover:dark:text-primary-200";
|
|
70
|
-
var onRenderLink = (onSelectObject) => (el, url) => {
|
|
71
|
-
const isInternal = url.startsWith("/") || // TODO(wittjosiah): This should probably be parsed out on paste?
|
|
72
|
-
url.startsWith(window.location.origin);
|
|
73
|
-
const options = isInternal ? {
|
|
74
|
-
onClick: () => {
|
|
75
|
-
const qualifiedId = url.split("/").at(-1);
|
|
76
|
-
invariant(qualifiedId, "Invalid link format.", {
|
|
77
|
-
F: __dxlog_file,
|
|
78
|
-
L: 148,
|
|
79
|
-
S: void 0,
|
|
80
|
-
A: [
|
|
81
|
-
"qualifiedId",
|
|
82
|
-
"'Invalid link format.'"
|
|
83
|
-
]
|
|
84
|
-
});
|
|
85
|
-
onSelectObject(qualifiedId);
|
|
86
|
-
}
|
|
87
|
-
} : {
|
|
88
|
-
href: url,
|
|
89
|
-
rel: "noreferrer",
|
|
90
|
-
target: "_blank"
|
|
91
|
-
};
|
|
92
|
-
const LinkIcon = isInternal ? ArrowSquareDown : ArrowSquareOut;
|
|
93
|
-
createRoot(el).render(/* @__PURE__ */ React.createElement(StrictMode, null, /* @__PURE__ */ React.createElement("a", {
|
|
94
|
-
...options,
|
|
95
|
-
className: hover
|
|
96
|
-
}, /* @__PURE__ */ React.createElement(LinkIcon, {
|
|
97
|
-
weight: "bold",
|
|
98
|
-
className: mx(getSize(4), "inline-block leading-none mis-1 cursor-pointer")
|
|
99
|
-
}))));
|
|
100
|
-
};
|
|
101
|
-
var renderLinkTooltip = (el, url) => {
|
|
102
|
-
const web = new URL(url);
|
|
103
|
-
createRoot(el).render(/* @__PURE__ */ React.createElement(StrictMode, null, /* @__PURE__ */ React.createElement("a", {
|
|
104
|
-
href: url,
|
|
105
|
-
target: "_blank",
|
|
106
|
-
rel: "noreferrer",
|
|
107
|
-
className: hover
|
|
108
|
-
}, web.origin, /* @__PURE__ */ React.createElement(ArrowSquareOut, {
|
|
109
|
-
weight: "bold",
|
|
110
|
-
className: mx(getSize(4), "inline-block leading-none mis-1 cursor-pointer")
|
|
111
|
-
}))));
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
export {
|
|
115
|
-
createBaseExtensions
|
|
116
|
-
};
|
|
117
|
-
//# sourceMappingURL=chunk-354DCID5.mjs.map
|