@dxos/plugin-markdown 0.8.4-main.dedc0f3 → 0.8.4-main.ead640a

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 (137) hide show
  1. package/dist/lib/browser/{MarkdownCard-JLUQITYK.mjs → MarkdownCard-AGWOTODZ.mjs} +20 -17
  2. package/dist/lib/browser/MarkdownCard-AGWOTODZ.mjs.map +7 -0
  3. package/dist/lib/browser/{MarkdownContainer-JW7TRDSA.mjs → MarkdownContainer-MV2UNAUV.mjs} +54 -58
  4. package/dist/lib/browser/MarkdownContainer-MV2UNAUV.mjs.map +7 -0
  5. package/dist/lib/browser/{anchor-sort-E33BSTYF.mjs → anchor-sort-YWJI3BKT.mjs} +4 -4
  6. package/dist/lib/browser/{anchor-sort-E33BSTYF.mjs.map → anchor-sort-YWJI3BKT.mjs.map} +1 -1
  7. package/dist/lib/browser/{app-graph-serializer-OX62DNPT.mjs → app-graph-serializer-KYDFCUOW.mjs} +7 -7
  8. package/dist/lib/browser/app-graph-serializer-KYDFCUOW.mjs.map +7 -0
  9. package/dist/lib/browser/{blueprint-definition-5YKFUHRU.mjs → blueprint-definition-BHRMFZAC.mjs} +3 -3
  10. package/dist/lib/browser/{chunk-LAVZ2W6X.mjs → chunk-6KU5DKP7.mjs} +6 -5
  11. package/dist/lib/browser/chunk-6KU5DKP7.mjs.map +7 -0
  12. package/dist/lib/browser/{chunk-F6JJLKLN.mjs → chunk-HBBEHOP3.mjs} +12 -8
  13. package/dist/lib/browser/chunk-HBBEHOP3.mjs.map +7 -0
  14. package/dist/lib/browser/{chunk-Z7P6JGGW.mjs → chunk-O6XUPW6S.mjs} +2 -2
  15. package/dist/lib/browser/{chunk-BEE7VQPU.mjs → chunk-XMT6PMU5.mjs} +6 -5
  16. package/dist/lib/browser/chunk-XMT6PMU5.mjs.map +7 -0
  17. package/dist/lib/browser/chunk-Z5PDJNBV.mjs +22 -0
  18. package/dist/lib/browser/index.mjs +9 -8
  19. package/dist/lib/browser/index.mjs.map +3 -3
  20. package/dist/lib/browser/{intent-resolver-WDDH56JC.mjs → intent-resolver-XHVCZZHU.mjs} +5 -5
  21. package/dist/lib/browser/intent-resolver-XHVCZZHU.mjs.map +7 -0
  22. package/dist/lib/browser/meta.json +1 -1
  23. package/dist/lib/browser/{react-surface-L3NTMD4D.mjs → react-surface-3A2GO3BN.mjs} +7 -7
  24. package/dist/lib/browser/{react-surface-L3NTMD4D.mjs.map → react-surface-3A2GO3BN.mjs.map} +1 -1
  25. package/dist/lib/browser/{settings-AABBTB4Q.mjs → settings-XY265Y2Q.mjs} +4 -4
  26. package/dist/lib/browser/{settings-AABBTB4Q.mjs.map → settings-XY265Y2Q.mjs.map} +1 -1
  27. package/dist/lib/browser/{state-FTHQQX7V.mjs → state-6QODXCSZ.mjs} +3 -3
  28. package/dist/lib/browser/{state-FTHQQX7V.mjs.map → state-6QODXCSZ.mjs.map} +1 -1
  29. package/dist/lib/browser/{toolkit-2AJTHG74.mjs → toolkit-YA65QX2S.mjs} +15 -13
  30. package/dist/lib/browser/toolkit-YA65QX2S.mjs.map +7 -0
  31. package/dist/lib/browser/types/index.mjs +1 -1
  32. package/dist/lib/node-esm/{MarkdownCard-XL5EVSJ7.mjs → MarkdownCard-B2IWTFOC.mjs} +20 -17
  33. package/dist/lib/node-esm/MarkdownCard-B2IWTFOC.mjs.map +7 -0
  34. package/dist/lib/node-esm/{MarkdownContainer-HRGQXIXP.mjs → MarkdownContainer-J2R3DLCQ.mjs} +54 -58
  35. package/dist/lib/node-esm/MarkdownContainer-J2R3DLCQ.mjs.map +7 -0
  36. package/dist/lib/node-esm/{anchor-sort-ALP2NH24.mjs → anchor-sort-FCRYL2FX.mjs} +4 -4
  37. package/dist/lib/node-esm/{anchor-sort-ALP2NH24.mjs.map → anchor-sort-FCRYL2FX.mjs.map} +1 -1
  38. package/dist/lib/node-esm/{app-graph-serializer-56TD3BMX.mjs → app-graph-serializer-FAUQM3BH.mjs} +7 -7
  39. package/dist/lib/node-esm/app-graph-serializer-FAUQM3BH.mjs.map +7 -0
  40. package/dist/lib/node-esm/{blueprint-definition-GVW67KGV.mjs → blueprint-definition-XYFKMIDR.mjs} +3 -3
  41. package/dist/lib/node-esm/chunk-7RDNIMTF.mjs +24 -0
  42. package/dist/lib/node-esm/{chunk-DVK63TD3.mjs → chunk-FVI7LPC3.mjs} +12 -8
  43. package/dist/lib/node-esm/chunk-FVI7LPC3.mjs.map +7 -0
  44. package/dist/lib/node-esm/{chunk-FXILAQ5F.mjs → chunk-FWZKC6X5.mjs} +6 -5
  45. package/dist/lib/node-esm/chunk-FWZKC6X5.mjs.map +7 -0
  46. package/dist/lib/node-esm/{chunk-J7A6TUB2.mjs → chunk-XO3IEQJE.mjs} +2 -2
  47. package/dist/lib/node-esm/{chunk-O6EXWGGS.mjs → chunk-ZBXV4ON7.mjs} +6 -5
  48. package/dist/lib/node-esm/chunk-ZBXV4ON7.mjs.map +7 -0
  49. package/dist/lib/node-esm/index.mjs +9 -8
  50. package/dist/lib/node-esm/index.mjs.map +3 -3
  51. package/dist/lib/node-esm/{intent-resolver-2I5HKCUU.mjs → intent-resolver-7A2EXGZQ.mjs} +5 -5
  52. package/dist/lib/node-esm/intent-resolver-7A2EXGZQ.mjs.map +7 -0
  53. package/dist/lib/node-esm/meta.json +1 -1
  54. package/dist/lib/node-esm/{react-surface-YZSZFR5D.mjs → react-surface-RCLL5WVQ.mjs} +7 -7
  55. package/dist/lib/node-esm/{react-surface-YZSZFR5D.mjs.map → react-surface-RCLL5WVQ.mjs.map} +1 -1
  56. package/dist/lib/node-esm/{settings-CXGR6DH4.mjs → settings-H3UDD3KO.mjs} +4 -4
  57. package/dist/lib/node-esm/{settings-CXGR6DH4.mjs.map → settings-H3UDD3KO.mjs.map} +1 -1
  58. package/dist/lib/node-esm/{state-NWMQ3XAI.mjs → state-W3PECOJX.mjs} +3 -3
  59. package/dist/lib/node-esm/{state-NWMQ3XAI.mjs.map → state-W3PECOJX.mjs.map} +1 -1
  60. package/dist/lib/node-esm/{toolkit-RC44I2MI.mjs → toolkit-HSIKUGNK.mjs} +15 -13
  61. package/dist/lib/node-esm/toolkit-HSIKUGNK.mjs.map +7 -0
  62. package/dist/lib/node-esm/types/index.mjs +1 -1
  63. package/dist/types/src/MarkdownPlugin.d.ts +1 -1
  64. package/dist/types/src/MarkdownPlugin.d.ts.map +1 -1
  65. package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -1
  66. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  67. package/dist/types/src/capabilities/toolkit.d.ts.map +1 -1
  68. package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts.map +1 -1
  69. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts +0 -1
  70. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts.map +1 -1
  71. package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -1
  72. package/dist/types/src/components/MarkdownContainer.stories.d.ts +4 -4
  73. package/dist/types/src/components/MarkdownContainer.stories.d.ts.map +1 -1
  74. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts +4 -4
  75. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts.map +1 -1
  76. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts +1 -1
  77. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts.map +1 -1
  78. package/dist/types/src/components/Suggestions.stories.d.ts +1 -2
  79. package/dist/types/src/components/Suggestions.stories.d.ts.map +1 -1
  80. package/dist/types/src/components/Toolbar.stories.d.ts +1 -1
  81. package/dist/types/src/components/Toolbar.stories.d.ts.map +1 -1
  82. package/dist/types/src/functions/diff.d.ts.map +1 -1
  83. package/dist/types/src/functions/open.d.ts.map +1 -1
  84. package/dist/types/src/hooks/useSelectCurrentThread.d.ts +1 -1
  85. package/dist/types/src/hooks/useSelectCurrentThread.d.ts.map +1 -1
  86. package/dist/types/src/types/Markdown.d.ts +5 -3
  87. package/dist/types/src/types/Markdown.d.ts.map +1 -1
  88. package/dist/types/src/types/MarkdownAction.d.ts +2 -1
  89. package/dist/types/src/types/MarkdownAction.d.ts.map +1 -1
  90. package/dist/types/src/util.d.ts +1 -1
  91. package/dist/types/src/util.d.ts.map +1 -1
  92. package/dist/types/tsconfig.tsbuildinfo +1 -1
  93. package/package.json +54 -54
  94. package/src/MarkdownPlugin.tsx +98 -98
  95. package/src/capabilities/app-graph-serializer.ts +2 -2
  96. package/src/capabilities/artifact-definition.ts +3 -2
  97. package/src/capabilities/intent-resolver.ts +2 -1
  98. package/src/capabilities/toolkit.ts +9 -7
  99. package/src/components/MarkdownCard/MarkdownCard.stories.tsx +3 -6
  100. package/src/components/MarkdownCard/MarkdownCard.tsx +17 -16
  101. package/src/components/MarkdownContainer.stories.tsx +42 -33
  102. package/src/components/MarkdownContainer.tsx +16 -14
  103. package/src/components/MarkdownEditor/MarkdownEditor.stories.tsx +11 -10
  104. package/src/components/MarkdownEditor/MarkdownEditor.tsx +35 -35
  105. package/src/components/Suggestions.stories.tsx +28 -27
  106. package/src/components/Toolbar.stories.tsx +5 -5
  107. package/src/extensions.tsx +3 -3
  108. package/src/functions/diff.ts +4 -2
  109. package/src/functions/open.ts +4 -2
  110. package/src/hooks/useSelectCurrentThread.tsx +2 -2
  111. package/src/types/Markdown.ts +4 -2
  112. package/src/types/MarkdownAction.ts +1 -1
  113. package/src/util.tsx +8 -2
  114. package/dist/lib/browser/MarkdownCard-JLUQITYK.mjs.map +0 -7
  115. package/dist/lib/browser/MarkdownContainer-JW7TRDSA.mjs.map +0 -7
  116. package/dist/lib/browser/app-graph-serializer-OX62DNPT.mjs.map +0 -7
  117. package/dist/lib/browser/chunk-BEE7VQPU.mjs.map +0 -7
  118. package/dist/lib/browser/chunk-F6JJLKLN.mjs.map +0 -7
  119. package/dist/lib/browser/chunk-LAVZ2W6X.mjs.map +0 -7
  120. package/dist/lib/browser/chunk-SUOK6YMI.mjs +0 -22
  121. package/dist/lib/browser/intent-resolver-WDDH56JC.mjs.map +0 -7
  122. package/dist/lib/browser/toolkit-2AJTHG74.mjs.map +0 -7
  123. package/dist/lib/node-esm/MarkdownCard-XL5EVSJ7.mjs.map +0 -7
  124. package/dist/lib/node-esm/MarkdownContainer-HRGQXIXP.mjs.map +0 -7
  125. package/dist/lib/node-esm/app-graph-serializer-56TD3BMX.mjs.map +0 -7
  126. package/dist/lib/node-esm/chunk-DVK63TD3.mjs.map +0 -7
  127. package/dist/lib/node-esm/chunk-FXILAQ5F.mjs.map +0 -7
  128. package/dist/lib/node-esm/chunk-JC2YWB5D.mjs +0 -24
  129. package/dist/lib/node-esm/chunk-O6EXWGGS.mjs.map +0 -7
  130. package/dist/lib/node-esm/intent-resolver-2I5HKCUU.mjs.map +0 -7
  131. package/dist/lib/node-esm/toolkit-RC44I2MI.mjs.map +0 -7
  132. /package/dist/lib/browser/{blueprint-definition-5YKFUHRU.mjs.map → blueprint-definition-BHRMFZAC.mjs.map} +0 -0
  133. /package/dist/lib/browser/{chunk-Z7P6JGGW.mjs.map → chunk-O6XUPW6S.mjs.map} +0 -0
  134. /package/dist/lib/browser/{chunk-SUOK6YMI.mjs.map → chunk-Z5PDJNBV.mjs.map} +0 -0
  135. /package/dist/lib/node-esm/{blueprint-definition-GVW67KGV.mjs.map → blueprint-definition-XYFKMIDR.mjs.map} +0 -0
  136. /package/dist/lib/node-esm/{chunk-JC2YWB5D.mjs.map → chunk-7RDNIMTF.mjs.map} +0 -0
  137. /package/dist/lib/node-esm/{chunk-J7A6TUB2.mjs.map → chunk-XO3IEQJE.mjs.map} +0 -0
@@ -2,28 +2,34 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useMemo } from 'react';
9
7
 
10
- import { Capabilities, IntentPlugin, SettingsPlugin, Surface, contributes } 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 { Testing, type ValueGenerator, createObjectFactory } 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,62 +40,65 @@ 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
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],
65
+ types: [Markdown.Document, DataType.Text, DataType.Person, DataType.Organization],
55
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
 
@@ -10,12 +10,13 @@ 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
- type CommandMenuGroup,
18
- type CommandMenuItem,
18
+ type PopoverMenuGroup,
19
+ type PopoverMenuItem,
19
20
  type PreviewLinkRef,
20
21
  type PreviewOptions,
21
22
  insertAtCursor,
@@ -94,8 +95,9 @@ export const MarkdownContainer = ({
94
95
  ),
95
96
  [objectForms, schemaWhiteList],
96
97
  );
97
- const onLinkQuery = useCallback(
98
- async (query?: string): Promise<CommandMenuGroup[]> => {
98
+
99
+ const handleLinkQuery = useCallback(
100
+ async (query?: string): Promise<PopoverMenuGroup[]> => {
99
101
  const name = query?.startsWith('@') ? query.slice(1).toLowerCase() : (query?.toLowerCase() ?? '');
100
102
  const results = await space?.db.query(Query.select(filter)).run();
101
103
  // TODO(wittjosiah): Use `Obj.Any` type.
@@ -114,7 +116,7 @@ export const MarkdownContainer = ({
114
116
  results?.objects
115
117
  .filter((object) => toLocalizedString(getLabel(object), t).toLowerCase().includes(name))
116
118
  // TODO(wittjosiah): Remove `any` type.
117
- .map((object: any): CommandMenuItem => {
119
+ .map((object: any): PopoverMenuItem => {
118
120
  const metadata = resolve(Obj.getTypename(object)!);
119
121
  const label = toLocalizedString(getLabel(object), t);
120
122
  return {
@@ -122,7 +124,7 @@ export const MarkdownContainer = ({
122
124
  label,
123
125
  icon: metadata.icon,
124
126
  onSelect: (view, head) => {
125
- const link = `[${label}][${Obj.getDXN(object)}]`;
127
+ const link = `[${label}](${Obj.getDXN(object)})`;
126
128
  if (query?.startsWith('@')) {
127
129
  insertAtLineStart(view, head, `!${link}\n`);
128
130
  } else {
@@ -136,6 +138,7 @@ export const MarkdownContainer = ({
136
138
  [filter, resolve, space],
137
139
  );
138
140
 
141
+ // TODO(burdon): Reconcile variants.
139
142
  const editor = doc ? (
140
143
  <DocumentEditor
141
144
  id={fullyQualifiedId(object)}
@@ -146,7 +149,7 @@ export const MarkdownContainer = ({
146
149
  settings={settings}
147
150
  scrollPastEnd={scrollPastEnd}
148
151
  onViewModeChange={onViewModeChange}
149
- onLinkQuery={space ? onLinkQuery : undefined}
152
+ onLinkQuery={space ? handleLinkQuery : undefined}
150
153
  />
151
154
  ) : text ? (
152
155
  <MarkdownEditor
@@ -159,10 +162,9 @@ export const MarkdownContainer = ({
159
162
  inputMode={settings.editorInputMode}
160
163
  scrollPastEnd={scrollPastEnd}
161
164
  onViewModeChange={onViewModeChange}
162
- onLinkQuery={space ? onLinkQuery : undefined}
165
+ onLinkQuery={space ? handleLinkQuery : undefined}
163
166
  />
164
167
  ) : (
165
- // TODO(burdon): Normalize with above.
166
168
  <MarkdownEditor
167
169
  id={id}
168
170
  role={role}
@@ -173,7 +175,7 @@ export const MarkdownContainer = ({
173
175
  inputMode={settings.editorInputMode}
174
176
  scrollPastEnd={scrollPastEnd}
175
177
  onViewModeChange={onViewModeChange}
176
- onLinkQuery={space ? onLinkQuery : undefined}
178
+ onLinkQuery={space ? handleLinkQuery : undefined}
177
179
  />
178
180
  );
179
181
 
@@ -190,9 +192,9 @@ export const MarkdownContainer = ({
190
192
  // TODO(wittjosiah): This shouldn't be "card" but "block".
191
193
  // It's not a preview card but an interactive embedded object.
192
194
  const PreviewBlock = ({ link, el }: { link: PreviewLinkRef; el: HTMLElement }) => {
193
- const echoDXN = useMemo(() => DXN.parse(link.ref).asEchoDXN(), [link.ref]);
194
- const space = useSpace(echoDXN?.spaceId);
195
- 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;
196
198
  const data = useMemo(() => ({ subject }), [subject]);
197
199
 
198
200
  return createPortal(<Surface role='card--transclusion' data={data} limit={1} />, el);
@@ -2,17 +2,16 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
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
16
  import { translations } from '../../translations';
18
17
 
@@ -28,20 +27,22 @@ type StoryProps = MarkdownEditorProps & {
28
27
  const DefaultStory = ({ content = '# Test', toolbar }: StoryProps) => {
29
28
  const doc = useMemo(() => createObject({ content }), [content]); // TODO(burdon): Remove dependency on createObject.
30
29
  const extensions = useMemo(() => [automerge(createDocAccessor(doc, ['content']))], [doc]);
31
- 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
+ );
32
37
  };
33
38
 
34
39
  const meta = {
35
40
  title: 'plugins/plugin-markdown/MarkdownEditor',
36
41
  component: MarkdownEditor as any,
37
42
  render: DefaultStory,
38
- decorators: [
39
- withPluginManager({ plugins: [IntentPlugin()] }),
40
- withAttention,
41
- withTheme,
42
- withLayout({ fullscreen: true }),
43
- ],
43
+ decorators: [withTheme, withPluginManager({ plugins: [IntentPlugin()] }), withAttention],
44
44
  parameters: {
45
+ layout: 'fullscreen',
45
46
  translations: [...translations, ...editorTranslations],
46
47
  },
47
48
  } satisfies Meta<typeof DefaultStory>;
@@ -8,40 +8,38 @@ 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
- type CommandMenuGroup,
15
13
  type DNDOptions,
16
- Domino,
17
14
  type EditorInputMode,
18
15
  type EditorSelectionState,
19
16
  type EditorStateStore,
20
17
  EditorToolbar,
21
18
  type EditorToolbarActionGraphProps,
22
19
  type EditorViewMode,
23
- RefPopover,
24
- type UseCommandMenuOptions,
20
+ type PopoverMenuGroup,
21
+ PopoverMenuProvider,
22
+ type UsePopoverMenuProps,
25
23
  type UseTextEditorProps,
26
24
  addLink,
27
- coreSlashCommands,
28
25
  createBasicExtensions,
29
26
  createMarkdownExtensions,
30
27
  createThemeExtensions,
31
28
  dropFile,
32
29
  editorGutter,
33
30
  editorSlots,
34
- filterItems,
31
+ filterMenuGroups,
32
+ formattingCommands,
35
33
  linkSlashCommands,
36
34
  processEditorPayload,
37
35
  stackItemContentEditorClassNames,
38
- useCommandMenu,
39
36
  useEditorToolbarState,
40
37
  useFormattingState,
38
+ usePopoverMenu,
41
39
  useTextEditor,
42
40
  } from '@dxos/react-ui-editor';
43
41
  import { StackItem } from '@dxos/react-ui-stack';
44
- import { isNonNullable, isNotFalsy } from '@dxos/util';
42
+ import { isNonNullable, isTruthy } from '@dxos/util';
45
43
 
46
44
  import { useSelectCurrentThread } from '../../hooks';
47
45
  import { meta } from '../../meta';
@@ -53,16 +51,16 @@ export type MarkdownEditorProps = {
53
51
  toolbar?: boolean;
54
52
  inputMode?: EditorInputMode;
55
53
  scrollPastEnd?: boolean;
56
- slashCommandGroups?: CommandMenuGroup[];
54
+ slashCommandGroups?: PopoverMenuGroup[];
57
55
  customActions?: EditorToolbarActionGraphProps['customActions'];
58
56
  // TODO(wittjosiah): Generalize custom toolbar actions (e.g. comment, upload, etc.)
59
57
  viewMode?: EditorViewMode;
60
58
  editorStateStore?: EditorStateStore;
61
59
  onViewModeChange?: (id: string, mode: EditorViewMode) => void;
62
- onLinkQuery?: (query?: string) => Promise<CommandMenuGroup[]>;
60
+ onLinkQuery?: (query?: string) => Promise<PopoverMenuGroup[]>;
63
61
  onFileUpload?: (file: File) => Promise<FileInfo | undefined>;
64
- } & Pick<UseTextEditorProps, 'initialValue' | 'extensions'> &
65
- Partial<Pick<MarkdownPluginState, 'extensionProviders'>>;
62
+ } & (Pick<UseTextEditorProps, 'initialValue' | 'extensions'> &
63
+ Partial<Pick<MarkdownPluginState, 'extensionProviders'>>);
66
64
 
67
65
  /**
68
66
  * Base markdown editor component.
@@ -76,24 +74,27 @@ export const MarkdownEditor = ({
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(
82
- (trigger: string, query?: string) => {
79
+ const getMenu = useCallback<NonNullable<UsePopoverMenuProps['getMenu']>>(
80
+ ({ text, trigger }) => {
83
81
  switch (trigger) {
84
- case '@':
85
- return onLinkQuery?.(query) ?? [];
82
+ case '@': {
83
+ return onLinkQuery?.(text) ?? [];
84
+ }
85
+
86
86
  case '/':
87
- default:
88
- return filterItems([coreSlashCommands, linkSlashCommands, ...(slashCommandGroups ?? [])], (item) =>
89
- query ? toLocalizedString(item.label, t).toLowerCase().includes(query.toLowerCase()) : true,
87
+ default: {
88
+ return filterMenuGroups([formattingCommands, linkSlashCommands, ...(slashCommandGroups ?? [])], (item) =>
89
+ text ? toLocalizedString(item.label, t).toLowerCase().includes(text.toLowerCase()) : true,
90
90
  );
91
+ }
91
92
  }
92
93
  },
93
94
  [onLinkQuery, slashCommandGroups],
94
95
  );
95
96
 
96
- const options = useMemo<UseCommandMenuOptions>(() => {
97
+ const options = useMemo<UsePopoverMenuProps>(() => {
97
98
  const trigger = onLinkQuery ? ['/', '@'] : ['/'];
98
99
  return {
99
100
  viewRef,
@@ -102,11 +103,11 @@ export const MarkdownEditor = ({
102
103
  delay: 3_000,
103
104
  content: () =>
104
105
  Domino.of('div')
105
- .child(
106
+ .children(
106
107
  Domino.of('span').text('Press'),
107
108
  ...trigger.map((text) =>
108
109
  Domino.of('span')
109
- .classNames('border border-separator rounded-sm mx-1 px-1.5 pt-[1px] pb-[2px]')
110
+ .classNames('mx-1 px-1.5 pt-[1px] pb-[2px] border border-separator rounded-sm')
110
111
  .text(text),
111
112
  ),
112
113
  Domino.of('span').text('for commands.'),
@@ -115,20 +116,19 @@ export const MarkdownEditor = ({
115
116
  },
116
117
  getMenu,
117
118
  };
118
- }, [getMenu]);
119
+ }, [onLinkQuery, getMenu]);
119
120
 
120
- const { commandMenu, groupsRef, currentItem, onSelect, ...refPopoverProps } = useCommandMenu(options);
121
- const extensions = useMemo(() => [extensionsParam, commandMenu].filter(isNotFalsy), [extensionsParam, commandMenu]);
121
+ const { groupsRef, extension, ...commandMenuProps } = usePopoverMenu(options);
122
+ const extensions = useMemo(() => [extensionsParam, extension].filter(isTruthy), [extensionsParam, extension]);
122
123
 
123
124
  return (
124
- <RefPopover modal={false} {...refPopoverProps}>
125
+ <PopoverMenuProvider view={viewRef.current} groups={groupsRef.current} {...commandMenuProps}>
125
126
  <MarkdownEditorImpl ref={viewRef} {...props} extensions={extensions} />
126
- <CommandMenu groups={groupsRef.current} currentItem={currentItem} onSelect={onSelect} />
127
- </RefPopover>
127
+ </PopoverMenuProvider>
128
128
  );
129
129
  };
130
130
 
131
- const MarkdownEditorImpl = forwardRef<EditorView | undefined, MarkdownEditorProps>(
131
+ const MarkdownEditorImpl = forwardRef<EditorView | null, MarkdownEditorProps>(
132
132
  (
133
133
  {
134
134
  id,
@@ -192,7 +192,7 @@ const MarkdownEditorImpl = forwardRef<EditorView | undefined, MarkdownEditorProp
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) {
@@ -2,24 +2,19 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta } from '@storybook/react-vite';
8
- import { Match, Option, Schema, pipe } from 'effect';
6
+ import * as Function from 'effect/Function';
7
+ import * as Match from 'effect/Match';
8
+ import * as Option from 'effect/Option';
9
+ import * as Schema from 'effect/Schema';
9
10
  import React, { type FC, useEffect, useMemo, useState } from 'react';
10
11
 
11
- import {
12
- Capabilities,
13
- IntentPlugin,
14
- SettingsPlugin,
15
- contributes,
16
- useCapability,
17
- useIntentDispatcher,
18
- } from '@dxos/app-framework';
12
+ import { Capabilities, IntentPlugin, SettingsPlugin, useCapability, useIntentDispatcher } from '@dxos/app-framework';
19
13
  import { withPluginManager } from '@dxos/app-framework/testing';
20
14
  import { Obj, Ref, Type } from '@dxos/echo';
21
15
  import { invariant } from '@dxos/invariant';
22
16
  import { ClientPlugin } from '@dxos/plugin-client';
17
+ import { GraphPlugin } from '@dxos/plugin-graph';
23
18
  import { PreviewPlugin } from '@dxos/plugin-preview';
24
19
  import { SpacePlugin } from '@dxos/plugin-space';
25
20
  import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
@@ -27,11 +22,12 @@ import { ThemePlugin } from '@dxos/plugin-theme';
27
22
  import { faker } from '@dxos/random';
28
23
  import { createDocAccessor, fullyQualifiedId, toCursorRange, useQueue, useSpace } from '@dxos/react-client/echo';
29
24
  import { IconButton, Toolbar } from '@dxos/react-ui';
30
- import { type EditorSelection, type Range, command, useTextEditor } from '@dxos/react-ui-editor';
25
+ import { withTheme } from '@dxos/react-ui/testing';
26
+ import { type EditorSelection, type Range, useTextEditor } from '@dxos/react-ui-editor';
31
27
  import { StackItem } from '@dxos/react-ui-stack';
32
28
  import { defaultTx } from '@dxos/react-ui-theme';
33
29
  import { DataType } from '@dxos/schema';
34
- import { withLayout } from '@dxos/storybook-utils';
30
+ import { render } from '@dxos/storybook-utils';
35
31
 
36
32
  import { MarkdownCapabilities } from '../capabilities';
37
33
  import { MarkdownPlugin } from '../MarkdownPlugin';
@@ -83,7 +79,7 @@ const TestChat: FC<{ doc: Markdown.Document; content: string }> = ({ doc, conten
83
79
 
84
80
  const text = await doc.content.load();
85
81
  const accessor = createDocAccessor(text, ['content']);
86
- const cursor = pipe(
82
+ const cursor = Function.pipe(
87
83
  editorState.getState(fullyQualifiedId(doc))?.selection,
88
84
  Option.fromNullable,
89
85
  Option.map(selectionToRange),
@@ -133,7 +129,7 @@ const DefaultStory = ({ document, chat }: { document: string; chat: string }) =>
133
129
  content: document.replaceAll(/\[(\w+)\]/g, (_, label) => {
134
130
  const obj = space.db.add(Obj.make(TestItem, { title: label, description: faker.lorem.paragraph() }));
135
131
  const dxn = Ref.make(obj).dxn.toString();
136
- return `[${label}][${dxn}]`;
132
+ return `[${label}](${dxn})`;
137
133
  }),
138
134
  }),
139
135
  );
@@ -142,45 +138,50 @@ const DefaultStory = ({ document, chat }: { document: string; chat: string }) =>
142
138
  }, [space]);
143
139
 
144
140
  if (!space || !doc) {
145
- return <></>;
141
+ return null;
146
142
  }
147
143
 
144
+ // TODO(burdon): Layout issue.
148
145
  return (
149
- <>
146
+ <div className='grid grid-cols-2 bs-full overflow-hidden'>
150
147
  <MarkdownContainer id={doc.id} object={doc} settings={settings} editorStateStore={editorState} />
151
148
  <TestChat doc={doc} content={chat} />
152
- </>
149
+ </div>
153
150
  );
154
151
  };
155
152
 
156
- // TODO(burdon): Make consistent.
157
153
  const storybook: Meta<typeof DefaultStory> = {
158
154
  title: 'plugins/plugin-markdown/Suggestions',
159
- render: DefaultStory,
155
+ render: render(DefaultStory),
160
156
  decorators: [
157
+ withTheme,
161
158
  withPluginManager({
162
159
  plugins: [
163
- ThemePlugin({ tx: defaultTx }),
164
- StorybookLayoutPlugin(),
165
160
  ClientPlugin({
166
161
  types: [Markdown.Document, TestItem],
167
162
  onClientInitialized: async ({ client }) => {
168
163
  await client.halo.createIdentity();
169
164
  },
170
165
  }),
171
- SpacePlugin(),
172
- SettingsPlugin(),
166
+ SpacePlugin({}),
167
+ GraphPlugin(),
173
168
  IntentPlugin(),
169
+ SettingsPlugin(),
170
+
171
+ // UI
172
+ ThemePlugin({ tx: defaultTx }),
174
173
  MarkdownPlugin(),
175
174
  PreviewPlugin(),
175
+ StorybookLayoutPlugin({}),
176
176
  ],
177
- capabilities: [contributes(MarkdownCapabilities.Extensions, [() => command()])],
178
177
  }),
179
- withLayout({ fullscreen: true, classNames: 'grid grid-cols-2' }),
180
178
  ],
181
179
  parameters: {
180
+ layout: 'fullscreen',
181
+ controls: {
182
+ disable: true,
183
+ },
182
184
  translations,
183
- controls: { disable: true },
184
185
  },
185
186
  };
186
187
 
@@ -2,16 +2,16 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useCallback, useState } from 'react';
9
7
 
8
+ import { createObject } from '@dxos/echo-db';
10
9
  import { invariant } from '@dxos/invariant';
11
10
  import { PublicKey } from '@dxos/keys';
12
11
  import { faker } from '@dxos/random';
13
12
  import { createDocAccessor } from '@dxos/react-client/echo';
14
13
  import { useThemeContext } from '@dxos/react-ui';
14
+ import { withTheme } from '@dxos/react-ui/testing';
15
15
  import {
16
16
  type Comment,
17
17
  EditorToolbar,
@@ -31,7 +31,6 @@ import {
31
31
  useTextEditor,
32
32
  } from '@dxos/react-ui-editor';
33
33
  import { DataType } from '@dxos/schema';
34
- import { withLayout, withTheme } from '@dxos/storybook-utils';
35
34
 
36
35
  faker.seed(101);
37
36
 
@@ -41,7 +40,7 @@ type StoryProps = {
41
40
 
42
41
  const DefaultStory = ({ content = '' }: StoryProps) => {
43
42
  const { themeMode } = useThemeContext();
44
- const [text] = useState(DataType.makeText(content));
43
+ const [text] = useState(createObject(DataType.makeText(content)));
45
44
  const toolbarState = useEditorToolbarState({ viewMode: 'preview' });
46
45
  const formattingObserver = useFormattingState(toolbarState);
47
46
  const { parentRef, view } = useTextEditor(() => {
@@ -101,8 +100,9 @@ const meta = {
101
100
  title: 'plugins/plugin-markdown/Toolbar',
102
101
  component: EditorToolbar as any,
103
102
  render: DefaultStory,
104
- decorators: [withTheme, withLayout({ fullscreen: true })],
103
+ decorators: [withTheme],
105
104
  parameters: {
105
+ layout: 'fullscreen',
106
106
  translations,
107
107
  },
108
108
  } satisfies Meta<typeof DefaultStory>;
@@ -41,7 +41,7 @@ import {
41
41
  } from '@dxos/react-ui-editor';
42
42
  import { defaultTx } from '@dxos/react-ui-theme';
43
43
  import { type DataType } from '@dxos/schema';
44
- import { isNotFalsy } from '@dxos/util';
44
+ import { isTruthy } from '@dxos/util';
45
45
 
46
46
  import { MarkdownCapabilities } from './capabilities';
47
47
  import { type Markdown } from './types';
@@ -157,7 +157,7 @@ export const useExtensions = ({
157
157
  }),
158
158
  baseExtensions,
159
159
  pluginExtensions,
160
- ].filter(isNotFalsy),
160
+ ].filter(isTruthy),
161
161
  [baseExtensions, pluginExtensions, document, document?.content?.target, text, id, space, identity],
162
162
  );
163
163
  };
@@ -178,7 +178,7 @@ const createBaseExtensions = ({
178
178
  selectionManager && selectionChange(selectionManager),
179
179
  settings.editorInputMode && InputModeExtensions[settings.editorInputMode],
180
180
  settings.folding && folding(),
181
- ].filter(isNotFalsy);
181
+ ].filter(isTruthy);
182
182
 
183
183
  //
184
184
  // Markdown
@@ -2,7 +2,8 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Effect, Schema } from 'effect';
5
+ import * as Effect from 'effect/Effect';
6
+ import * as Schema from 'effect/Schema';
6
7
 
7
8
  import { ArtifactId, applyDiffs } from '@dxos/assistant';
8
9
  import { createDocAccessor } from '@dxos/echo-db';
@@ -13,7 +14,8 @@ import { Markdown } from '../types';
13
14
 
14
15
  // TODO(wittjosiah): Reconcile with ThreadAction.AddProposal.
15
16
  export default defineFunction({
16
- name: 'dxos.org/function/markdown/diff',
17
+ key: 'dxos.org/function/markdown/diff',
18
+ name: 'Diff',
17
19
  description: trim`
18
20
  Applies a set of diffs to the markdown document.
19
21
  `,