@dxos/plugin-markdown 0.8.4-main.e098934 → 0.8.4-main.e8ec1fe

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 (241) hide show
  1. package/dist/lib/browser/MarkdownCard-JYMDPKV5.mjs +12 -0
  2. package/dist/lib/browser/MarkdownContainer-Y75XSVBX.mjs +15 -0
  3. package/dist/lib/browser/{anchor-sort-E33BSTYF.mjs → anchor-sort-3HGPGCOO.mjs} +6 -7
  4. package/dist/lib/browser/anchor-sort-3HGPGCOO.mjs.map +7 -0
  5. package/dist/lib/browser/{app-graph-serializer-OX62DNPT.mjs → app-graph-serializer-POZN234F.mjs} +10 -10
  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-Z7P6JGGW.mjs → chunk-22XSSNBS.mjs} +7 -4
  10. package/dist/lib/browser/{chunk-Z7P6JGGW.mjs.map → chunk-22XSSNBS.mjs.map} +2 -2
  11. package/dist/lib/browser/chunk-2MLGSYRN.mjs +20 -0
  12. package/dist/lib/browser/chunk-2MLGSYRN.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/{chunk-LAVZ2W6X.mjs → chunk-GH6GQSBL.mjs} +9 -8
  16. package/dist/lib/browser/chunk-GH6GQSBL.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-OY6CGPOO.mjs → chunk-IBCHVMZW.mjs} +2 -2
  18. package/dist/lib/browser/{chunk-OY6CGPOO.mjs.map → chunk-IBCHVMZW.mjs.map} +2 -2
  19. package/dist/lib/browser/chunk-K3LXOU3E.mjs +827 -0
  20. package/dist/lib/browser/chunk-K3LXOU3E.mjs.map +7 -0
  21. package/dist/lib/browser/chunk-PBJLFIOX.mjs +96 -0
  22. package/dist/lib/browser/chunk-PBJLFIOX.mjs.map +7 -0
  23. package/dist/lib/browser/{chunk-BEE7VQPU.mjs → chunk-QYSEJ5GP.mjs} +13 -12
  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 +25 -24
  28. package/dist/lib/browser/index.mjs.map +3 -3
  29. package/dist/lib/browser/{intent-resolver-WDDH56JC.mjs → intent-resolver-Z5L7TXUK.mjs} +8 -8
  30. package/dist/lib/browser/intent-resolver-Z5L7TXUK.mjs.map +7 -0
  31. package/dist/lib/browser/meta.json +1 -1
  32. package/dist/lib/browser/{react-surface-NY5K2I5R.mjs → react-surface-GO5ZOKNN.mjs} +59 -63
  33. package/dist/lib/browser/react-surface-GO5ZOKNN.mjs.map +7 -0
  34. package/dist/lib/browser/{settings-AABBTB4Q.mjs → settings-TZUDB5EW.mjs} +5 -5
  35. package/dist/lib/browser/{settings-AABBTB4Q.mjs.map → settings-TZUDB5EW.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.mjs +13 -0
  39. package/dist/lib/browser/toolkit.mjs.map +7 -0
  40. package/dist/lib/browser/types/index.mjs +2 -2
  41. package/dist/lib/node-esm/MarkdownCard-ZXPJLUYO.mjs +13 -0
  42. package/dist/lib/node-esm/MarkdownCard-ZXPJLUYO.mjs.map +7 -0
  43. package/dist/lib/node-esm/MarkdownContainer-YRDSRDCS.mjs +16 -0
  44. package/dist/lib/node-esm/MarkdownContainer-YRDSRDCS.mjs.map +7 -0
  45. package/dist/lib/node-esm/{anchor-sort-ALP2NH24.mjs → anchor-sort-PCDXEBJ2.mjs} +6 -7
  46. package/dist/lib/node-esm/anchor-sort-PCDXEBJ2.mjs.map +7 -0
  47. package/dist/lib/node-esm/{app-graph-serializer-56TD3BMX.mjs → app-graph-serializer-NF65JYAS.mjs} +10 -10
  48. package/dist/lib/node-esm/app-graph-serializer-NF65JYAS.mjs.map +7 -0
  49. package/dist/lib/node-esm/blueprint-definition-ENKJZYQS.mjs +14 -0
  50. package/dist/lib/node-esm/blueprint-definition-ENKJZYQS.mjs.map +7 -0
  51. package/dist/lib/node-esm/{chunk-J7A6TUB2.mjs → chunk-AMHACOXW.mjs} +7 -4
  52. package/dist/lib/node-esm/{chunk-J7A6TUB2.mjs.map → chunk-AMHACOXW.mjs.map} +2 -2
  53. package/dist/lib/node-esm/chunk-CT7CFX5G.mjs +828 -0
  54. package/dist/lib/node-esm/chunk-CT7CFX5G.mjs.map +7 -0
  55. package/dist/lib/node-esm/{chunk-CB2R4YIY.mjs → chunk-GMMVSXQ6.mjs} +2 -2
  56. package/dist/lib/node-esm/{chunk-CB2R4YIY.mjs.map → chunk-GMMVSXQ6.mjs.map} +2 -2
  57. package/dist/lib/node-esm/chunk-HAIEWPU7.mjs +151 -0
  58. package/dist/lib/node-esm/chunk-HAIEWPU7.mjs.map +7 -0
  59. package/dist/lib/node-esm/chunk-KCHUTL3Q.mjs +22 -0
  60. package/dist/lib/node-esm/chunk-KCHUTL3Q.mjs.map +7 -0
  61. package/dist/lib/node-esm/{chunk-FXILAQ5F.mjs → chunk-NGYJNQ6A.mjs} +13 -12
  62. package/dist/lib/node-esm/chunk-NGYJNQ6A.mjs.map +7 -0
  63. package/dist/lib/node-esm/chunk-PIOOG7A5.mjs +97 -0
  64. package/dist/lib/node-esm/chunk-PIOOG7A5.mjs.map +7 -0
  65. package/dist/lib/node-esm/{chunk-O6EXWGGS.mjs → chunk-PLZ7EVCT.mjs} +9 -8
  66. package/dist/lib/node-esm/chunk-PLZ7EVCT.mjs.map +7 -0
  67. package/dist/lib/node-esm/{chunk-VCG2U522.mjs → chunk-SHAMSMKQ.mjs} +4 -4
  68. package/dist/lib/node-esm/chunk-SHAMSMKQ.mjs.map +7 -0
  69. package/dist/lib/node-esm/index.mjs +25 -24
  70. package/dist/lib/node-esm/index.mjs.map +3 -3
  71. package/dist/lib/node-esm/{intent-resolver-2I5HKCUU.mjs → intent-resolver-6B3PFQ5F.mjs} +8 -8
  72. package/dist/lib/node-esm/intent-resolver-6B3PFQ5F.mjs.map +7 -0
  73. package/dist/lib/node-esm/meta.json +1 -1
  74. package/dist/lib/node-esm/{react-surface-AUWSLYJS.mjs → react-surface-I46BPCWT.mjs} +59 -63
  75. package/dist/lib/node-esm/react-surface-I46BPCWT.mjs.map +7 -0
  76. package/dist/lib/node-esm/{settings-CXGR6DH4.mjs → settings-CJ3T5EX4.mjs} +5 -5
  77. package/dist/lib/node-esm/{settings-CXGR6DH4.mjs.map → settings-CJ3T5EX4.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.mjs +14 -0
  81. package/dist/lib/node-esm/toolkit.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/anchor-sort.d.ts +2 -4
  86. package/dist/types/src/capabilities/anchor-sort.d.ts.map +1 -1
  87. package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -1
  88. package/dist/types/src/capabilities/blueprint-definition.d.ts +5 -3
  89. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -1
  90. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  91. package/dist/types/src/capabilities/index.d.ts +1 -5
  92. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  93. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  94. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  95. package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts +3 -3
  96. package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts.map +1 -1
  97. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts +0 -1
  98. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts.map +1 -1
  99. package/dist/types/src/components/MarkdownContainer.d.ts +8 -12
  100. package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -1
  101. package/dist/types/src/components/MarkdownContainer.stories.d.ts +7 -4
  102. package/dist/types/src/components/MarkdownContainer.stories.d.ts.map +1 -1
  103. package/dist/types/src/components/MarkdownEditor/FileUpload.d.ts +11 -0
  104. package/dist/types/src/components/MarkdownEditor/FileUpload.d.ts.map +1 -0
  105. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts +42 -23
  106. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts.map +1 -1
  107. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts +5 -110
  108. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts.map +1 -1
  109. package/dist/types/src/components/MarkdownEditor/MarkdownEditorContent.d.ts +26 -0
  110. package/dist/types/src/components/MarkdownEditor/MarkdownEditorContent.d.ts.map +1 -0
  111. package/dist/types/src/components/MarkdownEditor/MarkdownEditorToolbar.d.ts +12 -0
  112. package/dist/types/src/components/MarkdownEditor/MarkdownEditorToolbar.d.ts.map +1 -0
  113. package/dist/types/src/components/Suggestions.stories.d.ts +1 -2
  114. package/dist/types/src/components/Suggestions.stories.d.ts.map +1 -1
  115. package/dist/types/src/components/index.d.ts +3 -1
  116. package/dist/types/src/components/index.d.ts.map +1 -1
  117. package/dist/types/src/functions/create.d.ts +8 -0
  118. package/dist/types/src/functions/create.d.ts.map +1 -0
  119. package/dist/types/src/functions/create.test.d.ts +2 -0
  120. package/dist/types/src/functions/create.test.d.ts.map +1 -0
  121. package/dist/types/src/functions/index.d.ts +17 -2
  122. package/dist/types/src/functions/index.d.ts.map +1 -1
  123. package/dist/types/src/functions/open.d.ts +1 -1
  124. package/dist/types/src/functions/open.d.ts.map +1 -1
  125. package/dist/types/src/functions/{diff.d.ts → update.d.ts} +2 -2
  126. package/dist/types/src/functions/update.d.ts.map +1 -0
  127. package/dist/types/src/functions/update.test.d.ts +2 -0
  128. package/dist/types/src/functions/update.test.d.ts.map +1 -0
  129. package/dist/types/src/hooks/index.d.ts +3 -0
  130. package/dist/types/src/hooks/index.d.ts.map +1 -1
  131. package/dist/types/src/hooks/useEditorMenuOptions.d.ts +9 -0
  132. package/dist/types/src/hooks/useEditorMenuOptions.d.ts.map +1 -0
  133. package/dist/types/src/hooks/useExtensions.d.ts +21 -0
  134. package/dist/types/src/hooks/useExtensions.d.ts.map +1 -0
  135. package/dist/types/src/hooks/useLinkQuery.d.ts +4 -0
  136. package/dist/types/src/hooks/useLinkQuery.d.ts.map +1 -0
  137. package/dist/types/src/hooks/useSelectCurrentThread.d.ts +1 -1
  138. package/dist/types/src/hooks/useSelectCurrentThread.d.ts.map +1 -1
  139. package/dist/types/src/testing.d.ts +6 -0
  140. package/dist/types/src/testing.d.ts.map +1 -0
  141. package/dist/types/src/toolkit.d.ts +3 -0
  142. package/dist/types/src/toolkit.d.ts.map +1 -0
  143. package/dist/types/src/translations.d.ts +3 -0
  144. package/dist/types/src/translations.d.ts.map +1 -1
  145. package/dist/types/src/types/Markdown.d.ts +6 -4
  146. package/dist/types/src/types/Markdown.d.ts.map +1 -1
  147. package/dist/types/src/types/MarkdownAction.d.ts +2 -1
  148. package/dist/types/src/types/MarkdownAction.d.ts.map +1 -1
  149. package/dist/types/src/types/index.d.ts.map +1 -1
  150. package/dist/types/src/util.d.ts +3 -3
  151. package/dist/types/src/util.d.ts.map +1 -1
  152. package/dist/types/tsconfig.tsbuildinfo +1 -1
  153. package/package.json +71 -55
  154. package/src/MarkdownPlugin.tsx +99 -101
  155. package/src/capabilities/anchor-sort.ts +3 -3
  156. package/src/capabilities/app-graph-serializer.ts +5 -5
  157. package/src/capabilities/artifact-definition.ts +6 -5
  158. package/src/capabilities/blueprint-definition.ts +30 -26
  159. package/src/capabilities/capabilities.ts +1 -0
  160. package/src/capabilities/index.ts +1 -2
  161. package/src/capabilities/intent-resolver.ts +3 -2
  162. package/src/capabilities/react-surface.tsx +44 -66
  163. package/src/components/MarkdownCard/MarkdownCard.stories.tsx +6 -9
  164. package/src/components/MarkdownCard/MarkdownCard.tsx +52 -38
  165. package/src/components/MarkdownContainer.stories.tsx +74 -38
  166. package/src/components/MarkdownContainer.tsx +78 -220
  167. package/src/components/MarkdownEditor/FileUpload.tsx +63 -0
  168. package/src/components/MarkdownEditor/MarkdownEditor.stories.tsx +55 -35
  169. package/src/components/MarkdownEditor/MarkdownEditor.tsx +221 -270
  170. package/src/components/MarkdownEditor/MarkdownEditorContent.tsx +136 -0
  171. package/src/components/MarkdownEditor/MarkdownEditorToolbar.tsx +63 -0
  172. package/src/components/Suggestions.stories.tsx +40 -33
  173. package/src/components/index.ts +3 -1
  174. package/src/functions/create.conversations.json +1 -0
  175. package/src/functions/create.test.ts +128 -0
  176. package/src/functions/create.ts +34 -0
  177. package/src/functions/index.ts +9 -2
  178. package/src/functions/open.ts +4 -2
  179. package/src/functions/update.conversations.json +1 -0
  180. package/src/functions/update.test.ts +151 -0
  181. package/src/functions/{diff.ts → update.ts} +4 -2
  182. package/src/hooks/index.ts +3 -0
  183. package/src/hooks/useEditorMenuOptions.ts +71 -0
  184. package/src/{extensions.tsx → hooks/useExtensions.tsx} +60 -81
  185. package/src/hooks/useLinkQuery.ts +83 -0
  186. package/src/hooks/useSelectCurrentThread.tsx +15 -5
  187. package/src/meta.ts +3 -3
  188. package/src/testing.ts +27 -0
  189. package/src/toolkit.ts +6 -0
  190. package/src/translations.ts +3 -0
  191. package/src/types/Markdown.ts +9 -7
  192. package/src/types/MarkdownAction.ts +1 -1
  193. package/src/types/index.ts +1 -0
  194. package/src/util.tsx +10 -5
  195. package/dist/lib/browser/MarkdownCard-JLUQITYK.mjs +0 -80
  196. package/dist/lib/browser/MarkdownCard-JLUQITYK.mjs.map +0 -7
  197. package/dist/lib/browser/MarkdownContainer-HMPNQMNG.mjs +0 -751
  198. package/dist/lib/browser/MarkdownContainer-HMPNQMNG.mjs.map +0 -7
  199. package/dist/lib/browser/anchor-sort-E33BSTYF.mjs.map +0 -7
  200. package/dist/lib/browser/app-graph-serializer-OX62DNPT.mjs.map +0 -7
  201. package/dist/lib/browser/blueprint-definition-5YKFUHRU.mjs +0 -11
  202. package/dist/lib/browser/chunk-BEE7VQPU.mjs.map +0 -7
  203. package/dist/lib/browser/chunk-F6JJLKLN.mjs +0 -102
  204. package/dist/lib/browser/chunk-F6JJLKLN.mjs.map +0 -7
  205. package/dist/lib/browser/chunk-LAVZ2W6X.mjs.map +0 -7
  206. package/dist/lib/browser/chunk-ODB2PTBP.mjs.map +0 -7
  207. package/dist/lib/browser/chunk-XE66SH4O.mjs +0 -22
  208. package/dist/lib/browser/chunk-XE66SH4O.mjs.map +0 -7
  209. package/dist/lib/browser/intent-resolver-WDDH56JC.mjs.map +0 -7
  210. package/dist/lib/browser/react-surface-NY5K2I5R.mjs.map +0 -7
  211. package/dist/lib/browser/toolkit-2AJTHG74.mjs +0 -74
  212. package/dist/lib/browser/toolkit-2AJTHG74.mjs.map +0 -7
  213. package/dist/lib/node-esm/MarkdownCard-XL5EVSJ7.mjs +0 -81
  214. package/dist/lib/node-esm/MarkdownCard-XL5EVSJ7.mjs.map +0 -7
  215. package/dist/lib/node-esm/MarkdownContainer-5FTROSXN.mjs +0 -752
  216. package/dist/lib/node-esm/MarkdownContainer-5FTROSXN.mjs.map +0 -7
  217. package/dist/lib/node-esm/anchor-sort-ALP2NH24.mjs.map +0 -7
  218. package/dist/lib/node-esm/app-graph-serializer-56TD3BMX.mjs.map +0 -7
  219. package/dist/lib/node-esm/blueprint-definition-GVW67KGV.mjs +0 -12
  220. package/dist/lib/node-esm/chunk-DVK63TD3.mjs +0 -103
  221. package/dist/lib/node-esm/chunk-DVK63TD3.mjs.map +0 -7
  222. package/dist/lib/node-esm/chunk-FXILAQ5F.mjs.map +0 -7
  223. package/dist/lib/node-esm/chunk-O6EXWGGS.mjs.map +0 -7
  224. package/dist/lib/node-esm/chunk-Q2BREK2M.mjs +0 -24
  225. package/dist/lib/node-esm/chunk-Q2BREK2M.mjs.map +0 -7
  226. package/dist/lib/node-esm/chunk-VCG2U522.mjs.map +0 -7
  227. package/dist/lib/node-esm/intent-resolver-2I5HKCUU.mjs.map +0 -7
  228. package/dist/lib/node-esm/react-surface-AUWSLYJS.mjs.map +0 -7
  229. package/dist/lib/node-esm/toolkit-RC44I2MI.mjs +0 -75
  230. package/dist/lib/node-esm/toolkit-RC44I2MI.mjs.map +0 -7
  231. package/dist/types/src/capabilities/toolkit.d.ts +0 -4
  232. package/dist/types/src/capabilities/toolkit.d.ts.map +0 -1
  233. package/dist/types/src/components/Toolbar.stories.d.ts +0 -48
  234. package/dist/types/src/components/Toolbar.stories.d.ts.map +0 -1
  235. package/dist/types/src/extensions.d.ts +0 -22
  236. package/dist/types/src/extensions.d.ts.map +0 -1
  237. package/dist/types/src/functions/diff.d.ts.map +0 -1
  238. package/src/capabilities/toolkit.ts +0 -47
  239. package/src/components/Toolbar.stories.tsx +0 -118
  240. /package/dist/lib/browser/{blueprint-definition-5YKFUHRU.mjs.map → MarkdownCard-JYMDPKV5.mjs.map} +0 -0
  241. /package/dist/lib/{node-esm/blueprint-definition-GVW67KGV.mjs.map → browser/MarkdownContainer-Y75XSVBX.mjs.map} +0 -0
@@ -0,0 +1,151 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { describe, expect, it } from '@effect/vitest';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Layer from 'effect/Layer';
8
+
9
+ import { AiService, ConsolePrinter, MemoizedAiService } from '@dxos/ai';
10
+ import { TestAiService } from '@dxos/ai/testing';
11
+ import {
12
+ AiConversation,
13
+ type ContextBinding,
14
+ GenerationObserver,
15
+ makeToolExecutionServiceFromFunctions,
16
+ makeToolResolverFromFunctions,
17
+ } from '@dxos/assistant';
18
+ import { Blueprint } from '@dxos/blueprints';
19
+ import { PropertiesType } from '@dxos/client-protocol';
20
+ import { Obj, Query, Ref } from '@dxos/echo';
21
+ import { TestHelpers, acquireReleaseResource } from '@dxos/effect';
22
+ import {
23
+ CredentialsService,
24
+ DatabaseService,
25
+ FunctionInvocationService,
26
+ QueueService,
27
+ TracingService,
28
+ } from '@dxos/functions';
29
+ import { FunctionInvocationServiceLayerTest, TestDatabaseLayer } from '@dxos/functions-runtime/testing';
30
+ import { invariant } from '@dxos/invariant';
31
+ import { ObjectId } from '@dxos/keys';
32
+ import { Markdown } from '@dxos/plugin-markdown/types';
33
+ import { Collection } from '@dxos/schema';
34
+ import { HasSubject, type Message } from '@dxos/types';
35
+
36
+ import { WithProperties, testToolkit } from '../testing';
37
+ import { MarkdownBlueprint, MarkdownFunction } from '../toolkit';
38
+
39
+ ObjectId.dangerouslyDisableRandomness();
40
+
41
+ const TestLayer = Layer.mergeAll(
42
+ AiService.model('@anthropic/claude-opus-4-0'),
43
+ makeToolResolverFromFunctions([MarkdownFunction.create, MarkdownFunction.open, MarkdownFunction.update], testToolkit),
44
+ makeToolExecutionServiceFromFunctions(testToolkit, testToolkit.toLayer({}) as any),
45
+ ).pipe(
46
+ Layer.provideMerge(
47
+ FunctionInvocationServiceLayerTest({
48
+ functions: [MarkdownFunction.create, MarkdownFunction.open, MarkdownFunction.update],
49
+ }),
50
+ ),
51
+ Layer.provideMerge(
52
+ Layer.mergeAll(
53
+ TestAiService(),
54
+ TestDatabaseLayer({
55
+ spaceKey: 'fixed',
56
+ indexing: { vector: true },
57
+ types: [PropertiesType, Collection.Collection, Blueprint.Blueprint, Markdown.Document, HasSubject.HasSubject],
58
+ }),
59
+ CredentialsService.configuredLayer([]),
60
+ TracingService.layerNoop,
61
+ ),
62
+ ),
63
+ );
64
+
65
+ describe('update', () => {
66
+ it.effect(
67
+ 'call a function to update a markdown document',
68
+ Effect.fnUntraced(
69
+ function* (_) {
70
+ const doc = Markdown.make({
71
+ name: 'BlueYard',
72
+ content: 'Founders and portfolio of BlueYard.',
73
+ });
74
+ yield* DatabaseService.add(doc);
75
+
76
+ yield* FunctionInvocationService.invokeFunction(MarkdownFunction.update, {
77
+ id: doc.id,
78
+ diffs: ['- Founders', '+ # Founders'],
79
+ });
80
+
81
+ const updatedDoc = yield* DatabaseService.resolve(Obj.getDXN(doc), Markdown.Document);
82
+ expect(updatedDoc.name).toBe(doc.name);
83
+ const text = yield* DatabaseService.load(updatedDoc.content);
84
+ expect(text.content).toBe('# Founders and portfolio of BlueYard.');
85
+ },
86
+ WithProperties,
87
+ Effect.provide(TestLayer),
88
+ TestHelpers.provideTestContext,
89
+ ),
90
+ );
91
+
92
+ it.scoped(
93
+ 'create and update a markdown document',
94
+ Effect.fnUntraced(
95
+ function* (_) {
96
+ const queue = yield* QueueService.createQueue<Message.Message | ContextBinding>();
97
+ const conversation = yield* acquireReleaseResource(() => new AiConversation(queue));
98
+
99
+ yield* DatabaseService.flush({ indexes: true });
100
+ const markdownBlueprint = yield* DatabaseService.add(Obj.clone(MarkdownBlueprint));
101
+ yield* Effect.promise(() =>
102
+ conversation.context.bind({
103
+ blueprints: [Ref.make(markdownBlueprint)],
104
+ }),
105
+ );
106
+
107
+ const observer = GenerationObserver.fromPrinter(new ConsolePrinter());
108
+
109
+ yield* conversation.createRequest({
110
+ observer,
111
+ prompt: `Create a document with a cookie recipe.`,
112
+ });
113
+ {
114
+ const { objects: docs } = yield* DatabaseService.runQuery(Query.type(Markdown.Document));
115
+ if (docs.length !== 1) {
116
+ throw new Error(`Expected 1 document; got ${docs.length}: ${docs.map((_) => _.name)}`);
117
+ }
118
+
119
+ const doc = docs[0];
120
+ invariant(Obj.instanceOf(Markdown.Document, doc));
121
+ console.log({
122
+ name: doc.name,
123
+ content: yield* DatabaseService.load(doc.content).pipe(Effect.map((_) => _.content)),
124
+ });
125
+ }
126
+
127
+ yield* conversation.createRequest({
128
+ observer,
129
+ prompt: 'Add a section with a holiday-themed variation.',
130
+ });
131
+ {
132
+ const { objects: docs } = yield* DatabaseService.runQuery(Query.type(Markdown.Document));
133
+ if (docs.length !== 1) {
134
+ throw new Error(`Expected 1 document; got ${docs.length}: ${docs.map((_) => _.name)}`);
135
+ }
136
+
137
+ const doc = docs[0];
138
+ invariant(Obj.instanceOf(Markdown.Document, doc));
139
+ console.log({
140
+ name: doc.name,
141
+ content: yield* DatabaseService.load(doc.content).pipe(Effect.map((_) => _.content)),
142
+ });
143
+ }
144
+ },
145
+ WithProperties,
146
+ Effect.provide(TestLayer),
147
+ TestHelpers.provideTestContext,
148
+ ),
149
+ MemoizedAiService.isGenerationEnabled() ? 240_000 : 30_000,
150
+ );
151
+ });
@@ -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/update',
18
+ name: 'Update',
17
19
  description: trim`
18
20
  Applies a set of diffs to the markdown document.
19
21
  `,
@@ -2,4 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ export * from './useEditorMenuOptions';
6
+ export * from './useExtensions';
7
+ export * from './useLinkQuery';
5
8
  export * from './useSelectCurrentThread';
@@ -0,0 +1,71 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type EditorView } from '@codemirror/view';
6
+ import { useCallback, useMemo, useRef } from 'react';
7
+
8
+ import { Domino, toLocalizedString, useTranslation } from '@dxos/react-ui';
9
+ import {
10
+ type EditorMenuGroup,
11
+ type UseEditorMenuProps,
12
+ filterMenuGroups,
13
+ formattingCommands,
14
+ linkSlashCommands,
15
+ } from '@dxos/react-ui-editor';
16
+
17
+ import { meta } from '../meta';
18
+
19
+ export type UseEditorMenuOptionsProps = {
20
+ editorView?: EditorView;
21
+ slashCommandGroups?: EditorMenuGroup[];
22
+ onLinkQuery?: (query?: string) => Promise<EditorMenuGroup[]>;
23
+ };
24
+
25
+ export const useEditorMenuOptions = ({
26
+ editorView,
27
+ slashCommandGroups,
28
+ onLinkQuery,
29
+ }: UseEditorMenuOptionsProps): UseEditorMenuProps => {
30
+ const { t } = useTranslation(meta.id);
31
+
32
+ const getMenu = useCallback<NonNullable<UseEditorMenuProps['getMenu']>>(
33
+ ({ text, trigger }) => {
34
+ switch (trigger) {
35
+ case '@': {
36
+ return onLinkQuery?.(text) ?? [];
37
+ }
38
+
39
+ case '/':
40
+ default: {
41
+ return filterMenuGroups([formattingCommands, linkSlashCommands, ...(slashCommandGroups ?? [])], (item) =>
42
+ text ? toLocalizedString(item.label, t).toLowerCase().includes(text.toLowerCase()) : true,
43
+ );
44
+ }
45
+ }
46
+ },
47
+ [slashCommandGroups, onLinkQuery],
48
+ );
49
+
50
+ const viewRef = useRef(editorView);
51
+ return useMemo<UseEditorMenuProps>(() => {
52
+ const trigger = onLinkQuery ? ['/', '@'] : ['/'];
53
+ const placeholder = {
54
+ delay: 3_000,
55
+ content: () =>
56
+ Domino.of('div')
57
+ .children(
58
+ Domino.of('span').text('Press'),
59
+ ...trigger.map((text) =>
60
+ Domino.of('span')
61
+ .classNames('mx-1 pli-1.5 pt-[1px] pb-[2px] border border-separator rounded-sm')
62
+ .text(text),
63
+ ),
64
+ Domino.of('span').text('for commands.'),
65
+ )
66
+ .build(),
67
+ };
68
+
69
+ return { viewRef, getMenu, trigger, placeholder };
70
+ }, [getMenu, onLinkQuery]);
71
+ };
@@ -6,16 +6,13 @@ 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 } from '@dxos/app-framework';
10
+ import { useIntentDispatcher } from '@dxos/app-framework/react';
16
11
  import { debounceAndThrottle } from '@dxos/async';
12
+ import { Obj } from '@dxos/echo';
17
13
  import { invariant } from '@dxos/invariant';
18
- import { createDocAccessor, fullyQualifiedId, getSpace } from '@dxos/react-client/echo';
14
+ import { createDocAccessor } from '@dxos/react-client/echo';
15
+ import { getSpace } from '@dxos/react-client/echo';
19
16
  import { useIdentity } from '@dxos/react-client/halo';
20
17
  import { Icon, ThemeProvider } from '@dxos/react-ui';
21
18
  import { type SelectionManager } from '@dxos/react-ui-attention';
@@ -36,23 +33,24 @@ import {
36
33
  linkTooltip,
37
34
  listener,
38
35
  preview,
36
+ replacer,
39
37
  selectionState,
40
38
  typewriter,
41
39
  } from '@dxos/react-ui-editor';
42
40
  import { defaultTx } from '@dxos/react-ui-theme';
43
- import { type DataType } from '@dxos/schema';
44
- import { isNotFalsy } from '@dxos/util';
41
+ import { Text } from '@dxos/schema';
42
+ import { isTruthy } from '@dxos/util';
43
+
44
+ import { Markdown } from '../types';
45
+ import { setFallbackName } from '../util';
45
46
 
46
- import { MarkdownCapabilities } from './capabilities';
47
- import { type Markdown } from './types';
48
- import { setFallbackName } from './util';
47
+ export type DocumentType = Markdown.Document | Text.Text | { id: string; text: string };
49
48
 
50
- type ExtensionsOptions = {
51
- document?: Markdown.Document;
52
- id?: string;
53
- text?: DataType.Text;
49
+ export type ExtensionsOptions = {
50
+ id: string;
51
+ object?: DocumentType;
54
52
  dispatch?: PromiseIntentDispatcher;
55
- settings: Markdown.Settings;
53
+ settings?: Markdown.Settings;
56
54
  selectionManager?: SelectionManager;
57
55
  viewMode?: EditorViewMode;
58
56
  editorStateStore?: EditorStateStore;
@@ -61,9 +59,8 @@ type ExtensionsOptions = {
61
59
 
62
60
  // TODO(burdon): Merge with createBaseExtensions below.
63
61
  export const useExtensions = ({
64
- document,
65
62
  id,
66
- text,
63
+ object,
67
64
  settings,
68
65
  selectionManager,
69
66
  viewMode,
@@ -72,93 +69,72 @@ export const useExtensions = ({
72
69
  }: ExtensionsOptions): Extension[] => {
73
70
  const { dispatchPromise: dispatch } = useIntentDispatcher();
74
71
  const identity = useIdentity();
75
- const space = getSpace(document) ?? getSpace(text);
72
+ const space = getSpace(object);
73
+
74
+ let target: Obj.Any | undefined;
75
+ if (Obj.instanceOf(Markdown.Document, object)) {
76
+ target = (object as Markdown.Document).content.target;
77
+ } else if (Obj.instanceOf(Text.Text, object)) {
78
+ target = object;
79
+ }
76
80
 
77
81
  // TODO(wittjosiah): Autocomplete is not working and this query is causing performance issues.
78
82
  // TODO(burdon): Unsubscribe.
79
83
  // const query = space?.db.query(Filter.type(DocumentType));
80
84
  // query?.subscribe();
85
+
81
86
  const baseExtensions = useMemo(
82
87
  () =>
83
88
  createBaseExtensions({
84
- document,
85
89
  id,
86
- text,
90
+ object,
87
91
  settings,
88
92
  selectionManager,
89
93
  viewMode,
90
94
  previewOptions,
91
95
  dispatch,
92
- // query,
93
96
  }),
94
97
  [
95
- document,
96
98
  id,
97
- text,
99
+ object,
98
100
  viewMode,
99
101
  dispatch,
100
102
  previewOptions,
101
103
  settings,
102
- settings.editorInputMode,
103
- settings.folding,
104
- settings.numberedHeadings,
105
- settings.debug,
106
- settings.typewriter,
104
+ settings?.debug,
105
+ settings?.editorInputMode,
106
+ settings?.folding,
107
+ settings?.numberedHeadings,
108
+ settings?.typewriter,
107
109
  selectionManager,
108
110
  ],
109
111
  );
110
112
 
111
- const extensionProviders = useCapabilities(MarkdownCapabilities.Extensions);
112
-
113
- //
114
- // External extensions from other plugins.
115
- //
116
- const pluginExtensions = useMemo<Extension[]>(() => {
117
- if (!document) {
118
- return [];
119
- }
120
-
121
- return extensionProviders.flat().reduce((acc: Extension[], provider) => {
122
- const extension = typeof provider === 'function' ? provider({ document }) : provider;
123
- if (extension) {
124
- acc.push(extension);
125
- }
126
-
127
- return acc;
128
- }, []);
129
- }, [extensionProviders, document]);
130
-
131
- //
132
- // Basic plugins.
133
- //
134
113
  return useMemo<Extension[]>(
135
114
  () =>
136
115
  [
116
+ // TODO(burdon): Pass this in?
137
117
  // NOTE: Data extensions must be first so that automerge is updated before other extensions compute their state.
138
- document &&
139
- createDataExtensions({
140
- id: document.id,
141
- text: document.content.target && createDocAccessor(document.content.target, ['content']),
142
- space,
143
- identity,
144
- }),
145
- text &&
146
- id &&
118
+ target &&
147
119
  createDataExtensions({
148
120
  id,
149
- text: createDocAccessor(text, ['content']),
121
+ text: createDocAccessor(target, ['content']),
150
122
  space,
151
123
  identity,
152
124
  }),
153
- selectionState(editorStateStore),
154
- document &&
125
+
126
+ // TODO(burdon): Reconcile with effect in parent.
127
+ Obj.instanceOf(Markdown.Document, object) &&
155
128
  listener({
156
- onChange: (text) => setFallbackName(document, text),
129
+ onChange: ({ text }) => {
130
+ setFallbackName(object as Markdown.Document, text);
131
+ },
157
132
  }),
133
+
158
134
  baseExtensions,
159
- pluginExtensions,
160
- ].filter(isNotFalsy),
161
- [baseExtensions, pluginExtensions, document, document?.content?.target, text, id, space, identity],
135
+ selectionState(editorStateStore),
136
+ ].filter(isTruthy),
137
+ [identity, space, id, object, target, baseExtensions],
162
138
  );
163
139
  };
164
140
 
@@ -166,8 +142,8 @@ export const useExtensions = ({
166
142
  * Create extension instances for editor.
167
143
  */
168
144
  const createBaseExtensions = ({
169
- document,
170
145
  id,
146
+ object,
171
147
  dispatch,
172
148
  settings,
173
149
  selectionManager,
@@ -176,9 +152,9 @@ const createBaseExtensions = ({
176
152
  }: ExtensionsOptions): Extension[] => {
177
153
  const extensions: Extension[] = [
178
154
  selectionManager && selectionChange(selectionManager),
179
- settings.editorInputMode && InputModeExtensions[settings.editorInputMode],
180
- settings.folding && folding(),
181
- ].filter(isNotFalsy);
155
+ settings?.editorInputMode && InputModeExtensions[settings.editorInputMode],
156
+ settings?.folding && folding(),
157
+ ].filter(isTruthy);
182
158
 
183
159
  //
184
160
  // Markdown
@@ -189,17 +165,18 @@ const createBaseExtensions = ({
189
165
  formattingKeymap(),
190
166
  decorateMarkdown({
191
167
  selectionChangeDelay: 100,
192
- numberedHeadings: settings.numberedHeadings ? { from: 2 } : undefined,
168
+ numberedHeadings: settings?.numberedHeadings ? { from: 2 } : undefined,
193
169
  // TODO(wittjosiah): For internal links, consider ignoring the link text and rendering the label of the object being linked to.
170
+ // TODO(burdon): Create dx-tag.
194
171
  renderLinkButton:
195
- dispatch && (document || id)
172
+ dispatch && (object || id)
196
173
  ? createLinkRenderer((id: string) => {
197
174
  void dispatch(
198
175
  createIntent(LayoutAction.Open, {
199
176
  part: 'main',
200
177
  subject: [id],
201
178
  options: {
202
- pivotId: document ? fullyQualifiedId(document) : id,
179
+ pivotId: object && Obj.isObject(object) ? Obj.getDXN(object).toString() : id,
203
180
  },
204
181
  }),
205
182
  );
@@ -208,11 +185,12 @@ const createBaseExtensions = ({
208
185
  }),
209
186
  linkTooltip(renderLinkTooltip),
210
187
  preview(previewOptions),
188
+ replacer(),
211
189
  ],
212
190
  );
213
191
  }
214
192
 
215
- if (settings.debug) {
193
+ if (settings?.debug) {
216
194
  const items = settings.typewriter?.split(/[,\n]/) ?? '';
217
195
  if (items) {
218
196
  extensions.push(typewriter({ items }));
@@ -222,7 +200,7 @@ const createBaseExtensions = ({
222
200
  return extensions;
223
201
  };
224
202
 
225
- export const selectionChange = (selectionManager: SelectionManager) => {
203
+ const selectionChange = (selectionManager: SelectionManager) => {
226
204
  return EditorView.updateListener.of(
227
205
  debounceAndThrottle((update: ViewUpdate) => {
228
206
  if (update.selectionSet) {
@@ -235,6 +213,7 @@ export const selectionChange = (selectionManager: SelectionManager) => {
235
213
  to: cursorConverter.toCursor(range.to),
236
214
  }))
237
215
  .filter(({ from, to }) => to > from);
216
+
238
217
  selectionManager.updateMultiRange(id, ranges);
239
218
  }
240
219
  }, 100),
@@ -293,8 +272,8 @@ const renderLinkTooltip: RenderCallback<{ url: string }> = (el, { url }) => {
293
272
  );
294
273
  };
295
274
 
296
- // TODO(burdon): Remove react rendering; use DOM directly.
297
- export const renderRoot = <T extends Element>(root: T, node: ReactNode): T => {
275
+ // TODO(burdon): REMOVE.
276
+ const renderRoot = <T extends Element>(root: T, node: ReactNode): T => {
298
277
  createRoot(root).render(<ThemeProvider tx={defaultTx}>{node}</ThemeProvider>);
299
278
  return root;
300
279
  };
@@ -0,0 +1,83 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { useCallback, useMemo } from 'react';
6
+
7
+ import { Capabilities } from '@dxos/app-framework';
8
+ import { useCapabilities, usePluginManager } from '@dxos/app-framework/react';
9
+ import { Filter, Obj, Query, Type } from '@dxos/echo';
10
+ import { ClientCapabilities } from '@dxos/plugin-client';
11
+ import { SpaceCapabilities } from '@dxos/plugin-space';
12
+ import { type Space } from '@dxos/react-client/echo';
13
+ import { toLocalizedString, useTranslation } from '@dxos/react-ui';
14
+ import { type EditorMenuGroup, type EditorMenuItem, insertAtCursor, insertAtLineStart } from '@dxos/react-ui-editor';
15
+
16
+ export const useLinkQuery = (space: Space | undefined) => {
17
+ const { t } = useTranslation();
18
+
19
+ const manager = usePluginManager();
20
+ const resolve = useCallback(
21
+ (typename: string) =>
22
+ manager.context.getCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ?? {},
23
+ [manager],
24
+ );
25
+
26
+ const objectForms = useCapabilities(SpaceCapabilities.ObjectForm);
27
+ const schemaWhiteList = useCapabilities(ClientCapabilities.SchemaWhiteList);
28
+ const filter = useMemo(
29
+ () =>
30
+ Filter.or(
31
+ ...objectForms.map((form) => Filter.type(form.objectSchema)),
32
+ ...schemaWhiteList.flat().map((schema) => Filter.typename(Type.getTypename(schema))),
33
+ ),
34
+ [objectForms, schemaWhiteList],
35
+ );
36
+
37
+ const handleLinkQuery = useCallback(
38
+ async (query?: string): Promise<EditorMenuGroup[]> => {
39
+ const name = query?.startsWith('@') ? query.slice(1).toLowerCase() : (query?.toLowerCase() ?? '');
40
+ const results = await space?.db.query(Query.select(filter)).run();
41
+
42
+ // TODO(wittjosiah): Use `Obj.Any` type.
43
+ const getLabel = (object: any) => {
44
+ const label = Obj.getLabel(object);
45
+ if (label) {
46
+ return label;
47
+ }
48
+
49
+ // TODO(wittjosiah): Remove metadata labels.
50
+ const type = Obj.getTypename(object)!;
51
+ const metadata = resolve(type);
52
+ return metadata.label?.(object) || ['object name placeholder', { ns: type, default: 'New object' }];
53
+ };
54
+
55
+ const items =
56
+ results?.objects
57
+ .filter((object) => toLocalizedString(getLabel(object), t).toLowerCase().includes(name))
58
+ // TODO(wittjosiah): Remove `any` type.
59
+ .map((object: any): EditorMenuItem => {
60
+ const metadata = resolve(Obj.getTypename(object)!);
61
+ const label = toLocalizedString(getLabel(object), t);
62
+ return {
63
+ id: object.id,
64
+ label,
65
+ icon: metadata.icon,
66
+ onSelect: ({ view, head }) => {
67
+ const link = `[${label}](${Obj.getDXN(object)})`;
68
+ if (query?.startsWith('@')) {
69
+ insertAtLineStart(view, head, `!${link}\n`);
70
+ } else {
71
+ insertAtCursor(view, head, `${link} `);
72
+ }
73
+ },
74
+ };
75
+ }) ?? [];
76
+
77
+ return [{ id: 'echo', items }];
78
+ },
79
+ [space, filter, resolve],
80
+ );
81
+
82
+ return handleLinkQuery;
83
+ };
@@ -3,10 +3,11 @@
3
3
  //
4
4
 
5
5
  import { EditorView } from '@codemirror/view';
6
- import { Schema } from 'effect';
6
+ import * as Schema from 'effect/Schema';
7
7
  import { useMemo } from 'react';
8
8
 
9
- import { LayoutAction, createResolver, useIntentResolver } from '@dxos/app-framework';
9
+ import { LayoutAction, createResolver } from '@dxos/app-framework';
10
+ import { useIntentResolver } from '@dxos/app-framework/react';
10
11
  import { invariant } from '@dxos/invariant';
11
12
  import { Cursor, setSelection } from '@dxos/react-ui-editor';
12
13
 
@@ -15,13 +16,19 @@ import { meta } from '../meta';
15
16
  /**
16
17
  * Handle scrolling and selection of the current thread in a markdown editor.
17
18
  */
18
- export const useSelectCurrentThread = (editorView: EditorView | undefined, documentId: string) => {
19
+ export const useSelectCurrentThread = (editorView: EditorView | null, documentId: string) => {
19
20
  const scrollIntoViewResolver = useMemo(
20
21
  () =>
21
22
  createResolver({
22
23
  intent: LayoutAction.UpdateLayout,
23
24
  position: 'hoist',
24
- filter: (data): data is { part: 'current'; subject: string; options: { cursor: string } } => {
25
+ filter: (
26
+ data,
27
+ ): data is {
28
+ part: 'current';
29
+ subject: string;
30
+ options: { cursor: string };
31
+ } => {
25
32
  if (!Schema.is(LayoutAction.ScrollIntoView.fields.input)(data)) {
26
33
  return false;
27
34
  }
@@ -35,7 +42,10 @@ export const useSelectCurrentThread = (editorView: EditorView | undefined, docum
35
42
  const selection = editorView.state.selection.main.from !== range.from ? { anchor: range.from } : undefined;
36
43
  const effects = [
37
44
  // NOTE: This does not use the DOM scrollIntoView function.
38
- EditorView.scrollIntoView(range.from, { y: 'start', yMargin: 96 }),
45
+ EditorView.scrollIntoView(range.from, {
46
+ y: 'start',
47
+ yMargin: 96,
48
+ }),
39
49
  ];
40
50
  if (selection) {
41
51
  // Update the editor selection to get bi-directional highlighting.
package/src/meta.ts CHANGED
@@ -9,11 +9,11 @@ export const meta: PluginMeta = {
9
9
  id: 'dxos.org/plugin/markdown',
10
10
  name: 'Markdown',
11
11
  description: trim`
12
- A collaborative and extensible Markdown editor.
13
- In addition to markdown capabilities, the plugin supports collaborative in-line comments.
14
- You can use documents to extend the memory of your personal agents and add context for automated workflows.
12
+ Full-featured collaborative markdown editor with real-time editing, inline comments, and rich formatting.
13
+ Supports AI-powered editing assistance and seamlessly integrates with other workspace objects.
15
14
  `,
16
15
  icon: 'ph--text-aa--regular',
16
+ iconHue: 'indigo',
17
17
  source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-markdown',
18
18
  screenshots: ['https://dxos.network/plugin-details-markdown-dark.png'],
19
19
  };