@dxos/plugin-markdown 0.8.4-main.5ea62a8 → 0.8.4-main.ae835ea

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 (206) hide show
  1. package/dist/lib/browser/MarkdownCard-SLM6QZYC.mjs +12 -0
  2. package/dist/lib/browser/MarkdownContainer-UZFQC6XY.mjs +15 -0
  3. package/dist/lib/browser/{anchor-sort-E33BSTYF.mjs → anchor-sort-4XPPLMZS.mjs} +5 -5
  4. package/dist/lib/browser/{anchor-sort-E33BSTYF.mjs.map → anchor-sort-4XPPLMZS.mjs.map} +1 -1
  5. package/dist/lib/browser/{app-graph-serializer-OX62DNPT.mjs → app-graph-serializer-QQFV4K6P.mjs} +8 -8
  6. package/dist/lib/browser/app-graph-serializer-QQFV4K6P.mjs.map +7 -0
  7. package/dist/lib/browser/blueprint-definition-BC5R3T72.mjs +11 -0
  8. package/dist/lib/browser/blueprint-definition-BC5R3T72.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-LAVZ2W6X.mjs → chunk-2LLVTQCK.mjs} +6 -5
  10. package/dist/lib/browser/chunk-2LLVTQCK.mjs.map +7 -0
  11. package/dist/lib/browser/{MarkdownCard-JLUQITYK.mjs → chunk-3VILQLA4.mjs} +46 -31
  12. package/dist/lib/browser/chunk-3VILQLA4.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-5AYTOIUF.mjs +822 -0
  14. package/dist/lib/browser/chunk-5AYTOIUF.mjs.map +7 -0
  15. package/dist/lib/browser/{chunk-Z7P6JGGW.mjs → chunk-A3CQYGCN.mjs} +7 -4
  16. package/dist/lib/browser/{chunk-Z7P6JGGW.mjs.map → chunk-A3CQYGCN.mjs.map} +2 -2
  17. package/dist/lib/browser/{chunk-ODB2PTBP.mjs → chunk-BQTYJOFB.mjs} +4 -4
  18. package/dist/lib/browser/chunk-BQTYJOFB.mjs.map +7 -0
  19. package/dist/lib/browser/chunk-GLEYXJX3.mjs +22 -0
  20. package/dist/lib/browser/{chunk-D7UYVHL6.mjs.map → chunk-GLEYXJX3.mjs.map} +3 -3
  21. package/dist/lib/browser/{chunk-OY6CGPOO.mjs → chunk-IBCHVMZW.mjs} +2 -2
  22. package/dist/lib/browser/{chunk-OY6CGPOO.mjs.map → chunk-IBCHVMZW.mjs.map} +2 -2
  23. package/dist/lib/browser/{chunk-ZVVKLB5L.mjs → chunk-JAETS5LE.mjs} +33 -48
  24. package/dist/lib/browser/chunk-JAETS5LE.mjs.map +7 -0
  25. package/dist/lib/browser/{chunk-BEE7VQPU.mjs → chunk-UKTCPHLI.mjs} +9 -8
  26. package/dist/lib/browser/chunk-UKTCPHLI.mjs.map +7 -0
  27. package/dist/lib/browser/index.mjs +21 -13
  28. package/dist/lib/browser/index.mjs.map +3 -3
  29. package/dist/lib/browser/{intent-resolver-WDDH56JC.mjs → intent-resolver-VQGMBNXZ.mjs} +7 -7
  30. package/dist/lib/browser/intent-resolver-VQGMBNXZ.mjs.map +7 -0
  31. package/dist/lib/browser/meta.json +1 -1
  32. package/dist/lib/browser/{react-surface-LN2XK2UN.mjs → react-surface-WOMJOPJE.mjs} +52 -50
  33. package/dist/lib/browser/react-surface-WOMJOPJE.mjs.map +7 -0
  34. package/dist/lib/browser/{settings-AABBTB4Q.mjs → settings-LBXJHVBU.mjs} +5 -5
  35. package/dist/lib/browser/{settings-AABBTB4Q.mjs.map → settings-LBXJHVBU.mjs.map} +1 -1
  36. package/dist/lib/browser/{state-FTHQQX7V.mjs → state-BTUKVZHY.mjs} +5 -5
  37. package/dist/lib/browser/{state-FTHQQX7V.mjs.map → state-BTUKVZHY.mjs.map} +1 -1
  38. package/dist/lib/browser/toolkit-YPIVDB4A.mjs +66 -0
  39. package/dist/lib/browser/toolkit-YPIVDB4A.mjs.map +7 -0
  40. package/dist/lib/browser/types/index.mjs +2 -2
  41. package/dist/lib/node-esm/MarkdownCard-MCWEFW4F.mjs +13 -0
  42. package/dist/lib/node-esm/MarkdownCard-MCWEFW4F.mjs.map +7 -0
  43. package/dist/lib/node-esm/MarkdownContainer-KAQOK7K5.mjs +16 -0
  44. package/dist/lib/node-esm/MarkdownContainer-KAQOK7K5.mjs.map +7 -0
  45. package/dist/lib/node-esm/{anchor-sort-ALP2NH24.mjs → anchor-sort-4SXYVYXT.mjs} +5 -5
  46. package/dist/lib/node-esm/{anchor-sort-ALP2NH24.mjs.map → anchor-sort-4SXYVYXT.mjs.map} +1 -1
  47. package/dist/lib/node-esm/{app-graph-serializer-56TD3BMX.mjs → app-graph-serializer-KBVRLQN2.mjs} +8 -8
  48. package/dist/lib/node-esm/app-graph-serializer-KBVRLQN2.mjs.map +7 -0
  49. package/dist/lib/node-esm/blueprint-definition-FPNOTEYC.mjs +12 -0
  50. package/dist/lib/node-esm/blueprint-definition-FPNOTEYC.mjs.map +7 -0
  51. package/dist/lib/node-esm/chunk-2Q4WCKWT.mjs +823 -0
  52. package/dist/lib/node-esm/chunk-2Q4WCKWT.mjs.map +7 -0
  53. package/dist/lib/node-esm/{chunk-Y422WR6A.mjs → chunk-DOB2MJAX.mjs} +33 -48
  54. package/dist/lib/node-esm/chunk-DOB2MJAX.mjs.map +7 -0
  55. package/dist/lib/node-esm/chunk-GGKPPGWA.mjs +24 -0
  56. package/dist/lib/node-esm/{chunk-JPXFCBC4.mjs.map → chunk-GGKPPGWA.mjs.map} +3 -3
  57. package/dist/lib/node-esm/{chunk-CB2R4YIY.mjs → chunk-GMMVSXQ6.mjs} +2 -2
  58. package/dist/lib/node-esm/{chunk-CB2R4YIY.mjs.map → chunk-GMMVSXQ6.mjs.map} +2 -2
  59. package/dist/lib/node-esm/{chunk-FXILAQ5F.mjs → chunk-JELROKGD.mjs} +9 -8
  60. package/dist/lib/node-esm/chunk-JELROKGD.mjs.map +7 -0
  61. package/dist/lib/node-esm/{chunk-O6EXWGGS.mjs → chunk-QH4MC5BE.mjs} +6 -5
  62. package/dist/lib/node-esm/chunk-QH4MC5BE.mjs.map +7 -0
  63. package/dist/lib/node-esm/{chunk-VCG2U522.mjs → chunk-SHAMSMKQ.mjs} +4 -4
  64. package/dist/lib/node-esm/chunk-SHAMSMKQ.mjs.map +7 -0
  65. package/dist/lib/node-esm/{chunk-J7A6TUB2.mjs → chunk-SJ2QRGPM.mjs} +7 -4
  66. package/dist/lib/node-esm/{chunk-J7A6TUB2.mjs.map → chunk-SJ2QRGPM.mjs.map} +2 -2
  67. package/dist/lib/node-esm/{MarkdownCard-XL5EVSJ7.mjs → chunk-YYSASY7X.mjs} +46 -31
  68. package/dist/lib/node-esm/chunk-YYSASY7X.mjs.map +7 -0
  69. package/dist/lib/node-esm/index.mjs +21 -13
  70. package/dist/lib/node-esm/index.mjs.map +3 -3
  71. package/dist/lib/node-esm/{intent-resolver-2I5HKCUU.mjs → intent-resolver-Q4XVI5EX.mjs} +7 -7
  72. package/dist/lib/node-esm/intent-resolver-Q4XVI5EX.mjs.map +7 -0
  73. package/dist/lib/node-esm/meta.json +1 -1
  74. package/dist/lib/node-esm/{react-surface-DJGGKYBD.mjs → react-surface-FAMZTAXK.mjs} +52 -50
  75. package/dist/lib/node-esm/react-surface-FAMZTAXK.mjs.map +7 -0
  76. package/dist/lib/node-esm/{settings-CXGR6DH4.mjs → settings-2YRA67H6.mjs} +5 -5
  77. package/dist/lib/node-esm/{settings-CXGR6DH4.mjs.map → settings-2YRA67H6.mjs.map} +1 -1
  78. package/dist/lib/node-esm/{state-NWMQ3XAI.mjs → state-K6EH7SRZ.mjs} +5 -5
  79. package/dist/lib/node-esm/{state-NWMQ3XAI.mjs.map → state-K6EH7SRZ.mjs.map} +1 -1
  80. package/dist/lib/node-esm/toolkit-36BFLIR3.mjs +67 -0
  81. package/dist/lib/node-esm/toolkit-36BFLIR3.mjs.map +7 -0
  82. package/dist/lib/node-esm/types/index.mjs +2 -2
  83. package/dist/types/src/MarkdownPlugin.d.ts +1 -1
  84. package/dist/types/src/MarkdownPlugin.d.ts.map +1 -1
  85. package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -1
  86. package/dist/types/src/capabilities/blueprint-definition.d.ts +1 -1
  87. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -1
  88. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  89. package/dist/types/src/capabilities/index.d.ts +1 -0
  90. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  91. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  92. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  93. package/dist/types/src/capabilities/toolkit.d.ts +20 -0
  94. package/dist/types/src/capabilities/toolkit.d.ts.map +1 -0
  95. package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts.map +1 -1
  96. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts +0 -1
  97. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts.map +1 -1
  98. package/dist/types/src/components/MarkdownContainer.d.ts +8 -12
  99. package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -1
  100. package/dist/types/src/components/MarkdownContainer.stories.d.ts +7 -4
  101. package/dist/types/src/components/MarkdownContainer.stories.d.ts.map +1 -1
  102. package/dist/types/src/components/MarkdownEditor/FileUpload.d.ts +11 -0
  103. package/dist/types/src/components/MarkdownEditor/FileUpload.d.ts.map +1 -0
  104. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts +42 -23
  105. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts.map +1 -1
  106. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts +5 -110
  107. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts.map +1 -1
  108. package/dist/types/src/components/MarkdownEditor/MarkdownEditorContent.d.ts +26 -0
  109. package/dist/types/src/components/MarkdownEditor/MarkdownEditorContent.d.ts.map +1 -0
  110. package/dist/types/src/components/MarkdownEditor/MarkdownEditorToolbar.d.ts +12 -0
  111. package/dist/types/src/components/MarkdownEditor/MarkdownEditorToolbar.d.ts.map +1 -0
  112. package/dist/types/src/components/Suggestions.stories.d.ts +1 -2
  113. package/dist/types/src/components/Suggestions.stories.d.ts.map +1 -1
  114. package/dist/types/src/components/index.d.ts +3 -1
  115. package/dist/types/src/components/index.d.ts.map +1 -1
  116. package/dist/types/src/functions/diff.d.ts.map +1 -1
  117. package/dist/types/src/functions/index.d.ts +0 -1
  118. package/dist/types/src/functions/index.d.ts.map +1 -1
  119. package/dist/types/src/functions/open.d.ts.map +1 -1
  120. package/dist/types/src/hooks/index.d.ts +3 -0
  121. package/dist/types/src/hooks/index.d.ts.map +1 -1
  122. package/dist/types/src/hooks/useExtensions.d.ts +21 -0
  123. package/dist/types/src/hooks/useExtensions.d.ts.map +1 -0
  124. package/dist/types/src/hooks/useLinkQuery.d.ts +4 -0
  125. package/dist/types/src/hooks/useLinkQuery.d.ts.map +1 -0
  126. package/dist/types/src/hooks/usePopoverMenuOptions.d.ts +9 -0
  127. package/dist/types/src/hooks/usePopoverMenuOptions.d.ts.map +1 -0
  128. package/dist/types/src/hooks/useSelectCurrentThread.d.ts +1 -1
  129. package/dist/types/src/hooks/useSelectCurrentThread.d.ts.map +1 -1
  130. package/dist/types/src/translations.d.ts +3 -0
  131. package/dist/types/src/translations.d.ts.map +1 -1
  132. package/dist/types/src/types/Markdown.d.ts +6 -4
  133. package/dist/types/src/types/Markdown.d.ts.map +1 -1
  134. package/dist/types/src/types/MarkdownAction.d.ts +3 -2
  135. package/dist/types/src/types/MarkdownAction.d.ts.map +1 -1
  136. package/dist/types/src/util.d.ts +1 -1
  137. package/dist/types/src/util.d.ts.map +1 -1
  138. package/dist/types/tsconfig.tsbuildinfo +1 -1
  139. package/package.json +58 -55
  140. package/src/MarkdownPlugin.tsx +100 -93
  141. package/src/capabilities/app-graph-serializer.ts +2 -2
  142. package/src/capabilities/artifact-definition.ts +4 -3
  143. package/src/capabilities/blueprint-definition.ts +5 -5
  144. package/src/capabilities/capabilities.ts +1 -0
  145. package/src/capabilities/index.ts +1 -0
  146. package/src/capabilities/intent-resolver.ts +2 -1
  147. package/src/capabilities/react-surface.tsx +35 -57
  148. package/src/capabilities/toolkit.ts +53 -0
  149. package/src/components/MarkdownCard/MarkdownCard.stories.tsx +3 -6
  150. package/src/components/MarkdownCard/MarkdownCard.tsx +42 -32
  151. package/src/components/MarkdownContainer.stories.tsx +75 -38
  152. package/src/components/MarkdownContainer.tsx +84 -218
  153. package/src/components/MarkdownEditor/FileUpload.tsx +63 -0
  154. package/src/components/MarkdownEditor/MarkdownEditor.stories.tsx +56 -35
  155. package/src/components/MarkdownEditor/MarkdownEditor.tsx +220 -272
  156. package/src/components/MarkdownEditor/MarkdownEditorContent.tsx +134 -0
  157. package/src/components/MarkdownEditor/MarkdownEditorToolbar.tsx +63 -0
  158. package/src/components/Suggestions.stories.tsx +28 -27
  159. package/src/components/index.ts +3 -1
  160. package/src/functions/diff.ts +4 -2
  161. package/src/functions/index.ts +0 -1
  162. package/src/functions/open.ts +4 -2
  163. package/src/hooks/index.ts +3 -0
  164. package/src/{extensions.tsx → hooks/useExtensions.tsx} +56 -114
  165. package/src/hooks/useLinkQuery.ts +82 -0
  166. package/src/hooks/usePopoverMenuOptions.ts +71 -0
  167. package/src/hooks/useSelectCurrentThread.tsx +2 -2
  168. package/src/meta.ts +3 -3
  169. package/src/translations.ts +3 -0
  170. package/src/types/Markdown.ts +6 -4
  171. package/src/types/MarkdownAction.ts +1 -1
  172. package/src/util.tsx +9 -2
  173. package/dist/lib/browser/MarkdownCard-JLUQITYK.mjs.map +0 -7
  174. package/dist/lib/browser/MarkdownContainer-7M37DXAD.mjs +0 -781
  175. package/dist/lib/browser/MarkdownContainer-7M37DXAD.mjs.map +0 -7
  176. package/dist/lib/browser/app-graph-serializer-OX62DNPT.mjs.map +0 -7
  177. package/dist/lib/browser/blueprint-definition-Z3RQGWUD.mjs +0 -11
  178. package/dist/lib/browser/chunk-BEE7VQPU.mjs.map +0 -7
  179. package/dist/lib/browser/chunk-D7UYVHL6.mjs +0 -20
  180. package/dist/lib/browser/chunk-LAVZ2W6X.mjs.map +0 -7
  181. package/dist/lib/browser/chunk-ODB2PTBP.mjs.map +0 -7
  182. package/dist/lib/browser/chunk-ZVVKLB5L.mjs.map +0 -7
  183. package/dist/lib/browser/intent-resolver-WDDH56JC.mjs.map +0 -7
  184. package/dist/lib/browser/react-surface-LN2XK2UN.mjs.map +0 -7
  185. package/dist/lib/node-esm/MarkdownCard-XL5EVSJ7.mjs.map +0 -7
  186. package/dist/lib/node-esm/MarkdownContainer-K3BPAGWO.mjs +0 -782
  187. package/dist/lib/node-esm/MarkdownContainer-K3BPAGWO.mjs.map +0 -7
  188. package/dist/lib/node-esm/app-graph-serializer-56TD3BMX.mjs.map +0 -7
  189. package/dist/lib/node-esm/blueprint-definition-735OAX33.mjs +0 -12
  190. package/dist/lib/node-esm/chunk-FXILAQ5F.mjs.map +0 -7
  191. package/dist/lib/node-esm/chunk-JPXFCBC4.mjs +0 -22
  192. package/dist/lib/node-esm/chunk-O6EXWGGS.mjs.map +0 -7
  193. package/dist/lib/node-esm/chunk-VCG2U522.mjs.map +0 -7
  194. package/dist/lib/node-esm/chunk-Y422WR6A.mjs.map +0 -7
  195. package/dist/lib/node-esm/intent-resolver-2I5HKCUU.mjs.map +0 -7
  196. package/dist/lib/node-esm/react-surface-DJGGKYBD.mjs.map +0 -7
  197. package/dist/types/src/components/Toolbar.stories.d.ts +0 -48
  198. package/dist/types/src/components/Toolbar.stories.d.ts.map +0 -1
  199. package/dist/types/src/extensions.d.ts +0 -24
  200. package/dist/types/src/extensions.d.ts.map +0 -1
  201. package/dist/types/src/functions/create.d.ts +0 -12
  202. package/dist/types/src/functions/create.d.ts.map +0 -1
  203. package/src/components/Toolbar.stories.tsx +0 -118
  204. package/src/functions/create.ts +0 -23
  205. /package/dist/lib/browser/{blueprint-definition-Z3RQGWUD.mjs.map → MarkdownCard-SLM6QZYC.mjs.map} +0 -0
  206. /package/dist/lib/{node-esm/blueprint-definition-735OAX33.mjs.map → browser/MarkdownContainer-UZFQC6XY.mjs.map} +0 -0
@@ -0,0 +1,63 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type EditorView } from '@codemirror/view';
6
+ import React, { useCallback, useState } from 'react';
7
+
8
+ import { type FileInfo } from '@dxos/app-framework';
9
+ import { invariant } from '@dxos/invariant';
10
+ import { type ThemedClassName } from '@dxos/react-ui';
11
+ import { EditorToolbar, type EditorToolbarProps, type EditorViewMode } from '@dxos/react-ui-editor';
12
+
13
+ import { FileUpload, type FileUploadAction } from './FileUpload';
14
+
15
+ export type MarkdownEditorToolbarProps = ThemedClassName<
16
+ {
17
+ id: string;
18
+ editorView?: EditorView;
19
+ onFileUpload?: (file: File) => Promise<FileInfo | undefined>;
20
+ } & Pick<EditorToolbarProps, 'role' | 'state' | 'customActions' | 'onViewModeChange'>
21
+ >;
22
+
23
+ export const MarkdownEditorToolbar = ({
24
+ classNames,
25
+ id,
26
+ role,
27
+ state,
28
+ editorView,
29
+ customActions,
30
+ onFileUpload,
31
+ onViewModeChange,
32
+ }: MarkdownEditorToolbarProps) => {
33
+ const [upload, setUpload] = useState<FileUploadAction | null>(null);
34
+ const handleRef = useCallback((next: FileUploadAction) => setUpload(() => next), []);
35
+
36
+ const handleViewModeChange = useCallback((mode: EditorViewMode) => onViewModeChange?.(mode), [onViewModeChange]);
37
+
38
+ const getView = useCallback(() => {
39
+ invariant(editorView);
40
+ return editorView;
41
+ }, [editorView]);
42
+
43
+ if (!editorView) {
44
+ return <div />;
45
+ }
46
+
47
+ return (
48
+ <>
49
+ <EditorToolbar
50
+ classNames={classNames}
51
+ attendableId={id}
52
+ role={role}
53
+ state={state}
54
+ customActions={customActions}
55
+ getView={getView}
56
+ onImageUpload={upload ?? undefined}
57
+ onViewModeChange={handleViewModeChange}
58
+ />
59
+
60
+ {onFileUpload && <FileUpload ref={handleRef} editorView={editorView} onFileUpload={onFileUpload} />}
61
+ </>
62
+ );
63
+ };
@@ -2,24 +2,19 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta } from '@storybook/react-vite';
8
- import { Match, Option, Schema, pipe } from 'effect';
6
+ import * as Function from 'effect/Function';
7
+ import * as Match from 'effect/Match';
8
+ import * as Option from 'effect/Option';
9
+ import * as Schema from 'effect/Schema';
9
10
  import React, { type FC, useEffect, useMemo, useState } from 'react';
10
11
 
11
- import {
12
- Capabilities,
13
- IntentPlugin,
14
- SettingsPlugin,
15
- contributes,
16
- useCapability,
17
- useIntentDispatcher,
18
- } from '@dxos/app-framework';
12
+ import { Capabilities, IntentPlugin, SettingsPlugin, useCapability, useIntentDispatcher } from '@dxos/app-framework';
19
13
  import { withPluginManager } from '@dxos/app-framework/testing';
20
14
  import { Obj, Ref, Type } from '@dxos/echo';
21
15
  import { invariant } from '@dxos/invariant';
22
16
  import { ClientPlugin } from '@dxos/plugin-client';
17
+ import { GraphPlugin } from '@dxos/plugin-graph';
23
18
  import { PreviewPlugin } from '@dxos/plugin-preview';
24
19
  import { SpacePlugin } from '@dxos/plugin-space';
25
20
  import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
@@ -27,11 +22,12 @@ import { ThemePlugin } from '@dxos/plugin-theme';
27
22
  import { faker } from '@dxos/random';
28
23
  import { createDocAccessor, fullyQualifiedId, toCursorRange, useQueue, useSpace } from '@dxos/react-client/echo';
29
24
  import { IconButton, Toolbar } from '@dxos/react-ui';
30
- import { type EditorSelection, type Range, command, useTextEditor } from '@dxos/react-ui-editor';
25
+ import { withTheme } from '@dxos/react-ui/testing';
26
+ import { type EditorSelection, type Range, useTextEditor } from '@dxos/react-ui-editor';
31
27
  import { StackItem } from '@dxos/react-ui-stack';
32
28
  import { defaultTx } from '@dxos/react-ui-theme';
33
29
  import { DataType } from '@dxos/schema';
34
- import { withLayout } from '@dxos/storybook-utils';
30
+ import { render } from '@dxos/storybook-utils';
35
31
 
36
32
  import { MarkdownCapabilities } from '../capabilities';
37
33
  import { MarkdownPlugin } from '../MarkdownPlugin';
@@ -83,7 +79,7 @@ const TestChat: FC<{ doc: Markdown.Document; content: string }> = ({ doc, conten
83
79
 
84
80
  const text = await doc.content.load();
85
81
  const accessor = createDocAccessor(text, ['content']);
86
- const cursor = pipe(
82
+ const cursor = Function.pipe(
87
83
  editorState.getState(fullyQualifiedId(doc))?.selection,
88
84
  Option.fromNullable,
89
85
  Option.map(selectionToRange),
@@ -133,7 +129,7 @@ const DefaultStory = ({ document, chat }: { document: string; chat: string }) =>
133
129
  content: document.replaceAll(/\[(\w+)\]/g, (_, label) => {
134
130
  const obj = space.db.add(Obj.make(TestItem, { title: label, description: faker.lorem.paragraph() }));
135
131
  const dxn = Ref.make(obj).dxn.toString();
136
- return `[${label}][${dxn}]`;
132
+ return `[${label}](${dxn})`;
137
133
  }),
138
134
  }),
139
135
  );
@@ -142,45 +138,50 @@ const DefaultStory = ({ document, chat }: { document: string; chat: string }) =>
142
138
  }, [space]);
143
139
 
144
140
  if (!space || !doc) {
145
- return <></>;
141
+ return null;
146
142
  }
147
143
 
144
+ // TODO(burdon): Layout issue.
148
145
  return (
149
- <>
146
+ <div className='grid grid-cols-2 bs-full overflow-hidden'>
150
147
  <MarkdownContainer id={doc.id} object={doc} settings={settings} editorStateStore={editorState} />
151
148
  <TestChat doc={doc} content={chat} />
152
- </>
149
+ </div>
153
150
  );
154
151
  };
155
152
 
156
- // TODO(burdon): Make consistent.
157
153
  const storybook: Meta<typeof DefaultStory> = {
158
154
  title: 'plugins/plugin-markdown/Suggestions',
159
- render: DefaultStory,
155
+ render: render(DefaultStory),
160
156
  decorators: [
157
+ withTheme,
161
158
  withPluginManager({
162
159
  plugins: [
163
- ThemePlugin({ tx: defaultTx }),
164
- StorybookLayoutPlugin(),
165
160
  ClientPlugin({
166
161
  types: [Markdown.Document, TestItem],
167
162
  onClientInitialized: async ({ client }) => {
168
163
  await client.halo.createIdentity();
169
164
  },
170
165
  }),
171
- SpacePlugin(),
172
- SettingsPlugin(),
166
+ SpacePlugin({}),
167
+ GraphPlugin(),
173
168
  IntentPlugin(),
169
+ SettingsPlugin(),
170
+
171
+ // UI
172
+ ThemePlugin({ tx: defaultTx }),
174
173
  MarkdownPlugin(),
175
174
  PreviewPlugin(),
175
+ StorybookLayoutPlugin({}),
176
176
  ],
177
- capabilities: [contributes(MarkdownCapabilities.Extensions, [() => command()])],
178
177
  }),
179
- withLayout({ fullscreen: true, classNames: 'grid grid-cols-2' }),
180
178
  ],
181
179
  parameters: {
180
+ layout: 'fullscreen',
181
+ controls: {
182
+ disable: true,
183
+ },
182
184
  translations,
183
- controls: { disable: true },
184
185
  },
185
186
  };
186
187
 
@@ -4,7 +4,9 @@
4
4
 
5
5
  import { lazy } from 'react';
6
6
 
7
+ export * from './MarkdownCard';
8
+ export * from './MarkdownContainer';
7
9
  export * from './MarkdownSettings';
8
10
 
9
- export const MarkdownContainer = lazy(() => import('./MarkdownContainer'));
10
11
  export const MarkdownCard = lazy(() => import('./MarkdownCard'));
12
+ export const MarkdownContainer = lazy(() => import('./MarkdownContainer'));
@@ -2,7 +2,8 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Effect, Schema } from 'effect';
5
+ import * as Effect from 'effect/Effect';
6
+ import * as Schema from 'effect/Schema';
6
7
 
7
8
  import { ArtifactId, applyDiffs } from '@dxos/assistant';
8
9
  import { createDocAccessor } from '@dxos/echo-db';
@@ -13,7 +14,8 @@ import { Markdown } from '../types';
13
14
 
14
15
  // TODO(wittjosiah): Reconcile with ThreadAction.AddProposal.
15
16
  export default defineFunction({
16
- name: 'dxos.org/function/markdown/diff',
17
+ key: 'dxos.org/function/markdown/diff',
18
+ name: 'Diff',
17
19
  description: trim`
18
20
  Applies a set of diffs to the markdown document.
19
21
  `,
@@ -2,6 +2,5 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- export { default as create } from './create';
6
5
  export { default as diff } from './diff';
7
6
  export { default as open } from './open';
@@ -2,7 +2,8 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Effect, Schema } from 'effect';
5
+ import * as Effect from 'effect/Effect';
6
+ import * as Schema from 'effect/Schema';
6
7
 
7
8
  import { ArtifactId } from '@dxos/assistant';
8
9
  import { DatabaseService, defineFunction } from '@dxos/functions';
@@ -10,7 +11,8 @@ import { DatabaseService, defineFunction } from '@dxos/functions';
10
11
  import { Markdown } from '../types';
11
12
 
12
13
  export default defineFunction({
13
- name: 'dxos.org/function/markdown/open',
14
+ key: 'dxos.org/function/markdown/open',
15
+ name: 'Open',
14
16
  description: 'Opens and reads the contents of a new markdown document.',
15
17
  inputSchema: Schema.Struct({
16
18
  id: ArtifactId.annotations({
@@ -2,4 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ export * from './useExtensions';
6
+ export * from './useLinkQuery';
7
+ export * from './usePopoverMenuOptions';
5
8
  export * from './useSelectCurrentThread';
@@ -6,21 +6,16 @@ import { type ViewUpdate } from '@codemirror/view';
6
6
  import React, { type AnchorHTMLAttributes, type ReactNode, useMemo } from 'react';
7
7
  import { createRoot } from 'react-dom/client';
8
8
 
9
- import {
10
- LayoutAction,
11
- type PromiseIntentDispatcher,
12
- createIntent,
13
- useCapabilities,
14
- useIntentDispatcher,
15
- } from '@dxos/app-framework';
9
+ import { LayoutAction, type PromiseIntentDispatcher, createIntent, useIntentDispatcher } from '@dxos/app-framework';
16
10
  import { debounceAndThrottle } from '@dxos/async';
11
+ import { Obj } from '@dxos/echo';
17
12
  import { invariant } from '@dxos/invariant';
18
- import { type QueryResult, createDocAccessor, fullyQualifiedId, getSpace } from '@dxos/react-client/echo';
13
+ import { createDocAccessor, fullyQualifiedId } from '@dxos/react-client/echo';
14
+ import { getSpace } from '@dxos/react-client/echo';
19
15
  import { useIdentity } from '@dxos/react-client/halo';
20
16
  import { Icon, ThemeProvider } from '@dxos/react-ui';
21
17
  import { type SelectionManager } from '@dxos/react-ui-attention';
22
18
  import {
23
- type AutocompleteResult,
24
19
  Cursor,
25
20
  type EditorStateStore,
26
21
  EditorView,
@@ -29,7 +24,6 @@ import {
29
24
  InputModeExtensions,
30
25
  type PreviewOptions,
31
26
  type RenderCallback,
32
- autocomplete,
33
27
  createDataExtensions,
34
28
  decorateMarkdown,
35
29
  documentId,
@@ -42,20 +36,19 @@ import {
42
36
  typewriter,
43
37
  } from '@dxos/react-ui-editor';
44
38
  import { defaultTx } from '@dxos/react-ui-theme';
45
- import { type DataType } from '@dxos/schema';
46
- import { isNotFalsy } from '@dxos/util';
39
+ import { DataType } from '@dxos/schema';
40
+ import { isTruthy } from '@dxos/util';
41
+
42
+ import { Markdown } from '../types';
43
+ import { setFallbackName } from '../util';
47
44
 
48
- import { MarkdownCapabilities } from './capabilities';
49
- import { type Markdown } from './types';
50
- import { setFallbackName } from './util';
45
+ export type DocumentType = Markdown.Document | DataType.Text | { id: string; text: string };
51
46
 
52
- type ExtensionsOptions = {
53
- document?: Markdown.Document;
54
- id?: string;
55
- text?: DataType.Text;
47
+ export type ExtensionsOptions = {
48
+ id: string;
49
+ object?: DocumentType;
56
50
  dispatch?: PromiseIntentDispatcher;
57
- query?: QueryResult<Markdown.Document>;
58
- settings: Markdown.Settings;
51
+ settings?: Markdown.Settings;
59
52
  selectionManager?: SelectionManager;
60
53
  viewMode?: EditorViewMode;
61
54
  editorStateStore?: EditorStateStore;
@@ -64,9 +57,8 @@ type ExtensionsOptions = {
64
57
 
65
58
  // TODO(burdon): Merge with createBaseExtensions below.
66
59
  export const useExtensions = ({
67
- document,
68
60
  id,
69
- text,
61
+ object,
70
62
  settings,
71
63
  selectionManager,
72
64
  viewMode,
@@ -75,93 +67,66 @@ export const useExtensions = ({
75
67
  }: ExtensionsOptions): Extension[] => {
76
68
  const { dispatchPromise: dispatch } = useIntentDispatcher();
77
69
  const identity = useIdentity();
78
- const space = getSpace(document) ?? getSpace(text);
70
+ const space = getSpace(object);
71
+
72
+ let target: Obj.Any | undefined;
73
+ if (Obj.instanceOf(Markdown.Document, object)) {
74
+ target = (object as Markdown.Document).content.target;
75
+ } else if (Obj.instanceOf(DataType.Text, object)) {
76
+ target = object;
77
+ }
79
78
 
80
79
  // TODO(wittjosiah): Autocomplete is not working and this query is causing performance issues.
81
80
  // TODO(burdon): Unsubscribe.
82
81
  // const query = space?.db.query(Filter.type(DocumentType));
83
82
  // query?.subscribe();
83
+
84
84
  const baseExtensions = useMemo(
85
85
  () =>
86
86
  createBaseExtensions({
87
- document,
88
87
  id,
89
- text,
88
+ object,
90
89
  settings,
91
90
  selectionManager,
92
91
  viewMode,
93
92
  previewOptions,
94
93
  dispatch,
95
- // query,
96
94
  }),
97
95
  [
98
- document,
99
96
  id,
100
- text,
97
+ object,
101
98
  viewMode,
102
99
  dispatch,
103
100
  previewOptions,
104
101
  settings,
105
- settings.editorInputMode,
106
- settings.folding,
107
- settings.numberedHeadings,
108
- settings.debug,
109
- settings.typewriter,
102
+ settings?.debug,
103
+ settings?.editorInputMode,
104
+ settings?.folding,
105
+ settings?.numberedHeadings,
106
+ settings?.typewriter,
110
107
  selectionManager,
111
108
  ],
112
109
  );
113
110
 
114
- const extensionProviders = useCapabilities(MarkdownCapabilities.Extensions);
115
-
116
- //
117
- // External extensions from other plugins.
118
- //
119
- const pluginExtensions = useMemo<Extension[]>(() => {
120
- if (!document) {
121
- return [];
122
- }
123
-
124
- return extensionProviders.flat().reduce((acc: Extension[], provider) => {
125
- const extension = typeof provider === 'function' ? provider({ document }) : provider;
126
- if (extension) {
127
- acc.push(extension);
128
- }
129
-
130
- return acc;
131
- }, []);
132
- }, [extensionProviders, document]);
133
-
134
- //
135
- // Basic plugins.
136
- //
137
111
  return useMemo<Extension[]>(
138
112
  () =>
139
113
  [
114
+ // TODO(burdon): Pass this in?
140
115
  // NOTE: Data extensions must be first so that automerge is updated before other extensions compute their state.
141
- document &&
142
- createDataExtensions({
143
- id: document.id,
144
- text: document.content.target && createDocAccessor(document.content.target, ['content']),
145
- space,
146
- identity,
147
- }),
148
- text &&
149
- id &&
150
- createDataExtensions({
151
- id,
152
- text: createDocAccessor(text, ['content']),
153
- space,
154
- identity,
155
- }),
156
- selectionState(editorStateStore),
157
- document &&
116
+ target && createDataExtensions({ id, text: createDocAccessor(target, ['content']), space, identity }),
117
+
118
+ // TODO(burdon): Reconcile with effect in parent.
119
+ Obj.instanceOf(Markdown.Document, object) &&
158
120
  listener({
159
- onChange: (text) => setFallbackName(document, text),
121
+ onChange: ({ text }) => {
122
+ setFallbackName(object as Markdown.Document, text);
123
+ },
160
124
  }),
125
+
161
126
  baseExtensions,
162
- pluginExtensions,
163
- ].filter(isNotFalsy),
164
- [baseExtensions, pluginExtensions, document, document?.content?.target, text, id, space, identity],
127
+ selectionState(editorStateStore),
128
+ ].filter(isTruthy),
129
+ [identity, space, id, object, target, baseExtensions],
165
130
  );
166
131
  };
167
132
 
@@ -169,20 +134,19 @@ export const useExtensions = ({
169
134
  * Create extension instances for editor.
170
135
  */
171
136
  const createBaseExtensions = ({
172
- document,
173
137
  id,
138
+ object,
174
139
  dispatch,
175
140
  settings,
176
141
  selectionManager,
177
- query,
178
142
  viewMode,
179
143
  previewOptions,
180
144
  }: ExtensionsOptions): Extension[] => {
181
145
  const extensions: Extension[] = [
182
146
  selectionManager && selectionChange(selectionManager),
183
- settings.editorInputMode && InputModeExtensions[settings.editorInputMode],
184
- settings.folding && folding(),
185
- ].filter(isNotFalsy);
147
+ settings?.editorInputMode && InputModeExtensions[settings.editorInputMode],
148
+ settings?.folding && folding(),
149
+ ].filter(isTruthy);
186
150
 
187
151
  //
188
152
  // Markdown
@@ -193,17 +157,18 @@ const createBaseExtensions = ({
193
157
  formattingKeymap(),
194
158
  decorateMarkdown({
195
159
  selectionChangeDelay: 100,
196
- numberedHeadings: settings.numberedHeadings ? { from: 2 } : undefined,
160
+ numberedHeadings: settings?.numberedHeadings ? { from: 2 } : undefined,
197
161
  // TODO(wittjosiah): For internal links, consider ignoring the link text and rendering the label of the object being linked to.
162
+ // TODO(burdon): Create dx-tag.
198
163
  renderLinkButton:
199
- dispatch && (document || id)
164
+ dispatch && (object || id)
200
165
  ? createLinkRenderer((id: string) => {
201
166
  void dispatch(
202
167
  createIntent(LayoutAction.Open, {
203
168
  part: 'main',
204
169
  subject: [id],
205
170
  options: {
206
- pivotId: document ? fullyQualifiedId(document) : id,
171
+ pivotId: object ? fullyQualifiedId(object) : id,
207
172
  },
208
173
  }),
209
174
  );
@@ -216,31 +181,7 @@ const createBaseExtensions = ({
216
181
  );
217
182
  }
218
183
 
219
- //
220
- // Autocomplete object links.
221
- //
222
- if (query) {
223
- extensions.push(
224
- autocomplete({
225
- onSearch: (text: string) => {
226
- // TODO(burdon): Specify filter (e.g., stack).
227
- return query.objects
228
- .map<AutocompleteResult | undefined>((object) =>
229
- object.name?.length && object.id !== document?.id
230
- ? {
231
- label: object.name,
232
- // TODO(burdon): Factor out URL builder.
233
- apply: `[${object.name}](/${fullyQualifiedId(object)})`,
234
- }
235
- : undefined,
236
- )
237
- .filter(isNotFalsy);
238
- },
239
- }),
240
- );
241
- }
242
-
243
- if (settings.debug) {
184
+ if (settings?.debug) {
244
185
  const items = settings.typewriter?.split(/[,\n]/) ?? '';
245
186
  if (items) {
246
187
  extensions.push(typewriter({ items }));
@@ -250,7 +191,7 @@ const createBaseExtensions = ({
250
191
  return extensions;
251
192
  };
252
193
 
253
- export const selectionChange = (selectionManager: SelectionManager) => {
194
+ const selectionChange = (selectionManager: SelectionManager) => {
254
195
  return EditorView.updateListener.of(
255
196
  debounceAndThrottle((update: ViewUpdate) => {
256
197
  if (update.selectionSet) {
@@ -263,6 +204,7 @@ export const selectionChange = (selectionManager: SelectionManager) => {
263
204
  to: cursorConverter.toCursor(range.to),
264
205
  }))
265
206
  .filter(({ from, to }) => to > from);
207
+
266
208
  selectionManager.updateMultiRange(id, ranges);
267
209
  }
268
210
  }, 100),
@@ -321,8 +263,8 @@ const renderLinkTooltip: RenderCallback<{ url: string }> = (el, { url }) => {
321
263
  );
322
264
  };
323
265
 
324
- // TODO(burdon): Remove react rendering; use DOM directly.
325
- export const renderRoot = <T extends Element>(root: T, node: ReactNode): T => {
266
+ // TODO(burdon): REMOVE.
267
+ const renderRoot = <T extends Element>(root: T, node: ReactNode): T => {
326
268
  createRoot(root).render(<ThemeProvider tx={defaultTx}>{node}</ThemeProvider>);
327
269
  return root;
328
270
  };
@@ -0,0 +1,82 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { useCallback, useMemo } from 'react';
6
+
7
+ import { Capabilities, useCapabilities, usePluginManager } from '@dxos/app-framework';
8
+ import { Filter, Obj, Query, Type } from '@dxos/echo';
9
+ import { ClientCapabilities } from '@dxos/plugin-client';
10
+ import { SpaceCapabilities } from '@dxos/plugin-space';
11
+ import { type Space } from '@dxos/react-client/echo';
12
+ import { toLocalizedString, useTranslation } from '@dxos/react-ui';
13
+ import { type PopoverMenuGroup, type PopoverMenuItem, insertAtCursor, insertAtLineStart } from '@dxos/react-ui-editor';
14
+
15
+ export const useLinkQuery = (space: Space | undefined) => {
16
+ const { t } = useTranslation();
17
+
18
+ const manager = usePluginManager();
19
+ const resolve = useCallback(
20
+ (typename: string) =>
21
+ manager.context.getCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ?? {},
22
+ [manager],
23
+ );
24
+
25
+ const objectForms = useCapabilities(SpaceCapabilities.ObjectForm);
26
+ const schemaWhiteList = useCapabilities(ClientCapabilities.SchemaWhiteList);
27
+ const filter = useMemo(
28
+ () =>
29
+ Filter.or(
30
+ ...objectForms.map((form) => Filter.type(form.objectSchema)),
31
+ ...schemaWhiteList.flat().map((schema) => Filter.typename(Type.getTypename(schema))),
32
+ ),
33
+ [objectForms, schemaWhiteList],
34
+ );
35
+
36
+ const handleLinkQuery = useCallback(
37
+ async (query?: string): Promise<PopoverMenuGroup[]> => {
38
+ const name = query?.startsWith('@') ? query.slice(1).toLowerCase() : (query?.toLowerCase() ?? '');
39
+ const results = await space?.db.query(Query.select(filter)).run();
40
+
41
+ // TODO(wittjosiah): Use `Obj.Any` type.
42
+ const getLabel = (object: any) => {
43
+ const label = Obj.getLabel(object);
44
+ if (label) {
45
+ return label;
46
+ }
47
+
48
+ // TODO(wittjosiah): Remove metadata labels.
49
+ const type = Obj.getTypename(object)!;
50
+ const metadata = resolve(type);
51
+ return metadata.label?.(object) || ['object name placeholder', { ns: type, default: 'New object' }];
52
+ };
53
+
54
+ const items =
55
+ results?.objects
56
+ .filter((object) => toLocalizedString(getLabel(object), t).toLowerCase().includes(name))
57
+ // TODO(wittjosiah): Remove `any` type.
58
+ .map((object: any): PopoverMenuItem => {
59
+ const metadata = resolve(Obj.getTypename(object)!);
60
+ const label = toLocalizedString(getLabel(object), t);
61
+ return {
62
+ id: object.id,
63
+ label,
64
+ icon: metadata.icon,
65
+ onSelect: (view, head) => {
66
+ const link = `[${label}](${Obj.getDXN(object)})`;
67
+ if (query?.startsWith('@')) {
68
+ insertAtLineStart(view, head, `!${link}\n`);
69
+ } else {
70
+ insertAtCursor(view, head, `${link} `);
71
+ }
72
+ },
73
+ };
74
+ }) ?? [];
75
+
76
+ return [{ id: 'echo', items }];
77
+ },
78
+ [space, filter, resolve],
79
+ );
80
+
81
+ return handleLinkQuery;
82
+ };