@dxos/plugin-markdown 0.7.4 → 0.7.5-labs.071a3e2
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-ZVCWASOD.mjs} +187 -124
- package/dist/lib/browser/MarkdownContainer-ZVCWASOD.mjs.map +7 -0
- package/dist/lib/browser/app-graph-serializer-BG3MHYI4.mjs +51 -0
- package/dist/lib/browser/app-graph-serializer-BG3MHYI4.mjs.map +7 -0
- package/dist/lib/browser/chunk-EZ65DY2X.mjs +16 -0
- package/dist/lib/browser/chunk-EZ65DY2X.mjs.map +7 -0
- package/dist/lib/browser/{chunk-TZN5FGB2.mjs → chunk-NPLFZ76Q.mjs} +24 -13
- package/dist/lib/browser/chunk-NPLFZ76Q.mjs.map +7 -0
- package/dist/lib/browser/chunk-P4K367ZX.mjs +66 -0
- package/dist/lib/browser/chunk-P4K367ZX.mjs.map +7 -0
- package/dist/lib/browser/chunk-ROJ4VUZB.mjs +47 -0
- package/dist/lib/browser/chunk-ROJ4VUZB.mjs.map +7 -0
- package/dist/lib/browser/chunk-YTHIPV5Q.mjs +22 -0
- package/dist/lib/browser/chunk-YTHIPV5Q.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-CCOPGHVY.mjs +43 -0
- package/dist/lib/browser/intent-resolver-CCOPGHVY.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/react-surface-FDSOMV5N.mjs +149 -0
- package/dist/lib/browser/react-surface-FDSOMV5N.mjs.map +7 -0
- package/dist/lib/browser/settings-U7E4DUWJ.mjs +28 -0
- package/dist/lib/browser/settings-U7E4DUWJ.mjs.map +7 -0
- package/dist/lib/browser/state-QMQXUPSI.mjs +37 -0
- package/dist/lib/browser/state-QMQXUPSI.mjs.map +7 -0
- package/dist/lib/browser/thread-Y4WMNFBC.mjs +36 -0
- package/dist/lib/browser/thread-Y4WMNFBC.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +4 -4
- package/dist/lib/node/{MarkdownContainer-6OKJOHTZ.cjs → MarkdownContainer-HJR4TB7X.cjs} +179 -118
- package/dist/lib/node/MarkdownContainer-HJR4TB7X.cjs.map +7 -0
- package/dist/lib/node/app-graph-serializer-QZWERV4M.cjs +62 -0
- package/dist/lib/node/app-graph-serializer-QZWERV4M.cjs.map +7 -0
- package/dist/lib/node/{meta.cjs → chunk-23NQGZX3.cjs} +13 -8
- package/dist/lib/node/chunk-23NQGZX3.cjs.map +7 -0
- package/dist/lib/node/{chunk-KEPAM4JP.cjs → chunk-2WJ7TUBY.cjs} +39 -14
- package/dist/lib/node/chunk-2WJ7TUBY.cjs.map +7 -0
- package/dist/lib/node/chunk-CZXXBKMN.cjs +86 -0
- package/dist/lib/node/chunk-CZXXBKMN.cjs.map +7 -0
- package/dist/lib/node/{chunk-PHHIPRJC.cjs → chunk-QZ4XQYNC.cjs} +16 -9
- package/dist/lib/node/chunk-QZ4XQYNC.cjs.map +7 -0
- package/dist/lib/node/chunk-ZO5ABSHT.cjs +64 -0
- package/dist/lib/node/chunk-ZO5ABSHT.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-5LCIY27K.cjs +56 -0
- package/dist/lib/node/intent-resolver-5LCIY27K.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/react-surface-Y4IB5T27.cjs +162 -0
- package/dist/lib/node/react-surface-Y4IB5T27.cjs.map +7 -0
- package/dist/lib/node/settings-4OYDMRVY.cjs +42 -0
- package/dist/lib/node/settings-4OYDMRVY.cjs.map +7 -0
- package/dist/lib/node/state-QSA5AOLH.cjs +51 -0
- package/dist/lib/node/state-QSA5AOLH.cjs.map +7 -0
- package/dist/lib/node/thread-BZE6RKFE.cjs +52 -0
- package/dist/lib/node/thread-BZE6RKFE.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-ILR7OVRM.mjs} +187 -124
- package/dist/lib/node-esm/MarkdownContainer-ILR7OVRM.mjs.map +7 -0
- package/dist/lib/node-esm/app-graph-serializer-H7LI476D.mjs +52 -0
- package/dist/lib/node-esm/app-graph-serializer-H7LI476D.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-BBVPC53M.mjs +24 -0
- package/dist/lib/node-esm/chunk-BBVPC53M.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-MMVIEZ43.mjs +67 -0
- package/dist/lib/node-esm/chunk-MMVIEZ43.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-OXPPXUUU.mjs +48 -0
- package/dist/lib/node-esm/chunk-OXPPXUUU.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-NUMUUCYF.mjs → chunk-T62F7XQE.mjs} +24 -13
- package/dist/lib/node-esm/chunk-T62F7XQE.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-Y5QKDC4V.mjs +17 -0
- package/dist/lib/node-esm/chunk-Y5QKDC4V.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-HFEI5NFC.mjs +44 -0
- package/dist/lib/node-esm/intent-resolver-HFEI5NFC.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/react-surface-N5F4I26E.mjs +150 -0
- package/dist/lib/node-esm/react-surface-N5F4I26E.mjs.map +7 -0
- package/dist/lib/node-esm/settings-MEN2YBLA.mjs +29 -0
- package/dist/lib/node-esm/settings-MEN2YBLA.mjs.map +7 -0
- package/dist/lib/node-esm/state-FF4NXLWU.mjs +38 -0
- package/dist/lib/node-esm/state-FF4NXLWU.mjs.map +7 -0
- package/dist/lib/node-esm/thread-O2YDKT77.mjs +37 -0
- package/dist/lib/node-esm/thread-O2YDKT77.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 +2 -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 +76 -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 +33 -0
- package/src/capabilities/react-surface.tsx +71 -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 +91 -61
- package/src/components/MarkdownSettings.tsx +3 -3
- package/src/components/Toolbar.stories.tsx +16 -22
- package/src/extensions.tsx +25 -22
- package/src/hooks/useSelectCurrentThread.tsx +22 -14
- package/src/index.ts +2 -5
- package/src/meta.ts +7 -2
- 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 +0 -15
- 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 +0 -17
- 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,71 @@
|
|
|
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 { SettingsStore } from '@dxos/local-storage';
|
|
9
|
+
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
10
|
+
|
|
11
|
+
import { MarkdownCapabilities } from './capabilities';
|
|
12
|
+
import { MarkdownContainer, MarkdownSettings } from '../components';
|
|
13
|
+
import { MARKDOWN_PLUGIN } from '../meta';
|
|
14
|
+
import { DocumentType, isEditorModel, type MarkdownSettingsProps } from '../types';
|
|
15
|
+
|
|
16
|
+
export default () =>
|
|
17
|
+
contributes(Capabilities.ReactSurface, [
|
|
18
|
+
createSurface({
|
|
19
|
+
id: `${MARKDOWN_PLUGIN}/document`,
|
|
20
|
+
role: ['article', 'section'],
|
|
21
|
+
filter: (data): data is { subject: DocumentType } => data.subject instanceof DocumentType,
|
|
22
|
+
component: ({ data, role }) => {
|
|
23
|
+
const settingsStore = useCapability(Capabilities.SettingsStore);
|
|
24
|
+
const settings = settingsStore.getStore<MarkdownSettingsProps>(MARKDOWN_PLUGIN)!.value;
|
|
25
|
+
const { state, editorState, getViewMode, setViewMode } = useCapability(MarkdownCapabilities.State);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<MarkdownContainer
|
|
29
|
+
id={fullyQualifiedId(data.subject)}
|
|
30
|
+
object={data.subject}
|
|
31
|
+
role={role}
|
|
32
|
+
settings={settings}
|
|
33
|
+
extensionProviders={state.extensionProviders}
|
|
34
|
+
viewMode={getViewMode(fullyQualifiedId(data.subject))}
|
|
35
|
+
editorStateStore={editorState}
|
|
36
|
+
onViewModeChange={setViewMode}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
createSurface({
|
|
42
|
+
id: `${MARKDOWN_PLUGIN}/editor`,
|
|
43
|
+
role: ['article', 'section'],
|
|
44
|
+
filter: (data): data is { subject: { id: string; text: string } } => isEditorModel(data.subject),
|
|
45
|
+
component: ({ data, role }) => {
|
|
46
|
+
const settingsStore = useCapability(Capabilities.SettingsStore);
|
|
47
|
+
const settings = settingsStore.getStore<MarkdownSettingsProps>(MARKDOWN_PLUGIN)!.value;
|
|
48
|
+
const { state, editorState, getViewMode, setViewMode } = useCapability(MarkdownCapabilities.State);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<MarkdownContainer
|
|
52
|
+
id={data.subject.id}
|
|
53
|
+
object={data.subject}
|
|
54
|
+
role={role}
|
|
55
|
+
settings={settings}
|
|
56
|
+
extensionProviders={state.extensionProviders}
|
|
57
|
+
viewMode={getViewMode(data.subject.id)}
|
|
58
|
+
editorStateStore={editorState}
|
|
59
|
+
onViewModeChange={setViewMode}
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
createSurface({
|
|
65
|
+
id: `${MARKDOWN_PLUGIN}/settings`,
|
|
66
|
+
role: 'article',
|
|
67
|
+
filter: (data): data is { subject: SettingsStore<MarkdownSettingsProps> } =>
|
|
68
|
+
data.subject instanceof SettingsStore && data.subject.prefix === MARKDOWN_PLUGIN,
|
|
69
|
+
component: ({ data: { subject } }) => <MarkdownSettings settings={subject.value} />,
|
|
70
|
+
}),
|
|
71
|
+
]);
|
|
@@ -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,
|
|
10
|
+
import { createIntent, type FileInfo, LayoutAction, 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,26 @@ export const MarkdownEditor = ({
|
|
|
89
89
|
[extensionProviders],
|
|
90
90
|
);
|
|
91
91
|
|
|
92
|
-
// TODO(Zan):
|
|
93
|
-
const
|
|
94
|
-
const onCommentClick = useCallback(() => {
|
|
95
|
-
|
|
96
|
-
{
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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(
|
|
96
|
+
createIntent(LayoutAction.UpdateComplementary, {
|
|
97
|
+
part: 'complementary',
|
|
98
|
+
subject: 'comments',
|
|
99
|
+
options: { state: 'expanded' },
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
105
102
|
}, [dispatch]);
|
|
106
103
|
const commentClickObserver = useCommentClickListener(onCommentClick);
|
|
107
104
|
|
|
105
|
+
// TODO(wittjosiah): Factor out to file uploader plugin.
|
|
108
106
|
// Drag files.
|
|
109
107
|
const handleDrop: DNDOptions['onDrop'] = async (view, { files }) => {
|
|
110
108
|
const file = files[0];
|
|
111
109
|
const info = file && onFileUpload ? await onFileUpload(file) : undefined;
|
|
112
110
|
if (info) {
|
|
113
|
-
|
|
111
|
+
processEditorPayload(view, { type: 'image', data: info.url });
|
|
114
112
|
}
|
|
115
113
|
};
|
|
116
114
|
|
|
@@ -156,57 +154,79 @@ export const MarkdownEditor = ({
|
|
|
156
154
|
useTest(editorView);
|
|
157
155
|
useSelectCurrentThread(editorView, id);
|
|
158
156
|
|
|
157
|
+
// https://react-dropzone.js.org/#src
|
|
158
|
+
const { acceptedFiles, getInputProps, open } = useDropzone({
|
|
159
|
+
multiple: false,
|
|
160
|
+
noDrag: true,
|
|
161
|
+
accept: {
|
|
162
|
+
'image/*': ['.jpg', '.jpeg', '.png', '.gif'],
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
if (editorView && onFileUpload && acceptedFiles.length) {
|
|
168
|
+
requestAnimationFrame(async () => {
|
|
169
|
+
// NOTE: Clone file since react-dropzone patches in a non-standard `path` property, which confuses IPFS.
|
|
170
|
+
const f = acceptedFiles[0];
|
|
171
|
+
const file = new File([f], f.name, {
|
|
172
|
+
type: f.type,
|
|
173
|
+
lastModified: f.lastModified,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const info = await onFileUpload(file);
|
|
177
|
+
if (info) {
|
|
178
|
+
processEditorPayload(editorView, { type: 'image', data: info.url });
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}, [acceptedFiles, editorView]);
|
|
183
|
+
|
|
159
184
|
// Toolbar handler.
|
|
160
185
|
const handleToolbarAction = useActionHandler(editorView);
|
|
161
|
-
const handleAction = (
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
186
|
+
const handleAction = useCallback(
|
|
187
|
+
(action: EditorAction) => {
|
|
188
|
+
switch (action.properties.type) {
|
|
189
|
+
case 'search': {
|
|
190
|
+
if (editorView) {
|
|
191
|
+
openSearchPanel(editorView);
|
|
192
|
+
}
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
case 'view-mode': {
|
|
196
|
+
onViewModeChange?.(id, action.properties.data);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
case 'image': {
|
|
200
|
+
open();
|
|
201
|
+
return;
|
|
166
202
|
}
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
case 'view-mode': {
|
|
170
|
-
onViewModeChange?.(id, action.data);
|
|
171
|
-
return;
|
|
172
203
|
}
|
|
173
|
-
}
|
|
174
204
|
|
|
175
|
-
|
|
176
|
-
|
|
205
|
+
handleToolbarAction?.(action);
|
|
206
|
+
},
|
|
207
|
+
[editorView, onViewModeChange, open],
|
|
208
|
+
);
|
|
177
209
|
|
|
178
210
|
return (
|
|
179
|
-
<StackItem.Content toolbar={toolbar}>
|
|
211
|
+
<StackItem.Content toolbar={!!toolbar}>
|
|
180
212
|
{toolbar && (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
>
|
|
188
|
-
<Toolbar.Root
|
|
189
|
-
classNames={[textBlockWidth, !hasAttention && 'opacity-20']}
|
|
190
|
-
state={formattingState && { ...formattingState, ...commentsState }}
|
|
213
|
+
<>
|
|
214
|
+
<EditorToolbar
|
|
215
|
+
attendableId={id}
|
|
216
|
+
role={role}
|
|
217
|
+
state={toolbarState}
|
|
218
|
+
customActions={onFileUpload ? createUploadAction : undefined}
|
|
191
219
|
onAction={handleAction}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
<Toolbar.Separator />
|
|
196
|
-
<Toolbar.View mode={viewMode ?? DEFAULT_VIEW_MODE} />
|
|
197
|
-
<Toolbar.Actions />
|
|
198
|
-
</Toolbar.Root>
|
|
199
|
-
</div>
|
|
220
|
+
/>
|
|
221
|
+
<input {...getInputProps()} />
|
|
222
|
+
</>
|
|
200
223
|
)}
|
|
201
224
|
<div
|
|
202
225
|
role='none'
|
|
203
226
|
ref={parentRef}
|
|
204
227
|
data-testid='composer.markdownRoot'
|
|
205
228
|
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
|
-
)}
|
|
229
|
+
className={stackItemContentEditorClassNames(role)}
|
|
210
230
|
{...focusAttributes}
|
|
211
231
|
/>
|
|
212
232
|
</StackItem.Content>
|
|
@@ -223,3 +243,13 @@ const useTest = (view?: EditorView) => {
|
|
|
223
243
|
}
|
|
224
244
|
}, [view]);
|
|
225
245
|
};
|
|
246
|
+
|
|
247
|
+
export const createUploadAction = () => ({
|
|
248
|
+
nodes: [
|
|
249
|
+
createEditorAction({ type: 'image', testId: 'editor.toolbar.image' }, 'ph--image-square--regular', [
|
|
250
|
+
'upload image label',
|
|
251
|
+
{ ns: MARKDOWN_PLUGIN },
|
|
252
|
+
]),
|
|
253
|
+
],
|
|
254
|
+
edges: [{ source: 'root', target: 'image' }],
|
|
255
|
+
});
|
|
@@ -6,7 +6,7 @@ import React from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { Input, Select, useTranslation } from '@dxos/react-ui';
|
|
8
8
|
import { type EditorInputMode, EditorInputModes, type EditorViewMode, EditorViewModes } from '@dxos/react-ui-editor';
|
|
9
|
-
import { DeprecatedFormInput } from '@dxos/react-ui-form';
|
|
9
|
+
import { DeprecatedFormContainer, DeprecatedFormInput } from '@dxos/react-ui-form';
|
|
10
10
|
|
|
11
11
|
import { MARKDOWN_PLUGIN } from '../meta';
|
|
12
12
|
import { type MarkdownSettingsProps } from '../types';
|
|
@@ -16,7 +16,7 @@ export const MarkdownSettings = ({ settings }: { settings: MarkdownSettingsProps
|
|
|
16
16
|
|
|
17
17
|
// TODO(wittjosiah): Add skill test confirmation for entering vim mode.
|
|
18
18
|
return (
|
|
19
|
-
|
|
19
|
+
<DeprecatedFormContainer>
|
|
20
20
|
<DeprecatedFormInput label={t('default view mode label')}>
|
|
21
21
|
<Select.Root
|
|
22
22
|
value={settings.defaultViewMode}
|
|
@@ -100,6 +100,6 @@ export const MarkdownSettings = ({ settings }: { settings: MarkdownSettingsProps
|
|
|
100
100
|
>
|
|
101
101
|
<Input.Switch checked={settings.debug} onCheckedChange={(checked) => (settings.debug = !!checked)} />
|
|
102
102
|
</DeprecatedFormInput>
|
|
103
|
-
|
|
103
|
+
</DeprecatedFormContainer>
|
|
104
104
|
);
|
|
105
105
|
};
|
|
@@ -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' },
|