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

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 (206) hide show
  1. package/dist/lib/browser/MarkdownCard-SLM6QZYC.mjs +12 -0
  2. package/dist/lib/browser/MarkdownContainer-UZFQC6XY.mjs +15 -0
  3. package/dist/lib/browser/{anchor-sort-E33BSTYF.mjs → anchor-sort-4XPPLMZS.mjs} +5 -5
  4. package/dist/lib/browser/{anchor-sort-E33BSTYF.mjs.map → anchor-sort-4XPPLMZS.mjs.map} +1 -1
  5. package/dist/lib/browser/{app-graph-serializer-OX62DNPT.mjs → app-graph-serializer-QQFV4K6P.mjs} +8 -8
  6. package/dist/lib/browser/app-graph-serializer-QQFV4K6P.mjs.map +7 -0
  7. package/dist/lib/browser/blueprint-definition-BC5R3T72.mjs +11 -0
  8. package/dist/lib/browser/blueprint-definition-BC5R3T72.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-LAVZ2W6X.mjs → chunk-2LLVTQCK.mjs} +6 -5
  10. package/dist/lib/browser/chunk-2LLVTQCK.mjs.map +7 -0
  11. package/dist/lib/browser/{MarkdownCard-JLUQITYK.mjs → chunk-3VILQLA4.mjs} +46 -31
  12. package/dist/lib/browser/chunk-3VILQLA4.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-5AYTOIUF.mjs +822 -0
  14. package/dist/lib/browser/chunk-5AYTOIUF.mjs.map +7 -0
  15. package/dist/lib/browser/{chunk-Z7P6JGGW.mjs → chunk-A3CQYGCN.mjs} +7 -4
  16. package/dist/lib/browser/{chunk-Z7P6JGGW.mjs.map → chunk-A3CQYGCN.mjs.map} +2 -2
  17. package/dist/lib/browser/{chunk-ODB2PTBP.mjs → chunk-BQTYJOFB.mjs} +4 -4
  18. package/dist/lib/browser/chunk-BQTYJOFB.mjs.map +7 -0
  19. package/dist/lib/browser/chunk-GLEYXJX3.mjs +22 -0
  20. package/dist/lib/browser/{chunk-D7UYVHL6.mjs.map → chunk-GLEYXJX3.mjs.map} +3 -3
  21. package/dist/lib/browser/{chunk-OY6CGPOO.mjs → chunk-IBCHVMZW.mjs} +2 -2
  22. package/dist/lib/browser/{chunk-OY6CGPOO.mjs.map → chunk-IBCHVMZW.mjs.map} +2 -2
  23. package/dist/lib/browser/{chunk-ZVVKLB5L.mjs → chunk-JAETS5LE.mjs} +33 -48
  24. package/dist/lib/browser/chunk-JAETS5LE.mjs.map +7 -0
  25. package/dist/lib/browser/{chunk-BEE7VQPU.mjs → chunk-UKTCPHLI.mjs} +9 -8
  26. package/dist/lib/browser/chunk-UKTCPHLI.mjs.map +7 -0
  27. package/dist/lib/browser/index.mjs +21 -13
  28. package/dist/lib/browser/index.mjs.map +3 -3
  29. package/dist/lib/browser/{intent-resolver-WDDH56JC.mjs → intent-resolver-VQGMBNXZ.mjs} +7 -7
  30. package/dist/lib/browser/intent-resolver-VQGMBNXZ.mjs.map +7 -0
  31. package/dist/lib/browser/meta.json +1 -1
  32. package/dist/lib/browser/{react-surface-LN2XK2UN.mjs → react-surface-WOMJOPJE.mjs} +52 -50
  33. package/dist/lib/browser/react-surface-WOMJOPJE.mjs.map +7 -0
  34. package/dist/lib/browser/{settings-AABBTB4Q.mjs → settings-LBXJHVBU.mjs} +5 -5
  35. package/dist/lib/browser/{settings-AABBTB4Q.mjs.map → settings-LBXJHVBU.mjs.map} +1 -1
  36. package/dist/lib/browser/{state-FTHQQX7V.mjs → state-BTUKVZHY.mjs} +5 -5
  37. package/dist/lib/browser/{state-FTHQQX7V.mjs.map → state-BTUKVZHY.mjs.map} +1 -1
  38. package/dist/lib/browser/toolkit-YPIVDB4A.mjs +66 -0
  39. package/dist/lib/browser/toolkit-YPIVDB4A.mjs.map +7 -0
  40. package/dist/lib/browser/types/index.mjs +2 -2
  41. package/dist/lib/node-esm/MarkdownCard-MCWEFW4F.mjs +13 -0
  42. package/dist/lib/node-esm/MarkdownCard-MCWEFW4F.mjs.map +7 -0
  43. package/dist/lib/node-esm/MarkdownContainer-KAQOK7K5.mjs +16 -0
  44. package/dist/lib/node-esm/MarkdownContainer-KAQOK7K5.mjs.map +7 -0
  45. package/dist/lib/node-esm/{anchor-sort-ALP2NH24.mjs → anchor-sort-4SXYVYXT.mjs} +5 -5
  46. package/dist/lib/node-esm/{anchor-sort-ALP2NH24.mjs.map → anchor-sort-4SXYVYXT.mjs.map} +1 -1
  47. package/dist/lib/node-esm/{app-graph-serializer-56TD3BMX.mjs → app-graph-serializer-KBVRLQN2.mjs} +8 -8
  48. package/dist/lib/node-esm/app-graph-serializer-KBVRLQN2.mjs.map +7 -0
  49. package/dist/lib/node-esm/blueprint-definition-FPNOTEYC.mjs +12 -0
  50. package/dist/lib/node-esm/blueprint-definition-FPNOTEYC.mjs.map +7 -0
  51. package/dist/lib/node-esm/chunk-2Q4WCKWT.mjs +823 -0
  52. package/dist/lib/node-esm/chunk-2Q4WCKWT.mjs.map +7 -0
  53. package/dist/lib/node-esm/{chunk-Y422WR6A.mjs → chunk-DOB2MJAX.mjs} +33 -48
  54. package/dist/lib/node-esm/chunk-DOB2MJAX.mjs.map +7 -0
  55. package/dist/lib/node-esm/chunk-GGKPPGWA.mjs +24 -0
  56. package/dist/lib/node-esm/{chunk-JPXFCBC4.mjs.map → chunk-GGKPPGWA.mjs.map} +3 -3
  57. package/dist/lib/node-esm/{chunk-CB2R4YIY.mjs → chunk-GMMVSXQ6.mjs} +2 -2
  58. package/dist/lib/node-esm/{chunk-CB2R4YIY.mjs.map → chunk-GMMVSXQ6.mjs.map} +2 -2
  59. package/dist/lib/node-esm/{chunk-FXILAQ5F.mjs → chunk-JELROKGD.mjs} +9 -8
  60. package/dist/lib/node-esm/chunk-JELROKGD.mjs.map +7 -0
  61. package/dist/lib/node-esm/{chunk-O6EXWGGS.mjs → chunk-QH4MC5BE.mjs} +6 -5
  62. package/dist/lib/node-esm/chunk-QH4MC5BE.mjs.map +7 -0
  63. package/dist/lib/node-esm/{chunk-VCG2U522.mjs → chunk-SHAMSMKQ.mjs} +4 -4
  64. package/dist/lib/node-esm/chunk-SHAMSMKQ.mjs.map +7 -0
  65. package/dist/lib/node-esm/{chunk-J7A6TUB2.mjs → chunk-SJ2QRGPM.mjs} +7 -4
  66. package/dist/lib/node-esm/{chunk-J7A6TUB2.mjs.map → chunk-SJ2QRGPM.mjs.map} +2 -2
  67. package/dist/lib/node-esm/{MarkdownCard-XL5EVSJ7.mjs → chunk-YYSASY7X.mjs} +46 -31
  68. package/dist/lib/node-esm/chunk-YYSASY7X.mjs.map +7 -0
  69. package/dist/lib/node-esm/index.mjs +21 -13
  70. package/dist/lib/node-esm/index.mjs.map +3 -3
  71. package/dist/lib/node-esm/{intent-resolver-2I5HKCUU.mjs → intent-resolver-Q4XVI5EX.mjs} +7 -7
  72. package/dist/lib/node-esm/intent-resolver-Q4XVI5EX.mjs.map +7 -0
  73. package/dist/lib/node-esm/meta.json +1 -1
  74. package/dist/lib/node-esm/{react-surface-DJGGKYBD.mjs → react-surface-FAMZTAXK.mjs} +52 -50
  75. package/dist/lib/node-esm/react-surface-FAMZTAXK.mjs.map +7 -0
  76. package/dist/lib/node-esm/{settings-CXGR6DH4.mjs → settings-2YRA67H6.mjs} +5 -5
  77. package/dist/lib/node-esm/{settings-CXGR6DH4.mjs.map → settings-2YRA67H6.mjs.map} +1 -1
  78. package/dist/lib/node-esm/{state-NWMQ3XAI.mjs → state-K6EH7SRZ.mjs} +5 -5
  79. package/dist/lib/node-esm/{state-NWMQ3XAI.mjs.map → state-K6EH7SRZ.mjs.map} +1 -1
  80. package/dist/lib/node-esm/toolkit-36BFLIR3.mjs +67 -0
  81. package/dist/lib/node-esm/toolkit-36BFLIR3.mjs.map +7 -0
  82. package/dist/lib/node-esm/types/index.mjs +2 -2
  83. package/dist/types/src/MarkdownPlugin.d.ts +1 -1
  84. package/dist/types/src/MarkdownPlugin.d.ts.map +1 -1
  85. package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -1
  86. package/dist/types/src/capabilities/blueprint-definition.d.ts +1 -1
  87. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -1
  88. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  89. package/dist/types/src/capabilities/index.d.ts +1 -0
  90. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  91. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  92. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  93. package/dist/types/src/capabilities/toolkit.d.ts +20 -0
  94. package/dist/types/src/capabilities/toolkit.d.ts.map +1 -0
  95. package/dist/types/src/components/MarkdownCard/MarkdownCard.d.ts.map +1 -1
  96. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts +0 -1
  97. package/dist/types/src/components/MarkdownCard/MarkdownCard.stories.d.ts.map +1 -1
  98. package/dist/types/src/components/MarkdownContainer.d.ts +8 -12
  99. package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -1
  100. package/dist/types/src/components/MarkdownContainer.stories.d.ts +7 -4
  101. package/dist/types/src/components/MarkdownContainer.stories.d.ts.map +1 -1
  102. package/dist/types/src/components/MarkdownEditor/FileUpload.d.ts +11 -0
  103. package/dist/types/src/components/MarkdownEditor/FileUpload.d.ts.map +1 -0
  104. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts +42 -23
  105. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.d.ts.map +1 -1
  106. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts +5 -110
  107. package/dist/types/src/components/MarkdownEditor/MarkdownEditor.stories.d.ts.map +1 -1
  108. package/dist/types/src/components/MarkdownEditor/MarkdownEditorContent.d.ts +26 -0
  109. package/dist/types/src/components/MarkdownEditor/MarkdownEditorContent.d.ts.map +1 -0
  110. package/dist/types/src/components/MarkdownEditor/MarkdownEditorToolbar.d.ts +12 -0
  111. package/dist/types/src/components/MarkdownEditor/MarkdownEditorToolbar.d.ts.map +1 -0
  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/index.d.ts +3 -1
  115. package/dist/types/src/components/index.d.ts.map +1 -1
  116. package/dist/types/src/functions/diff.d.ts.map +1 -1
  117. package/dist/types/src/functions/index.d.ts +0 -1
  118. package/dist/types/src/functions/index.d.ts.map +1 -1
  119. package/dist/types/src/functions/open.d.ts.map +1 -1
  120. package/dist/types/src/hooks/index.d.ts +3 -0
  121. package/dist/types/src/hooks/index.d.ts.map +1 -1
  122. package/dist/types/src/hooks/useExtensions.d.ts +21 -0
  123. package/dist/types/src/hooks/useExtensions.d.ts.map +1 -0
  124. package/dist/types/src/hooks/useLinkQuery.d.ts +4 -0
  125. package/dist/types/src/hooks/useLinkQuery.d.ts.map +1 -0
  126. package/dist/types/src/hooks/usePopoverMenuOptions.d.ts +9 -0
  127. package/dist/types/src/hooks/usePopoverMenuOptions.d.ts.map +1 -0
  128. package/dist/types/src/hooks/useSelectCurrentThread.d.ts +1 -1
  129. package/dist/types/src/hooks/useSelectCurrentThread.d.ts.map +1 -1
  130. package/dist/types/src/translations.d.ts +3 -0
  131. package/dist/types/src/translations.d.ts.map +1 -1
  132. package/dist/types/src/types/Markdown.d.ts +6 -4
  133. package/dist/types/src/types/Markdown.d.ts.map +1 -1
  134. package/dist/types/src/types/MarkdownAction.d.ts +3 -2
  135. package/dist/types/src/types/MarkdownAction.d.ts.map +1 -1
  136. package/dist/types/src/util.d.ts +1 -1
  137. package/dist/types/src/util.d.ts.map +1 -1
  138. package/dist/types/tsconfig.tsbuildinfo +1 -1
  139. package/package.json +58 -55
  140. package/src/MarkdownPlugin.tsx +100 -93
  141. package/src/capabilities/app-graph-serializer.ts +2 -2
  142. package/src/capabilities/artifact-definition.ts +4 -3
  143. package/src/capabilities/blueprint-definition.ts +5 -5
  144. package/src/capabilities/capabilities.ts +1 -0
  145. package/src/capabilities/index.ts +1 -0
  146. package/src/capabilities/intent-resolver.ts +2 -1
  147. package/src/capabilities/react-surface.tsx +35 -57
  148. package/src/capabilities/toolkit.ts +53 -0
  149. package/src/components/MarkdownCard/MarkdownCard.stories.tsx +3 -6
  150. package/src/components/MarkdownCard/MarkdownCard.tsx +42 -32
  151. package/src/components/MarkdownContainer.stories.tsx +75 -38
  152. package/src/components/MarkdownContainer.tsx +84 -218
  153. package/src/components/MarkdownEditor/FileUpload.tsx +63 -0
  154. package/src/components/MarkdownEditor/MarkdownEditor.stories.tsx +56 -35
  155. package/src/components/MarkdownEditor/MarkdownEditor.tsx +220 -272
  156. package/src/components/MarkdownEditor/MarkdownEditorContent.tsx +134 -0
  157. package/src/components/MarkdownEditor/MarkdownEditorToolbar.tsx +63 -0
  158. package/src/components/Suggestions.stories.tsx +28 -27
  159. package/src/components/index.ts +3 -1
  160. package/src/functions/diff.ts +4 -2
  161. package/src/functions/index.ts +0 -1
  162. package/src/functions/open.ts +4 -2
  163. package/src/hooks/index.ts +3 -0
  164. package/src/{extensions.tsx → hooks/useExtensions.tsx} +56 -114
  165. package/src/hooks/useLinkQuery.ts +82 -0
  166. package/src/hooks/usePopoverMenuOptions.ts +71 -0
  167. package/src/hooks/useSelectCurrentThread.tsx +2 -2
  168. package/src/meta.ts +3 -3
  169. package/src/translations.ts +3 -0
  170. package/src/types/Markdown.ts +6 -4
  171. package/src/types/MarkdownAction.ts +1 -1
  172. package/src/util.tsx +9 -2
  173. package/dist/lib/browser/MarkdownCard-JLUQITYK.mjs.map +0 -7
  174. package/dist/lib/browser/MarkdownContainer-7M37DXAD.mjs +0 -781
  175. package/dist/lib/browser/MarkdownContainer-7M37DXAD.mjs.map +0 -7
  176. package/dist/lib/browser/app-graph-serializer-OX62DNPT.mjs.map +0 -7
  177. package/dist/lib/browser/blueprint-definition-Z3RQGWUD.mjs +0 -11
  178. package/dist/lib/browser/chunk-BEE7VQPU.mjs.map +0 -7
  179. package/dist/lib/browser/chunk-D7UYVHL6.mjs +0 -20
  180. package/dist/lib/browser/chunk-LAVZ2W6X.mjs.map +0 -7
  181. package/dist/lib/browser/chunk-ODB2PTBP.mjs.map +0 -7
  182. package/dist/lib/browser/chunk-ZVVKLB5L.mjs.map +0 -7
  183. package/dist/lib/browser/intent-resolver-WDDH56JC.mjs.map +0 -7
  184. package/dist/lib/browser/react-surface-LN2XK2UN.mjs.map +0 -7
  185. package/dist/lib/node-esm/MarkdownCard-XL5EVSJ7.mjs.map +0 -7
  186. package/dist/lib/node-esm/MarkdownContainer-K3BPAGWO.mjs +0 -782
  187. package/dist/lib/node-esm/MarkdownContainer-K3BPAGWO.mjs.map +0 -7
  188. package/dist/lib/node-esm/app-graph-serializer-56TD3BMX.mjs.map +0 -7
  189. package/dist/lib/node-esm/blueprint-definition-735OAX33.mjs +0 -12
  190. package/dist/lib/node-esm/chunk-FXILAQ5F.mjs.map +0 -7
  191. package/dist/lib/node-esm/chunk-JPXFCBC4.mjs +0 -22
  192. package/dist/lib/node-esm/chunk-O6EXWGGS.mjs.map +0 -7
  193. package/dist/lib/node-esm/chunk-VCG2U522.mjs.map +0 -7
  194. package/dist/lib/node-esm/chunk-Y422WR6A.mjs.map +0 -7
  195. package/dist/lib/node-esm/intent-resolver-2I5HKCUU.mjs.map +0 -7
  196. package/dist/lib/node-esm/react-surface-DJGGKYBD.mjs.map +0 -7
  197. package/dist/types/src/components/Toolbar.stories.d.ts +0 -48
  198. package/dist/types/src/components/Toolbar.stories.d.ts.map +0 -1
  199. package/dist/types/src/extensions.d.ts +0 -24
  200. package/dist/types/src/extensions.d.ts.map +0 -1
  201. package/dist/types/src/functions/create.d.ts +0 -12
  202. package/dist/types/src/functions/create.d.ts.map +0 -1
  203. package/src/components/Toolbar.stories.tsx +0 -118
  204. package/src/functions/create.ts +0 -23
  205. /package/dist/lib/browser/{blueprint-definition-Z3RQGWUD.mjs.map → MarkdownCard-SLM6QZYC.mjs.map} +0 -0
  206. /package/dist/lib/{node-esm/blueprint-definition-735OAX33.mjs.map → browser/MarkdownContainer-UZFQC6XY.mjs.map} +0 -0
@@ -2,8 +2,6 @@
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 from 'react';
9
7
 
@@ -11,8 +9,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 { withLayout, withTheme } from '@dxos/storybook-utils';
16
14
 
17
15
  import { translations } from '../../translations';
18
16
 
@@ -31,18 +29,17 @@ const meta: Meta<typeof MarkdownCard> = {
31
29
  );
32
30
  },
33
31
  decorators: [
32
+ withTheme,
34
33
  withPluginManager({
35
34
  plugins: [IntentPlugin()],
36
35
  }),
37
- withTheme,
38
- withLayout(),
39
36
  ],
40
37
  parameters: {
41
38
  layout: 'centered',
42
39
  translations,
43
40
  },
44
41
  tags: ['cards'],
45
- } satisfies Meta<typeof MarkdownCard>;
42
+ };
46
43
 
47
44
  export default meta;
48
45
 
@@ -2,38 +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';
19
-
20
- // TODO(burdon): Factor out.
21
- const getTitle = (subject: Markdown.Document | DataType.Text, fallback: string) => {
22
- if (Obj.instanceOf(Markdown.Document, subject)) {
23
- return subject.name ?? subject.fallbackName ?? getFallbackName(subject.content?.target?.content ?? fallback);
24
- } else if (Obj.instanceOf(DataType.Text, subject)) {
25
- return getFallbackName(subject.content);
26
- }
27
- };
28
-
29
- // TODO(burdon): Factor out.
30
- const getSnippet = (subject: Markdown.Document | DataType.Text, fallback: string) => {
31
- if (Obj.instanceOf(Markdown.Document, subject)) {
32
- return getAbstract(subject.content?.target?.content ?? fallback);
33
- } else if (Obj.instanceOf(DataType.Text, subject)) {
34
- return getAbstract(subject.content);
35
- }
36
- };
18
+ import { getContentSnippet, getFallbackName } from '../../util';
37
19
 
38
20
  export type MarkdownCardProps = PreviewProps<Markdown.Document | DataType.Text>;
39
21
 
@@ -41,12 +23,13 @@ export const MarkdownCard = ({ subject, role }: MarkdownCardProps) => {
41
23
  const { dispatchPromise: dispatch } = useIntentDispatcher();
42
24
  const { t } = useTranslation(meta.id);
43
25
  const snippet = getSnippet(subject, t('fallback abstract'));
26
+ const info = getInfo(subject);
44
27
 
45
28
  // TODO(wittjosiah): Factor out so this component isn't dependent on the app framework.
46
29
  const handleNavigate = useCallback(
47
30
  () =>
48
31
  dispatch(
49
- pipe(
32
+ Function.pipe(
50
33
  createIntent(LayoutAction.UpdatePopover, {
51
34
  part: 'popover',
52
35
  subject: null,
@@ -60,16 +43,43 @@ export const MarkdownCard = ({ subject, role }: MarkdownCardProps) => {
60
43
 
61
44
  return (
62
45
  <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
- )}
46
+ <Card.Heading classNames='flex items-center'>
47
+ {getTitle(subject, t('fallback title'))}
48
+ <span className='grow' />
49
+ <IconButton
50
+ iconOnly
51
+ icon='ph--arrow-right--regular'
52
+ label={t('navigate to document label')}
53
+ onClick={handleNavigate}
54
+ />
55
+ </Card.Heading>
56
+ {snippet && <Card.Text classNames='line-clamp-3 text-sm text-description'>{snippet}</Card.Text>}
57
+ <Card.Text classNames='text-xs text-description'>
58
+ {info.words} {t('words label', { count: info.words })}
59
+ </Card.Text>
73
60
  </Card.SurfaceRoot>
74
61
  );
75
62
  };
63
+
64
+ const getInfo = (subject: Markdown.Document | DataType.Text) => {
65
+ const text = (Obj.instanceOf(Markdown.Document, subject) ? subject.content?.target?.content : subject.content) ?? '';
66
+ return { words: text.split(' ').length };
67
+ };
68
+
69
+ // TODO(burdon): Factor out.
70
+ const getTitle = (subject: Markdown.Document | DataType.Text, fallback: string) => {
71
+ if (Obj.instanceOf(Markdown.Document, subject)) {
72
+ return subject.name ?? subject.fallbackName ?? getFallbackName(subject.content?.target?.content ?? fallback);
73
+ } else if (Obj.instanceOf(DataType.Text, subject)) {
74
+ return getFallbackName(subject.content);
75
+ }
76
+ };
77
+
78
+ // TODO(burdon): Factor out.
79
+ const getSnippet = (subject: Markdown.Document | DataType.Text, fallback: string) => {
80
+ if (Obj.instanceOf(Markdown.Document, subject)) {
81
+ return Obj.getDescription(subject) || getContentSnippet(subject.content?.target?.content ?? fallback);
82
+ } else if (Obj.instanceOf(DataType.Text, subject)) {
83
+ return getContentSnippet(subject.content ?? fallback);
84
+ }
85
+ };
@@ -2,28 +2,35 @@
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
- import { useQuery, useSpace } from '@dxos/react-client/echo';
27
+ import { fullyQualifiedId, useQuery, useSpace } from '@dxos/react-client/echo';
28
+ import { useAsyncEffect } from '@dxos/react-ui';
29
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
30
+ import { useAttentionAttributes } from '@dxos/react-ui-attention';
23
31
  import { defaultTx } from '@dxos/react-ui-theme';
24
32
  import { DataType } from '@dxos/schema';
25
- import { Testing, type ValueGenerator, createObjectFactory } from '@dxos/schema/testing';
26
- import { withLayout } from '@dxos/storybook-utils';
33
+ import { type ValueGenerator, createObjectFactory } from '@dxos/schema/testing';
27
34
 
28
35
  import { MarkdownPlugin } from '../MarkdownPlugin';
29
36
  import { translations } from '../translations';
@@ -34,62 +41,87 @@ faker.seed(1);
34
41
  const generator: ValueGenerator = faker as any;
35
42
 
36
43
  const DefaultStory = () => {
44
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
37
45
  const space = useSpace();
38
46
  const [doc] = useQuery(space, Query.type(Markdown.Document));
39
47
  const data = useMemo(() => ({ subject: doc }), [doc]);
48
+ const id = doc && fullyQualifiedId(doc);
49
+ const attentionAttrs = useAttentionAttributes(id);
50
+
51
+ useAsyncEffect(async () => {
52
+ if (space) {
53
+ await dispatch(createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: space.id }));
54
+ }
55
+ }, [space, dispatch]);
40
56
 
41
- return <Surface role='article' data={data} />;
57
+ return (
58
+ <div className='contents' {...attentionAttrs}>
59
+ <Surface role='article' data={data} limit={1} />
60
+ </div>
61
+ );
42
62
  };
43
63
 
44
64
  const meta = {
45
65
  title: 'plugins/plugin-markdown/MarkdownContainer',
46
66
  render: DefaultStory,
47
67
  decorators: [
48
- withPluginManager({
68
+ withTheme,
69
+ withLayout({ container: 'column' }),
70
+ withPluginManager<{ title?: string; content?: string }>((context) => ({
49
71
  plugins: [
50
- AttentionPlugin(),
51
- ThemePlugin({ tx: defaultTx }),
52
- StorybookLayoutPlugin(),
53
72
  ClientPlugin({
54
- types: [Markdown.Document, DataType.Text, Testing.Contact],
73
+ types: [Markdown.Document, DataType.Text, DataType.Person, DataType.Organization],
55
74
  onClientInitialized: async ({ client }) => {
56
75
  await client.halo.createIdentity();
57
76
  await client.spaces.waitUntilReady();
58
77
  await client.spaces.default.waitUntilReady();
78
+
59
79
  const space = client.spaces.default;
60
- const doc = Markdown.makeDocument({ name: 'Test', content: '# Test\n\n' });
61
- 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 }]);
82
+
83
+ const queue = space.queues.create();
84
+ const kai = Obj.make(DataType.Person, { fullName: 'Kai' });
85
+ const dxos = Obj.make(DataType.Organization, { name: 'DXOS' });
86
+ await queue.append([kai, dxos]);
87
+
88
+ space.db.add(
89
+ Markdown.makeDocument({
90
+ name: context.args.title ?? 'Testing',
91
+ content: [
92
+ `# ${context.args.title ?? 'Testing'}`,
93
+ context.args.content ?? '',
94
+ // TODO(burdon): Popovers not currently working.
95
+ '## Here are some objects',
96
+ `![Alice](${Obj.getDXN(kai)})`,
97
+ `![DXOS](${Obj.getDXN(dxos)})`,
98
+ '',
99
+ 'END',
100
+ '',
101
+ ].join('\n\n'),
102
+ }),
103
+ );
104
+
64
105
  await space.db.flush({ indexes: true });
65
106
  },
66
107
  }),
67
- SpacePlugin(),
68
- SettingsPlugin(),
108
+ SpacePlugin({}),
109
+ GraphPlugin(),
69
110
  IntentPlugin(),
111
+ SettingsPlugin(),
112
+ // UI
113
+ ThemePlugin({ tx: defaultTx }),
114
+ AttentionPlugin(),
70
115
  MarkdownPlugin(),
71
116
  PreviewPlugin(),
72
- GraphPlugin(),
117
+ StorybookLayoutPlugin({}),
73
118
  ],
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
- }),
86
- ],
87
- }),
88
- withLayout({ fullscreen: true }),
119
+ })),
89
120
  ],
90
121
  parameters: {
91
- translations,
122
+ layout: 'fullscreen',
92
123
  controls: { disable: true },
124
+ translations,
93
125
  },
94
126
  } satisfies Meta<typeof Capabilities>;
95
127
 
@@ -97,4 +129,9 @@ export default meta;
97
129
 
98
130
  type Story = StoryObj<typeof meta>;
99
131
 
100
- export const Default: Story = {};
132
+ export const Default: Story = {
133
+ args: {
134
+ title: 'Testing',
135
+ content: ['This is a line with **some** formatting.'].join('\n\n'),
136
+ },
137
+ };
@@ -2,253 +2,119 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { type Extension } from '@codemirror/state';
5
6
  import { Rx } from '@effect-rx/rx-react';
6
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
7
- import { createPortal } from 'react-dom';
8
-
9
- import { Capabilities, Surface, useAppGraph, useCapabilities, usePluginManager } from '@dxos/app-framework';
10
- import { DXN, Filter, Obj, Query, Type } from '@dxos/echo';
11
- import { ClientCapabilities } from '@dxos/plugin-client';
12
- import { SpaceCapabilities } from '@dxos/plugin-space';
13
- import { fullyQualifiedId, getSpace, useQuery, useSpace } from '@dxos/react-client/echo';
14
- import { toLocalizedString, useTranslation } from '@dxos/react-ui';
7
+ import React, { useMemo } from 'react';
8
+
9
+ import { Capabilities, useAppGraph, useCapabilities } from '@dxos/app-framework';
10
+ import { Obj } from '@dxos/echo';
11
+ import { fullyQualifiedId, getSpace } from '@dxos/react-client/echo';
15
12
  import { type SelectionManager } from '@dxos/react-ui-attention';
16
- import {
17
- type CommandMenuGroup,
18
- type CommandMenuItem,
19
- type PreviewLinkRef,
20
- type PreviewOptions,
21
- insertAtCursor,
22
- insertAtLineStart,
23
- } from '@dxos/react-ui-editor';
13
+ import { StackItem } from '@dxos/react-ui-stack';
24
14
  import { DataType } from '@dxos/schema';
25
15
 
26
- import { useExtensions } from '../extensions';
27
- import { Markdown } from '../types';
28
- import { getFallbackName } from '../util';
16
+ import { MarkdownCapabilities } from '../capabilities';
17
+ import { type DocumentType, useLinkQuery } from '../hooks';
18
+ import { Markdown, type MarkdownPluginState } from '../types';
29
19
 
30
- import { MarkdownEditor, type MarkdownEditorProps } from './MarkdownEditor';
20
+ import { MarkdownEditor, type MarkdownEditorContentProps, type MarkdownEditorRootProps } from './MarkdownEditor';
31
21
 
32
- export type MarkdownContainerProps = Pick<
33
- MarkdownEditorProps,
34
- 'role' | 'extensionProviders' | 'viewMode' | 'editorStateStore' | 'onViewModeChange'
35
- > & {
36
- id: string;
37
- object: Markdown.Document | DataType.Text | any;
22
+ export type MarkdownContainerProps = {
23
+ role?: string;
24
+ object: DocumentType;
38
25
  settings: Markdown.Settings;
39
26
  selectionManager?: SelectionManager;
40
- };
27
+ } & (Pick<MarkdownEditorRootProps, 'id' | 'viewMode' | 'onViewModeChange'> &
28
+ Pick<MarkdownEditorContentProps, 'editorStateStore'> &
29
+ Pick<MarkdownPluginState, 'extensionProviders'>);
30
+
31
+ // TODO(burdon): Attention doesn't update in storybook.
32
+ // TODO(burdon): Toolbar state (currently not working in labs: e.g., heading, list, table).
33
+ // Heading state is correct (see react-ui-editor headings.ts, but the toolbar isn't updated).
34
+ // TODO(burdon): View mode (currently not working in labs).
35
+ // TODO(burdon): Test update document name.
36
+ // TODO(burdon): Test comment threads.
37
+ // TODO(burdon): Test preview blocks.
38
+ // TODO(burdon): Test file upload.
41
39
 
42
40
  export const MarkdownContainer = ({
43
41
  id,
44
42
  role,
45
43
  object,
46
44
  settings,
47
- selectionManager,
48
- viewMode,
49
- editorStateStore,
50
- onViewModeChange,
45
+ extensionProviders,
46
+ ...props
51
47
  }: MarkdownContainerProps) => {
52
- const { t } = useTranslation();
53
- const scrollPastEnd = role === 'article';
54
- const doc = Obj.instanceOf(Markdown.Document, object) ? object : undefined;
55
- const text = Obj.instanceOf(DataType.Text, object) ? object : undefined;
56
- const [previewBlocks, setPreviewBlocks] = useState<{ link: PreviewLinkRef; el: HTMLElement }[]>([]);
57
- const previewOptions = useMemo(
58
- (): PreviewOptions => ({
59
- addBlockContainer: (link, el) => {
60
- setPreviewBlocks((prev) => [...prev, { link, el }]);
61
- },
62
- removeBlockContainer: (link) => {
63
- setPreviewBlocks((prev) => prev.filter(({ link: prevLink }) => prevLink.ref !== link.ref));
64
- },
65
- }),
66
- [],
67
- );
68
- const extensions = useExtensions({
69
- document: doc,
70
- text,
71
- id,
72
- settings,
73
- selectionManager,
74
- viewMode,
75
- editorStateStore,
76
- previewOptions,
77
- });
78
-
79
- // TODO(wittjosiah): Factor out.
80
- const manager = usePluginManager();
81
- const resolve = useCallback(
82
- (typename: string) =>
83
- manager.context.getCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ?? {},
84
- [manager],
85
- );
86
48
  const space = getSpace(object);
87
- const objectForms = useCapabilities(SpaceCapabilities.ObjectForm);
88
- const schemaWhiteList = useCapabilities(ClientCapabilities.SchemaWhiteList);
89
- const filter = useMemo(
90
- () =>
91
- Filter.or(
92
- ...objectForms.map((form) => Filter.type(form.objectSchema)),
93
- ...schemaWhiteList.flat().map((schema) => Filter.typename(Type.getTypename(schema))),
94
- ),
95
- [objectForms, schemaWhiteList],
96
- );
97
- const onLinkQuery = useCallback(
98
- async (query?: string): Promise<CommandMenuGroup[]> => {
99
- const name = query?.startsWith('@') ? query.slice(1).toLowerCase() : (query?.toLowerCase() ?? '');
100
- const results = await space?.db.query(Query.select(filter)).run();
101
- // TODO(wittjosiah): Use `Obj.Any` type.
102
- const getLabel = (object: any) => {
103
- const label = Obj.getLabel(object);
104
- if (label) {
105
- return label;
106
- }
107
-
108
- // TODO(wittjosiah): Remove metadata labels.
109
- const type = Obj.getTypename(object)!;
110
- const metadata = resolve(type);
111
- return metadata.label?.(object) || ['object name placeholder', { ns: type, default: 'New object' }];
112
- };
113
- const items =
114
- results?.objects
115
- .filter((object) => toLocalizedString(getLabel(object), t).toLowerCase().includes(name))
116
- // TODO(wittjosiah): Remove `any` type.
117
- .map((object: any): CommandMenuItem => {
118
- const metadata = resolve(Obj.getTypename(object)!);
119
- const label = toLocalizedString(getLabel(object), t);
120
- return {
121
- id: object.id,
122
- label,
123
- icon: metadata.icon,
124
- onSelect: (view, head) => {
125
- const link = `[${label}][${Obj.getDXN(object)}]`;
126
- if (query?.startsWith('@')) {
127
- insertAtLineStart(view, head, `!${link}\n`);
128
- } else {
129
- insertAtCursor(view, head, `${link} `);
130
- }
131
- },
132
- };
133
- }) ?? [];
134
- return [{ id: 'echo', items }];
135
- },
136
- [filter, resolve, space],
137
- );
138
-
139
- const editor = doc ? (
140
- <DocumentEditor
141
- id={fullyQualifiedId(object)}
142
- role={role}
143
- document={doc}
144
- extensions={extensions}
145
- viewMode={viewMode}
146
- settings={settings}
147
- scrollPastEnd={scrollPastEnd}
148
- onViewModeChange={onViewModeChange}
149
- onLinkQuery={space ? onLinkQuery : undefined}
150
- />
151
- ) : text ? (
152
- <MarkdownEditor
153
- id={id}
154
- role={role}
155
- initialValue={text.content}
156
- extensions={extensions}
157
- viewMode={viewMode}
158
- toolbar={settings.toolbar}
159
- inputMode={settings.editorInputMode}
160
- scrollPastEnd={scrollPastEnd}
161
- onViewModeChange={onViewModeChange}
162
- onLinkQuery={space ? onLinkQuery : undefined}
163
- />
164
- ) : (
165
- // TODO(burdon): Normalize with above.
166
- <MarkdownEditor
167
- id={id}
168
- role={role}
169
- initialValue={object.text}
170
- extensions={extensions}
171
- viewMode={viewMode}
172
- toolbar={settings.toolbar}
173
- inputMode={settings.editorInputMode}
174
- scrollPastEnd={scrollPastEnd}
175
- onViewModeChange={onViewModeChange}
176
- onLinkQuery={space ? onLinkQuery : undefined}
177
- />
178
- );
179
-
180
- return (
181
- <>
182
- {editor}
183
- {previewBlocks.map(({ link, el }) => (
184
- <PreviewBlock key={link.ref} link={link} el={el} />
185
- ))}
186
- </>
187
- );
188
- };
189
-
190
- // TODO(wittjosiah): This shouldn't be "card" but "block".
191
- // It's not a preview card but an interactive embedded object.
192
- 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 ?? '')));
196
- const data = useMemo(() => ({ subject }), [subject]);
197
-
198
- return createPortal(<Surface role='card--transclusion' data={data} limit={1} />, el);
199
- };
200
-
201
- type DocumentEditorProps = Omit<MarkdownContainerProps, 'object' | 'extensionProviders' | 'editorStateStore'> &
202
- Pick<MarkdownEditorProps, 'id' | 'scrollPastEnd' | 'extensions' | 'onLinkQuery'> & {
203
- document: Markdown.Document;
204
- };
205
-
206
- export const DocumentEditor = ({ id, document: doc, settings, viewMode, ...props }: DocumentEditorProps) => {
207
- const space = getSpace(doc);
208
-
209
- // Migrate gradually to `fallbackName`.
210
- useEffect(() => {
211
- if (typeof doc.fallbackName === 'string') {
212
- return;
49
+ const isDocument = Obj.instanceOf(Markdown.Document, object);
50
+ const isText = Obj.instanceOf(DataType.Text, object);
51
+ const attendableId = isDocument ? fullyQualifiedId(object) : undefined;
52
+
53
+ // Extensions from other plugins.
54
+ // TODO(burdon): Document MarkdownPluginState.extensionProviders
55
+ const otherExtensionProviders = useCapabilities(MarkdownCapabilities.Extensions);
56
+ const extensions = useMemo<Extension[]>(() => {
57
+ if (!Obj.instanceOf(Markdown.Document, object)) {
58
+ return [];
213
59
  }
214
60
 
215
- const fallbackName = doc.content?.target?.content ? getFallbackName(doc.content.target.content) : undefined;
216
- if (fallbackName) {
217
- doc.fallbackName = fallbackName;
218
- }
219
- }, [doc, doc.content]);
220
-
221
- // File dragging.
222
- const [upload] = useCapabilities(Capabilities.FileUploader);
223
- const handleFileUpload = useMemo(() => {
224
- if (space === undefined || upload === undefined) {
225
- return undefined;
226
- }
61
+ return [...(otherExtensionProviders ?? []), ...(extensionProviders ?? [])]
62
+ .flat()
63
+ .reduce((acc: Extension[], provider) => {
64
+ const extension =
65
+ typeof provider === 'function' ? provider({ document: object as Markdown.Document }) : provider;
66
+ if (extension) {
67
+ acc.push(extension);
68
+ }
227
69
 
228
- // TODO(burdon): Re-order props: space, file.
229
- return async (file: File) => upload!(file, space);
230
- }, [space, upload]);
70
+ return acc;
71
+ }, []);
72
+ }, [extensionProviders, otherExtensionProviders, object]);
231
73
 
74
+ // Toolbar actions from app graph.
232
75
  const { graph } = useAppGraph();
233
76
  const customActions = useMemo(() => {
234
77
  return Rx.make((get) => {
235
78
  const actions = get(graph.actions(id));
236
79
  const nodes = actions.filter((action) => action.properties.disposition === 'toolbar');
237
- return { nodes, edges: nodes.map((node) => ({ source: 'root', target: node.id })) };
80
+ const edges = nodes.map((node) => ({ source: 'root', target: node.id }));
81
+ return { nodes, edges };
238
82
  });
239
83
  }, [graph]);
240
84
 
85
+ // File upload.
86
+ const [upload] = useCapabilities(Capabilities.FileUploader);
87
+ const handleFileUpload = useMemo(() => {
88
+ if (!space || !upload) {
89
+ return undefined;
90
+ }
91
+
92
+ return async (file: File) => upload(space, file);
93
+ }, [space, upload]);
94
+
95
+ // Query for @ refs.
96
+ const handleLinkQuery = useLinkQuery(space);
97
+
241
98
  return (
242
- <MarkdownEditor
243
- id={id}
244
- initialValue={doc.content?.target?.content}
245
- viewMode={viewMode}
246
- toolbar={settings.toolbar}
247
- customActions={customActions}
248
- inputMode={settings.editorInputMode}
249
- onFileUpload={handleFileUpload}
250
- {...props}
251
- />
99
+ <StackItem.Content toolbar={settings.toolbar}>
100
+ <MarkdownEditor.Root
101
+ id={attendableId ?? id}
102
+ object={object}
103
+ extensions={extensions}
104
+ onFileUpload={handleFileUpload}
105
+ onLinkQuery={handleLinkQuery}
106
+ {...props}
107
+ >
108
+ {settings.toolbar && (
109
+ <MarkdownEditor.Toolbar id={attendableId ?? id} role={role} customActions={customActions} />
110
+ )}
111
+ <MarkdownEditor.Content
112
+ initialValue={isDocument ? object.content?.target?.content : isText ? object.content : object.text}
113
+ scrollPastEnd={role === 'article'}
114
+ />
115
+ <MarkdownEditor.Blocks />
116
+ </MarkdownEditor.Root>
117
+ </StackItem.Content>
252
118
  );
253
119
  };
254
120