@dxos/plugin-markdown 0.6.12-main.f9d0246 → 0.6.12-staging.e11e696

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.
Files changed (112) hide show
  1. package/dist/lib/browser/MarkdownContainer-OWVU5WMN.mjs +467 -0
  2. package/dist/lib/browser/MarkdownContainer-OWVU5WMN.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-7WORDTCY.mjs +50 -0
  4. package/dist/lib/browser/chunk-7WORDTCY.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-N7FS4CK2.mjs → chunk-OUZCML5B.mjs} +3 -1
  6. package/dist/lib/browser/chunk-OUZCML5B.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +54 -101
  8. package/dist/lib/browser/index.mjs.map +4 -4
  9. package/dist/lib/browser/meta.json +1 -1
  10. package/dist/lib/browser/types/index.mjs +5 -3
  11. package/dist/lib/node/MarkdownContainer-IWMLWI6Z.cjs +482 -0
  12. package/dist/lib/node/MarkdownContainer-IWMLWI6Z.cjs.map +7 -0
  13. package/dist/lib/node/{chunk-RVNU7MRZ.cjs → chunk-HVDIBL5H.cjs} +9 -6
  14. package/dist/lib/node/chunk-HVDIBL5H.cjs.map +7 -0
  15. package/dist/lib/node/chunk-L2FIDO4L.cjs +72 -0
  16. package/dist/lib/node/chunk-L2FIDO4L.cjs.map +7 -0
  17. package/dist/lib/node/index.cjs +76 -119
  18. package/dist/lib/node/index.cjs.map +4 -4
  19. package/dist/lib/node/meta.json +1 -1
  20. package/dist/lib/node/types/index.cjs +7 -5
  21. package/dist/lib/node/types/index.cjs.map +2 -2
  22. package/dist/lib/node-esm/MarkdownContainer-MLTDWWGB.mjs +468 -0
  23. package/dist/lib/node-esm/MarkdownContainer-MLTDWWGB.mjs.map +7 -0
  24. package/dist/lib/node-esm/{chunk-3CHPA7Y7.mjs → chunk-4AGP7IJE.mjs} +3 -1
  25. package/dist/lib/node-esm/chunk-4AGP7IJE.mjs.map +7 -0
  26. package/dist/lib/node-esm/chunk-YOIARYUO.mjs +51 -0
  27. package/dist/lib/node-esm/chunk-YOIARYUO.mjs.map +7 -0
  28. package/dist/lib/node-esm/index.mjs +54 -101
  29. package/dist/lib/node-esm/index.mjs.map +4 -4
  30. package/dist/lib/node-esm/meta.json +1 -1
  31. package/dist/lib/node-esm/types/index.mjs +5 -3
  32. package/dist/types/src/MarkdownPlugin.d.ts.map +1 -1
  33. package/dist/types/src/components/MarkdownContainer.d.ts +15 -0
  34. package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -0
  35. package/dist/types/src/components/MarkdownEditor.d.ts +7 -2
  36. package/dist/types/src/components/MarkdownEditor.d.ts.map +1 -1
  37. package/dist/types/src/components/MarkdownEditor.stories.d.ts +3 -3
  38. package/dist/types/src/components/MarkdownEditor.stories.d.ts.map +1 -1
  39. package/dist/types/src/components/index.d.ts +2 -11
  40. package/dist/types/src/components/index.d.ts.map +1 -1
  41. package/dist/types/src/extensions.d.ts +11 -15
  42. package/dist/types/src/extensions.d.ts.map +1 -1
  43. package/dist/types/src/types/document.d.ts +8 -1
  44. package/dist/types/src/types/document.d.ts.map +1 -1
  45. package/dist/types/src/types/types.d.ts +1 -1
  46. package/dist/types/src/types/types.d.ts.map +1 -1
  47. package/package.json +32 -31
  48. package/src/MarkdownPlugin.tsx +31 -86
  49. package/src/components/MarkdownContainer.tsx +108 -0
  50. package/src/components/MarkdownEditor.stories.tsx +11 -5
  51. package/src/components/MarkdownEditor.tsx +25 -35
  52. package/src/components/index.ts +2 -14
  53. package/src/extensions.tsx +124 -67
  54. package/src/types/document.ts +12 -0
  55. package/src/types/types.ts +2 -1
  56. package/dist/lib/browser/DocumentCard-RKENHCNE.mjs +0 -11
  57. package/dist/lib/browser/DocumentCard-RKENHCNE.mjs.map +0 -7
  58. package/dist/lib/browser/DocumentEditor-JZI4663Q.mjs +0 -11
  59. package/dist/lib/browser/DocumentEditor-JZI4663Q.mjs.map +0 -7
  60. package/dist/lib/browser/MarkdownEditor-5EUT7P66.mjs +0 -10
  61. package/dist/lib/browser/MarkdownEditor-5EUT7P66.mjs.map +0 -7
  62. package/dist/lib/browser/chunk-354DCID5.mjs +0 -117
  63. package/dist/lib/browser/chunk-354DCID5.mjs.map +0 -7
  64. package/dist/lib/browser/chunk-AFWT6Z7B.mjs +0 -86
  65. package/dist/lib/browser/chunk-AFWT6Z7B.mjs.map +0 -7
  66. package/dist/lib/browser/chunk-AVGUYNPR.mjs +0 -220
  67. package/dist/lib/browser/chunk-AVGUYNPR.mjs.map +0 -7
  68. package/dist/lib/browser/chunk-N7FS4CK2.mjs.map +0 -7
  69. package/dist/lib/browser/chunk-QIO7ZONH.mjs +0 -164
  70. package/dist/lib/browser/chunk-QIO7ZONH.mjs.map +0 -7
  71. package/dist/lib/node/DocumentCard-ECPKHJT7.cjs +0 -32
  72. package/dist/lib/node/DocumentCard-ECPKHJT7.cjs.map +0 -7
  73. package/dist/lib/node/DocumentEditor-QPH26VQZ.cjs +0 -29
  74. package/dist/lib/node/DocumentEditor-QPH26VQZ.cjs.map +0 -7
  75. package/dist/lib/node/MarkdownEditor-ZD4W4UJ6.cjs +0 -31
  76. package/dist/lib/node/MarkdownEditor-ZD4W4UJ6.cjs.map +0 -7
  77. package/dist/lib/node/chunk-2T7LIVPT.cjs +0 -246
  78. package/dist/lib/node/chunk-2T7LIVPT.cjs.map +0 -7
  79. package/dist/lib/node/chunk-5ZWOR7JF.cjs +0 -114
  80. package/dist/lib/node/chunk-5ZWOR7JF.cjs.map +0 -7
  81. package/dist/lib/node/chunk-KDXONMFH.cjs +0 -189
  82. package/dist/lib/node/chunk-KDXONMFH.cjs.map +0 -7
  83. package/dist/lib/node/chunk-KTYIOXL5.cjs +0 -149
  84. package/dist/lib/node/chunk-KTYIOXL5.cjs.map +0 -7
  85. package/dist/lib/node/chunk-RVNU7MRZ.cjs.map +0 -7
  86. package/dist/lib/node-esm/DocumentCard-RHVY4Z6L.mjs +0 -12
  87. package/dist/lib/node-esm/DocumentCard-RHVY4Z6L.mjs.map +0 -7
  88. package/dist/lib/node-esm/DocumentEditor-6F5ONTIO.mjs +0 -12
  89. package/dist/lib/node-esm/DocumentEditor-6F5ONTIO.mjs.map +0 -7
  90. package/dist/lib/node-esm/MarkdownEditor-OS7YG6MA.mjs +0 -11
  91. package/dist/lib/node-esm/MarkdownEditor-OS7YG6MA.mjs.map +0 -7
  92. package/dist/lib/node-esm/chunk-3CHPA7Y7.mjs.map +0 -7
  93. package/dist/lib/node-esm/chunk-NZ4H6J5H.mjs +0 -165
  94. package/dist/lib/node-esm/chunk-NZ4H6J5H.mjs.map +0 -7
  95. package/dist/lib/node-esm/chunk-OE3CMI5X.mjs +0 -87
  96. package/dist/lib/node-esm/chunk-OE3CMI5X.mjs.map +0 -7
  97. package/dist/lib/node-esm/chunk-QARGBUBS.mjs +0 -221
  98. package/dist/lib/node-esm/chunk-QARGBUBS.mjs.map +0 -7
  99. package/dist/lib/node-esm/chunk-RO5FEVW6.mjs +0 -119
  100. package/dist/lib/node-esm/chunk-RO5FEVW6.mjs.map +0 -7
  101. package/dist/types/src/components/DocumentCard.d.ts +0 -16
  102. package/dist/types/src/components/DocumentCard.d.ts.map +0 -1
  103. package/dist/types/src/components/DocumentEditor.d.ts +0 -14
  104. package/dist/types/src/components/DocumentEditor.d.ts.map +0 -1
  105. package/dist/types/src/components/HeadingMenu.d.ts +0 -13
  106. package/dist/types/src/components/HeadingMenu.d.ts.map +0 -1
  107. package/dist/types/src/components/Layout.d.ts +0 -6
  108. package/dist/types/src/components/Layout.d.ts.map +0 -1
  109. package/src/components/DocumentCard.tsx +0 -107
  110. package/src/components/DocumentEditor.tsx +0 -142
  111. package/src/components/HeadingMenu.tsx +0 -46
  112. package/src/components/Layout.tsx +0 -27
@@ -3,10 +3,9 @@
3
3
  //
4
4
 
5
5
  import { TextAa } from '@phosphor-icons/react';
6
- import React, { type Ref } from 'react';
6
+ import React from 'react';
7
7
 
8
8
  import {
9
- isObject,
10
9
  parseIntentPlugin,
11
10
  resolvePlugin,
12
11
  LayoutAction,
@@ -33,12 +32,11 @@ import {
33
32
  EditorViewModes,
34
33
  translations as editorTranslations,
35
34
  } from '@dxos/react-ui-editor';
36
- import { isTileComponentProps } from '@dxos/react-ui-mosaic';
37
35
 
38
- import { type DocumentItemProps, DocumentCard, DocumentEditor, MarkdownEditor, MarkdownSettings } from './components';
36
+ import { MarkdownContainer, MarkdownSettings } from './components';
39
37
  import meta, { MARKDOWN_PLUGIN } from './meta';
40
38
  import translations from './translations';
41
- import { DocumentType, TextType } from './types';
39
+ import { DocumentType, isEditorModel, TextType } from './types';
42
40
  import {
43
41
  type MarkdownPluginProvides,
44
42
  type MarkdownSettingsProps,
@@ -47,19 +45,8 @@ import {
47
45
  } from './types';
48
46
  import { markdownExtensionPlugins, serializer } from './util';
49
47
 
50
- /**
51
- * Checks if an object conforms to the interface needed to render an editor.
52
- */
53
- const isEditorModel = (data: any): data is { id: string; text: string } => {
54
- return (
55
- data &&
56
- typeof data === 'object' &&
57
- 'id' in data &&
58
- typeof data.id === 'string' &&
59
- 'text' in data &&
60
- typeof data.text === 'string'
61
- );
62
- };
48
+ // TODO(burdon): Normalize active/object.
49
+ const getDoc = (object: any) => (object instanceof DocumentType ? object : undefined);
63
50
 
64
51
  export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
65
52
  const settings = new LocalStorageStore<MarkdownSettingsProps>(MARKDOWN_PLUGIN, {
@@ -71,13 +58,8 @@ export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
71
58
 
72
59
  const state = new LocalStorageStore<MarkdownPluginState>(MARKDOWN_PLUGIN, { extensionProviders: [], viewMode: {} });
73
60
 
74
- const getViewMode = (id?: string) => {
75
- return (id && state.values.viewMode[id]) || settings.values.defaultViewMode;
76
- };
77
-
78
- const setViewMode = (id: string, nextViewMode: EditorViewMode) => {
79
- state.values.viewMode[id] = nextViewMode;
80
- };
61
+ const getViewMode = (id: string) => (id && state.values.viewMode[id]) || settings.values.defaultViewMode;
62
+ const setViewMode = (id: string, viewMode: EditorViewMode) => (state.values.viewMode[id] = viewMode);
81
63
 
82
64
  return {
83
65
  meta,
@@ -108,7 +90,7 @@ export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
108
90
 
109
91
  markdownExtensionPlugins(plugins).forEach((plugin) => {
110
92
  const { extensions } = plugin.provides.markdown;
111
- state.values.extensionProviders.push(extensions);
93
+ state.values.extensionProviders?.push(extensions);
112
94
  });
113
95
  },
114
96
  provides: {
@@ -267,71 +249,34 @@ export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
267
249
  },
268
250
  },
269
251
  surface: {
270
- component: ({ data, role, ...props }, forwardedRef) => {
271
- const doc =
272
- data.active instanceof DocumentType
273
- ? data.active
274
- : data.object instanceof DocumentType
275
- ? data.object
276
- : undefined;
277
-
252
+ component: ({ data, role }) => {
278
253
  switch (role) {
279
254
  case 'section':
280
255
  case 'article': {
281
- if (doc && doc.content) {
282
- return (
283
- <DocumentEditor
284
- role={role}
285
- coordinate={data.coordinate as LayoutCoordinate}
286
- document={doc}
287
- extensionProviders={state.values.extensionProviders}
288
- settings={settings.values}
289
- scrollPastEnd
290
- viewMode={getViewMode(fullyQualifiedId(doc))}
291
- onViewModeChange={setViewMode}
292
- />
293
- );
294
- } else if (isEditorModel(data.object)) {
295
- return (
296
- <MarkdownEditor
297
- id={data.object.id}
298
- role={role}
299
- coordinate={data.coordinate as LayoutCoordinate}
300
- initialValue={data.object.text}
301
- extensionProviders={state.values.extensionProviders}
302
- inputMode={settings.values.editorInputMode}
303
- toolbar={settings.values.toolbar}
304
- scrollPastEnd
305
- viewMode={getViewMode(data.object.id)}
306
- onViewModeChange={setViewMode}
307
- />
308
- );
309
- }
310
- break;
311
- }
312
-
313
- case 'card': {
314
- if (
315
- isObject(data.content) &&
316
- typeof data.content.id === 'string' &&
317
- data.content.object instanceof DocumentType
318
- ) {
319
- // isTileComponentProps is a type guard for these props.
320
- // `props` will not pass this guard without transforming `data` into `item`.
321
- const cardProps = {
322
- ...props,
323
- item: {
324
- id: data.content.id,
325
- object: data.content.object,
326
- color: typeof data.content.color === 'string' ? data.content.color : undefined,
327
- } as DocumentItemProps,
328
- };
256
+ // TODO(burdon): Normalize types (from FilesPlugin).
257
+ const doc = getDoc(data.active) ?? getDoc(data.object);
258
+ const { id, object } = isEditorModel(data.object)
259
+ ? { id: data.object.id, object: data.object }
260
+ : doc
261
+ ? { id: fullyQualifiedId(doc), object: doc }
262
+ : {};
329
263
 
330
- return isTileComponentProps(cardProps) ? (
331
- <DocumentCard {...cardProps} settings={settings.values} ref={forwardedRef as Ref<HTMLDivElement>} />
332
- ) : null;
264
+ if (!id || !object) {
265
+ return null;
333
266
  }
334
- break;
267
+
268
+ return (
269
+ <MarkdownContainer
270
+ id={id}
271
+ object={object}
272
+ role={role}
273
+ coordinate={data.coordinate as LayoutCoordinate}
274
+ settings={settings.values}
275
+ extensionProviders={state.values.extensionProviders}
276
+ viewMode={getViewMode(id)}
277
+ onViewModeChange={setViewMode}
278
+ />
279
+ );
335
280
  }
336
281
 
337
282
  case 'settings': {
@@ -0,0 +1,108 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useEffect, useMemo } from 'react';
6
+
7
+ import { useResolvePlugin, parseFileManagerPlugin } from '@dxos/app-framework';
8
+ import { fullyQualifiedId, getSpace } from '@dxos/react-client/echo';
9
+ import { localStorageStateStoreAdapter, type EditorSelectionState } from '@dxos/react-ui-editor';
10
+
11
+ import { MarkdownEditor, type MarkdownEditorProps } from './MarkdownEditor';
12
+ import { useExtensions } from '../extensions';
13
+ import { DocumentType, type MarkdownSettingsProps } from '../types';
14
+ import { getFallbackName } from '../util';
15
+
16
+ export type MarkdownContainerProps = Pick<
17
+ MarkdownEditorProps,
18
+ 'role' | 'coordinate' | 'extensionProviders' | 'viewMode' | 'onViewModeChange'
19
+ > & {
20
+ id: string;
21
+ object: DocumentType | any;
22
+ settings: MarkdownSettingsProps;
23
+ };
24
+
25
+ // TODO(burdon): Factor out difference for ECHO and non-ECHO objects; i.e., single component.
26
+ const MarkdownContainer = ({ role, id, object, settings, ...props }: MarkdownContainerProps) => {
27
+ const scrollPastEnd = role === 'article';
28
+ if (object instanceof DocumentType) {
29
+ return (
30
+ <DocumentEditor
31
+ id={fullyQualifiedId(object)}
32
+ document={object}
33
+ settings={settings}
34
+ scrollPastEnd={scrollPastEnd}
35
+ {...props}
36
+ />
37
+ );
38
+ } else {
39
+ return (
40
+ <MarkdownEditor
41
+ id={id}
42
+ initialValue={object.text}
43
+ toolbar={settings.toolbar}
44
+ scrollPastEnd={scrollPastEnd}
45
+ {...props}
46
+ />
47
+ );
48
+ }
49
+ };
50
+
51
+ type DocumentEditorProps = Omit<MarkdownContainerProps, 'object'> & { document: DocumentType } & Pick<
52
+ MarkdownEditorProps,
53
+ 'id' | 'scrollPastEnd'
54
+ >;
55
+
56
+ export const DocumentEditor = ({
57
+ id,
58
+ document: doc,
59
+ extensionProviders,
60
+ settings,
61
+ viewMode,
62
+ ...props
63
+ }: DocumentEditorProps) => {
64
+ const space = getSpace(doc);
65
+ const initialValue = useMemo(() => doc.content?.content, [doc.content]);
66
+ const extensions = useExtensions({ extensionProviders, document: doc, settings, viewMode });
67
+
68
+ // Migrate gradually to `fallbackName`.
69
+ useEffect(() => {
70
+ if (!doc.fallbackName && doc.content?.content) {
71
+ doc.fallbackName = getFallbackName(doc.content.content);
72
+ }
73
+ }, [doc, doc.content]);
74
+
75
+ // Restore last selection and scroll point.
76
+ const { scrollTo, selection } = useMemo<EditorSelectionState>(
77
+ () => localStorageStateStoreAdapter.getState(id) ?? {},
78
+ [id, doc],
79
+ );
80
+
81
+ // File dragging.
82
+ const fileManagerPlugin = useResolvePlugin(parseFileManagerPlugin);
83
+ const handleFileUpload = useMemo(() => {
84
+ if (space === undefined || fileManagerPlugin?.provides.file.upload === undefined) {
85
+ return undefined;
86
+ }
87
+
88
+ // TODO(burdon): Re-order props: space, file.
89
+ return async (file: File) => fileManagerPlugin?.provides?.file?.upload?.(file, space);
90
+ }, [space, fileManagerPlugin]);
91
+
92
+ return (
93
+ <MarkdownEditor
94
+ id={id}
95
+ initialValue={initialValue}
96
+ extensions={extensions}
97
+ scrollTo={scrollTo}
98
+ selection={selection}
99
+ toolbar={settings.toolbar}
100
+ inputMode={settings.editorInputMode}
101
+ viewMode={viewMode}
102
+ onFileUpload={handleFileUpload}
103
+ {...props}
104
+ />
105
+ );
106
+ };
107
+
108
+ export default MarkdownContainer;
@@ -3,13 +3,15 @@
3
3
  //
4
4
 
5
5
  import '@dxos-theme';
6
+
6
7
  import React, { useMemo, type FC } from 'react';
7
8
 
8
9
  import { createDocAccessor, createEchoObject } from '@dxos/react-client/echo';
9
- import { automerge } from '@dxos/react-ui-editor';
10
+ import { Main } from '@dxos/react-ui';
11
+ import { editorWithToolbarLayout, automerge } from '@dxos/react-ui-editor';
12
+ import { topbarBlockPaddingStart } from '@dxos/react-ui-theme';
10
13
  import { withLayout, withTheme } from '@dxos/storybook-utils';
11
14
 
12
- import { MainLayout } from './Layout';
13
15
  import { MarkdownEditor } from './MarkdownEditor';
14
16
 
15
17
  const Story: FC<{
@@ -20,9 +22,13 @@ const Story: FC<{
20
22
  const extensions = useMemo(() => [automerge(createDocAccessor(doc, ['content']))], [doc]);
21
23
 
22
24
  return (
23
- <MainLayout toolbar={toolbar}>
25
+ <Main.Content
26
+ bounce
27
+ data-toolbar={toolbar ? 'enabled' : 'disabled'}
28
+ classNames={[topbarBlockPaddingStart, editorWithToolbarLayout]}
29
+ >
24
30
  <MarkdownEditor id='test' initialValue={doc.content} extensions={extensions} toolbar={toolbar} />
25
- </MainLayout>
31
+ </Main.Content>
26
32
  );
27
33
  };
28
34
 
@@ -30,8 +36,8 @@ export default {
30
36
  title: 'plugin-markdown/EditorMain',
31
37
  component: MarkdownEditor,
32
38
  decorators: [withTheme, withLayout({ tooltips: true })],
33
- render: Story,
34
39
  parameters: { layout: 'fullscreen' },
40
+ render: Story,
35
41
  };
36
42
 
37
43
  const content = Array.from({ length: 100 })
@@ -6,35 +6,28 @@ import { openSearchPanel } from '@codemirror/search';
6
6
  import { type EditorView } from '@codemirror/view';
7
7
  import React, { useMemo, useEffect, useCallback } from 'react';
8
8
 
9
- import {
10
- type FileInfo,
11
- LayoutAction,
12
- type LayoutCoordinate,
13
- useResolvePlugin,
14
- parseLayoutPlugin,
15
- useIntentDispatcher,
16
- } from '@dxos/app-framework';
9
+ import { type FileInfo, LayoutAction, type LayoutCoordinate, useIntentDispatcher } from '@dxos/app-framework';
17
10
  import { useThemeContext, useTranslation } from '@dxos/react-ui';
18
- import { useIsDirectlyAttended } from '@dxos/react-ui-attention';
11
+ import { useAttendableAttributes, useAttention } from '@dxos/react-ui-attention';
19
12
  import {
20
13
  type Action,
21
14
  type DNDOptions,
22
15
  type EditorViewMode,
23
16
  type EditorInputMode,
24
- type UseTextEditorProps,
25
17
  Toolbar,
18
+ type UseTextEditorProps,
26
19
  createBasicExtensions,
27
20
  createMarkdownExtensions,
28
21
  createThemeExtensions,
29
22
  dropFile,
23
+ editorContent,
24
+ editorGutter,
30
25
  processAction,
31
26
  useActionHandler,
32
27
  useCommentState,
33
28
  useCommentClickListener,
34
29
  useFormattingState,
35
30
  useTextEditor,
36
- editorContent,
37
- editorGutter,
38
31
  } from '@dxos/react-ui-editor';
39
32
  import { sectionToolbarLayout } from '@dxos/react-ui-stack';
40
33
  import { textBlockWidth, focusRing, mx } from '@dxos/react-ui-theme';
@@ -44,19 +37,13 @@ import { useSelectCurrentThread } from '../hooks';
44
37
  import { MARKDOWN_PLUGIN } from '../meta';
45
38
  import { type MarkdownPluginState } from '../types';
46
39
 
47
- // TODO(Zan): Factor into a shared location.
48
- const attentionFragment = mx(
49
- 'group-focus-within/editor:attention-surface group-[[aria-current]]/editor:attention-surface',
50
- 'group-focus-within/editor:border-separator',
51
- );
52
-
53
40
  const DEFAULT_VIEW_MODE: EditorViewMode = 'preview';
54
41
 
55
42
  export type MarkdownEditorProps = {
56
43
  id: string;
44
+ role?: string;
57
45
  coordinate?: LayoutCoordinate;
58
46
  inputMode?: EditorInputMode;
59
- role?: string;
60
47
  scrollPastEnd?: boolean;
61
48
  toolbar?: boolean;
62
49
  viewMode?: EditorViewMode;
@@ -65,6 +52,12 @@ export type MarkdownEditorProps = {
65
52
  } & Pick<UseTextEditorProps, 'initialValue' | 'scrollTo' | 'selection' | 'extensions'> &
66
53
  Partial<Pick<MarkdownPluginState, 'extensionProviders'>>;
67
54
 
55
+ /**
56
+ * Base markdown editor component.
57
+ *
58
+ * This component provides all the features of the markdown editor that do no depend on ECHO.
59
+ * This allows it to be used as a common editor for markdown content on arbitrary backends (e.g. files).
60
+ */
68
61
  export const MarkdownEditor = ({
69
62
  id,
70
63
  role = 'article',
@@ -82,13 +75,13 @@ export const MarkdownEditor = ({
82
75
  const { t } = useTranslation(MARKDOWN_PLUGIN);
83
76
  const { themeMode } = useThemeContext();
84
77
  const dispatch = useIntentDispatcher();
85
- const layoutPlugin = useResolvePlugin(parseLayoutPlugin);
86
78
  const [formattingState, formattingObserver] = useFormattingState();
87
- const isDirectlyAttended = useIsDirectlyAttended(id);
79
+ const attendableAttributes = useAttendableAttributes(id);
80
+ const { hasAttention } = useAttention(id);
88
81
 
89
82
  // Extensions from other plugins.
83
+ // TODO(burdon): Reconcile with DocumentEditor.useExtensions.
90
84
  const providerExtensions = useMemo(
91
- // TODO(burdon): Must pass object to provider.
92
85
  () => extensionProviders?.flatMap((provider) => provider({})).filter(nonNullable),
93
86
  [extensionProviders],
94
87
  );
@@ -141,7 +134,7 @@ export const MarkdownEditor = ({
141
134
  scrollTo,
142
135
  selection,
143
136
  // TODO(wittjosiah): Autofocus based on layout is racy.
144
- autoFocus: layoutPlugin?.provides.layout ? layoutPlugin?.provides.layout.scrollIntoView === id : true,
137
+ // autoFocus: layoutPlugin?.provides.layout ? layoutPlugin?.provides.layout.scrollIntoView === id : true,
145
138
  moveToEndOfLine: true,
146
139
  }),
147
140
  }),
@@ -177,25 +170,24 @@ export const MarkdownEditor = ({
177
170
  {...(role === 'section'
178
171
  ? { className: 'flex flex-col' }
179
172
  : {
180
- className: 'contents group/editor',
181
- ...(isDirectlyAttended && { 'aria-current': 'location' }),
173
+ className: 'contents',
174
+ // TODO(wittjosiah): Factor out to `useAttendableAttributes`?
175
+ ...(hasAttention && { 'aria-current': 'location' }),
176
+ ...attendableAttributes,
182
177
  })}
183
178
  >
184
179
  {toolbar && (
185
- <div role='none' className={mx('flex shrink-0 justify-center overflow-x-auto', attentionFragment)}>
180
+ <div role='none' className='flex shrink-0 justify-center overflow-x-auto attention-surface'>
186
181
  <Toolbar.Root
187
182
  classNames={
188
183
  role === 'section'
189
184
  ? [
190
185
  textBlockWidth,
191
186
  'z-[2] group-focus-within/section:visible',
192
- !isDirectlyAttended && 'invisible',
187
+ !hasAttention && 'invisible',
193
188
  sectionToolbarLayout,
194
189
  ]
195
- : [
196
- textBlockWidth,
197
- 'group-focus-within/editor:border-separator group-[[aria-current]]/editor:border-separator',
198
- ]
190
+ : [textBlockWidth]
199
191
  }
200
192
  state={formattingState && { ...formattingState, ...commentsState }}
201
193
  onAction={handleAction}
@@ -219,8 +211,8 @@ export const MarkdownEditor = ({
219
211
  : mx(
220
212
  'flex is-full bs-full overflow-hidden',
221
213
  focusRing,
222
- attentionFragment,
223
- 'focus-visible:ring-inset',
214
+ 'focus-visible:ring-inset attention-surface',
215
+ 'p-0.5', // TODO(burdon): Handle padding for focusRing consistently.
224
216
  'data-[toolbar=disabled]:pbs-2 data-[toolbar=disabled]:row-span-2',
225
217
  )
226
218
  }
@@ -240,5 +232,3 @@ const useTest = (view?: EditorView) => {
240
232
  }
241
233
  }, [view]);
242
234
  };
243
-
244
- export default MarkdownEditor;
@@ -2,20 +2,8 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import React, { type LazyExoticComponent } from 'react';
5
+ import React 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
- // Lazily load components for content surfaces.
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 = React.lazy(() => import('./MarkdownContainer'));