@dxos/plugin-markdown 0.8.4-main.406dc2a → 0.8.4-main.548089c

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 (224) hide show
  1. package/dist/lib/browser/MarkdownCard-L3BS2SED.mjs +12 -0
  2. package/dist/lib/browser/MarkdownContainer-KF3YL6CI.mjs +15 -0
  3. package/dist/lib/browser/{anchor-sort-YWJI3BKT.mjs → anchor-sort-3HGPGCOO.mjs} +4 -5
  4. package/dist/lib/browser/anchor-sort-3HGPGCOO.mjs.map +7 -0
  5. package/dist/lib/browser/{app-graph-serializer-KYDFCUOW.mjs → app-graph-serializer-POZN234F.mjs} +6 -6
  6. package/dist/lib/browser/app-graph-serializer-POZN234F.mjs.map +7 -0
  7. package/dist/lib/browser/blueprint-definition-GIPKFDY5.mjs +13 -0
  8. package/dist/lib/browser/blueprint-definition-GIPKFDY5.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-O6XUPW6S.mjs → chunk-22XSSNBS.mjs} +7 -4
  10. package/dist/lib/browser/{chunk-O6XUPW6S.mjs.map → chunk-22XSSNBS.mjs.map} +2 -2
  11. package/dist/lib/browser/chunk-2YCH7AOL.mjs +827 -0
  12. package/dist/lib/browser/chunk-2YCH7AOL.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-ODB2PTBP.mjs → chunk-BQTYJOFB.mjs} +4 -4
  14. package/dist/lib/browser/chunk-BQTYJOFB.mjs.map +7 -0
  15. package/dist/lib/browser/{MarkdownCard-AGWOTODZ.mjs → chunk-BU7S64EA.mjs} +36 -24
  16. package/dist/lib/browser/chunk-BU7S64EA.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-6KU5DKP7.mjs → chunk-GH6GQSBL.mjs} +8 -8
  18. package/dist/lib/browser/chunk-GH6GQSBL.mjs.map +7 -0
  19. package/dist/lib/browser/{chunk-OY6CGPOO.mjs → chunk-IBCHVMZW.mjs} +2 -2
  20. package/dist/lib/browser/{chunk-OY6CGPOO.mjs.map → chunk-IBCHVMZW.mjs.map} +2 -2
  21. package/dist/lib/browser/chunk-IGS3RCFE.mjs +20 -0
  22. package/dist/lib/browser/chunk-IGS3RCFE.mjs.map +7 -0
  23. package/dist/lib/browser/{chunk-XMT6PMU5.mjs → chunk-QYSEJ5GP.mjs} +9 -9
  24. package/dist/lib/browser/chunk-QYSEJ5GP.mjs.map +7 -0
  25. package/dist/lib/browser/chunk-Y53FQREH.mjs +150 -0
  26. package/dist/lib/browser/chunk-Y53FQREH.mjs.map +7 -0
  27. package/dist/lib/browser/index.mjs +22 -21
  28. package/dist/lib/browser/index.mjs.map +3 -3
  29. package/dist/lib/browser/{intent-resolver-XHVCZZHU.mjs → intent-resolver-Z5L7TXUK.mjs} +5 -5
  30. package/dist/lib/browser/{intent-resolver-XHVCZZHU.mjs.map → intent-resolver-Z5L7TXUK.mjs.map} +3 -3
  31. package/dist/lib/browser/meta.json +1 -1
  32. package/dist/lib/browser/{react-surface-3A2GO3BN.mjs → react-surface-45NYBFCG.mjs} +56 -54
  33. package/dist/lib/browser/react-surface-45NYBFCG.mjs.map +7 -0
  34. package/dist/lib/browser/{settings-XY265Y2Q.mjs → settings-TZUDB5EW.mjs} +3 -3
  35. package/dist/lib/browser/{state-6QODXCSZ.mjs → state-BTUKVZHY.mjs} +3 -3
  36. package/dist/lib/browser/toolkit.mjs +13 -0
  37. package/dist/lib/browser/toolkit.mjs.map +7 -0
  38. package/dist/lib/browser/types/index.mjs +2 -2
  39. package/dist/lib/node-esm/MarkdownCard-UUQLN6XK.mjs +13 -0
  40. package/dist/lib/node-esm/MarkdownCard-UUQLN6XK.mjs.map +7 -0
  41. package/dist/lib/node-esm/MarkdownContainer-PQBLVUW4.mjs +16 -0
  42. package/dist/lib/node-esm/MarkdownContainer-PQBLVUW4.mjs.map +7 -0
  43. package/dist/lib/node-esm/{anchor-sort-FCRYL2FX.mjs → anchor-sort-PCDXEBJ2.mjs} +4 -5
  44. package/dist/lib/node-esm/anchor-sort-PCDXEBJ2.mjs.map +7 -0
  45. package/dist/lib/node-esm/{app-graph-serializer-FAUQM3BH.mjs → app-graph-serializer-NF65JYAS.mjs} +6 -6
  46. package/dist/lib/node-esm/app-graph-serializer-NF65JYAS.mjs.map +7 -0
  47. package/dist/lib/node-esm/blueprint-definition-ENKJZYQS.mjs +14 -0
  48. package/dist/lib/node-esm/blueprint-definition-ENKJZYQS.mjs.map +7 -0
  49. package/dist/lib/node-esm/chunk-2PYNDVNK.mjs +828 -0
  50. package/dist/lib/node-esm/chunk-2PYNDVNK.mjs.map +7 -0
  51. package/dist/lib/node-esm/{MarkdownCard-B2IWTFOC.mjs → chunk-6XVGFGRW.mjs} +36 -24
  52. package/dist/lib/node-esm/chunk-6XVGFGRW.mjs.map +7 -0
  53. package/dist/lib/node-esm/{chunk-XO3IEQJE.mjs → chunk-AMHACOXW.mjs} +7 -4
  54. package/dist/lib/node-esm/{chunk-XO3IEQJE.mjs.map → chunk-AMHACOXW.mjs.map} +2 -2
  55. package/dist/lib/node-esm/chunk-ENYE7TQ5.mjs +22 -0
  56. package/dist/lib/node-esm/chunk-ENYE7TQ5.mjs.map +7 -0
  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-HAIEWPU7.mjs +151 -0
  60. package/dist/lib/node-esm/chunk-HAIEWPU7.mjs.map +7 -0
  61. package/dist/lib/node-esm/{chunk-FWZKC6X5.mjs → chunk-NGYJNQ6A.mjs} +9 -9
  62. package/dist/lib/node-esm/chunk-NGYJNQ6A.mjs.map +7 -0
  63. package/dist/lib/node-esm/{chunk-ZBXV4ON7.mjs → chunk-PLZ7EVCT.mjs} +8 -8
  64. package/dist/lib/node-esm/chunk-PLZ7EVCT.mjs.map +7 -0
  65. package/dist/lib/node-esm/{chunk-VCG2U522.mjs → chunk-SHAMSMKQ.mjs} +4 -4
  66. package/dist/lib/node-esm/chunk-SHAMSMKQ.mjs.map +7 -0
  67. package/dist/lib/node-esm/index.mjs +22 -21
  68. package/dist/lib/node-esm/index.mjs.map +3 -3
  69. package/dist/lib/node-esm/{intent-resolver-7A2EXGZQ.mjs → intent-resolver-6B3PFQ5F.mjs} +5 -5
  70. package/dist/lib/node-esm/{intent-resolver-7A2EXGZQ.mjs.map → intent-resolver-6B3PFQ5F.mjs.map} +3 -3
  71. package/dist/lib/node-esm/meta.json +1 -1
  72. package/dist/lib/node-esm/{react-surface-RCLL5WVQ.mjs → react-surface-CPMCARNW.mjs} +56 -54
  73. package/dist/lib/node-esm/react-surface-CPMCARNW.mjs.map +7 -0
  74. package/dist/lib/node-esm/{settings-H3UDD3KO.mjs → settings-CJ3T5EX4.mjs} +3 -3
  75. package/dist/lib/node-esm/{state-W3PECOJX.mjs → state-K6EH7SRZ.mjs} +3 -3
  76. package/dist/lib/node-esm/toolkit.mjs +14 -0
  77. package/dist/lib/node-esm/toolkit.mjs.map +7 -0
  78. package/dist/lib/node-esm/types/index.mjs +2 -2
  79. package/dist/types/src/MarkdownPlugin.d.ts.map +1 -1
  80. package/dist/types/src/capabilities/anchor-sort.d.ts +2 -4
  81. package/dist/types/src/capabilities/anchor-sort.d.ts.map +1 -1
  82. package/dist/types/src/capabilities/blueprint-definition.d.ts +5 -3
  83. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -1
  84. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  85. package/dist/types/src/capabilities/index.d.ts +1 -5
  86. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  87. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  88. package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts +2 -2
  89. package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts.map +1 -1
  90. package/dist/types/src/components/MarkdownContainer.d.ts +8 -12
  91. package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -1
  92. package/dist/types/src/components/MarkdownContainer.stories.d.ts +3 -0
  93. package/dist/types/src/components/MarkdownContainer.stories.d.ts.map +1 -1
  94. package/dist/types/src/components/MarkdownEditor/FileUpload.d.ts +11 -0
  95. package/dist/types/src/components/MarkdownEditor/FileUpload.d.ts.map +1 -0
  96. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts +42 -23
  97. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts.map +1 -1
  98. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts +5 -110
  99. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts.map +1 -1
  100. package/dist/types/src/components/MarkdownEditor/MarkdownEditorContent.d.ts +26 -0
  101. package/dist/types/src/components/MarkdownEditor/MarkdownEditorContent.d.ts.map +1 -0
  102. package/dist/types/src/components/MarkdownEditor/MarkdownEditorToolbar.d.ts +12 -0
  103. package/dist/types/src/components/MarkdownEditor/MarkdownEditorToolbar.d.ts.map +1 -0
  104. package/dist/types/src/components/Suggestions.stories.d.ts.map +1 -1
  105. package/dist/types/src/components/index.d.ts +3 -1
  106. package/dist/types/src/components/index.d.ts.map +1 -1
  107. package/dist/types/src/functions/create.d.ts +8 -0
  108. package/dist/types/src/functions/create.d.ts.map +1 -0
  109. package/dist/types/src/functions/create.test.d.ts +2 -0
  110. package/dist/types/src/functions/create.test.d.ts.map +1 -0
  111. package/dist/types/src/functions/index.d.ts +17 -2
  112. package/dist/types/src/functions/index.d.ts.map +1 -1
  113. package/dist/types/src/functions/{diff.d.ts → update.d.ts} +1 -1
  114. package/dist/types/src/functions/update.d.ts.map +1 -0
  115. package/dist/types/src/functions/update.test.d.ts +2 -0
  116. package/dist/types/src/functions/update.test.d.ts.map +1 -0
  117. package/dist/types/src/hooks/index.d.ts +3 -0
  118. package/dist/types/src/hooks/index.d.ts.map +1 -1
  119. package/dist/types/src/hooks/useExtensions.d.ts +21 -0
  120. package/dist/types/src/hooks/useExtensions.d.ts.map +1 -0
  121. package/dist/types/src/hooks/useLinkQuery.d.ts +4 -0
  122. package/dist/types/src/hooks/useLinkQuery.d.ts.map +1 -0
  123. package/dist/types/src/hooks/usePopoverMenuOptions.d.ts +9 -0
  124. package/dist/types/src/hooks/usePopoverMenuOptions.d.ts.map +1 -0
  125. package/dist/types/src/hooks/useSelectCurrentThread.d.ts.map +1 -1
  126. package/dist/types/src/testing.d.ts +6 -0
  127. package/dist/types/src/testing.d.ts.map +1 -0
  128. package/dist/types/src/toolkit.d.ts +3 -0
  129. package/dist/types/src/toolkit.d.ts.map +1 -0
  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 +1 -1
  133. package/dist/types/src/types/Markdown.d.ts.map +1 -1
  134. package/dist/types/src/types/index.d.ts.map +1 -1
  135. package/dist/types/src/util.d.ts +3 -3
  136. package/dist/types/src/util.d.ts.map +1 -1
  137. package/dist/types/tsconfig.tsbuildinfo +1 -1
  138. package/package.json +58 -42
  139. package/src/MarkdownPlugin.tsx +10 -11
  140. package/src/capabilities/anchor-sort.ts +3 -3
  141. package/src/capabilities/app-graph-serializer.ts +3 -3
  142. package/src/capabilities/artifact-definition.ts +3 -3
  143. package/src/capabilities/blueprint-definition.ts +30 -26
  144. package/src/capabilities/capabilities.ts +1 -0
  145. package/src/capabilities/index.ts +1 -2
  146. package/src/capabilities/intent-resolver.ts +1 -1
  147. package/src/capabilities/react-surface.tsx +42 -64
  148. package/src/components/MarkdownCard/MarkdownCard.stories.tsx +3 -3
  149. package/src/components/MarkdownCard/MarkdownCard.tsx +35 -23
  150. package/src/components/MarkdownContainer.stories.tsx +54 -27
  151. package/src/components/MarkdownContainer.tsx +77 -221
  152. package/src/components/MarkdownEditor/FileUpload.tsx +63 -0
  153. package/src/components/MarkdownEditor/MarkdownEditor.stories.tsx +57 -37
  154. package/src/components/MarkdownEditor/MarkdownEditor.tsx +217 -268
  155. package/src/components/MarkdownEditor/MarkdownEditorContent.tsx +134 -0
  156. package/src/components/MarkdownEditor/MarkdownEditorToolbar.tsx +63 -0
  157. package/src/components/Suggestions.stories.tsx +14 -8
  158. package/src/components/index.ts +3 -1
  159. package/src/functions/create.conversations.json +1 -0
  160. package/src/functions/create.test.ts +128 -0
  161. package/src/functions/create.ts +34 -0
  162. package/src/functions/index.ts +9 -2
  163. package/src/functions/update.conversations.json +1 -0
  164. package/src/functions/update.test.ts +151 -0
  165. package/src/functions/{diff.ts → update.ts} +2 -2
  166. package/src/hooks/index.ts +3 -0
  167. package/src/{extensions.tsx → hooks/useExtensions.tsx} +57 -78
  168. package/src/hooks/useLinkQuery.ts +83 -0
  169. package/src/hooks/usePopoverMenuOptions.ts +71 -0
  170. package/src/hooks/useSelectCurrentThread.tsx +13 -3
  171. package/src/meta.ts +3 -3
  172. package/src/testing.ts +27 -0
  173. package/src/toolkit.ts +6 -0
  174. package/src/translations.ts +3 -0
  175. package/src/types/Markdown.ts +6 -6
  176. package/src/types/index.ts +1 -0
  177. package/src/util.tsx +7 -8
  178. package/dist/lib/browser/MarkdownCard-AGWOTODZ.mjs.map +0 -7
  179. package/dist/lib/browser/MarkdownContainer-MV2UNAUV.mjs +0 -751
  180. package/dist/lib/browser/MarkdownContainer-MV2UNAUV.mjs.map +0 -7
  181. package/dist/lib/browser/anchor-sort-YWJI3BKT.mjs.map +0 -7
  182. package/dist/lib/browser/app-graph-serializer-KYDFCUOW.mjs.map +0 -7
  183. package/dist/lib/browser/blueprint-definition-BHRMFZAC.mjs +0 -11
  184. package/dist/lib/browser/chunk-6KU5DKP7.mjs.map +0 -7
  185. package/dist/lib/browser/chunk-HBBEHOP3.mjs +0 -106
  186. package/dist/lib/browser/chunk-HBBEHOP3.mjs.map +0 -7
  187. package/dist/lib/browser/chunk-ODB2PTBP.mjs.map +0 -7
  188. package/dist/lib/browser/chunk-XMT6PMU5.mjs.map +0 -7
  189. package/dist/lib/browser/chunk-Z5PDJNBV.mjs +0 -22
  190. package/dist/lib/browser/chunk-Z5PDJNBV.mjs.map +0 -7
  191. package/dist/lib/browser/react-surface-3A2GO3BN.mjs.map +0 -7
  192. package/dist/lib/browser/toolkit-YA65QX2S.mjs +0 -76
  193. package/dist/lib/browser/toolkit-YA65QX2S.mjs.map +0 -7
  194. package/dist/lib/node-esm/MarkdownCard-B2IWTFOC.mjs.map +0 -7
  195. package/dist/lib/node-esm/MarkdownContainer-J2R3DLCQ.mjs +0 -752
  196. package/dist/lib/node-esm/MarkdownContainer-J2R3DLCQ.mjs.map +0 -7
  197. package/dist/lib/node-esm/anchor-sort-FCRYL2FX.mjs.map +0 -7
  198. package/dist/lib/node-esm/app-graph-serializer-FAUQM3BH.mjs.map +0 -7
  199. package/dist/lib/node-esm/blueprint-definition-XYFKMIDR.mjs +0 -12
  200. package/dist/lib/node-esm/chunk-7RDNIMTF.mjs +0 -24
  201. package/dist/lib/node-esm/chunk-7RDNIMTF.mjs.map +0 -7
  202. package/dist/lib/node-esm/chunk-FVI7LPC3.mjs +0 -107
  203. package/dist/lib/node-esm/chunk-FVI7LPC3.mjs.map +0 -7
  204. package/dist/lib/node-esm/chunk-FWZKC6X5.mjs.map +0 -7
  205. package/dist/lib/node-esm/chunk-VCG2U522.mjs.map +0 -7
  206. package/dist/lib/node-esm/chunk-ZBXV4ON7.mjs.map +0 -7
  207. package/dist/lib/node-esm/react-surface-RCLL5WVQ.mjs.map +0 -7
  208. package/dist/lib/node-esm/toolkit-HSIKUGNK.mjs +0 -77
  209. package/dist/lib/node-esm/toolkit-HSIKUGNK.mjs.map +0 -7
  210. package/dist/types/src/capabilities/toolkit.d.ts +0 -4
  211. package/dist/types/src/capabilities/toolkit.d.ts.map +0 -1
  212. package/dist/types/src/components/Toolbar.stories.d.ts +0 -48
  213. package/dist/types/src/components/Toolbar.stories.d.ts.map +0 -1
  214. package/dist/types/src/extensions.d.ts +0 -22
  215. package/dist/types/src/extensions.d.ts.map +0 -1
  216. package/dist/types/src/functions/diff.d.ts.map +0 -1
  217. package/src/capabilities/toolkit.ts +0 -49
  218. package/src/components/Toolbar.stories.tsx +0 -118
  219. /package/dist/lib/browser/{blueprint-definition-BHRMFZAC.mjs.map → MarkdownCard-L3BS2SED.mjs.map} +0 -0
  220. /package/dist/lib/{node-esm/blueprint-definition-XYFKMIDR.mjs.map → browser/MarkdownContainer-KF3YL6CI.mjs.map} +0 -0
  221. /package/dist/lib/browser/{settings-XY265Y2Q.mjs.map → settings-TZUDB5EW.mjs.map} +0 -0
  222. /package/dist/lib/browser/{state-6QODXCSZ.mjs.map → state-BTUKVZHY.mjs.map} +0 -0
  223. /package/dist/lib/node-esm/{settings-H3UDD3KO.mjs.map → settings-CJ3T5EX4.mjs.map} +0 -0
  224. /package/dist/lib/node-esm/{state-W3PECOJX.mjs.map → state-K6EH7SRZ.mjs.map} +0 -0
@@ -5,15 +5,8 @@
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { useMemo } from 'react';
7
7
 
8
- import {
9
- type Capabilities,
10
- IntentPlugin,
11
- LayoutAction,
12
- SettingsPlugin,
13
- Surface,
14
- createIntent,
15
- useIntentDispatcher,
16
- } from '@dxos/app-framework';
8
+ import { type Capabilities, IntentPlugin, LayoutAction, SettingsPlugin, createIntent } from '@dxos/app-framework';
9
+ import { Surface, useIntentDispatcher } from '@dxos/app-framework/react';
17
10
  import { withPluginManager } from '@dxos/app-framework/testing';
18
11
  import { Obj, Query } from '@dxos/echo';
19
12
  import { AttentionPlugin } from '@dxos/plugin-attention';
@@ -26,10 +19,12 @@ import { ThemePlugin } from '@dxos/plugin-theme';
26
19
  import { faker } from '@dxos/random';
27
20
  import { useQuery, useSpace } from '@dxos/react-client/echo';
28
21
  import { useAsyncEffect } from '@dxos/react-ui';
29
- import { withTheme } from '@dxos/react-ui/testing';
22
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
23
+ import { useAttentionAttributes } from '@dxos/react-ui-attention';
30
24
  import { defaultTx } from '@dxos/react-ui-theme';
31
- import { DataType } from '@dxos/schema';
25
+ import { Text } from '@dxos/schema';
32
26
  import { type ValueGenerator, createObjectFactory } from '@dxos/schema/testing';
27
+ import { Organization, Person } from '@dxos/types';
33
28
 
34
29
  import { MarkdownPlugin } from '../MarkdownPlugin';
35
30
  import { translations } from '../translations';
@@ -44,14 +39,25 @@ const DefaultStory = () => {
44
39
  const space = useSpace();
45
40
  const [doc] = useQuery(space, Query.type(Markdown.Document));
46
41
  const data = useMemo(() => ({ subject: doc }), [doc]);
42
+ const id = doc && Obj.getDXN(doc).toString();
43
+ const attentionAttrs = useAttentionAttributes(id);
47
44
 
48
45
  useAsyncEffect(async () => {
49
46
  if (space) {
50
- await dispatch(createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: space.id }));
47
+ await dispatch(
48
+ createIntent(LayoutAction.SwitchWorkspace, {
49
+ part: 'workspace',
50
+ subject: space.id,
51
+ }),
52
+ );
51
53
  }
52
54
  }, [space, dispatch]);
53
55
 
54
- return <Surface role='article' data={data} limit={1} />;
56
+ return (
57
+ <div className='contents' {...attentionAttrs}>
58
+ <Surface role='article' data={data} limit={1} />
59
+ </div>
60
+ );
55
61
  };
56
62
 
57
63
  const meta = {
@@ -59,26 +65,42 @@ const meta = {
59
65
  render: DefaultStory,
60
66
  decorators: [
61
67
  withTheme,
62
- withPluginManager({
68
+ withLayout({ container: 'column' }),
69
+ withPluginManager<{ title?: string; content?: string }>((context) => ({
63
70
  plugins: [
64
71
  ClientPlugin({
65
- types: [Markdown.Document, DataType.Text, DataType.Person, DataType.Organization],
72
+ types: [Markdown.Document, Text.Text, Person.Person, Organization.Organization],
66
73
  onClientInitialized: async ({ client }) => {
67
74
  await client.halo.createIdentity();
68
75
  await client.spaces.waitUntilReady();
69
76
  await client.spaces.default.waitUntilReady();
77
+
70
78
  const space = client.spaces.default;
71
- const queue = space.queues.create();
72
- const alice = Obj.make(DataType.Person, { fullName: 'Alice' });
73
- const acme = Obj.make(DataType.Organization, { name: 'ACME' });
74
- await queue.append([alice, acme]);
75
- const doc = Markdown.makeDocument({
76
- name: 'Test',
77
- content: `# Test\n\n![Alice](${Obj.getDXN(alice)})\n\n![ACME](${Obj.getDXN(acme)})`,
78
- });
79
- space.db.add(doc);
80
79
  const createObjects = createObjectFactory(space.db, generator);
81
- await createObjects([{ type: DataType.Organization, count: 10 }]);
80
+ await createObjects([{ type: Organization.Organization, count: 10 }]);
81
+
82
+ const queue = space.queues.create();
83
+ const kai = Obj.make(Person.Person, { fullName: 'Kai' });
84
+ const dxos = Obj.make(Organization.Organization, { name: 'DXOS' });
85
+ await queue.append([kai, dxos]);
86
+
87
+ space.db.add(
88
+ Markdown.make({
89
+ name: context.args.title ?? 'Testing',
90
+ content: [
91
+ `# ${context.args.title ?? 'Testing'}`,
92
+ context.args.content ?? '',
93
+ // TODO(burdon): Popovers not currently working.
94
+ '## Here are some objects',
95
+ `![Alice](${Obj.getDXN(kai)})`,
96
+ `![DXOS](${Obj.getDXN(dxos)})`,
97
+ '',
98
+ 'END',
99
+ '',
100
+ ].join('\n\n'),
101
+ }),
102
+ );
103
+
82
104
  await space.db.flush({ indexes: true });
83
105
  },
84
106
  }),
@@ -93,7 +115,7 @@ const meta = {
93
115
  PreviewPlugin(),
94
116
  StorybookLayoutPlugin({}),
95
117
  ],
96
- }),
118
+ })),
97
119
  ],
98
120
  parameters: {
99
121
  layout: 'fullscreen',
@@ -106,4 +128,9 @@ export default meta;
106
128
 
107
129
  type Story = StoryObj<typeof meta>;
108
130
 
109
- export const Default: Story = {};
131
+ export const Default: Story = {
132
+ args: {
133
+ title: 'Testing',
134
+ content: ['This is a line with **some** formatting.'].join('\n\n'),
135
+ },
136
+ };
@@ -2,255 +2,111 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Rx } from '@effect-rx/rx-react';
6
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
7
- import { createPortal } from 'react-dom';
8
-
9
- import { Capabilities, Surface, useAppGraph, useCapabilities, usePluginManager } from '@dxos/app-framework';
10
- import { DXN, Filter, Obj, Query, Type } from '@dxos/echo';
11
- import { ClientCapabilities } from '@dxos/plugin-client';
12
- import { SpaceCapabilities } from '@dxos/plugin-space';
13
- import { useClient } from '@dxos/react-client';
14
- import { fullyQualifiedId, getSpace } from '@dxos/react-client/echo';
15
- import { toLocalizedString, useTranslation } from '@dxos/react-ui';
5
+ import { type Extension } from '@codemirror/state';
6
+ import { Atom } from '@effect-atom/atom-react';
7
+ import React, { useMemo } from 'react';
8
+
9
+ import { Capabilities } from '@dxos/app-framework';
10
+ import { useAppGraph, useCapabilities } from '@dxos/app-framework/react';
11
+ import { Obj } from '@dxos/echo';
12
+ import { getSpace } from '@dxos/react-client/echo';
16
13
  import { type SelectionManager } from '@dxos/react-ui-attention';
17
- import {
18
- type PopoverMenuGroup,
19
- type PopoverMenuItem,
20
- type PreviewLinkRef,
21
- type PreviewOptions,
22
- insertAtCursor,
23
- insertAtLineStart,
24
- } from '@dxos/react-ui-editor';
25
- import { DataType } from '@dxos/schema';
14
+ import { StackItem } from '@dxos/react-ui-stack';
15
+ import { Text } from '@dxos/schema';
26
16
 
27
- import { useExtensions } from '../extensions';
28
- import { Markdown } from '../types';
29
- import { getFallbackName } from '../util';
17
+ import { MarkdownCapabilities } from '../capabilities';
18
+ import { type DocumentType, useLinkQuery } from '../hooks';
19
+ import { Markdown, type MarkdownPluginState } from '../types';
30
20
 
31
- import { MarkdownEditor, type MarkdownEditorProps } from './MarkdownEditor';
21
+ import { MarkdownEditor, type MarkdownEditorContentProps, type MarkdownEditorRootProps } from './MarkdownEditor';
32
22
 
33
- export type MarkdownContainerProps = Pick<
34
- MarkdownEditorProps,
35
- 'role' | 'extensionProviders' | 'viewMode' | 'editorStateStore' | 'onViewModeChange'
36
- > & {
37
- id: string;
38
- object: Markdown.Document | DataType.Text | any;
23
+ export type MarkdownContainerProps = {
24
+ role?: string;
25
+ object: DocumentType;
39
26
  settings: Markdown.Settings;
40
27
  selectionManager?: SelectionManager;
41
- };
28
+ } & (Pick<MarkdownEditorRootProps, 'id' | 'viewMode' | 'onViewModeChange'> &
29
+ Pick<MarkdownEditorContentProps, 'editorStateStore'> &
30
+ Pick<MarkdownPluginState, 'extensionProviders'>);
42
31
 
43
32
  export const MarkdownContainer = ({
44
33
  id,
45
34
  role,
46
35
  object,
47
36
  settings,
48
- selectionManager,
49
- viewMode,
50
- editorStateStore,
51
- onViewModeChange,
37
+ extensionProviders,
38
+ ...props
52
39
  }: MarkdownContainerProps) => {
53
- const { t } = useTranslation();
54
- const scrollPastEnd = role === 'article';
55
- const doc = Obj.instanceOf(Markdown.Document, object) ? object : undefined;
56
- const text = Obj.instanceOf(DataType.Text, object) ? object : undefined;
57
- const [previewBlocks, setPreviewBlocks] = useState<{ link: PreviewLinkRef; el: HTMLElement }[]>([]);
58
- const previewOptions = useMemo(
59
- (): PreviewOptions => ({
60
- addBlockContainer: (link, el) => {
61
- setPreviewBlocks((prev) => [...prev, { link, el }]);
62
- },
63
- removeBlockContainer: (link) => {
64
- setPreviewBlocks((prev) => prev.filter(({ link: prevLink }) => prevLink.ref !== link.ref));
65
- },
66
- }),
67
- [],
68
- );
69
- const extensions = useExtensions({
70
- document: doc,
71
- text,
72
- id,
73
- settings,
74
- selectionManager,
75
- viewMode,
76
- editorStateStore,
77
- previewOptions,
78
- });
79
-
80
- // TODO(wittjosiah): Factor out.
81
- const manager = usePluginManager();
82
- const resolve = useCallback(
83
- (typename: string) =>
84
- manager.context.getCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ?? {},
85
- [manager],
86
- );
87
40
  const space = getSpace(object);
88
- const objectForms = useCapabilities(SpaceCapabilities.ObjectForm);
89
- const schemaWhiteList = useCapabilities(ClientCapabilities.SchemaWhiteList);
90
- const filter = useMemo(
91
- () =>
92
- Filter.or(
93
- ...objectForms.map((form) => Filter.type(form.objectSchema)),
94
- ...schemaWhiteList.flat().map((schema) => Filter.typename(Type.getTypename(schema))),
95
- ),
96
- [objectForms, schemaWhiteList],
97
- );
41
+ const isDocument = Obj.instanceOf(Markdown.Document, object);
42
+ const isText = Obj.instanceOf(Text.Text, object);
43
+ const attendableId = isDocument ? Obj.getDXN(object).toString() : undefined;
44
+
45
+ // Extensions from other plugins.
46
+ // TODO(burdon): Document MarkdownPluginState.extensionProviders
47
+ const otherExtensionProviders = useCapabilities(MarkdownCapabilities.Extensions);
48
+ const extensions = useMemo<Extension[]>(() => {
49
+ if (!Obj.instanceOf(Markdown.Document, object)) {
50
+ return [];
51
+ }
98
52
 
99
- const handleLinkQuery = useCallback(
100
- async (query?: string): Promise<PopoverMenuGroup[]> => {
101
- const name = query?.startsWith('@') ? query.slice(1).toLowerCase() : (query?.toLowerCase() ?? '');
102
- const results = await space?.db.query(Query.select(filter)).run();
103
- // TODO(wittjosiah): Use `Obj.Any` type.
104
- const getLabel = (object: any) => {
105
- const label = Obj.getLabel(object);
106
- if (label) {
107
- return label;
53
+ return [...(otherExtensionProviders ?? []), ...(extensionProviders ?? [])]
54
+ .flat()
55
+ .reduce((acc: Extension[], provider) => {
56
+ const extension =
57
+ typeof provider === 'function' ? provider({ document: object as Markdown.Document }) : provider;
58
+ if (extension) {
59
+ acc.push(extension);
108
60
  }
109
61
 
110
- // TODO(wittjosiah): Remove metadata labels.
111
- const type = Obj.getTypename(object)!;
112
- const metadata = resolve(type);
113
- return metadata.label?.(object) || ['object name placeholder', { ns: type, default: 'New object' }];
114
- };
115
- const items =
116
- results?.objects
117
- .filter((object) => toLocalizedString(getLabel(object), t).toLowerCase().includes(name))
118
- // TODO(wittjosiah): Remove `any` type.
119
- .map((object: any): PopoverMenuItem => {
120
- const metadata = resolve(Obj.getTypename(object)!);
121
- const label = toLocalizedString(getLabel(object), t);
122
- return {
123
- id: object.id,
124
- label,
125
- icon: metadata.icon,
126
- onSelect: (view, head) => {
127
- const link = `[${label}](${Obj.getDXN(object)})`;
128
- if (query?.startsWith('@')) {
129
- insertAtLineStart(view, head, `!${link}\n`);
130
- } else {
131
- insertAtCursor(view, head, `${link} `);
132
- }
133
- },
134
- };
135
- }) ?? [];
136
- return [{ id: 'echo', items }];
137
- },
138
- [filter, resolve, space],
139
- );
140
-
141
- // TODO(burdon): Reconcile variants.
142
- const editor = doc ? (
143
- <DocumentEditor
144
- id={fullyQualifiedId(object)}
145
- role={role}
146
- document={doc}
147
- extensions={extensions}
148
- viewMode={viewMode}
149
- settings={settings}
150
- scrollPastEnd={scrollPastEnd}
151
- onViewModeChange={onViewModeChange}
152
- onLinkQuery={space ? handleLinkQuery : undefined}
153
- />
154
- ) : text ? (
155
- <MarkdownEditor
156
- id={id}
157
- role={role}
158
- initialValue={text.content}
159
- extensions={extensions}
160
- viewMode={viewMode}
161
- toolbar={settings.toolbar}
162
- inputMode={settings.editorInputMode}
163
- scrollPastEnd={scrollPastEnd}
164
- onViewModeChange={onViewModeChange}
165
- onLinkQuery={space ? handleLinkQuery : undefined}
166
- />
167
- ) : (
168
- <MarkdownEditor
169
- id={id}
170
- role={role}
171
- initialValue={object.text}
172
- extensions={extensions}
173
- viewMode={viewMode}
174
- toolbar={settings.toolbar}
175
- inputMode={settings.editorInputMode}
176
- scrollPastEnd={scrollPastEnd}
177
- onViewModeChange={onViewModeChange}
178
- onLinkQuery={space ? handleLinkQuery : undefined}
179
- />
180
- );
181
-
182
- return (
183
- <>
184
- {editor}
185
- {previewBlocks.map(({ link, el }) => (
186
- <PreviewBlock key={link.ref} link={link} el={el} />
187
- ))}
188
- </>
189
- );
190
- };
191
-
192
- // TODO(wittjosiah): This shouldn't be "card" but "block".
193
- // It's not a preview card but an interactive embedded object.
194
- const PreviewBlock = ({ link, el }: { link: PreviewLinkRef; el: HTMLElement }) => {
195
- const client = useClient();
196
- const dxn = DXN.parse(link.ref);
197
- const subject = client.graph.ref(dxn).target;
198
- const data = useMemo(() => ({ subject }), [subject]);
199
-
200
- return createPortal(<Surface role='card--transclusion' data={data} limit={1} />, el);
201
- };
202
-
203
- type DocumentEditorProps = Omit<MarkdownContainerProps, 'object' | 'extensionProviders' | 'editorStateStore'> &
204
- Pick<MarkdownEditorProps, 'id' | 'scrollPastEnd' | 'extensions' | 'onLinkQuery'> & {
205
- document: Markdown.Document;
206
- };
207
-
208
- export const DocumentEditor = ({ id, document: doc, settings, viewMode, ...props }: DocumentEditorProps) => {
209
- const space = getSpace(doc);
62
+ return acc;
63
+ }, []);
64
+ }, [extensionProviders, otherExtensionProviders, object]);
210
65
 
211
- // Migrate gradually to `fallbackName`.
212
- useEffect(() => {
213
- if (typeof doc.fallbackName === 'string') {
214
- return;
215
- }
216
-
217
- const fallbackName = doc.content?.target?.content ? getFallbackName(doc.content.target.content) : undefined;
218
- if (fallbackName) {
219
- doc.fallbackName = fallbackName;
220
- }
221
- }, [doc, doc.content]);
66
+ // Toolbar actions from app graph.
67
+ const { graph } = useAppGraph();
68
+ const customActions = useMemo(() => {
69
+ return Atom.make((get) => {
70
+ const actions = get(graph.actions(id));
71
+ const nodes = actions.filter((action) => action.properties.disposition === 'toolbar');
72
+ const edges = nodes.map((node) => ({ source: 'root', target: node.id }));
73
+ return { nodes, edges };
74
+ });
75
+ }, [graph]);
222
76
 
223
- // File dragging.
77
+ // File upload.
224
78
  const [upload] = useCapabilities(Capabilities.FileUploader);
225
79
  const handleFileUpload = useMemo(() => {
226
- if (space === undefined || upload === undefined) {
80
+ if (!space || !upload) {
227
81
  return undefined;
228
82
  }
229
83
 
230
- // TODO(burdon): Re-order props: space, file.
231
- return async (file: File) => upload!(file, space);
84
+ return async (file: File) => upload(space, file);
232
85
  }, [space, upload]);
233
86
 
234
- const { graph } = useAppGraph();
235
- const customActions = useMemo(() => {
236
- return Rx.make((get) => {
237
- const actions = get(graph.actions(id));
238
- const nodes = actions.filter((action) => action.properties.disposition === 'toolbar');
239
- return { nodes, edges: nodes.map((node) => ({ source: 'root', target: node.id })) };
240
- });
241
- }, [graph]);
87
+ // Query for @ refs.
88
+ const handleLinkQuery = useLinkQuery(space);
242
89
 
243
90
  return (
244
- <MarkdownEditor
245
- id={id}
246
- initialValue={doc.content?.target?.content}
247
- viewMode={viewMode}
248
- toolbar={settings.toolbar}
249
- customActions={customActions}
250
- inputMode={settings.editorInputMode}
251
- onFileUpload={handleFileUpload}
252
- {...props}
253
- />
91
+ <StackItem.Content toolbar={settings.toolbar}>
92
+ <MarkdownEditor.Root
93
+ id={attendableId ?? id}
94
+ object={object}
95
+ extensions={extensions}
96
+ onFileUpload={handleFileUpload}
97
+ onLinkQuery={handleLinkQuery}
98
+ {...props}
99
+ >
100
+ {settings.toolbar && (
101
+ <MarkdownEditor.Toolbar id={attendableId ?? id} role={role} customActions={customActions} />
102
+ )}
103
+ <MarkdownEditor.Content
104
+ initialValue={isDocument ? object.content?.target?.content : isText ? object.content : object.text}
105
+ scrollPastEnd={role === 'article'}
106
+ />
107
+ <MarkdownEditor.Blocks />
108
+ </MarkdownEditor.Root>
109
+ </StackItem.Content>
254
110
  );
255
111
  };
256
112
 
@@ -0,0 +1,63 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type EditorView } from '@codemirror/view';
6
+ import React, { forwardRef, useEffect, useImperativeHandle } from 'react';
7
+ import { createPortal } from 'react-dom';
8
+ import { useDropzone } from 'react-dropzone';
9
+
10
+ import { type FileInfo } from '@dxos/app-framework';
11
+ import { addLink } from '@dxos/react-ui-editor';
12
+
13
+ export const IMAGE_FILES = ['.jpg', '.jpeg', '.png', '.gif'];
14
+
15
+ export type FileUploadAction = () => void;
16
+
17
+ export type FileUploadProps = {
18
+ editorView?: EditorView;
19
+ onFileUpload?: (file: File) => Promise<FileInfo | undefined>;
20
+ };
21
+
22
+ // TODO(burdon): Factor out.
23
+ // TODO(burdon): Move to root? (support drag into document via dropzone).
24
+ export const FileUpload = forwardRef<FileUploadAction, FileUploadProps>(
25
+ ({ editorView, onFileUpload }, forwardedRef) => {
26
+ // https://react-dropzone.js.org
27
+ const { acceptedFiles, open, inputRef } = useDropzone({
28
+ disabled: !onFileUpload,
29
+ multiple: false,
30
+ noDrag: true,
31
+ accept: {
32
+ 'image/*': IMAGE_FILES,
33
+ },
34
+ });
35
+
36
+ useImperativeHandle(forwardedRef, () => open, []);
37
+
38
+ useEffect(() => {
39
+ if (editorView && acceptedFiles.length && onFileUpload) {
40
+ requestAnimationFrame(async () => {
41
+ // NOTE: Clone file since react-dropzone patches in a non-standard `path` property, which confuses IPFS.
42
+ const f = acceptedFiles[0];
43
+ const file = new File([f], f.name, {
44
+ type: f.type,
45
+ lastModified: f.lastModified,
46
+ });
47
+
48
+ // TODO(burdon): Factor out.
49
+ const info = await onFileUpload(file);
50
+ if (info) {
51
+ addLink({ url: info.url, image: true })(editorView);
52
+ }
53
+ });
54
+ }
55
+ }, [editorView, acceptedFiles, onFileUpload]);
56
+
57
+ if (!onFileUpload) {
58
+ return null;
59
+ }
60
+
61
+ return <>{createPortal(<input ref={inputRef} />, document.body)} </>;
62
+ },
63
+ );
@@ -3,63 +3,83 @@
3
3
  //
4
4
 
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
- import React, { useMemo } from 'react';
6
+ import React from 'react';
7
7
 
8
8
  import { IntentPlugin } from '@dxos/app-framework';
9
9
  import { withPluginManager } from '@dxos/app-framework/testing';
10
- import { createDocAccessor, createObject } from '@dxos/react-client/echo';
11
- import { withTheme } from '@dxos/react-ui/testing';
12
- import { withAttention } from '@dxos/react-ui-attention/testing';
13
- import { automerge, translations as editorTranslations } from '@dxos/react-ui-editor';
14
- import { Stack, StackItem } from '@dxos/react-ui-stack';
10
+ import { Filter, Obj } from '@dxos/echo';
11
+ import { AttentionPlugin } from '@dxos/plugin-attention';
12
+ import { ClientPlugin } from '@dxos/plugin-client';
13
+ import { useQuery, useSpace } from '@dxos/react-client/echo';
14
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
15
+ import { useAttentionAttributes } from '@dxos/react-ui-attention';
16
+ import { translations as editorTranslations } from '@dxos/react-ui-editor';
17
+ import { StackItem } from '@dxos/react-ui-stack';
15
18
 
16
19
  import { translations } from '../../translations';
20
+ import { Markdown } from '../../types';
17
21
 
18
- import { MarkdownEditor, type MarkdownEditorProps } from './MarkdownEditor';
22
+ import { MarkdownEditor, type MarkdownEditorRootProps } from './MarkdownEditor';
19
23
 
20
24
  const content = Array.from({ length: 100 }, (_, i) => `Line ${i + 1}`).join('\n');
21
25
 
22
- type StoryProps = MarkdownEditorProps & {
23
- content?: string;
24
- toolbar?: boolean;
25
- };
26
+ type StoryProps = Omit<MarkdownEditorRootProps, 'id' | 'extensions'>;
27
+
28
+ const DefaultStory = (props: StoryProps) => {
29
+ const space = useSpace();
30
+ const [doc] = useQuery(space?.db, Filter.type(Markdown.Document));
31
+ const id = doc && Obj.getDXN(doc).toString();
32
+ const attentionAttrs = useAttentionAttributes(id);
33
+
34
+ if (!id) {
35
+ return null;
36
+ }
26
37
 
27
- const DefaultStory = ({ content = '# Test', toolbar }: StoryProps) => {
28
- const doc = useMemo(() => createObject({ content }), [content]); // TODO(burdon): Remove dependency on createObject.
29
- const extensions = useMemo(() => [automerge(createDocAccessor(doc, ['content']))], [doc]);
38
+ // TODO(burdon): Toolbar attention isn't working in this story.
30
39
  return (
31
- <Stack orientation='horizontal' rail={false}>
32
- <StackItem.Root item={{ id: 'story' }}>
33
- <MarkdownEditor id='test' initialValue={doc.content} extensions={extensions} toolbar={toolbar} />
34
- </StackItem.Root>
35
- </Stack>
40
+ <div className='contents' {...attentionAttrs}>
41
+ <StackItem.Content toolbar>
42
+ <MarkdownEditor.Root id={id} object={doc} {...props}>
43
+ <MarkdownEditor.Toolbar id={id} />
44
+ <MarkdownEditor.Content />
45
+ </MarkdownEditor.Root>
46
+ </StackItem.Content>
47
+ </div>
36
48
  );
37
49
  };
38
50
 
39
- const meta = {
51
+ const meta: Meta<typeof DefaultStory> = {
40
52
  title: 'plugins/plugin-markdown/MarkdownEditor',
41
- component: MarkdownEditor as any,
42
- render: DefaultStory,
43
- decorators: [withTheme, withPluginManager({ plugins: [IntentPlugin()] }), withAttention],
53
+ component: DefaultStory,
54
+ render: DefaultStory as any,
55
+ decorators: [
56
+ withTheme,
57
+ withLayout({ container: 'column' }),
58
+ // TODO(burdon): Create story without client.
59
+ withPluginManager({
60
+ plugins: [
61
+ ClientPlugin({
62
+ types: [Markdown.Document],
63
+ onClientInitialized: async ({ client }) => {
64
+ await client.halo.createIdentity();
65
+ await client.spaces.waitUntilReady();
66
+ const space = client.spaces.default;
67
+ await space.waitUntilReady();
68
+ space.db.add(Markdown.make({ content }));
69
+ },
70
+ }),
71
+ IntentPlugin(),
72
+ AttentionPlugin(),
73
+ ],
74
+ }),
75
+ ],
44
76
  parameters: {
45
- layout: 'fullscreen',
46
77
  translations: [...translations, ...editorTranslations],
47
78
  },
48
- } satisfies Meta<typeof DefaultStory>;
79
+ };
49
80
 
50
81
  export default meta;
51
82
 
52
83
  type Story = StoryObj<typeof meta>;
53
84
 
54
- export const Default: Story = {
55
- args: {
56
- content,
57
- },
58
- };
59
-
60
- export const WithToolbar: Story = {
61
- args: {
62
- toolbar: true,
63
- content,
64
- },
65
- };
85
+ export const Default: Story = {};