@dxos/plugin-preview 0.8.4-main.3c1ae3b → 0.8.4-main.3fbcb4aa9b

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 (119) hide show
  1. package/dist/lib/neutral/PreviewPlugin.mjs +9 -0
  2. package/dist/lib/neutral/PreviewPlugin.mjs.map +7 -0
  3. package/dist/lib/neutral/PreviewPlugin.node.mjs +17 -0
  4. package/dist/lib/neutral/PreviewPlugin.node.mjs.map +7 -0
  5. package/dist/lib/neutral/capabilities/index.mjs +9 -0
  6. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  7. package/dist/lib/neutral/chunk-3SWUVHXJ.mjs +28 -0
  8. package/dist/lib/neutral/chunk-3SWUVHXJ.mjs.map +7 -0
  9. package/dist/lib/{browser/chunk-CEWM5LJG.mjs → neutral/chunk-52UEVZSF.mjs} +2 -2
  10. package/dist/lib/neutral/chunk-52UEVZSF.mjs.map +7 -0
  11. package/dist/lib/neutral/index.mjs +7 -0
  12. package/dist/lib/neutral/index.mjs.map +7 -0
  13. package/dist/lib/neutral/meta.json +1 -0
  14. package/dist/lib/neutral/meta.mjs +7 -0
  15. package/dist/lib/neutral/meta.mjs.map +7 -0
  16. package/dist/lib/neutral/plugin.mjs +11 -0
  17. package/dist/lib/neutral/plugin.mjs.map +7 -0
  18. package/dist/lib/neutral/preview-popover-MPPEMUIA.mjs +78 -0
  19. package/dist/lib/neutral/preview-popover-MPPEMUIA.mjs.map +7 -0
  20. package/dist/lib/neutral/react-surface-YFFJXXPQ.mjs +348 -0
  21. package/dist/lib/neutral/react-surface-YFFJXXPQ.mjs.map +7 -0
  22. package/dist/lib/neutral/testing.mjs +7 -0
  23. package/dist/lib/neutral/testing.mjs.map +7 -0
  24. package/dist/lib/neutral/translations.mjs +21 -0
  25. package/dist/lib/neutral/translations.mjs.map +7 -0
  26. package/dist/types/src/PreviewPlugin.d.ts +3 -1
  27. package/dist/types/src/PreviewPlugin.d.ts.map +1 -1
  28. package/dist/types/src/PreviewPlugin.node.d.ts +4 -0
  29. package/dist/types/src/PreviewPlugin.node.d.ts.map +1 -0
  30. package/dist/types/src/PreviewPlugin.test.d.ts +2 -0
  31. package/dist/types/src/PreviewPlugin.test.d.ts.map +1 -0
  32. package/dist/types/src/capabilities/index.d.ts +3 -2
  33. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  34. package/dist/types/src/capabilities/preview-popover.d.ts +3 -2
  35. package/dist/types/src/capabilities/preview-popover.d.ts.map +1 -1
  36. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  37. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  38. package/dist/types/src/cards/ExpandoCard.d.ts +4 -0
  39. package/dist/types/src/cards/ExpandoCard.d.ts.map +1 -0
  40. package/dist/types/src/cards/FormCard.d.ts +2 -3
  41. package/dist/types/src/cards/FormCard.d.ts.map +1 -1
  42. package/dist/types/src/cards/JsonCard.d.ts +5 -0
  43. package/dist/types/src/cards/JsonCard.d.ts.map +1 -0
  44. package/dist/types/src/cards/OrganizationCard.d.ts +2 -2
  45. package/dist/types/src/cards/OrganizationCard.d.ts.map +1 -1
  46. package/dist/types/src/cards/PersonCard.d.ts +2 -2
  47. package/dist/types/src/cards/PersonCard.d.ts.map +1 -1
  48. package/dist/types/src/cards/ProjectCard.d.ts +3 -3
  49. package/dist/types/src/cards/ProjectCard.d.ts.map +1 -1
  50. package/dist/types/src/cards/TaskCard.d.ts +2 -2
  51. package/dist/types/src/cards/TaskCard.d.ts.map +1 -1
  52. package/dist/types/src/cards/index.d.ts +2 -0
  53. package/dist/types/src/cards/index.d.ts.map +1 -1
  54. package/dist/types/src/index.d.ts +0 -2
  55. package/dist/types/src/index.d.ts.map +1 -1
  56. package/dist/types/src/meta.d.ts +2 -2
  57. package/dist/types/src/meta.d.ts.map +1 -1
  58. package/dist/types/src/plugin.d.ts +3 -0
  59. package/dist/types/src/plugin.d.ts.map +1 -0
  60. package/dist/types/src/stories/Card.stories.d.ts +23 -14
  61. package/dist/types/src/stories/Card.stories.d.ts.map +1 -1
  62. package/dist/types/src/stories/testing.d.ts +15 -12
  63. package/dist/types/src/stories/testing.d.ts.map +1 -1
  64. package/dist/types/src/testing.d.ts +2 -0
  65. package/dist/types/src/testing.d.ts.map +1 -0
  66. package/dist/types/src/translations.d.ts +8 -9
  67. package/dist/types/src/translations.d.ts.map +1 -1
  68. package/dist/types/tsconfig.tsbuildinfo +1 -1
  69. package/package.json +87 -39
  70. package/src/PreviewPlugin.node.ts +16 -0
  71. package/src/PreviewPlugin.test.ts +25 -0
  72. package/src/PreviewPlugin.tsx +16 -25
  73. package/src/capabilities/index.ts +3 -3
  74. package/src/capabilities/preview-popover.ts +68 -41
  75. package/src/capabilities/react-surface.tsx +104 -108
  76. package/src/cards/ExpandoCard.tsx +76 -0
  77. package/src/cards/FormCard.tsx +23 -32
  78. package/src/cards/JsonCard.tsx +19 -0
  79. package/src/cards/OrganizationCard.tsx +11 -16
  80. package/src/cards/PersonCard.tsx +51 -49
  81. package/src/cards/ProjectCard.tsx +9 -11
  82. package/src/cards/TaskCard.tsx +24 -26
  83. package/src/cards/index.ts +2 -0
  84. package/src/index.ts +0 -3
  85. package/src/meta.ts +3 -3
  86. package/src/plugin.ts +9 -0
  87. package/src/stories/Card.stories.tsx +83 -24
  88. package/src/stories/testing.tsx +64 -83
  89. package/src/testing.ts +7 -0
  90. package/src/translations.ts +8 -8
  91. package/dist/lib/browser/chunk-CEWM5LJG.mjs.map +0 -7
  92. package/dist/lib/browser/index.mjs +0 -64
  93. package/dist/lib/browser/index.mjs.map +0 -7
  94. package/dist/lib/browser/meta.json +0 -1
  95. package/dist/lib/browser/preview-popover-4QZ2WDE6.mjs +0 -65
  96. package/dist/lib/browser/preview-popover-4QZ2WDE6.mjs.map +0 -7
  97. package/dist/lib/browser/react-surface-4AKVMPIJ.mjs +0 -513
  98. package/dist/lib/browser/react-surface-4AKVMPIJ.mjs.map +0 -7
  99. package/dist/lib/node-esm/chunk-Q5MUNELK.mjs +0 -19
  100. package/dist/lib/node-esm/chunk-Q5MUNELK.mjs.map +0 -7
  101. package/dist/lib/node-esm/index.mjs +0 -65
  102. package/dist/lib/node-esm/index.mjs.map +0 -7
  103. package/dist/lib/node-esm/meta.json +0 -1
  104. package/dist/lib/node-esm/preview-popover-FJ2ARIGS.mjs +0 -67
  105. package/dist/lib/node-esm/preview-popover-FJ2ARIGS.mjs.map +0 -7
  106. package/dist/lib/node-esm/react-surface-ZJ4C32OR.mjs +0 -514
  107. package/dist/lib/node-esm/react-surface-ZJ4C32OR.mjs.map +0 -7
  108. package/dist/types/src/components/CardComponents.d.ts +0 -16
  109. package/dist/types/src/components/CardComponents.d.ts.map +0 -1
  110. package/dist/types/src/components/CardSubjectMenu.d.ts +0 -8
  111. package/dist/types/src/components/CardSubjectMenu.d.ts.map +0 -1
  112. package/dist/types/src/components/index.d.ts +0 -3
  113. package/dist/types/src/components/index.d.ts.map +0 -1
  114. package/dist/types/src/types.d.ts +0 -12
  115. package/dist/types/src/types.d.ts.map +0 -1
  116. package/src/components/CardComponents.tsx +0 -67
  117. package/src/components/CardSubjectMenu.tsx +0 -103
  118. package/src/components/index.ts +0 -6
  119. package/src/types.ts +0 -25
package/package.json CHANGED
@@ -1,67 +1,115 @@
1
1
  {
2
2
  "name": "@dxos/plugin-preview",
3
- "version": "0.8.4-main.3c1ae3b",
3
+ "version": "0.8.4-main.3fbcb4aa9b",
4
4
  "description": "Preview plugin",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "DXOS.org",
9
13
  "sideEffects": true,
10
14
  "type": "module",
15
+ "imports": {
16
+ "#capabilities": {
17
+ "source": "./src/capabilities/index.ts",
18
+ "types": "./dist/types/src/capabilities/index.d.ts",
19
+ "default": "./dist/lib/neutral/capabilities/index.mjs"
20
+ },
21
+ "#meta": {
22
+ "source": "./src/meta.ts",
23
+ "types": "./dist/types/src/meta.d.ts",
24
+ "default": "./dist/lib/neutral/meta.mjs"
25
+ },
26
+ "#plugin": {
27
+ "source": {
28
+ "node": "./src/PreviewPlugin.node.ts",
29
+ "default": "./src/PreviewPlugin.tsx"
30
+ },
31
+ "types": "./dist/types/src/PreviewPlugin.d.ts",
32
+ "node": "./dist/lib/neutral/PreviewPlugin.node.mjs",
33
+ "default": "./dist/lib/neutral/PreviewPlugin.mjs"
34
+ },
35
+ "#translations": {
36
+ "source": "./src/translations.ts",
37
+ "types": "./dist/types/src/translations.d.ts",
38
+ "default": "./dist/lib/neutral/translations.mjs"
39
+ }
40
+ },
11
41
  "exports": {
12
42
  ".": {
13
43
  "source": "./src/index.ts",
14
44
  "types": "./dist/types/src/index.d.ts",
15
- "browser": "./dist/lib/browser/index.mjs"
45
+ "default": "./dist/lib/neutral/index.mjs"
46
+ },
47
+ "./plugin": {
48
+ "source": "./src/plugin.ts",
49
+ "types": "./dist/types/src/plugin.d.ts",
50
+ "default": "./dist/lib/neutral/plugin.mjs"
51
+ },
52
+ "./testing": {
53
+ "source": "./src/testing.ts",
54
+ "types": "./dist/types/src/testing.d.ts",
55
+ "default": "./dist/lib/neutral/testing.mjs"
56
+ },
57
+ "./translations": {
58
+ "source": "./src/translations.ts",
59
+ "types": "./dist/types/src/translations.d.ts",
60
+ "default": "./dist/lib/neutral/translations.mjs"
16
61
  }
17
62
  },
18
63
  "types": "dist/types/src/index.d.ts",
19
- "typesVersions": {
20
- "*": {}
21
- },
22
64
  "files": [
23
65
  "dist",
24
66
  "src"
25
67
  ],
26
68
  "dependencies": {
27
- "@effect-atom/atom-react": "^0.3.4",
28
- "@preact-signals/safe-react": "^0.9.0",
29
- "@dxos/app-graph": "0.8.4-main.3c1ae3b",
30
- "@dxos/app-framework": "0.8.4-main.3c1ae3b",
31
- "@dxos/async": "0.8.4-main.3c1ae3b",
32
- "@dxos/lit-ui": "0.8.4-main.3c1ae3b",
33
- "@dxos/echo": "0.8.4-main.3c1ae3b",
34
- "@dxos/client": "0.8.4-main.3c1ae3b",
35
- "@dxos/keys": "0.8.4-main.3c1ae3b",
36
- "@dxos/log": "0.8.4-main.3c1ae3b",
37
- "@dxos/plugin-attention": "0.8.4-main.3c1ae3b",
38
- "@dxos/plugin-client": "0.8.4-main.3c1ae3b",
39
- "@dxos/plugin-space": "0.8.4-main.3c1ae3b",
40
- "@dxos/react-ui": "0.8.4-main.3c1ae3b",
41
- "@dxos/react-ui-editor": "0.8.4-main.3c1ae3b",
42
- "@dxos/react-client": "0.8.4-main.3c1ae3b",
43
- "@dxos/plugin-deck": "0.8.4-main.3c1ae3b",
44
- "@dxos/react-ui-menu": "0.8.4-main.3c1ae3b",
45
- "@dxos/react-ui-stack": "0.8.4-main.3c1ae3b",
46
- "@dxos/react-ui-form": "0.8.4-main.3c1ae3b",
47
- "@dxos/react-ui-table": "0.8.4-main.3c1ae3b",
48
- "@dxos/react-ui-theme": "0.8.4-main.3c1ae3b",
49
- "@dxos/schema": "0.8.4-main.3c1ae3b",
50
- "@dxos/types": "0.8.4-main.3c1ae3b",
51
- "@dxos/util": "0.8.4-main.3c1ae3b"
69
+ "@effect-atom/atom-react": "^0.5.0",
70
+ "@dxos/app-graph": "0.8.4-main.3fbcb4aa9b",
71
+ "@dxos/app-toolkit": "0.8.4-main.3fbcb4aa9b",
72
+ "@dxos/app-framework": "0.8.4-main.3fbcb4aa9b",
73
+ "@dxos/client": "0.8.4-main.3fbcb4aa9b",
74
+ "@dxos/echo": "0.8.4-main.3fbcb4aa9b",
75
+ "@dxos/effect": "0.8.4-main.3fbcb4aa9b",
76
+ "@dxos/keys": "0.8.4-main.3fbcb4aa9b",
77
+ "@dxos/lit-ui": "0.8.4-main.3fbcb4aa9b",
78
+ "@dxos/operation": "0.8.4-main.3fbcb4aa9b",
79
+ "@dxos/plugin-client": "0.8.4-main.3fbcb4aa9b",
80
+ "@dxos/plugin-attention": "0.8.4-main.3fbcb4aa9b",
81
+ "@dxos/log": "0.8.4-main.3fbcb4aa9b",
82
+ "@dxos/react-client": "0.8.4-main.3fbcb4aa9b",
83
+ "@dxos/plugin-deck": "0.8.4-main.3fbcb4aa9b",
84
+ "@dxos/react-ui": "0.8.4-main.3fbcb4aa9b",
85
+ "@dxos/react-ui-editor": "0.8.4-main.3fbcb4aa9b",
86
+ "@dxos/react-ui-form": "0.8.4-main.3fbcb4aa9b",
87
+ "@dxos/react-ui-menu": "0.8.4-main.3fbcb4aa9b",
88
+ "@dxos/async": "0.8.4-main.3fbcb4aa9b",
89
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.3fbcb4aa9b",
90
+ "@dxos/react-ui-mosaic": "0.8.4-main.3fbcb4aa9b",
91
+ "@dxos/react-ui-table": "0.8.4-main.3fbcb4aa9b",
92
+ "@dxos/schema": "0.8.4-main.3fbcb4aa9b",
93
+ "@dxos/types": "0.8.4-main.3fbcb4aa9b",
94
+ "@dxos/ui-editor": "0.8.4-main.3fbcb4aa9b",
95
+ "@dxos/util": "0.8.4-main.3fbcb4aa9b",
96
+ "@dxos/ui-theme": "0.8.4-main.3fbcb4aa9b"
52
97
  },
53
98
  "devDependencies": {
54
- "effect": "3.18.3",
55
- "react": "~19.2.0",
56
- "react-dom": "~19.2.0",
57
- "vite": "7.1.9",
58
- "@dxos/random": "0.8.4-main.3c1ae3b",
59
- "@dxos/storybook-utils": "0.8.4-main.3c1ae3b"
99
+ "effect": "3.20.0",
100
+ "react": "~19.2.3",
101
+ "react-dom": "~19.2.3",
102
+ "vite": "^8.0.10",
103
+ "@dxos/echo-db": "0.8.4-main.3fbcb4aa9b",
104
+ "@dxos/plugin-testing": "0.8.4-main.3fbcb4aa9b",
105
+ "@dxos/random": "0.8.4-main.3fbcb4aa9b",
106
+ "@dxos/react-ui-components": "0.8.4-main.3fbcb4aa9b",
107
+ "@dxos/storybook-utils": "0.8.4-main.3fbcb4aa9b"
60
108
  },
61
109
  "peerDependencies": {
62
- "effect": "^3.13.3",
63
- "react": "^19.0.0",
64
- "react-dom": "^19.0.0"
110
+ "effect": "3.20.0",
111
+ "react": "~19.2.3",
112
+ "react-dom": "~19.2.3"
65
113
  },
66
114
  "publishConfig": {
67
115
  "access": "public"
@@ -0,0 +1,16 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { Plugin } from '@dxos/app-framework';
6
+ import { AppPlugin } from '@dxos/app-toolkit';
7
+ import { Organization, Person } from '@dxos/types';
8
+
9
+ import { meta } from '#meta';
10
+
11
+ export const PreviewPlugin = Plugin.define(meta).pipe(
12
+ AppPlugin.addSchemaModule({ schema: [Person.Person, Organization.Organization] }),
13
+ Plugin.make,
14
+ );
15
+
16
+ export default PreviewPlugin;
@@ -0,0 +1,25 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { describe, test } from 'vitest';
6
+
7
+ import { ClientPlugin } from '@dxos/plugin-client/plugin';
8
+ import { createComposerTestApp } from '@dxos/plugin-testing/harness';
9
+
10
+ import { PreviewPlugin } from '#plugin';
11
+
12
+ import { meta } from './meta';
13
+
14
+ const moduleId = (name: string) => `${meta.id}.module.${name}`;
15
+
16
+ describe('PreviewPlugin', () => {
17
+ test('modules activate on the expected events', { timeout: 10_000 }, async ({ expect }) => {
18
+ await using harness = await createComposerTestApp({
19
+ plugins: [ClientPlugin({}), PreviewPlugin()],
20
+ });
21
+
22
+ // After autoStart: schema auto-cascades from ClientPlugin.
23
+ expect(harness.manager.getActive()).toContain(moduleId('schema'));
24
+ });
25
+ });
@@ -2,33 +2,24 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Capabilities, Events, contributes, defineModule, definePlugin } from '@dxos/app-framework';
6
- import { ClientCapabilities, ClientEvents } from '@dxos/plugin-client';
5
+ import { ActivationEvents, Plugin } from '@dxos/app-framework';
6
+ import { AppPlugin } from '@dxos/app-toolkit';
7
7
  import { Organization, Person } from '@dxos/types';
8
8
 
9
- import { PreviewPopover, ReactSurface } from './capabilities';
10
- import { meta } from './meta';
11
- import { translations } from './translations';
9
+ import { PreviewPopover, ReactSurface } from '#capabilities';
10
+ import { meta } from '#meta';
11
+ import { translations } from '#translations';
12
12
 
13
- export const PreviewPlugin = definePlugin(meta, () => [
14
- defineModule({
15
- id: `${meta.id}/module/translations`,
16
- activatesOn: Events.SetupTranslations,
17
- activate: () => contributes(Capabilities.Translations, translations),
18
- }),
19
- defineModule({
20
- id: `${meta.id}/module/schema`,
21
- activatesOn: ClientEvents.SetupSchema,
22
- activate: () => [contributes(ClientCapabilities.Schema, [Person.Person, Organization.Organization])],
23
- }),
24
- defineModule({
25
- id: `${meta.id}/module/preview-popover`,
26
- activatesOn: Events.Startup,
13
+ export const PreviewPlugin = Plugin.define(meta).pipe(
14
+ AppPlugin.addSchemaModule({ schema: [Person.Person, Organization.Organization] }),
15
+ AppPlugin.addSurfaceModule({ activate: ReactSurface }),
16
+ AppPlugin.addTranslationsModule({ translations }),
17
+ Plugin.addModule({
18
+ id: 'preview-popover',
19
+ activatesOn: ActivationEvents.Startup,
27
20
  activate: PreviewPopover,
28
21
  }),
29
- defineModule({
30
- id: `${meta.id}/module/react-surface`,
31
- activatesOn: Events.SetupReactSurface,
32
- activate: ReactSurface,
33
- }),
34
- ]);
22
+ Plugin.make,
23
+ );
24
+
25
+ export default PreviewPlugin;
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { lazy } from '@dxos/app-framework';
5
+ import { Capability } from '@dxos/app-framework';
6
6
 
7
- export const PreviewPopover = lazy(() => import('./preview-popover'));
8
- export const ReactSurface = lazy(() => import('./react-surface'));
7
+ export const PreviewPopover = Capability.lazy('PreviewPopover', () => import('./preview-popover'));
8
+ export const ReactSurface = Capability.lazy('ReactSurface', () => import('./react-surface'));
@@ -2,68 +2,95 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Capabilities, LayoutAction, type PluginContext, contributes, createIntent } from '@dxos/app-framework';
5
+ import * as Effect from 'effect/Effect';
6
+
7
+ import { Capabilities, Capability } from '@dxos/app-framework';
8
+ import { AppCapabilities, LayoutOperation, getPersonalSpace, getSpaceIdFromPath } from '@dxos/app-toolkit';
6
9
  import { addEventListener } from '@dxos/async';
7
10
  import { type Client } from '@dxos/client';
8
- import { type Space, parseId } from '@dxos/client/echo';
11
+ import { type Space } from '@dxos/client/echo';
12
+ import { Obj } from '@dxos/echo';
9
13
  import { DXN } from '@dxos/keys';
10
14
  import { log } from '@dxos/log';
11
15
  import { ClientCapabilities } from '@dxos/plugin-client';
12
16
  import { DX_ANCHOR_ACTIVATE, type DxAnchorActivate } from '@dxos/react-ui';
13
- import { type PreviewLinkRef, type PreviewLinkTarget } from '@dxos/react-ui-editor';
17
+ import { type PreviewLinkRef, type PreviewLinkTarget } from '@dxos/ui-editor';
14
18
 
15
19
  const customEventOptions = { capture: true, passive: false };
16
20
 
17
21
  const handlePreviewLookup = async (
18
22
  client: Client,
19
23
  defaultSpace: Space,
20
- { ref, label }: PreviewLinkRef,
24
+ { dxn, label }: PreviewLinkRef,
21
25
  ): Promise<PreviewLinkTarget | null> => {
22
26
  try {
23
- const object = await defaultSpace.db.makeRef(DXN.parse(ref)).load();
27
+ const object = await defaultSpace.db.makeRef(DXN.parse(dxn)).load();
24
28
  return { label, object };
25
29
  } catch {
26
30
  return null;
27
31
  }
28
32
  };
29
33
 
30
- export default (context: PluginContext) => {
31
- // TODO(wittjosiah): Factor out lookup handlers to other plugins to make not ECHO-specific.
32
- const handleAnchorActivate = async ({ refId, label, trigger }: DxAnchorActivate) => {
33
- const { dispatchPromise: dispatch } = context.getCapability(Capabilities.IntentDispatcher);
34
- const client = context.getCapability(ClientCapabilities.Client);
35
- const [layout] = context.getCapabilities(Capabilities.Layout);
36
- const { spaceId } = parseId(layout.workspace);
37
- const space = (spaceId && client.spaces.get(spaceId)) ?? client.spaces.default;
38
- const result = await handlePreviewLookup(client, space, { ref: refId, label });
39
- if (!result) {
40
- return;
41
- }
34
+ export default Capability.makeModule(
35
+ Effect.fnUntraced(function* () {
36
+ // Get context for lazy capability access in callbacks.
37
+ const capabilities = yield* Capability.Service;
38
+
39
+ // TODO(wittjosiah): Factor out lookup handlers to other plugins to make not ECHO-specific.
40
+ const handleAnchorActivate = async ({
41
+ dxn,
42
+ label,
43
+ trigger,
44
+ kind = 'card',
45
+ title: titleProp,
46
+ side,
47
+ props,
48
+ }: DxAnchorActivate) => {
49
+ const { invokePromise } = capabilities.get(Capabilities.OperationInvoker);
50
+ const client = capabilities.get(ClientCapabilities.Client);
51
+ const registry = capabilities.get(Capabilities.AtomRegistry);
52
+ // Layout is optional: in standalone harnesses (Storybook, tests) no plugin contributes
53
+ // `AppCapabilities.Layout`, and `getAll` returns an empty array. Reading `registry.get(undefined)`
54
+ // would crash inside Atom's identity check (`'~atom/Serializable' in undefined`). When layout
55
+ // isn't available, fall through to the personal-space default.
56
+ const [layoutAtom] = capabilities.getAll(AppCapabilities.Layout);
57
+ const spaceId = layoutAtom && getSpaceIdFromPath(registry.get(layoutAtom).workspace);
58
+ const space = (spaceId && client.spaces.get(spaceId)) ?? getPersonalSpace(client);
59
+ if (!space) {
60
+ return;
61
+ }
62
+ const result = await handlePreviewLookup(client, space, { dxn, label });
63
+ if (!result) {
64
+ return;
65
+ }
66
+
67
+ const title = titleProp ?? Obj.getLabel(result.object);
42
68
 
43
- await dispatch(
44
- createIntent(LayoutAction.UpdatePopover, {
45
- part: 'popover',
69
+ await invokePromise(LayoutOperation.UpdatePopover, {
70
+ subjectRef: dxn,
46
71
  subject: result.object,
47
- options: {
48
- state: true,
49
- variant: 'virtual',
50
- anchor: trigger,
51
- },
52
- }),
53
- );
54
- };
72
+ state: true,
73
+ variant: 'virtual',
74
+ anchor: trigger,
75
+ ...(kind && { kind }),
76
+ ...(title && { title }),
77
+ ...(side && { side }),
78
+ ...(props && { props }),
79
+ });
80
+ };
55
81
 
56
- let cleanup: () => void;
57
- if (document.defaultView) {
58
- cleanup = addEventListener(
59
- document.defaultView,
60
- DX_ANCHOR_ACTIVATE as any,
61
- handleAnchorActivate,
62
- customEventOptions,
63
- );
64
- } else {
65
- log.warn('No default view found');
66
- }
82
+ let cleanup: () => void;
83
+ if (document.defaultView) {
84
+ cleanup = addEventListener(
85
+ document.defaultView,
86
+ DX_ANCHOR_ACTIVATE as any,
87
+ handleAnchorActivate,
88
+ customEventOptions,
89
+ );
90
+ } else {
91
+ log.warn('no default view found');
92
+ }
67
93
 
68
- return contributes(Capabilities.Null, null, () => cleanup());
69
- };
94
+ return Capability.contributes(Capabilities.Null, null, () => Effect.sync(() => cleanup?.()));
95
+ }),
96
+ );
@@ -2,121 +2,117 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback } from 'react';
5
+ import * as Effect from 'effect/Effect';
6
+ import React from 'react';
6
7
 
7
- import { Capabilities, LayoutAction, contributes, createIntent, createSurface } from '@dxos/app-framework';
8
- import { Surface, SurfaceCardRole, useIntentDispatcher } from '@dxos/app-framework/react';
9
- import { getSpace } from '@dxos/client/echo';
8
+ import { Capabilities, Capability } from '@dxos/app-framework';
9
+ import { Surface } from '@dxos/app-framework/ui';
10
+ import { AppSurface } from '@dxos/app-toolkit/ui';
10
11
  import { Obj } from '@dxos/echo';
11
- import { useActiveSpace } from '@dxos/plugin-space';
12
- import { type ProjectionModel } from '@dxos/schema';
13
- import { Organization, Person, Project, Task } from '@dxos/types';
12
+ import { Card } from '@dxos/react-ui';
13
+ import { Expando, type ProjectionModel } from '@dxos/schema';
14
+ import { Organization, Person, Pipeline, Task } from '@dxos/types';
14
15
 
15
- import { FormCard, OrganizationCard, PersonCard, ProjectCard, TaskCard } from '../cards';
16
- import { meta } from '../meta';
17
- import { type CardPreviewProps } from '../types';
16
+ import { ExpandoCard, FormCard, JsonCard, OrganizationCard, PersonCard, ProjectCard, TaskCard } from '../cards';
18
17
 
19
- export default () =>
20
- contributes(Capabilities.ReactSurface, [
21
- //
22
- // Specific schema types.
23
- // TODO(burdon): Create helpers and factor out.
24
- //
18
+ export default Capability.makeModule(() =>
19
+ Effect.succeed(
20
+ Capability.contributes(Capabilities.ReactSurface, [
21
+ //
22
+ // Specific schema types.
23
+ // TODO(burdon): Create helpers and factor out.
24
+ //
25
25
 
26
- // TODO(burdon): Infer Role type.
27
- createSurface<{ subject: Person.Person }>({
28
- id: `${meta.id}/schema-popover--contact`,
29
- role: SurfaceCardRole.literals as any,
30
- filter: (data): data is { subject: Person.Person } => Obj.instanceOf(Person.Person, data.subject),
31
- component: ({ data, role }) => {
32
- const { dispatchPromise: dispatch } = useIntentDispatcher();
33
- const space = getSpace(data.subject);
34
- const activeSpace = useActiveSpace(); // TODO(burdon): Disambiguate with space?
35
- const handleSelect = useCallback<NonNullable<CardPreviewProps['onSelect']>>(
36
- (object) =>
37
- dispatch(
38
- createIntent(LayoutAction.Open, {
39
- part: 'main',
40
- subject: [Obj.getDXN(object).toString()],
41
- options: {
42
- workspace: space?.id,
43
- },
44
- }),
45
- ),
46
- [dispatch],
47
- );
26
+ Surface.create<{ subject: Person.Person }>({
27
+ id: 'schema-popover--contact',
28
+ position: 'hoist',
29
+ filter: AppSurface.object(AppSurface.Card, Person.Person),
30
+ component: ({ data, role }) => {
31
+ return (
32
+ <>
33
+ <PersonCard role={role} subject={data.subject} />
34
+ <Surface.Surface type={AppSurface.Related} data={data} limit={1} />
35
+ </>
36
+ );
37
+ },
38
+ }),
39
+ Surface.create({
40
+ id: 'schema-popover--organization',
41
+ position: 'hoist',
42
+ filter: AppSurface.object(AppSurface.Card, Organization.Organization),
43
+ component: ({ data, role }) => {
44
+ return (
45
+ <>
46
+ <OrganizationCard role={role} subject={data.subject} />
47
+ <Surface.Surface type={AppSurface.Related} data={data} limit={1} />
48
+ </>
49
+ );
50
+ },
51
+ }),
52
+ Surface.create({
53
+ id: 'schema-popover--project',
54
+ position: 'hoist',
55
+ filter: AppSurface.object(AppSurface.Card, Pipeline.Pipeline),
56
+ component: ({ data, role }) => {
57
+ return <ProjectCard role={role} subject={data.subject} />;
58
+ },
59
+ }),
60
+ Surface.create({
61
+ id: 'schema-popover--task',
62
+ position: 'hoist',
63
+ filter: AppSurface.object(AppSurface.Card, Task.Task),
64
+ component: ({ data, role }) => {
65
+ return <TaskCard role={role} subject={data.subject} />;
66
+ },
67
+ }),
68
+ Surface.create<AppSurface.ObjectCardData<Expando.Expando>>({
69
+ id: 'schema-popover--expando',
70
+ filter: AppSurface.object(AppSurface.Card, Expando.Expando),
71
+ component: ({ data, role }) => {
72
+ return <ExpandoCard role={role} subject={data.subject} ignorePaths={data.ignorePaths} />;
73
+ },
74
+ }),
48
75
 
49
- return (
50
- <PersonCard
51
- role={role as SurfaceCardRole}
52
- subject={data.subject}
53
- activeSpace={activeSpace}
54
- onSelect={handleSelect}
55
- >
56
- {role === 'card--popover' && <Surface role='related' data={data} />}
57
- </PersonCard>
58
- );
59
- },
60
- }),
61
- createSurface({
62
- id: `${meta.id}/schema-popover--organization`,
63
- role: SurfaceCardRole.literals as any,
64
- filter: (data): data is { subject: Organization.Organization } =>
65
- Obj.instanceOf(Organization.Organization, data.subject),
66
- component: ({ data, role }) => {
67
- const activeSpace = useActiveSpace();
68
- return (
69
- <OrganizationCard role={role as SurfaceCardRole} subject={data.subject} activeSpace={activeSpace}>
70
- {role === 'card--popover' && <Surface role='related' data={data} />}
71
- </OrganizationCard>
72
- );
73
- },
74
- }),
75
- createSurface({
76
- id: `${meta.id}/schema-popover--project`,
77
- role: SurfaceCardRole.literals as any,
78
- filter: (data): data is { subject: Project.Project } => Obj.instanceOf(Project.Project, data.subject),
79
- component: ({ data, role }) => {
80
- const activeSpace = useActiveSpace();
81
- return <ProjectCard subject={data.subject} role={role as SurfaceCardRole} activeSpace={activeSpace} />;
82
- },
83
- }),
84
- createSurface({
85
- id: `${meta.id}/schema-popover--task`,
86
- role: SurfaceCardRole.literals as any,
87
- filter: (data): data is { subject: Task.Task } => Obj.instanceOf(Task.Task, data.subject),
88
- component: ({ data, role }) => {
89
- return <TaskCard subject={data.subject} role={role as SurfaceCardRole} />;
90
- },
91
- }),
76
+ //
77
+ // Fallback for any object.
78
+ //
92
79
 
93
- //
94
- // Fallback for any object.
95
- //
80
+ Surface.create({
81
+ id: 'fallback-popover',
82
+ role: 'card--content',
83
+ position: 'fallback',
84
+ filter: (data): data is { subject: Obj.Unknown; projection?: ProjectionModel } => Obj.isObject(data.subject),
85
+ component: ({ data, role }) => {
86
+ return <FormCard role={role} subject={data.subject} projection={data.projection} />;
87
+ },
88
+ }),
96
89
 
97
- createSurface({
98
- id: `${meta.id}/fallback-popover`,
99
- role: SurfaceCardRole.literals as any,
100
- position: 'fallback',
101
- filter: (data): data is { subject: Obj.Any; projection?: ProjectionModel } => Obj.isObject(data.subject),
102
- component: ({ data, role }) => {
103
- return <FormCard subject={data.subject} projection={data.projection} role={role as SurfaceCardRole} />;
104
- },
105
- }),
90
+ Surface.create({
91
+ id: 'fallback-json',
92
+ role: 'card--content',
93
+ position: 'fallback',
94
+ filter: (data): data is Record<string, unknown> => true,
95
+ component: ({ data }) => {
96
+ return <JsonCard data={data} />;
97
+ },
98
+ }),
106
99
 
107
- createSurface({
108
- id: `${meta.id}/section`,
109
- role: ['section'],
110
- position: 'fallback',
111
- filter: (data): data is { subject: Obj.Any } => Obj.isObject(data.subject),
112
- component: ({ data }) => {
113
- return (
114
- <div role='none' className='flex is-full justify-center'>
115
- <div role='none' className='pbs-2 pbe-2 card-min-width card-max-width'>
116
- <Surface role='card' data={data} limit={1} />
100
+ Surface.create({
101
+ id: 'section',
102
+ position: 'fallback',
103
+ filter: AppSurface.subject(AppSurface.Section, Obj.isObject),
104
+ component: ({ data }) => {
105
+ return (
106
+ <div className='flex w-full justify-center'>
107
+ <div className='py-2 dx-card-min-width dx-card-max-width'>
108
+ <Card.Root>
109
+ <Surface.Surface type={AppSurface.Card} data={data} limit={1} />
110
+ </Card.Root>
111
+ </div>
117
112
  </div>
118
- </div>
119
- );
120
- },
121
- }),
122
- ]);
113
+ );
114
+ },
115
+ }),
116
+ ]),
117
+ ),
118
+ );