@dxos/plugin-markdown 0.8.4-main.28f8d3d → 0.8.4-main.406dc2a

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 (178) hide show
  1. package/dist/lib/browser/{MarkdownCard-IMCR4A4V.mjs → MarkdownCard-AGWOTODZ.mjs} +21 -18
  2. package/dist/lib/browser/MarkdownCard-AGWOTODZ.mjs.map +7 -0
  3. package/dist/lib/browser/{MarkdownContainer-VGJPHMVG.mjs → MarkdownContainer-MV2UNAUV.mjs} +64 -93
  4. package/dist/lib/browser/MarkdownContainer-MV2UNAUV.mjs.map +7 -0
  5. package/dist/lib/browser/{anchor-sort-AJKRIWFD.mjs → anchor-sort-YWJI3BKT.mjs} +5 -5
  6. package/dist/lib/browser/{anchor-sort-AJKRIWFD.mjs.map → anchor-sort-YWJI3BKT.mjs.map} +1 -1
  7. package/dist/lib/browser/{app-graph-serializer-OIS3MZX2.mjs → app-graph-serializer-KYDFCUOW.mjs} +8 -9
  8. package/dist/lib/browser/app-graph-serializer-KYDFCUOW.mjs.map +7 -0
  9. package/dist/lib/browser/blueprint-definition-BHRMFZAC.mjs +11 -0
  10. package/dist/lib/browser/blueprint-definition-BHRMFZAC.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-JMBQG2ZC.mjs → chunk-6KU5DKP7.mjs} +6 -5
  12. package/dist/lib/browser/chunk-6KU5DKP7.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-HBBEHOP3.mjs +106 -0
  14. package/dist/lib/browser/chunk-HBBEHOP3.mjs.map +7 -0
  15. package/dist/lib/browser/{chunk-MVL4K3OD.mjs → chunk-O6XUPW6S.mjs} +4 -3
  16. package/dist/lib/browser/chunk-O6XUPW6S.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-IKPZHFTW.mjs → chunk-ODB2PTBP.mjs} +2 -4
  18. package/dist/lib/browser/{chunk-IKPZHFTW.mjs.map → chunk-ODB2PTBP.mjs.map} +3 -3
  19. package/dist/lib/browser/{chunk-LMJPXTPL.mjs → chunk-OY6CGPOO.mjs} +2 -2
  20. package/dist/lib/browser/{chunk-JTIGSUMB.mjs → chunk-XMT6PMU5.mjs} +11 -12
  21. package/dist/lib/browser/chunk-XMT6PMU5.mjs.map +7 -0
  22. package/dist/lib/browser/chunk-Z5PDJNBV.mjs +22 -0
  23. package/dist/lib/browser/chunk-Z5PDJNBV.mjs.map +7 -0
  24. package/dist/lib/browser/index.mjs +31 -16
  25. package/dist/lib/browser/index.mjs.map +3 -3
  26. package/dist/lib/browser/{intent-resolver-BLLHRSTQ.mjs → intent-resolver-XHVCZZHU.mjs} +20 -18
  27. package/dist/lib/browser/intent-resolver-XHVCZZHU.mjs.map +7 -0
  28. package/dist/lib/browser/meta.json +1 -1
  29. package/dist/lib/browser/{react-surface-NL3BZR6H.mjs → react-surface-3A2GO3BN.mjs} +38 -31
  30. package/dist/lib/browser/react-surface-3A2GO3BN.mjs.map +7 -0
  31. package/dist/lib/browser/{settings-Z7ZV7SLC.mjs → settings-XY265Y2Q.mjs} +5 -5
  32. package/dist/lib/browser/{settings-Z7ZV7SLC.mjs.map → settings-XY265Y2Q.mjs.map} +1 -1
  33. package/dist/lib/browser/{state-ORTZIEJU.mjs → state-6QODXCSZ.mjs} +5 -5
  34. package/dist/lib/browser/{state-ORTZIEJU.mjs.map → state-6QODXCSZ.mjs.map} +1 -1
  35. package/dist/lib/browser/toolkit-YA65QX2S.mjs +76 -0
  36. package/dist/lib/browser/toolkit-YA65QX2S.mjs.map +7 -0
  37. package/dist/lib/browser/types/index.mjs +2 -2
  38. package/dist/lib/node-esm/{MarkdownCard-JV2YDV3M.mjs → MarkdownCard-B2IWTFOC.mjs} +21 -18
  39. package/dist/lib/node-esm/MarkdownCard-B2IWTFOC.mjs.map +7 -0
  40. package/dist/lib/node-esm/{MarkdownContainer-BJRNCXJZ.mjs → MarkdownContainer-J2R3DLCQ.mjs} +64 -93
  41. package/dist/lib/node-esm/MarkdownContainer-J2R3DLCQ.mjs.map +7 -0
  42. package/dist/lib/node-esm/{anchor-sort-N7WEA5E3.mjs → anchor-sort-FCRYL2FX.mjs} +5 -5
  43. package/dist/lib/node-esm/{anchor-sort-N7WEA5E3.mjs.map → anchor-sort-FCRYL2FX.mjs.map} +1 -1
  44. package/dist/lib/node-esm/{app-graph-serializer-Q3B44VFB.mjs → app-graph-serializer-FAUQM3BH.mjs} +8 -9
  45. package/dist/lib/node-esm/app-graph-serializer-FAUQM3BH.mjs.map +7 -0
  46. package/dist/lib/node-esm/blueprint-definition-XYFKMIDR.mjs +12 -0
  47. package/dist/lib/node-esm/blueprint-definition-XYFKMIDR.mjs.map +7 -0
  48. package/dist/lib/node-esm/chunk-7RDNIMTF.mjs +24 -0
  49. package/dist/lib/node-esm/chunk-7RDNIMTF.mjs.map +7 -0
  50. package/dist/lib/node-esm/{chunk-AYWAYBAY.mjs → chunk-CB2R4YIY.mjs} +2 -2
  51. package/dist/lib/node-esm/chunk-FVI7LPC3.mjs +107 -0
  52. package/dist/lib/node-esm/chunk-FVI7LPC3.mjs.map +7 -0
  53. package/dist/lib/node-esm/{chunk-KIRZFVX5.mjs → chunk-FWZKC6X5.mjs} +11 -12
  54. package/dist/lib/node-esm/chunk-FWZKC6X5.mjs.map +7 -0
  55. package/dist/lib/node-esm/{chunk-H4I2FGDZ.mjs → chunk-VCG2U522.mjs} +2 -4
  56. package/dist/lib/node-esm/{chunk-H4I2FGDZ.mjs.map → chunk-VCG2U522.mjs.map} +3 -3
  57. package/dist/lib/node-esm/{chunk-Z4XQ6C3D.mjs → chunk-XO3IEQJE.mjs} +4 -3
  58. package/dist/lib/node-esm/chunk-XO3IEQJE.mjs.map +7 -0
  59. package/dist/lib/node-esm/{chunk-JDMMLOB6.mjs → chunk-ZBXV4ON7.mjs} +6 -5
  60. package/dist/lib/node-esm/chunk-ZBXV4ON7.mjs.map +7 -0
  61. package/dist/lib/node-esm/index.mjs +31 -16
  62. package/dist/lib/node-esm/index.mjs.map +3 -3
  63. package/dist/lib/node-esm/{intent-resolver-NHPUPSWK.mjs → intent-resolver-7A2EXGZQ.mjs} +20 -18
  64. package/dist/lib/node-esm/intent-resolver-7A2EXGZQ.mjs.map +7 -0
  65. package/dist/lib/node-esm/meta.json +1 -1
  66. package/dist/lib/node-esm/{react-surface-XJ6ODCBE.mjs → react-surface-RCLL5WVQ.mjs} +38 -31
  67. package/dist/lib/node-esm/react-surface-RCLL5WVQ.mjs.map +7 -0
  68. package/dist/lib/node-esm/{settings-UCXEWBCT.mjs → settings-H3UDD3KO.mjs} +5 -5
  69. package/dist/lib/node-esm/{settings-UCXEWBCT.mjs.map → settings-H3UDD3KO.mjs.map} +1 -1
  70. package/dist/lib/node-esm/{state-47WSZG54.mjs → state-W3PECOJX.mjs} +5 -5
  71. package/dist/lib/node-esm/{state-47WSZG54.mjs.map → state-W3PECOJX.mjs.map} +1 -1
  72. package/dist/lib/node-esm/toolkit-HSIKUGNK.mjs +77 -0
  73. package/dist/lib/node-esm/toolkit-HSIKUGNK.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/artifact-definition.d.ts +0 -8
  80. package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -1
  81. package/dist/types/src/capabilities/blueprint-definition.d.ts +5 -0
  82. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
  83. package/dist/types/src/capabilities/index.d.ts +11 -8
  84. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  85. package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
  86. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  87. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  88. package/dist/types/src/capabilities/settings.d.ts +1 -1
  89. package/dist/types/src/capabilities/state.d.ts +2 -2
  90. package/dist/types/src/capabilities/toolkit.d.ts +4 -0
  91. package/dist/types/src/capabilities/toolkit.d.ts.map +1 -0
  92. package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts.map +1 -1
  93. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts +5 -38
  94. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts.map +1 -1
  95. package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -1
  96. package/dist/types/src/components/MarkdownContainer.stories.d.ts +51 -5
  97. package/dist/types/src/components/MarkdownContainer.stories.d.ts.map +1 -1
  98. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts +5 -5
  99. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts.map +1 -1
  100. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts +111 -13
  101. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts.map +1 -1
  102. package/dist/types/src/components/MarkdownSettings/MarkdownSettings.d.ts.map +1 -1
  103. package/dist/types/src/components/Suggestions.stories.d.ts +1 -2
  104. package/dist/types/src/components/Suggestions.stories.d.ts.map +1 -1
  105. package/dist/types/src/components/Toolbar.stories.d.ts +45 -8
  106. package/dist/types/src/components/Toolbar.stories.d.ts.map +1 -1
  107. package/dist/types/src/extensions.d.ts +0 -2
  108. package/dist/types/src/extensions.d.ts.map +1 -1
  109. package/dist/types/src/functions/diff.d.ts +6 -0
  110. package/dist/types/src/functions/diff.d.ts.map +1 -0
  111. package/dist/types/src/functions/index.d.ts +3 -0
  112. package/dist/types/src/functions/index.d.ts.map +1 -0
  113. package/dist/types/src/functions/open.d.ts +7 -0
  114. package/dist/types/src/functions/open.d.ts.map +1 -0
  115. package/dist/types/src/hooks/useSelectCurrentThread.d.ts +1 -1
  116. package/dist/types/src/hooks/useSelectCurrentThread.d.ts.map +1 -1
  117. package/dist/types/src/meta.d.ts +0 -1
  118. package/dist/types/src/meta.d.ts.map +1 -1
  119. package/dist/types/src/translations.d.ts +1 -0
  120. package/dist/types/src/translations.d.ts.map +1 -1
  121. package/dist/types/src/types/Markdown.d.ts +7 -5
  122. package/dist/types/src/types/Markdown.d.ts.map +1 -1
  123. package/dist/types/src/types/MarkdownAction.d.ts +6 -14
  124. package/dist/types/src/types/MarkdownAction.d.ts.map +1 -1
  125. package/dist/types/src/types/types.d.ts.map +1 -1
  126. package/dist/types/src/util.d.ts +1 -1
  127. package/dist/types/src/util.d.ts.map +1 -1
  128. package/dist/types/tsconfig.tsbuildinfo +1 -1
  129. package/package.json +55 -52
  130. package/src/MarkdownPlugin.tsx +100 -86
  131. package/src/capabilities/app-graph-serializer.ts +3 -3
  132. package/src/capabilities/artifact-definition.ts +9 -16
  133. package/src/capabilities/blueprint-definition.ts +39 -0
  134. package/src/capabilities/index.ts +3 -0
  135. package/src/capabilities/intent-resolver.ts +11 -11
  136. package/src/capabilities/toolkit.ts +49 -0
  137. package/src/components/MarkdownCard/MarkdownCard.stories.tsx +8 -11
  138. package/src/components/MarkdownCard/MarkdownCard.tsx +17 -16
  139. package/src/components/MarkdownContainer.stories.tsx +46 -37
  140. package/src/components/MarkdownContainer.tsx +16 -14
  141. package/src/components/MarkdownEditor/MarkdownEditor.stories.tsx +19 -16
  142. package/src/components/MarkdownEditor/MarkdownEditor.tsx +47 -48
  143. package/src/components/MarkdownSettings/MarkdownSettings.tsx +77 -74
  144. package/src/components/Suggestions.stories.tsx +36 -38
  145. package/src/components/Toolbar.stories.tsx +20 -14
  146. package/src/extensions.tsx +4 -32
  147. package/src/functions/diff.ts +37 -0
  148. package/src/functions/index.ts +6 -0
  149. package/src/functions/open.ts +32 -0
  150. package/src/hooks/useSelectCurrentThread.tsx +2 -2
  151. package/src/meta.ts +0 -3
  152. package/src/translations.ts +1 -0
  153. package/src/types/Markdown.ts +6 -3
  154. package/src/types/MarkdownAction.ts +4 -6
  155. package/src/types/types.ts +1 -0
  156. package/src/util.tsx +8 -2
  157. package/dist/lib/browser/MarkdownCard-IMCR4A4V.mjs.map +0 -7
  158. package/dist/lib/browser/MarkdownContainer-VGJPHMVG.mjs.map +0 -7
  159. package/dist/lib/browser/app-graph-serializer-OIS3MZX2.mjs.map +0 -7
  160. package/dist/lib/browser/chunk-JHH7VL52.mjs +0 -18
  161. package/dist/lib/browser/chunk-JHH7VL52.mjs.map +0 -7
  162. package/dist/lib/browser/chunk-JMBQG2ZC.mjs.map +0 -7
  163. package/dist/lib/browser/chunk-JTIGSUMB.mjs.map +0 -7
  164. package/dist/lib/browser/chunk-MVL4K3OD.mjs.map +0 -7
  165. package/dist/lib/browser/intent-resolver-BLLHRSTQ.mjs.map +0 -7
  166. package/dist/lib/browser/react-surface-NL3BZR6H.mjs.map +0 -7
  167. package/dist/lib/node-esm/MarkdownCard-JV2YDV3M.mjs.map +0 -7
  168. package/dist/lib/node-esm/MarkdownContainer-BJRNCXJZ.mjs.map +0 -7
  169. package/dist/lib/node-esm/app-graph-serializer-Q3B44VFB.mjs.map +0 -7
  170. package/dist/lib/node-esm/chunk-H6TITL7A.mjs +0 -20
  171. package/dist/lib/node-esm/chunk-H6TITL7A.mjs.map +0 -7
  172. package/dist/lib/node-esm/chunk-JDMMLOB6.mjs.map +0 -7
  173. package/dist/lib/node-esm/chunk-KIRZFVX5.mjs.map +0 -7
  174. package/dist/lib/node-esm/chunk-Z4XQ6C3D.mjs.map +0 -7
  175. package/dist/lib/node-esm/intent-resolver-NHPUPSWK.mjs.map +0 -7
  176. package/dist/lib/node-esm/react-surface-XJ6ODCBE.mjs.map +0 -7
  177. /package/dist/lib/browser/{chunk-LMJPXTPL.mjs.map → chunk-OY6CGPOO.mjs.map} +0 -0
  178. /package/dist/lib/node-esm/{chunk-AYWAYBAY.mjs.map → chunk-CB2R4YIY.mjs.map} +0 -0
@@ -2,20 +2,20 @@
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
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';
12
- import { Button, Icon, useTranslation } from '@dxos/react-ui';
12
+ import { IconButton, useTranslation } from '@dxos/react-ui';
13
13
  import { Card } from '@dxos/react-ui-stack';
14
14
  import { DataType } from '@dxos/schema';
15
15
 
16
16
  import { meta } from '../../meta';
17
17
  import { Markdown } from '../../types';
18
- import { getAbstract, getFallbackName } from '../../util';
18
+ import { getContentSnippet, getFallbackName } from '../../util';
19
19
 
20
20
  // TODO(burdon): Factor out.
21
21
  const getTitle = (subject: Markdown.Document | DataType.Text, fallback: string) => {
@@ -29,9 +29,9 @@ const getTitle = (subject: Markdown.Document | DataType.Text, fallback: string)
29
29
  // TODO(burdon): Factor out.
30
30
  const getSnippet = (subject: Markdown.Document | DataType.Text, fallback: string) => {
31
31
  if (Obj.instanceOf(Markdown.Document, subject)) {
32
- return getAbstract(subject.content?.target?.content ?? fallback);
32
+ return Obj.getDescription(subject) || getContentSnippet(subject.content?.target?.content ?? fallback);
33
33
  } else if (Obj.instanceOf(DataType.Text, subject)) {
34
- return getAbstract(subject.content);
34
+ return getContentSnippet(subject.content ?? fallback);
35
35
  }
36
36
  };
37
37
 
@@ -46,7 +46,7 @@ export const MarkdownCard = ({ subject, role }: MarkdownCardProps) => {
46
46
  const handleNavigate = useCallback(
47
47
  () =>
48
48
  dispatch(
49
- pipe(
49
+ Function.pipe(
50
50
  createIntent(LayoutAction.UpdatePopover, {
51
51
  part: 'popover',
52
52
  subject: null,
@@ -60,16 +60,17 @@ export const MarkdownCard = ({ subject, role }: MarkdownCardProps) => {
60
60
 
61
61
  return (
62
62
  <Card.SurfaceRoot role={role}>
63
- <Card.Heading>{getTitle(subject, t('fallback title'))}</Card.Heading>
64
- {snippet && <Card.Text classNames='line-clamp-3 break-words col-span-2'>{snippet}</Card.Text>}
65
- {role === 'card--popover' && (
66
- <Card.Chrome>
67
- <Button onClick={handleNavigate}>
68
- <span className='grow'>{t('navigate to document label')}</span>
69
- <Icon icon='ph--arrow-right--regular' />
70
- </Button>
71
- </Card.Chrome>
72
- )}
63
+ <Card.Heading classNames='flex items-center'>
64
+ {getTitle(subject, t('fallback title'))}
65
+ <span className='grow' />
66
+ <IconButton
67
+ iconOnly
68
+ icon='ph--arrow-right--regular'
69
+ label={t('navigate to document label')}
70
+ onClick={handleNavigate}
71
+ />
72
+ </Card.Heading>
73
+ {snippet && <Card.Text classNames='line-clamp-3 text-sm text-description'>{snippet}</Card.Text>}
73
74
  </Card.SurfaceRoot>
74
75
  );
75
76
  };
@@ -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, 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,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],
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
 
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,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
- 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
16
  import { translations } from '../../translations';
18
17
 
@@ -28,33 +27,37 @@ 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
- const meta: Meta<typeof MarkdownEditor> = {
39
+ const meta = {
35
40
  title: 'plugins/plugin-markdown/MarkdownEditor',
36
- component: MarkdownEditor,
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>;
48
49
 
49
50
  export default meta;
50
51
 
51
- export const Default = {
52
+ type Story = StoryObj<typeof meta>;
53
+
54
+ export const Default: Story = {
52
55
  args: {
53
56
  content,
54
57
  },
55
58
  };
56
59
 
57
- export const WithToolbar = {
60
+ export const WithToolbar: Story = {
58
61
  args: {
59
62
  toolbar: true,
60
63
  content,
@@ -8,10 +8,8 @@ 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
14
  type EditorInputMode,
17
15
  type EditorSelectionState,
@@ -19,29 +17,29 @@ import {
19
17
  EditorToolbar,
20
18
  type EditorToolbarActionGraphProps,
21
19
  type EditorViewMode,
22
- RefPopover,
23
- type UseCommandMenuOptions,
20
+ type PopoverMenuGroup,
21
+ PopoverMenuProvider,
22
+ type UsePopoverMenuProps,
24
23
  type UseTextEditorProps,
25
24
  addLink,
26
- coreSlashCommands,
27
25
  createBasicExtensions,
28
- createElement,
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.
@@ -70,66 +68,67 @@ 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(
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,
100
101
  trigger,
101
102
  placeholder: {
102
103
  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
- },
104
+ content: () =>
105
+ Domino.of('div')
106
+ .children(
107
+ Domino.of('span').text('Press'),
108
+ ...trigger.map((text) =>
109
+ Domino.of('span')
110
+ .classNames('mx-1 px-1.5 pt-[1px] pb-[2px] border border-separator rounded-sm')
111
+ .text(text),
112
+ ),
113
+ Domino.of('span').text('for commands.'),
114
+ )
115
+ .build(),
115
116
  },
116
117
  getMenu,
117
118
  };
118
- }, [getMenu]);
119
-
120
- const { commandMenu, groupsRef, currentItem, onSelect, ...refPopoverProps } = useCommandMenu(options);
119
+ }, [onLinkQuery, getMenu]);
121
120
 
122
- const extensions = useMemo(() => [_extensions, commandMenu].filter(isNotFalsy), [_extensions, commandMenu]);
121
+ const { groupsRef, extension, ...commandMenuProps } = usePopoverMenu(options);
122
+ const extensions = useMemo(() => [extensionsParam, extension].filter(isTruthy), [extensionsParam, extension]);
123
123
 
124
124
  return (
125
- <RefPopover modal={false} {...refPopoverProps}>
125
+ <PopoverMenuProvider view={viewRef.current} groups={groupsRef.current} {...commandMenuProps}>
126
126
  <MarkdownEditorImpl ref={viewRef} {...props} extensions={extensions} />
127
- <CommandMenu groups={groupsRef.current} currentItem={currentItem} onSelect={onSelect} />
128
- </RefPopover>
127
+ </PopoverMenuProvider>
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,
@@ -187,13 +186,13 @@ const MarkdownEditorImpl = forwardRef<EditorView | undefined, MarkdownEditorProp
187
186
  scrollPastEnd: role === 'section' ? false : scrollPastEnd,
188
187
  search: true,
189
188
  }),
190
- createMarkdownExtensions({ themeMode }),
189
+ createMarkdownExtensions(),
191
190
  createThemeExtensions({ themeMode, syntaxHighlighting: true, slots: editorSlots }),
192
191
  editorGutter,
193
192
  role !== 'section' && onFileUpload && dropFile({ onDrop: handleDrop }),
194
193
  providerExtensions,
195
194
  extensions,
196
- ].filter(isNotFalsy),
195
+ ].filter(isTruthy),
197
196
  ...(role !== 'section' && {
198
197
  id,
199
198
  scrollTo,
@@ -206,7 +205,7 @@ const MarkdownEditorImpl = forwardRef<EditorView | undefined, MarkdownEditorProp
206
205
  [id, formattingObserver, viewMode, themeMode, extensions, providerExtensions],
207
206
  );
208
207
 
209
- useImperativeHandle(forwardedRef, () => editorView, [editorView]);
208
+ useImperativeHandle<EditorView | null, EditorView | null>(forwardedRef, () => editorView, [editorView]);
210
209
  useTest(editorView);
211
210
  useSelectCurrentThread(editorView, id);
212
211
 
@@ -285,7 +284,7 @@ const MarkdownEditorImpl = forwardRef<EditorView | undefined, MarkdownEditorProp
285
284
 
286
285
  // Expose editor view for playwright tests.
287
286
  // TODO(wittjosiah): Find a better way to expose this or find a way to limit it to test runs.
288
- const useTest = (view?: EditorView) => {
287
+ const useTest = (view: EditorView | null) => {
289
288
  useEffect(() => {
290
289
  const composer = (window as any).composer;
291
290
  if (composer) {