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

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 (235) hide show
  1. package/dist/lib/browser/MarkdownCard-TC3GGUSX.mjs +12 -0
  2. package/dist/lib/browser/MarkdownContainer-YF22DV4M.mjs +15 -0
  3. package/dist/lib/browser/{anchor-sort-E33BSTYF.mjs → anchor-sort-3MYLO74J.mjs} +10 -10
  4. package/dist/lib/browser/anchor-sort-3MYLO74J.mjs.map +7 -0
  5. package/dist/lib/browser/{app-graph-serializer-OX62DNPT.mjs → app-graph-serializer-BZJ4TQOE.mjs} +10 -10
  6. package/dist/lib/browser/app-graph-serializer-BZJ4TQOE.mjs.map +7 -0
  7. package/dist/lib/browser/blueprint-definition-HED54GGW.mjs +13 -0
  8. package/dist/lib/browser/blueprint-definition-HED54GGW.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-3IPVL3E4.mjs +20 -0
  10. package/dist/lib/browser/chunk-3IPVL3E4.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-BE3IO2CM.mjs +150 -0
  12. package/dist/lib/browser/chunk-BE3IO2CM.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-BEE7VQPU.mjs → chunk-CN35HEBX.mjs} +13 -12
  16. package/dist/lib/browser/chunk-CN35HEBX.mjs.map +7 -0
  17. package/dist/lib/browser/chunk-DLZ5RR3P.mjs +96 -0
  18. package/dist/lib/browser/chunk-DLZ5RR3P.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-Z7P6JGGW.mjs → chunk-NXT2E2BG.mjs} +9 -5
  22. package/dist/lib/browser/chunk-NXT2E2BG.mjs.map +7 -0
  23. package/dist/lib/browser/{chunk-LAVZ2W6X.mjs → chunk-OKNQ57GF.mjs} +9 -8
  24. package/dist/lib/browser/chunk-OKNQ57GF.mjs.map +7 -0
  25. package/dist/lib/browser/chunk-V7P7EIWG.mjs +829 -0
  26. package/dist/lib/browser/chunk-V7P7EIWG.mjs.map +7 -0
  27. package/dist/lib/browser/index.mjs +22 -28
  28. package/dist/lib/browser/index.mjs.map +3 -3
  29. package/dist/lib/browser/{intent-resolver-WDDH56JC.mjs → intent-resolver-55ASQRIW.mjs} +9 -9
  30. package/dist/lib/browser/intent-resolver-55ASQRIW.mjs.map +7 -0
  31. package/dist/lib/browser/meta.json +1 -1
  32. package/dist/lib/browser/{react-surface-LN2XK2UN.mjs → react-surface-GPD6T435.mjs} +59 -63
  33. package/dist/lib/browser/react-surface-GPD6T435.mjs.map +7 -0
  34. package/dist/lib/browser/{settings-AABBTB4Q.mjs → settings-G3ZOXJQY.mjs} +5 -5
  35. package/dist/lib/browser/{settings-AABBTB4Q.mjs.map → settings-G3ZOXJQY.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-FTEHVH7P.mjs +13 -0
  42. package/dist/lib/node-esm/MarkdownCard-FTEHVH7P.mjs.map +7 -0
  43. package/dist/lib/node-esm/MarkdownContainer-L75AIJFM.mjs +16 -0
  44. package/dist/lib/node-esm/MarkdownContainer-L75AIJFM.mjs.map +7 -0
  45. package/dist/lib/node-esm/{anchor-sort-ALP2NH24.mjs → anchor-sort-W4HCTYUQ.mjs} +10 -10
  46. package/dist/lib/node-esm/anchor-sort-W4HCTYUQ.mjs.map +7 -0
  47. package/dist/lib/node-esm/{app-graph-serializer-56TD3BMX.mjs → app-graph-serializer-OCTHXWLF.mjs} +10 -10
  48. package/dist/lib/node-esm/app-graph-serializer-OCTHXWLF.mjs.map +7 -0
  49. package/dist/lib/node-esm/blueprint-definition-HAQGJOMQ.mjs +14 -0
  50. package/dist/lib/node-esm/blueprint-definition-HAQGJOMQ.mjs.map +7 -0
  51. package/dist/lib/node-esm/chunk-3DN4DPVA.mjs +151 -0
  52. package/dist/lib/node-esm/chunk-3DN4DPVA.mjs.map +7 -0
  53. package/dist/lib/node-esm/{chunk-O6EXWGGS.mjs → chunk-7GN66TDQ.mjs} +9 -8
  54. package/dist/lib/node-esm/chunk-7GN66TDQ.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-J7A6TUB2.mjs → chunk-I5JSQBPI.mjs} +9 -5
  58. package/dist/lib/node-esm/chunk-I5JSQBPI.mjs.map +7 -0
  59. package/dist/lib/node-esm/{chunk-VCG2U522.mjs → chunk-SHAMSMKQ.mjs} +4 -4
  60. package/dist/lib/node-esm/chunk-SHAMSMKQ.mjs.map +7 -0
  61. package/dist/lib/node-esm/{chunk-JPXFCBC4.mjs → chunk-TYUGABTD.mjs} +8 -8
  62. package/dist/lib/node-esm/chunk-TYUGABTD.mjs.map +7 -0
  63. package/dist/lib/node-esm/chunk-UVLACSAE.mjs +830 -0
  64. package/dist/lib/node-esm/chunk-UVLACSAE.mjs.map +7 -0
  65. package/dist/lib/node-esm/{chunk-FXILAQ5F.mjs → chunk-YFRTKXTB.mjs} +13 -12
  66. package/dist/lib/node-esm/chunk-YFRTKXTB.mjs.map +7 -0
  67. package/dist/lib/node-esm/chunk-ZNS55FS3.mjs +97 -0
  68. package/dist/lib/node-esm/chunk-ZNS55FS3.mjs.map +7 -0
  69. package/dist/lib/node-esm/index.mjs +22 -28
  70. package/dist/lib/node-esm/index.mjs.map +3 -3
  71. package/dist/lib/node-esm/{intent-resolver-2I5HKCUU.mjs → intent-resolver-DTBVWCNO.mjs} +9 -9
  72. package/dist/lib/node-esm/intent-resolver-DTBVWCNO.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-QWG7CJ5U.mjs} +59 -63
  75. package/dist/lib/node-esm/react-surface-QWG7CJ5U.mjs.map +7 -0
  76. package/dist/lib/node-esm/{settings-CXGR6DH4.mjs → settings-IBFFCGNL.mjs} +5 -5
  77. package/dist/lib/node-esm/{settings-CXGR6DH4.mjs.map → settings-IBFFCGNL.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 -4
  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 +8 -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 +5 -9
  118. package/dist/types/src/functions/create.d.ts.map +1 -1
  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 -3
  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 +4 -0
  144. package/dist/types/src/translations.d.ts.map +1 -1
  145. package/dist/types/src/types/Markdown.d.ts +11 -9
  146. package/dist/types/src/types/Markdown.d.ts.map +1 -1
  147. package/dist/types/src/types/MarkdownAction.d.ts +6 -5
  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 +72 -56
  154. package/src/MarkdownPlugin.tsx +87 -98
  155. package/src/capabilities/anchor-sort.ts +7 -6
  156. package/src/capabilities/app-graph-serializer.ts +5 -5
  157. package/src/capabilities/artifact-definition.ts +7 -6
  158. package/src/capabilities/blueprint-definition.ts +30 -26
  159. package/src/capabilities/capabilities.ts +1 -0
  160. package/src/capabilities/index.ts +1 -1
  161. package/src/capabilities/intent-resolver.ts +4 -3
  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 -273
  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 +42 -34
  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 +20 -9
  177. package/src/functions/index.ts +9 -3
  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} +61 -110
  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 +4 -0
  191. package/src/types/Markdown.ts +9 -8
  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-7M37DXAD.mjs +0 -781
  198. package/dist/lib/browser/MarkdownContainer-7M37DXAD.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-Z3RQGWUD.mjs +0 -11
  202. package/dist/lib/browser/chunk-BEE7VQPU.mjs.map +0 -7
  203. package/dist/lib/browser/chunk-D7UYVHL6.mjs +0 -20
  204. package/dist/lib/browser/chunk-D7UYVHL6.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-Z7P6JGGW.mjs.map +0 -7
  208. package/dist/lib/browser/chunk-ZVVKLB5L.mjs +0 -121
  209. package/dist/lib/browser/chunk-ZVVKLB5L.mjs.map +0 -7
  210. package/dist/lib/browser/intent-resolver-WDDH56JC.mjs.map +0 -7
  211. package/dist/lib/browser/react-surface-LN2XK2UN.mjs.map +0 -7
  212. package/dist/lib/node-esm/MarkdownCard-XL5EVSJ7.mjs +0 -81
  213. package/dist/lib/node-esm/MarkdownCard-XL5EVSJ7.mjs.map +0 -7
  214. package/dist/lib/node-esm/MarkdownContainer-K3BPAGWO.mjs +0 -782
  215. package/dist/lib/node-esm/MarkdownContainer-K3BPAGWO.mjs.map +0 -7
  216. package/dist/lib/node-esm/anchor-sort-ALP2NH24.mjs.map +0 -7
  217. package/dist/lib/node-esm/app-graph-serializer-56TD3BMX.mjs.map +0 -7
  218. package/dist/lib/node-esm/blueprint-definition-735OAX33.mjs +0 -12
  219. package/dist/lib/node-esm/chunk-FXILAQ5F.mjs.map +0 -7
  220. package/dist/lib/node-esm/chunk-J7A6TUB2.mjs.map +0 -7
  221. package/dist/lib/node-esm/chunk-JPXFCBC4.mjs.map +0 -7
  222. package/dist/lib/node-esm/chunk-O6EXWGGS.mjs.map +0 -7
  223. package/dist/lib/node-esm/chunk-VCG2U522.mjs.map +0 -7
  224. package/dist/lib/node-esm/chunk-Y422WR6A.mjs +0 -122
  225. package/dist/lib/node-esm/chunk-Y422WR6A.mjs.map +0 -7
  226. package/dist/lib/node-esm/intent-resolver-2I5HKCUU.mjs.map +0 -7
  227. package/dist/lib/node-esm/react-surface-DJGGKYBD.mjs.map +0 -7
  228. package/dist/types/src/components/Toolbar.stories.d.ts +0 -48
  229. package/dist/types/src/components/Toolbar.stories.d.ts.map +0 -1
  230. package/dist/types/src/extensions.d.ts +0 -24
  231. package/dist/types/src/extensions.d.ts.map +0 -1
  232. package/dist/types/src/functions/diff.d.ts.map +0 -1
  233. package/src/components/Toolbar.stories.tsx +0 -118
  234. /package/dist/lib/browser/{blueprint-definition-Z3RQGWUD.mjs.map → MarkdownCard-TC3GGUSX.mjs.map} +0 -0
  235. /package/dist/lib/{node-esm/blueprint-definition-735OAX33.mjs.map → browser/MarkdownContainer-YF22DV4M.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 { SpaceProperties } 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: [SpaceProperties, 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,21 +6,17 @@ 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';
13
+ import { createDocAccessor } from '@dxos/echo-db';
17
14
  import { invariant } from '@dxos/invariant';
18
- import { type QueryResult, createDocAccessor, fullyQualifiedId, getSpace } 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';
22
19
  import {
23
- type AutocompleteResult,
24
20
  Cursor,
25
21
  type EditorStateStore,
26
22
  EditorView,
@@ -29,7 +25,6 @@ import {
29
25
  InputModeExtensions,
30
26
  type PreviewOptions,
31
27
  type RenderCallback,
32
- autocomplete,
33
28
  createDataExtensions,
34
29
  decorateMarkdown,
35
30
  documentId,
@@ -38,24 +33,24 @@ import {
38
33
  linkTooltip,
39
34
  listener,
40
35
  preview,
36
+ replacer,
41
37
  selectionState,
42
38
  typewriter,
43
39
  } from '@dxos/react-ui-editor';
44
40
  import { defaultTx } from '@dxos/react-ui-theme';
45
- import { type DataType } from '@dxos/schema';
46
- 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';
47
46
 
48
- import { MarkdownCapabilities } from './capabilities';
49
- import { type Markdown } from './types';
50
- import { setFallbackName } from './util';
47
+ export type DocumentType = Markdown.Document | Text.Text | { id: string; text: string };
51
48
 
52
- type ExtensionsOptions = {
53
- document?: Markdown.Document;
54
- id?: string;
55
- text?: DataType.Text;
49
+ export type ExtensionsOptions = {
50
+ id: string;
51
+ object?: DocumentType;
56
52
  dispatch?: PromiseIntentDispatcher;
57
- query?: QueryResult<Markdown.Document>;
58
- settings: Markdown.Settings;
53
+ settings?: Markdown.Settings;
59
54
  selectionManager?: SelectionManager;
60
55
  viewMode?: EditorViewMode;
61
56
  editorStateStore?: EditorStateStore;
@@ -64,9 +59,8 @@ type ExtensionsOptions = {
64
59
 
65
60
  // TODO(burdon): Merge with createBaseExtensions below.
66
61
  export const useExtensions = ({
67
- document,
68
62
  id,
69
- text,
63
+ object,
70
64
  settings,
71
65
  selectionManager,
72
66
  viewMode,
@@ -75,93 +69,72 @@ export const useExtensions = ({
75
69
  }: ExtensionsOptions): Extension[] => {
76
70
  const { dispatchPromise: dispatch } = useIntentDispatcher();
77
71
  const identity = useIdentity();
78
- 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
+ }
79
80
 
80
81
  // TODO(wittjosiah): Autocomplete is not working and this query is causing performance issues.
81
82
  // TODO(burdon): Unsubscribe.
82
83
  // const query = space?.db.query(Filter.type(DocumentType));
83
84
  // query?.subscribe();
85
+
84
86
  const baseExtensions = useMemo(
85
87
  () =>
86
88
  createBaseExtensions({
87
- document,
88
89
  id,
89
- text,
90
+ object,
90
91
  settings,
91
92
  selectionManager,
92
93
  viewMode,
93
94
  previewOptions,
94
95
  dispatch,
95
- // query,
96
96
  }),
97
97
  [
98
- document,
99
98
  id,
100
- text,
99
+ object,
101
100
  viewMode,
102
101
  dispatch,
103
102
  previewOptions,
104
103
  settings,
105
- settings.editorInputMode,
106
- settings.folding,
107
- settings.numberedHeadings,
108
- settings.debug,
109
- settings.typewriter,
104
+ settings?.debug,
105
+ settings?.editorInputMode,
106
+ settings?.folding,
107
+ settings?.numberedHeadings,
108
+ settings?.typewriter,
110
109
  selectionManager,
111
110
  ],
112
111
  );
113
112
 
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
113
  return useMemo<Extension[]>(
138
114
  () =>
139
115
  [
116
+ // TODO(burdon): Pass this in?
140
117
  // 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 &&
118
+ target &&
150
119
  createDataExtensions({
151
120
  id,
152
- text: createDocAccessor(text, ['content']),
153
- space,
121
+ text: createDocAccessor(target, ['content']),
122
+ messenger: space,
154
123
  identity,
155
124
  }),
156
- selectionState(editorStateStore),
157
- document &&
125
+
126
+ // TODO(burdon): Reconcile with effect in parent.
127
+ Obj.instanceOf(Markdown.Document, object) &&
158
128
  listener({
159
- onChange: (text) => setFallbackName(document, text),
129
+ onChange: ({ text }) => {
130
+ setFallbackName(object as Markdown.Document, text);
131
+ },
160
132
  }),
133
+
161
134
  baseExtensions,
162
- pluginExtensions,
163
- ].filter(isNotFalsy),
164
- [baseExtensions, pluginExtensions, document, document?.content?.target, text, id, space, identity],
135
+ selectionState(editorStateStore),
136
+ ].filter(isTruthy),
137
+ [identity, space, id, object, target, baseExtensions],
165
138
  );
166
139
  };
167
140
 
@@ -169,20 +142,19 @@ export const useExtensions = ({
169
142
  * Create extension instances for editor.
170
143
  */
171
144
  const createBaseExtensions = ({
172
- document,
173
145
  id,
146
+ object,
174
147
  dispatch,
175
148
  settings,
176
149
  selectionManager,
177
- query,
178
150
  viewMode,
179
151
  previewOptions,
180
152
  }: ExtensionsOptions): Extension[] => {
181
153
  const extensions: Extension[] = [
182
154
  selectionManager && selectionChange(selectionManager),
183
- settings.editorInputMode && InputModeExtensions[settings.editorInputMode],
184
- settings.folding && folding(),
185
- ].filter(isNotFalsy);
155
+ settings?.editorInputMode && InputModeExtensions[settings.editorInputMode],
156
+ settings?.folding && folding(),
157
+ ].filter(isTruthy);
186
158
 
187
159
  //
188
160
  // Markdown
@@ -193,17 +165,18 @@ const createBaseExtensions = ({
193
165
  formattingKeymap(),
194
166
  decorateMarkdown({
195
167
  selectionChangeDelay: 100,
196
- numberedHeadings: settings.numberedHeadings ? { from: 2 } : undefined,
168
+ numberedHeadings: settings?.numberedHeadings ? { from: 2 } : undefined,
197
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.
198
171
  renderLinkButton:
199
- dispatch && (document || id)
172
+ dispatch && (object || id)
200
173
  ? createLinkRenderer((id: string) => {
201
174
  void dispatch(
202
175
  createIntent(LayoutAction.Open, {
203
176
  part: 'main',
204
177
  subject: [id],
205
178
  options: {
206
- pivotId: document ? fullyQualifiedId(document) : id,
179
+ pivotId: object && Obj.isObject(object) ? Obj.getDXN(object).toString() : id,
207
180
  },
208
181
  }),
209
182
  );
@@ -212,35 +185,12 @@ const createBaseExtensions = ({
212
185
  }),
213
186
  linkTooltip(renderLinkTooltip),
214
187
  preview(previewOptions),
188
+ replacer(),
215
189
  ],
216
190
  );
217
191
  }
218
192
 
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) {
193
+ if (settings?.debug) {
244
194
  const items = settings.typewriter?.split(/[,\n]/) ?? '';
245
195
  if (items) {
246
196
  extensions.push(typewriter({ items }));
@@ -250,7 +200,7 @@ const createBaseExtensions = ({
250
200
  return extensions;
251
201
  };
252
202
 
253
- export const selectionChange = (selectionManager: SelectionManager) => {
203
+ const selectionChange = (selectionManager: SelectionManager) => {
254
204
  return EditorView.updateListener.of(
255
205
  debounceAndThrottle((update: ViewUpdate) => {
256
206
  if (update.selectionSet) {
@@ -263,6 +213,7 @@ export const selectionChange = (selectionManager: SelectionManager) => {
263
213
  to: cursorConverter.toCursor(range.to),
264
214
  }))
265
215
  .filter(({ from, to }) => to > from);
216
+
266
217
  selectionManager.updateMultiRange(id, ranges);
267
218
  }
268
219
  }, 100),
@@ -321,8 +272,8 @@ const renderLinkTooltip: RenderCallback<{ url: string }> = (el, { url }) => {
321
272
  );
322
273
  };
323
274
 
324
- // TODO(burdon): Remove react rendering; use DOM directly.
325
- 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 => {
326
277
  createRoot(root).render(<ThemeProvider tx={defaultTx}>{node}</ThemeProvider>);
327
278
  return root;
328
279
  };
@@ -0,0 +1,83 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import * as Option from 'effect/Option';
6
+ import { useCallback, useMemo } from 'react';
7
+
8
+ import { Capabilities } from '@dxos/app-framework';
9
+ import { usePluginManager } from '@dxos/app-framework/react';
10
+ import { Filter, Obj, Query, Type } from '@dxos/echo';
11
+ import { EntityKind, SystemTypeAnnotation, getTypeAnnotation } from '@dxos/echo/internal';
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 filter = useMemo(
27
+ () =>
28
+ Filter.or(
29
+ ...(space?.db.schemaRegistry.query({ location: ['database', 'runtime'] }).runSync() ?? [])
30
+ .filter((schema) => getTypeAnnotation(schema)?.kind !== EntityKind.Relation)
31
+ .filter((schema) => !SystemTypeAnnotation.get(schema).pipe(Option.getOrElse(() => false)))
32
+ .map((schema) => Filter.typename(Type.getTypename(schema))),
33
+ ),
34
+ [space],
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
+ };