@dxos/plugin-markdown 0.8.4-main.67995b8 → 0.8.4-main.c4373fc

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 (205) hide show
  1. package/dist/lib/browser/{MarkdownPreview-7VG3K24R.mjs → MarkdownCard-ZHTH6EVA.mjs} +13 -13
  2. package/dist/lib/browser/MarkdownCard-ZHTH6EVA.mjs.map +7 -0
  3. package/dist/lib/browser/{MarkdownContainer-NNBPE6A5.mjs → MarkdownContainer-XDYFBDCI.mjs} +281 -310
  4. package/dist/lib/browser/MarkdownContainer-XDYFBDCI.mjs.map +7 -0
  5. package/dist/lib/browser/{anchor-sort-Z7JQA7RL.mjs → anchor-sort-53E7TJGF.mjs} +5 -5
  6. package/dist/lib/browser/{anchor-sort-Z7JQA7RL.mjs.map → anchor-sort-53E7TJGF.mjs.map} +1 -1
  7. package/dist/lib/browser/{app-graph-serializer-ZT5OVF5G.mjs → app-graph-serializer-IEPNJZBR.mjs} +9 -10
  8. package/dist/lib/browser/app-graph-serializer-IEPNJZBR.mjs.map +7 -0
  9. package/dist/lib/browser/blueprint-definition-XDLVVSUN.mjs +11 -0
  10. package/dist/lib/browser/blueprint-definition-XDLVVSUN.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-EVQGTH3P.mjs +22 -0
  12. package/dist/lib/browser/chunk-EVQGTH3P.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-K7ZFMSC4.mjs → chunk-NGXGBWSS.mjs} +4 -3
  14. package/dist/lib/browser/chunk-NGXGBWSS.mjs.map +7 -0
  15. package/dist/lib/browser/{chunk-ZGY3DYC2.mjs → chunk-ODB2PTBP.mjs} +2 -4
  16. package/dist/lib/browser/{chunk-ZGY3DYC2.mjs.map → chunk-ODB2PTBP.mjs.map} +3 -3
  17. package/dist/lib/browser/{chunk-CD4E4K7J.mjs → chunk-OY6CGPOO.mjs} +2 -2
  18. package/dist/lib/browser/{chunk-CD4E4K7J.mjs.map → chunk-OY6CGPOO.mjs.map} +1 -1
  19. package/dist/lib/browser/{chunk-YHMGUSO7.mjs → chunk-PMBWHKDI.mjs} +10 -12
  20. package/dist/lib/browser/chunk-PMBWHKDI.mjs.map +7 -0
  21. package/dist/lib/browser/chunk-VZU7BRGW.mjs +106 -0
  22. package/dist/lib/browser/chunk-VZU7BRGW.mjs.map +7 -0
  23. package/dist/lib/browser/{chunk-VMX5SDGW.mjs → chunk-XYQDM6PM.mjs} +2 -2
  24. package/dist/lib/browser/index.mjs +29 -18
  25. package/dist/lib/browser/index.mjs.map +3 -3
  26. package/dist/lib/browser/{intent-resolver-EBEF7WEI.mjs → intent-resolver-66ZUQ6JC.mjs} +20 -18
  27. package/dist/lib/browser/intent-resolver-66ZUQ6JC.mjs.map +7 -0
  28. package/dist/lib/browser/meta.json +1 -1
  29. package/dist/lib/browser/{react-surface-ZPMZT4VU.mjs → react-surface-ONX5RNFM.mjs} +40 -33
  30. package/dist/lib/browser/react-surface-ONX5RNFM.mjs.map +7 -0
  31. package/dist/lib/browser/{settings-MBDK4TWE.mjs → settings-HXM36BCZ.mjs} +5 -5
  32. package/dist/lib/browser/{settings-MBDK4TWE.mjs.map → settings-HXM36BCZ.mjs.map} +1 -1
  33. package/dist/lib/browser/{state-ZA6PZPUI.mjs → state-6QODXCSZ.mjs} +5 -5
  34. package/dist/lib/browser/state-6QODXCSZ.mjs.map +7 -0
  35. package/dist/lib/browser/toolkit-73U3A7SD.mjs +76 -0
  36. package/dist/lib/browser/toolkit-73U3A7SD.mjs.map +7 -0
  37. package/dist/lib/browser/types/index.mjs +2 -2
  38. package/dist/lib/node-esm/{MarkdownPreview-UVWR2YK3.mjs → MarkdownCard-MEA4QAAI.mjs} +13 -13
  39. package/dist/lib/node-esm/MarkdownCard-MEA4QAAI.mjs.map +7 -0
  40. package/dist/lib/node-esm/{MarkdownContainer-Q6UO7DKT.mjs → MarkdownContainer-7GYMOJVT.mjs} +281 -310
  41. package/dist/lib/node-esm/MarkdownContainer-7GYMOJVT.mjs.map +7 -0
  42. package/dist/lib/node-esm/{anchor-sort-R6AAKYNG.mjs → anchor-sort-V2FWEYDN.mjs} +5 -5
  43. package/dist/lib/node-esm/{anchor-sort-R6AAKYNG.mjs.map → anchor-sort-V2FWEYDN.mjs.map} +1 -1
  44. package/dist/lib/node-esm/{app-graph-serializer-X4M5QEI6.mjs → app-graph-serializer-HIN4NMUG.mjs} +9 -10
  45. package/dist/lib/node-esm/app-graph-serializer-HIN4NMUG.mjs.map +7 -0
  46. package/dist/lib/node-esm/blueprint-definition-PDXZ67UQ.mjs +12 -0
  47. package/dist/lib/node-esm/blueprint-definition-PDXZ67UQ.mjs.map +7 -0
  48. package/dist/lib/node-esm/{chunk-6GCOJS4Y.mjs → chunk-35WAARP4.mjs} +10 -12
  49. package/dist/lib/node-esm/chunk-35WAARP4.mjs.map +7 -0
  50. package/dist/lib/node-esm/{chunk-B3J2M4YL.mjs → chunk-CB2R4YIY.mjs} +2 -2
  51. package/dist/lib/node-esm/{chunk-B3J2M4YL.mjs.map → chunk-CB2R4YIY.mjs.map} +1 -1
  52. package/dist/lib/node-esm/{chunk-LZK3TLKM.mjs → chunk-IOC54NCF.mjs} +2 -2
  53. package/dist/lib/node-esm/{chunk-RCIXKCVG.mjs → chunk-VCG2U522.mjs} +2 -4
  54. package/dist/lib/node-esm/{chunk-RCIXKCVG.mjs.map → chunk-VCG2U522.mjs.map} +3 -3
  55. package/dist/lib/node-esm/chunk-VELFUWUH.mjs +107 -0
  56. package/dist/lib/node-esm/chunk-VELFUWUH.mjs.map +7 -0
  57. package/dist/lib/node-esm/chunk-W4XXBX33.mjs +24 -0
  58. package/dist/lib/node-esm/chunk-W4XXBX33.mjs.map +7 -0
  59. package/dist/lib/node-esm/{chunk-YGNVDYMB.mjs → chunk-YGSNWTGP.mjs} +4 -3
  60. package/dist/lib/node-esm/chunk-YGSNWTGP.mjs.map +7 -0
  61. package/dist/lib/node-esm/index.mjs +29 -18
  62. package/dist/lib/node-esm/index.mjs.map +3 -3
  63. package/dist/lib/node-esm/{intent-resolver-L2UGZ72W.mjs → intent-resolver-TNRIFHNH.mjs} +20 -18
  64. package/dist/lib/node-esm/intent-resolver-TNRIFHNH.mjs.map +7 -0
  65. package/dist/lib/node-esm/meta.json +1 -1
  66. package/dist/lib/node-esm/{react-surface-3JJSTTQP.mjs → react-surface-X2GV6WQH.mjs} +40 -33
  67. package/dist/lib/node-esm/react-surface-X2GV6WQH.mjs.map +7 -0
  68. package/dist/lib/node-esm/{settings-LBDWWPZJ.mjs → settings-KPLQ5ONI.mjs} +5 -5
  69. package/dist/lib/node-esm/{settings-LBDWWPZJ.mjs.map → settings-KPLQ5ONI.mjs.map} +1 -1
  70. package/dist/lib/node-esm/{state-UIHO2SFZ.mjs → state-W3PECOJX.mjs} +5 -5
  71. package/dist/lib/node-esm/state-W3PECOJX.mjs.map +7 -0
  72. package/dist/lib/node-esm/toolkit-GUBDYF72.mjs +77 -0
  73. package/dist/lib/node-esm/toolkit-GUBDYF72.mjs.map +7 -0
  74. package/dist/lib/node-esm/types/index.mjs +2 -2
  75. package/dist/types/src/MarkdownPlugin.d.ts +1 -1
  76. package/dist/types/src/MarkdownPlugin.d.ts.map +1 -1
  77. package/dist/types/src/capabilities/anchor-sort.d.ts +2 -2
  78. package/dist/types/src/capabilities/app-graph-serializer.d.ts +1 -1
  79. package/dist/types/src/capabilities/app-graph-serializer.d.ts.map +1 -1
  80. package/dist/types/src/capabilities/artifact-definition.d.ts +1 -9
  81. package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -1
  82. package/dist/types/src/capabilities/blueprint-definition.d.ts +5 -0
  83. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
  84. package/dist/types/src/capabilities/capabilities.d.ts +1 -1
  85. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  86. package/dist/types/src/capabilities/index.d.ts +12 -10
  87. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  88. package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
  89. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  90. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  91. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  92. package/dist/types/src/capabilities/settings.d.ts +1 -1
  93. package/dist/types/src/capabilities/state.d.ts +2 -2
  94. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  95. package/dist/types/src/capabilities/toolkit.d.ts +4 -0
  96. package/dist/types/src/capabilities/toolkit.d.ts.map +1 -0
  97. package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts +7 -0
  98. package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts.map +1 -0
  99. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts +9 -0
  100. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts.map +1 -0
  101. package/dist/types/src/components/MarkdownCard/index.d.ts +4 -0
  102. package/dist/types/src/components/MarkdownCard/index.d.ts.map +1 -0
  103. package/dist/types/src/components/MarkdownContainer.d.ts +1 -1
  104. package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -1
  105. package/dist/types/src/components/MarkdownContainer.stories.d.ts +51 -5
  106. package/dist/types/src/components/MarkdownContainer.stories.d.ts.map +1 -1
  107. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts +1 -1
  108. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts.map +1 -1
  109. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts +111 -13
  110. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts.map +1 -1
  111. package/dist/types/src/components/MarkdownSettings/MarkdownSettings.d.ts.map +1 -1
  112. package/dist/types/src/components/Suggestions.stories.d.ts +1 -2
  113. package/dist/types/src/components/Suggestions.stories.d.ts.map +1 -1
  114. package/dist/types/src/components/Toolbar.stories.d.ts +45 -8
  115. package/dist/types/src/components/Toolbar.stories.d.ts.map +1 -1
  116. package/dist/types/src/components/index.d.ts +1 -1
  117. package/dist/types/src/components/index.d.ts.map +1 -1
  118. package/dist/types/src/extensions.d.ts +0 -2
  119. package/dist/types/src/extensions.d.ts.map +1 -1
  120. package/dist/types/src/functions/diff.d.ts +6 -0
  121. package/dist/types/src/functions/diff.d.ts.map +1 -0
  122. package/dist/types/src/functions/index.d.ts +3 -0
  123. package/dist/types/src/functions/index.d.ts.map +1 -0
  124. package/dist/types/src/functions/open.d.ts +7 -0
  125. package/dist/types/src/functions/open.d.ts.map +1 -0
  126. package/dist/types/src/hooks/useSelectCurrentThread.d.ts +1 -1
  127. package/dist/types/src/hooks/useSelectCurrentThread.d.ts.map +1 -1
  128. package/dist/types/src/index.d.ts +2 -1
  129. package/dist/types/src/index.d.ts.map +1 -1
  130. package/dist/types/src/meta.d.ts +0 -1
  131. package/dist/types/src/meta.d.ts.map +1 -1
  132. package/dist/types/src/translations.d.ts +1 -0
  133. package/dist/types/src/translations.d.ts.map +1 -1
  134. package/dist/types/src/types/Markdown.d.ts +5 -5
  135. package/dist/types/src/types/Markdown.d.ts.map +1 -1
  136. package/dist/types/src/types/MarkdownAction.d.ts +5 -14
  137. package/dist/types/src/types/MarkdownAction.d.ts.map +1 -1
  138. package/dist/types/src/types/types.d.ts.map +1 -1
  139. package/dist/types/tsconfig.tsbuildinfo +1 -1
  140. package/package.json +59 -56
  141. package/src/MarkdownPlugin.tsx +102 -94
  142. package/src/capabilities/app-graph-serializer.ts +4 -4
  143. package/src/capabilities/artifact-definition.ts +15 -19
  144. package/src/capabilities/blueprint-definition.ts +39 -0
  145. package/src/capabilities/capabilities.ts +1 -1
  146. package/src/capabilities/index.ts +3 -1
  147. package/src/capabilities/intent-resolver.ts +14 -13
  148. package/src/capabilities/react-surface.tsx +5 -4
  149. package/src/capabilities/state.ts +3 -2
  150. package/src/capabilities/toolkit.ts +49 -0
  151. package/src/components/{MarkdownPreview/MarkdownPreview.stories.tsx → MarkdownCard/MarkdownCard.stories.tsx} +14 -15
  152. package/src/components/{MarkdownPreview/MarkdownPreview.tsx → MarkdownCard/MarkdownCard.tsx} +6 -4
  153. package/src/components/MarkdownCard/index.ts +9 -0
  154. package/src/components/MarkdownContainer.stories.tsx +47 -38
  155. package/src/components/MarkdownContainer.tsx +16 -13
  156. package/src/components/MarkdownEditor/MarkdownEditor.stories.tsx +21 -17
  157. package/src/components/MarkdownEditor/MarkdownEditor.tsx +36 -36
  158. package/src/components/MarkdownSettings/MarkdownSettings.tsx +77 -74
  159. package/src/components/Suggestions.stories.tsx +42 -41
  160. package/src/components/Toolbar.stories.tsx +23 -17
  161. package/src/components/index.ts +1 -1
  162. package/src/extensions.tsx +10 -38
  163. package/src/functions/diff.ts +37 -0
  164. package/src/functions/index.ts +6 -0
  165. package/src/functions/open.ts +32 -0
  166. package/src/hooks/useSelectCurrentThread.tsx +3 -3
  167. package/src/index.ts +3 -1
  168. package/src/meta.ts +0 -3
  169. package/src/translations.ts +1 -0
  170. package/src/types/Markdown.ts +4 -3
  171. package/src/types/MarkdownAction.ts +5 -6
  172. package/src/types/types.ts +1 -0
  173. package/dist/lib/browser/MarkdownContainer-NNBPE6A5.mjs.map +0 -7
  174. package/dist/lib/browser/MarkdownPreview-7VG3K24R.mjs.map +0 -7
  175. package/dist/lib/browser/app-graph-serializer-ZT5OVF5G.mjs.map +0 -7
  176. package/dist/lib/browser/artifact-definition-7VNP5PCP.mjs +0 -145
  177. package/dist/lib/browser/artifact-definition-7VNP5PCP.mjs.map +0 -7
  178. package/dist/lib/browser/chunk-K7ZFMSC4.mjs.map +0 -7
  179. package/dist/lib/browser/chunk-VCUKIILA.mjs +0 -20
  180. package/dist/lib/browser/chunk-VCUKIILA.mjs.map +0 -7
  181. package/dist/lib/browser/chunk-YHMGUSO7.mjs.map +0 -7
  182. package/dist/lib/browser/intent-resolver-EBEF7WEI.mjs.map +0 -7
  183. package/dist/lib/browser/react-surface-ZPMZT4VU.mjs.map +0 -7
  184. package/dist/lib/browser/state-ZA6PZPUI.mjs.map +0 -7
  185. package/dist/lib/node-esm/MarkdownContainer-Q6UO7DKT.mjs.map +0 -7
  186. package/dist/lib/node-esm/MarkdownPreview-UVWR2YK3.mjs.map +0 -7
  187. package/dist/lib/node-esm/app-graph-serializer-X4M5QEI6.mjs.map +0 -7
  188. package/dist/lib/node-esm/artifact-definition-IRIILD7S.mjs +0 -146
  189. package/dist/lib/node-esm/artifact-definition-IRIILD7S.mjs.map +0 -7
  190. package/dist/lib/node-esm/chunk-6GCOJS4Y.mjs.map +0 -7
  191. package/dist/lib/node-esm/chunk-A7LLVI34.mjs +0 -22
  192. package/dist/lib/node-esm/chunk-A7LLVI34.mjs.map +0 -7
  193. package/dist/lib/node-esm/chunk-YGNVDYMB.mjs.map +0 -7
  194. package/dist/lib/node-esm/intent-resolver-L2UGZ72W.mjs.map +0 -7
  195. package/dist/lib/node-esm/react-surface-3JJSTTQP.mjs.map +0 -7
  196. package/dist/lib/node-esm/state-UIHO2SFZ.mjs.map +0 -7
  197. package/dist/types/src/components/MarkdownPreview/MarkdownPreview.d.ts +0 -6
  198. package/dist/types/src/components/MarkdownPreview/MarkdownPreview.d.ts.map +0 -1
  199. package/dist/types/src/components/MarkdownPreview/MarkdownPreview.stories.d.ts +0 -10
  200. package/dist/types/src/components/MarkdownPreview/MarkdownPreview.stories.d.ts.map +0 -1
  201. package/dist/types/src/components/MarkdownPreview/index.d.ts +0 -4
  202. package/dist/types/src/components/MarkdownPreview/index.d.ts.map +0 -1
  203. package/src/components/MarkdownPreview/index.ts +0 -9
  204. /package/dist/lib/browser/{chunk-VMX5SDGW.mjs.map → chunk-XYQDM6PM.mjs.map} +0 -0
  205. /package/dist/lib/node-esm/{chunk-LZK3TLKM.mjs.map → chunk-IOC54NCF.mjs.map} +0 -0
@@ -0,0 +1,49 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Tool from '@effect/ai/Tool';
6
+ import * as Toolkit from '@effect/ai/Toolkit';
7
+ import * as Effect from 'effect/Effect';
8
+ import * as Schema from 'effect/Schema';
9
+
10
+ import { Capabilities, type Capability, type PluginContext, contributes, createIntent } from '@dxos/app-framework';
11
+ import { invariant } from '@dxos/invariant';
12
+ import { getActiveSpace } from '@dxos/plugin-space';
13
+ import { SpaceAction } from '@dxos/plugin-space/types';
14
+
15
+ import { MarkdownAction } from '../types';
16
+
17
+ // TODO(burdon): Reconcile with functions (currently reuses plugin framework intents).
18
+ class MarkdownToolkit extends Toolkit.make(
19
+ Tool.make('create-document', {
20
+ description: 'Creates a new markdown document.',
21
+ parameters: {
22
+ name: Schema.optional(Schema.String),
23
+ content: Schema.optional(Schema.String),
24
+ },
25
+ // TODO(wittjosiah): Return document.
26
+ success: Schema.Any,
27
+ failure: Schema.Never,
28
+ }),
29
+ ) {
30
+ static layer = (context: PluginContext) =>
31
+ MarkdownToolkit.toLayer({
32
+ 'create-document': ({ name, content }) => {
33
+ const { dispatch } = context.getCapability(Capabilities.IntentDispatcher);
34
+ const space = getActiveSpace(context);
35
+ invariant(space, 'No active space');
36
+
37
+ return Effect.gen(function* () {
38
+ const { object } = yield* dispatch(createIntent(MarkdownAction.Create, { name, content }));
39
+ yield* dispatch(createIntent(SpaceAction.AddObject, { object, target: space }));
40
+ return { id: object.id };
41
+ }).pipe(Effect.orDie);
42
+ },
43
+ });
44
+ }
45
+
46
+ export default (context: PluginContext): Capability<any>[] => [
47
+ contributes(Capabilities.Toolkit, MarkdownToolkit),
48
+ contributes(Capabilities.ToolkitHandler, MarkdownToolkit.layer(context)),
49
+ ];
@@ -2,44 +2,43 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
- import { type StoryObj, type Meta } from '@storybook/react-vite';
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React from 'react';
9
7
 
10
8
  import { IntentPlugin } from '@dxos/app-framework';
11
9
  import { withPluginManager } from '@dxos/app-framework/testing';
12
10
  import { Markdown } from '@dxos/plugin-markdown/types';
13
11
  import { faker } from '@dxos/random';
12
+ import { withTheme } from '@dxos/react-ui/testing';
14
13
  import { CardContainer } from '@dxos/react-ui-stack/testing';
15
- import { withTheme, withLayout } from '@dxos/storybook-utils';
16
14
 
17
- import { MarkdownPreview } from './MarkdownPreview';
18
15
  import { translations } from '../../translations';
19
16
 
17
+ import { MarkdownCard } from './MarkdownCard';
18
+
20
19
  faker.seed(1234);
21
20
 
22
- const meta: Meta<typeof MarkdownPreview> = {
23
- title: 'Cards/plugin-markdown',
24
- component: MarkdownPreview,
21
+ const meta: Meta<typeof MarkdownCard> = {
22
+ title: 'plugins/plugin-markdown/Card',
23
+ component: MarkdownCard,
25
24
  render: ({ role, subject, ...args }) => {
26
25
  return (
27
26
  <CardContainer icon='ph--text-aa--regular' role={role}>
28
- <MarkdownPreview role={role} subject={subject} {...args} />
27
+ <MarkdownCard role={role} subject={subject} {...args} />
29
28
  </CardContainer>
30
29
  );
31
30
  },
32
31
  decorators: [
32
+ withTheme,
33
33
  withPluginManager({
34
34
  plugins: [IntentPlugin()],
35
35
  }),
36
- withTheme,
37
- withLayout(),
38
36
  ],
39
37
  parameters: {
40
38
  layout: 'centered',
41
39
  translations,
42
40
  },
41
+ tags: ['cards'],
43
42
  };
44
43
 
45
44
  export default meta;
@@ -56,9 +55,9 @@ export const Popover: Story = {
56
55
  },
57
56
  };
58
57
 
59
- export const Extrinsic: Story = {
58
+ export const Intrinsic: Story = {
60
59
  args: {
61
- role: 'card--extrinsic',
60
+ role: 'card--intrinsic',
62
61
  subject: Markdown.makeDocument({
63
62
  name: faker.lorem.words(3),
64
63
  content: faker.lorem.paragraphs(3),
@@ -66,9 +65,9 @@ export const Extrinsic: Story = {
66
65
  },
67
66
  };
68
67
 
69
- export const Intrinsic: Story = {
68
+ export const Extrinsic: Story = {
70
69
  args: {
71
- role: 'card--intrinsic',
70
+ role: 'card--extrinsic',
72
71
  subject: Markdown.makeDocument({
73
72
  name: faker.lorem.words(3),
74
73
  content: faker.lorem.paragraphs(3),
@@ -2,10 +2,10 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { pipe } from 'effect';
5
+ import * as Function from 'effect/Function';
6
6
  import React, { useCallback } from 'react';
7
7
 
8
- import { chain, createIntent, LayoutAction, useIntentDispatcher } from '@dxos/app-framework';
8
+ import { LayoutAction, chain, createIntent, useIntentDispatcher } from '@dxos/app-framework';
9
9
  import { Obj } from '@dxos/echo';
10
10
  import { type PreviewProps } from '@dxos/plugin-preview';
11
11
  import { fullyQualifiedId } from '@dxos/react-client/echo';
@@ -35,7 +35,9 @@ const getSnippet = (subject: Markdown.Document | DataType.Text, fallback: string
35
35
  }
36
36
  };
37
37
 
38
- export const MarkdownPreview = ({ subject, role }: PreviewProps<Markdown.Document | DataType.Text>) => {
38
+ export type MarkdownCardProps = PreviewProps<Markdown.Document | DataType.Text>;
39
+
40
+ export const MarkdownCard = ({ subject, role }: MarkdownCardProps) => {
39
41
  const { dispatchPromise: dispatch } = useIntentDispatcher();
40
42
  const { t } = useTranslation(meta.id);
41
43
  const snippet = getSnippet(subject, t('fallback abstract'));
@@ -44,7 +46,7 @@ export const MarkdownPreview = ({ subject, role }: PreviewProps<Markdown.Documen
44
46
  const handleNavigate = useCallback(
45
47
  () =>
46
48
  dispatch(
47
- pipe(
49
+ Function.pipe(
48
50
  createIntent(LayoutAction.UpdatePopover, {
49
51
  part: 'popover',
50
52
  subject: null,
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { MarkdownCard } from './MarkdownCard';
6
+
7
+ export * from './MarkdownCard';
8
+
9
+ export default MarkdownCard;
@@ -2,28 +2,34 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
- import { type Meta } from '@storybook/react-vite';
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useMemo } from 'react';
9
7
 
10
- import { Capabilities, contributes, IntentPlugin, SettingsPlugin, Surface } from '@dxos/app-framework';
8
+ import {
9
+ type Capabilities,
10
+ IntentPlugin,
11
+ LayoutAction,
12
+ SettingsPlugin,
13
+ Surface,
14
+ createIntent,
15
+ useIntentDispatcher,
16
+ } from '@dxos/app-framework';
11
17
  import { withPluginManager } from '@dxos/app-framework/testing';
12
- import { todo } from '@dxos/debug';
13
- import { Query, Type } from '@dxos/echo';
18
+ import { Obj, Query } from '@dxos/echo';
14
19
  import { AttentionPlugin } from '@dxos/plugin-attention';
15
20
  import { ClientPlugin } from '@dxos/plugin-client';
16
21
  import { GraphPlugin } from '@dxos/plugin-graph';
17
22
  import { PreviewPlugin } from '@dxos/plugin-preview';
18
- import { SpaceCapabilities, SpacePlugin } from '@dxos/plugin-space';
23
+ import { SpacePlugin } from '@dxos/plugin-space';
19
24
  import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
20
25
  import { ThemePlugin } from '@dxos/plugin-theme';
21
26
  import { faker } from '@dxos/random';
22
27
  import { useQuery, useSpace } from '@dxos/react-client/echo';
28
+ import { useAsyncEffect } from '@dxos/react-ui';
29
+ import { withTheme } from '@dxos/react-ui/testing';
23
30
  import { defaultTx } from '@dxos/react-ui-theme';
24
31
  import { DataType } from '@dxos/schema';
25
- import { createObjectFactory, Testing, type ValueGenerator } from '@dxos/schema/testing';
26
- import { withLayout } from '@dxos/storybook-utils';
32
+ import { type ValueGenerator, createObjectFactory } from '@dxos/schema/testing';
27
33
 
28
34
  import { MarkdownPlugin } from '../MarkdownPlugin';
29
35
  import { translations } from '../translations';
@@ -34,67 +40,70 @@ faker.seed(1);
34
40
  const generator: ValueGenerator = faker as any;
35
41
 
36
42
  const DefaultStory = () => {
43
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
37
44
  const space = useSpace();
38
45
  const [doc] = useQuery(space, Query.type(Markdown.Document));
39
46
  const data = useMemo(() => ({ subject: doc }), [doc]);
40
47
 
41
- return <Surface role='article' data={data} />;
48
+ useAsyncEffect(async () => {
49
+ if (space) {
50
+ await dispatch(createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: space.id }));
51
+ }
52
+ }, [space, dispatch]);
53
+
54
+ return <Surface role='article' data={data} limit={1} />;
42
55
  };
43
56
 
44
- const meta: Meta<typeof DefaultStory> = {
57
+ const meta = {
45
58
  title: 'plugins/plugin-markdown/MarkdownContainer',
46
59
  render: DefaultStory,
47
60
  decorators: [
61
+ withTheme,
48
62
  withPluginManager({
49
63
  plugins: [
50
- AttentionPlugin(),
51
- ThemePlugin({ tx: defaultTx }),
52
- StorybookLayoutPlugin(),
53
64
  ClientPlugin({
54
- types: [Markdown.Document, DataType.Text, Testing.Contact],
55
- onClientInitialized: async (_, client) => {
65
+ types: [Markdown.Document, DataType.Text, DataType.Person, DataType.Organization],
66
+ onClientInitialized: async ({ client }) => {
56
67
  await client.halo.createIdentity();
57
68
  await client.spaces.waitUntilReady();
58
69
  await client.spaces.default.waitUntilReady();
59
70
  const space = client.spaces.default;
60
- const doc = Markdown.makeDocument({ name: 'Test', content: '# Test\n\n' });
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
+ });
61
79
  space.db.add(doc);
62
80
  const createObjects = createObjectFactory(space.db, generator);
63
- await createObjects([{ type: Testing.Contact, count: 10 }]);
81
+ await createObjects([{ type: DataType.Organization, count: 10 }]);
64
82
  await space.db.flush({ indexes: true });
65
83
  },
66
84
  }),
67
- SpacePlugin(),
68
- SettingsPlugin(),
85
+ SpacePlugin({}),
86
+ GraphPlugin(),
69
87
  IntentPlugin(),
88
+ SettingsPlugin(),
89
+ // UI
90
+ ThemePlugin({ tx: defaultTx }),
91
+ AttentionPlugin(),
70
92
  MarkdownPlugin(),
71
93
  PreviewPlugin(),
72
- GraphPlugin(),
73
- ],
74
- capabilities: [
75
- // NOTE: Editor only queries for object form schemas when linking.
76
- contributes(SpaceCapabilities.ObjectForm, {
77
- objectSchema: Testing.Contact,
78
- getIntent: () => todo(),
79
- }),
80
- contributes(Capabilities.Metadata, {
81
- id: Type.getTypename(Testing.Contact),
82
- metadata: {
83
- icon: 'ph--user--regular',
84
- },
85
- }),
94
+ StorybookLayoutPlugin({}),
86
95
  ],
87
96
  }),
88
- withLayout({ fullscreen: true }),
89
97
  ],
90
98
  parameters: {
91
- translations,
99
+ layout: 'fullscreen',
92
100
  controls: { disable: true },
101
+ translations,
93
102
  },
94
- };
103
+ } satisfies Meta<typeof Capabilities>;
95
104
 
96
105
  export default meta;
97
106
 
98
- type Story = Meta<typeof DefaultStory>;
107
+ type Story = StoryObj<typeof meta>;
99
108
 
100
109
  export const Default: Story = {};
@@ -10,24 +10,26 @@ import { Capabilities, Surface, useAppGraph, useCapabilities, usePluginManager }
10
10
  import { DXN, Filter, Obj, Query, Type } from '@dxos/echo';
11
11
  import { ClientCapabilities } from '@dxos/plugin-client';
12
12
  import { SpaceCapabilities } from '@dxos/plugin-space';
13
- import { fullyQualifiedId, getSpace, useQuery, useSpace } from '@dxos/react-client/echo';
13
+ import { useClient } from '@dxos/react-client';
14
+ import { fullyQualifiedId, getSpace } from '@dxos/react-client/echo';
14
15
  import { toLocalizedString, useTranslation } from '@dxos/react-ui';
15
16
  import { type SelectionManager } from '@dxos/react-ui-attention';
16
17
  import {
17
18
  type CommandMenuGroup,
18
19
  type CommandMenuItem,
19
- insertAtCursor,
20
- insertAtLineStart,
21
20
  type PreviewLinkRef,
22
21
  type PreviewOptions,
22
+ insertAtCursor,
23
+ insertAtLineStart,
23
24
  } from '@dxos/react-ui-editor';
24
25
  import { DataType } from '@dxos/schema';
25
26
 
26
- import { MarkdownEditor, type MarkdownEditorProps } from './MarkdownEditor';
27
27
  import { useExtensions } from '../extensions';
28
28
  import { Markdown } from '../types';
29
29
  import { getFallbackName } from '../util';
30
30
 
31
+ import { MarkdownEditor, type MarkdownEditorProps } from './MarkdownEditor';
32
+
31
33
  export type MarkdownContainerProps = Pick<
32
34
  MarkdownEditorProps,
33
35
  'role' | 'extensionProviders' | 'viewMode' | 'editorStateStore' | 'onViewModeChange'
@@ -93,7 +95,8 @@ export const MarkdownContainer = ({
93
95
  ),
94
96
  [objectForms, schemaWhiteList],
95
97
  );
96
- const onLinkQuery = useCallback(
98
+
99
+ const handleLinkQuery = useCallback(
97
100
  async (query?: string): Promise<CommandMenuGroup[]> => {
98
101
  const name = query?.startsWith('@') ? query.slice(1).toLowerCase() : (query?.toLowerCase() ?? '');
99
102
  const results = await space?.db.query(Query.select(filter)).run();
@@ -121,7 +124,7 @@ export const MarkdownContainer = ({
121
124
  label,
122
125
  icon: metadata.icon,
123
126
  onSelect: (view, head) => {
124
- const link = `[${label}][${Obj.getDXN(object)}]`;
127
+ const link = `[${label}](${Obj.getDXN(object)})`;
125
128
  if (query?.startsWith('@')) {
126
129
  insertAtLineStart(view, head, `!${link}\n`);
127
130
  } else {
@@ -135,6 +138,7 @@ export const MarkdownContainer = ({
135
138
  [filter, resolve, space],
136
139
  );
137
140
 
141
+ // TODO(burdon): Reconcile variants.
138
142
  const editor = doc ? (
139
143
  <DocumentEditor
140
144
  id={fullyQualifiedId(object)}
@@ -145,7 +149,7 @@ export const MarkdownContainer = ({
145
149
  settings={settings}
146
150
  scrollPastEnd={scrollPastEnd}
147
151
  onViewModeChange={onViewModeChange}
148
- onLinkQuery={space ? onLinkQuery : undefined}
152
+ onLinkQuery={space ? handleLinkQuery : undefined}
149
153
  />
150
154
  ) : text ? (
151
155
  <MarkdownEditor
@@ -158,10 +162,9 @@ export const MarkdownContainer = ({
158
162
  inputMode={settings.editorInputMode}
159
163
  scrollPastEnd={scrollPastEnd}
160
164
  onViewModeChange={onViewModeChange}
161
- onLinkQuery={space ? onLinkQuery : undefined}
165
+ onLinkQuery={space ? handleLinkQuery : undefined}
162
166
  />
163
167
  ) : (
164
- // TODO(burdon): Normalize with above.
165
168
  <MarkdownEditor
166
169
  id={id}
167
170
  role={role}
@@ -172,7 +175,7 @@ export const MarkdownContainer = ({
172
175
  inputMode={settings.editorInputMode}
173
176
  scrollPastEnd={scrollPastEnd}
174
177
  onViewModeChange={onViewModeChange}
175
- onLinkQuery={space ? onLinkQuery : undefined}
178
+ onLinkQuery={space ? handleLinkQuery : undefined}
176
179
  />
177
180
  );
178
181
 
@@ -189,9 +192,9 @@ export const MarkdownContainer = ({
189
192
  // TODO(wittjosiah): This shouldn't be "card" but "block".
190
193
  // It's not a preview card but an interactive embedded object.
191
194
  const PreviewBlock = ({ link, el }: { link: PreviewLinkRef; el: HTMLElement }) => {
192
- const echoDXN = useMemo(() => DXN.parse(link.ref).asEchoDXN(), [link.ref]);
193
- const space = useSpace(echoDXN?.spaceId);
194
- const [subject] = useQuery(space, Query.select(Filter.ids(echoDXN?.echoId ?? '')));
195
+ const client = useClient();
196
+ const dxn = DXN.parse(link.ref);
197
+ const subject = client.graph.ref(dxn).target;
195
198
  const data = useMemo(() => ({ subject }), [subject]);
196
199
 
197
200
  return createPortal(<Surface role='card--transclusion' data={data} limit={1} />, el);
@@ -2,21 +2,21 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
- import { type Meta } from '@storybook/react-vite';
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useMemo } from 'react';
9
7
 
10
8
  import { IntentPlugin } from '@dxos/app-framework';
11
9
  import { withPluginManager } from '@dxos/app-framework/testing';
12
10
  import { createDocAccessor, createObject } from '@dxos/react-client/echo';
11
+ import { withTheme } from '@dxos/react-ui/testing';
13
12
  import { withAttention } from '@dxos/react-ui-attention/testing';
14
13
  import { automerge, translations as editorTranslations } from '@dxos/react-ui-editor';
15
- import { withLayout, withTheme } from '@dxos/storybook-utils';
14
+ import { Stack, StackItem } from '@dxos/react-ui-stack';
16
15
 
17
- import { MarkdownEditor, type MarkdownEditorProps } from './MarkdownEditor';
18
16
  import { translations } from '../../translations';
19
17
 
18
+ import { MarkdownEditor, type MarkdownEditorProps } from './MarkdownEditor';
19
+
20
20
  const content = Array.from({ length: 100 }, (_, i) => `Line ${i + 1}`).join('\n');
21
21
 
22
22
  type StoryProps = MarkdownEditorProps & {
@@ -27,33 +27,37 @@ type StoryProps = MarkdownEditorProps & {
27
27
  const DefaultStory = ({ content = '# Test', toolbar }: StoryProps) => {
28
28
  const doc = useMemo(() => createObject({ content }), [content]); // TODO(burdon): Remove dependency on createObject.
29
29
  const extensions = useMemo(() => [automerge(createDocAccessor(doc, ['content']))], [doc]);
30
- return <MarkdownEditor id='test' initialValue={doc.content} extensions={extensions} toolbar={toolbar} />;
30
+ 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>
36
+ );
31
37
  };
32
38
 
33
- const meta: Meta<typeof MarkdownEditor> = {
39
+ const meta = {
34
40
  title: 'plugins/plugin-markdown/MarkdownEditor',
35
- component: MarkdownEditor,
41
+ component: MarkdownEditor as any,
36
42
  render: DefaultStory,
37
- decorators: [
38
- withPluginManager({ plugins: [IntentPlugin()] }),
39
- withAttention,
40
- withTheme,
41
- withLayout({ fullscreen: true }),
42
- ],
43
+ decorators: [withTheme, withPluginManager({ plugins: [IntentPlugin()] }), withAttention],
43
44
  parameters: {
45
+ layout: 'fullscreen',
44
46
  translations: [...translations, ...editorTranslations],
45
47
  },
46
- };
48
+ } satisfies Meta<typeof DefaultStory>;
47
49
 
48
50
  export default meta;
49
51
 
50
- export const Default = {
52
+ type Story = StoryObj<typeof meta>;
53
+
54
+ export const Default: Story = {
51
55
  args: {
52
56
  content,
53
57
  },
54
58
  };
55
59
 
56
- export const WithToolbar = {
60
+ export const WithToolbar: Story = {
57
61
  args: {
58
62
  toolbar: true,
59
63
  content,
@@ -3,15 +3,15 @@
3
3
  //
4
4
 
5
5
  import { type EditorView } from '@codemirror/view';
6
- import React, { forwardRef, useMemo, useEffect, useCallback, useImperativeHandle, useRef } from 'react';
6
+ import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react';
7
7
  import { useDropzone } from 'react-dropzone';
8
8
 
9
9
  import { type FileInfo } from '@dxos/app-framework';
10
10
  import { invariant } from '@dxos/invariant';
11
- import { toLocalizedString, useThemeContext, useTranslation } from '@dxos/react-ui';
11
+ import { Domino, toLocalizedString, useThemeContext, useTranslation } from '@dxos/react-ui';
12
12
  import {
13
- CommandMenu,
14
13
  type CommandMenuGroup,
14
+ CommandMenuProvider,
15
15
  type DNDOptions,
16
16
  type EditorInputMode,
17
17
  type EditorSelectionState,
@@ -19,10 +19,9 @@ import {
19
19
  EditorToolbar,
20
20
  type EditorToolbarActionGraphProps,
21
21
  type EditorViewMode,
22
- RefPopover,
22
+ type UseCommandMenuOptions,
23
23
  type UseTextEditorProps,
24
24
  addLink,
25
- createElement,
26
25
  coreSlashCommands,
27
26
  createBasicExtensions,
28
27
  createMarkdownExtensions,
@@ -34,14 +33,13 @@ import {
34
33
  linkSlashCommands,
35
34
  processEditorPayload,
36
35
  stackItemContentEditorClassNames,
36
+ useCommandMenu,
37
37
  useEditorToolbarState,
38
38
  useFormattingState,
39
39
  useTextEditor,
40
- useCommandMenu,
41
- type UseCommandMenuOptions,
42
40
  } from '@dxos/react-ui-editor';
43
41
  import { StackItem } from '@dxos/react-ui-stack';
44
- import { isNotFalsy, isNonNullable } from '@dxos/util';
42
+ import { isNonNullable, isTruthy } from '@dxos/util';
45
43
 
46
44
  import { useSelectCurrentThread } from '../../hooks';
47
45
  import { meta } from '../../meta';
@@ -70,24 +68,26 @@ export type MarkdownEditorProps = {
70
68
  * This allows it to be used as a common editor for markdown content on arbitrary backends (e.g. files).
71
69
  */
72
70
  export const MarkdownEditor = ({
73
- extensions: _extensions,
71
+ extensions: extensionsParam,
74
72
  slashCommandGroups,
75
73
  onLinkQuery,
76
74
  ...props
77
75
  }: MarkdownEditorProps) => {
78
76
  const { t } = useTranslation();
79
- const viewRef = useRef<EditorView>();
77
+ const viewRef = useRef<EditorView>(null);
80
78
 
81
- const getMenu = useCallback(
79
+ const getMenu = useCallback<UseCommandMenuOptions['getMenu']>(
82
80
  (trigger: string, query?: string) => {
83
81
  switch (trigger) {
84
- case '@':
82
+ case '@': {
85
83
  return onLinkQuery?.(query) ?? [];
84
+ }
86
85
  case '/':
87
- default:
86
+ default: {
88
87
  return filterItems([coreSlashCommands, linkSlashCommands, ...(slashCommandGroups ?? [])], (item) =>
89
88
  query ? toLocalizedString(item.label, t).toLowerCase().includes(query.toLowerCase()) : true,
90
89
  );
90
+ }
91
91
  }
92
92
  },
93
93
  [onLinkQuery, slashCommandGroups],
@@ -100,36 +100,35 @@ export const MarkdownEditor = ({
100
100
  trigger,
101
101
  placeholder: {
102
102
  delay: 3_000,
103
- content: () => {
104
- return createElement('div', undefined, [
105
- createElement('span', { text: 'Press' }),
106
- ...trigger.map((text) =>
107
- createElement('span', {
108
- className: 'border border-separator rounded-sm mx-1 px-1.5 pt-[1px] pb-[2px]',
109
- text,
110
- }),
111
- ),
112
- createElement('span', { text: 'for commands.' }),
113
- ]);
114
- },
103
+ content: () =>
104
+ Domino.of('div')
105
+ .children(
106
+ Domino.of('span').text('Press'),
107
+ ...trigger.map((text) =>
108
+ Domino.of('span')
109
+ .classNames('border border-separator rounded-sm mx-1 px-1.5 pt-[1px] pb-[2px]')
110
+ .text(text),
111
+ ),
112
+ Domino.of('span').text('for commands.'),
113
+ )
114
+ .build(),
115
115
  },
116
116
  getMenu,
117
117
  };
118
- }, [getMenu]);
118
+ }, [onLinkQuery, getMenu]);
119
119
 
120
- const { commandMenu, groupsRef, currentItem, onSelect, ...refPopoverProps } = useCommandMenu(options);
120
+ const { groupsRef, commandMenu, ...commandMenuProps } = useCommandMenu(options);
121
121
 
122
- const extensions = useMemo(() => [_extensions, commandMenu].filter(isNotFalsy), [_extensions, commandMenu]);
122
+ const extensions = useMemo(() => [extensionsParam, commandMenu].filter(isTruthy), [extensionsParam, commandMenu]);
123
123
 
124
124
  return (
125
- <RefPopover modal={false} {...refPopoverProps}>
125
+ <CommandMenuProvider groups={groupsRef.current} {...commandMenuProps}>
126
126
  <MarkdownEditorImpl ref={viewRef} {...props} extensions={extensions} />
127
- <CommandMenu groups={groupsRef.current} currentItem={currentItem} onSelect={onSelect} />
128
- </RefPopover>
127
+ </CommandMenuProvider>
129
128
  );
130
129
  };
131
130
 
132
- const MarkdownEditorImpl = forwardRef<EditorView | undefined, MarkdownEditorProps>(
131
+ const MarkdownEditorImpl = forwardRef<EditorView | null, MarkdownEditorProps>(
133
132
  (
134
133
  {
135
134
  id,
@@ -185,14 +184,15 @@ const MarkdownEditorImpl = forwardRef<EditorView | undefined, MarkdownEditorProp
185
184
  readOnly: viewMode === 'readonly',
186
185
  placeholder: t('editor placeholder'),
187
186
  scrollPastEnd: role === 'section' ? false : scrollPastEnd,
187
+ search: true,
188
188
  }),
189
- createMarkdownExtensions({ themeMode }),
189
+ createMarkdownExtensions(),
190
190
  createThemeExtensions({ themeMode, syntaxHighlighting: true, slots: editorSlots }),
191
191
  editorGutter,
192
192
  role !== 'section' && onFileUpload && dropFile({ onDrop: handleDrop }),
193
193
  providerExtensions,
194
194
  extensions,
195
- ].filter(isNotFalsy),
195
+ ].filter(isTruthy),
196
196
  ...(role !== 'section' && {
197
197
  id,
198
198
  scrollTo,
@@ -205,7 +205,7 @@ const MarkdownEditorImpl = forwardRef<EditorView | undefined, MarkdownEditorProp
205
205
  [id, formattingObserver, viewMode, themeMode, extensions, providerExtensions],
206
206
  );
207
207
 
208
- useImperativeHandle(forwardedRef, () => editorView, [editorView]);
208
+ useImperativeHandle<EditorView | null, EditorView | null>(forwardedRef, () => editorView, [editorView]);
209
209
  useTest(editorView);
210
210
  useSelectCurrentThread(editorView, id);
211
211
 
@@ -284,7 +284,7 @@ const MarkdownEditorImpl = forwardRef<EditorView | undefined, MarkdownEditorProp
284
284
 
285
285
  // Expose editor view for playwright tests.
286
286
  // TODO(wittjosiah): Find a better way to expose this or find a way to limit it to test runs.
287
- const useTest = (view?: EditorView) => {
287
+ const useTest = (view: EditorView | null) => {
288
288
  useEffect(() => {
289
289
  const composer = (window as any).composer;
290
290
  if (composer) {