@dxos/plugin-board 0.8.4-main.3eb6e50203 → 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 (103) hide show
  1. package/dist/lib/{node-esm/chunk-HICZFU5T.mjs → neutral/BoardContainer-HS5KHT4R.mjs} +60 -57
  2. package/dist/lib/neutral/BoardContainer-HS5KHT4R.mjs.map +7 -0
  3. package/dist/lib/neutral/BoardPlugin.mjs +30 -0
  4. package/dist/lib/neutral/BoardPlugin.mjs.map +7 -0
  5. package/dist/lib/neutral/capabilities/index.mjs +11 -0
  6. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  7. package/dist/lib/neutral/chunk-EFV3E7KE.mjs +19 -0
  8. package/dist/lib/neutral/chunk-EFV3E7KE.mjs.map +7 -0
  9. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  10. package/dist/lib/{browser/chunk-7VKDDGS7.mjs → neutral/chunk-XXGRE3CK.mjs} +11 -10
  11. package/dist/lib/neutral/chunk-XXGRE3CK.mjs.map +7 -0
  12. package/dist/lib/neutral/components/index.mjs +1 -0
  13. package/dist/lib/neutral/containers/index.mjs +9 -0
  14. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  15. package/dist/lib/neutral/create-object-OWHPPT3P.mjs +27 -0
  16. package/dist/lib/neutral/create-object-OWHPPT3P.mjs.map +7 -0
  17. package/dist/lib/neutral/index.mjs +12 -0
  18. package/dist/lib/neutral/index.mjs.map +7 -0
  19. package/dist/lib/neutral/meta.json +1 -0
  20. package/dist/lib/neutral/meta.mjs +8 -0
  21. package/dist/lib/neutral/meta.mjs.map +7 -0
  22. package/dist/lib/neutral/plugin.mjs +12 -0
  23. package/dist/lib/neutral/plugin.mjs.map +7 -0
  24. package/dist/lib/neutral/react-surface-FZYKJHNB.mjs +26 -0
  25. package/dist/lib/neutral/react-surface-FZYKJHNB.mjs.map +7 -0
  26. package/dist/lib/neutral/translations.mjs +29 -0
  27. package/dist/lib/neutral/translations.mjs.map +7 -0
  28. package/dist/lib/{browser → neutral}/types/index.mjs +2 -1
  29. package/dist/lib/neutral/types/index.mjs.map +7 -0
  30. package/dist/types/src/BoardPlugin.d.ts +1 -0
  31. package/dist/types/src/BoardPlugin.d.ts.map +1 -1
  32. package/dist/types/src/BoardPlugin.test.d.ts +2 -0
  33. package/dist/types/src/BoardPlugin.test.d.ts.map +1 -0
  34. package/dist/types/src/capabilities/create-object.d.ts +11 -0
  35. package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
  36. package/dist/types/src/capabilities/index.d.ts +8 -1
  37. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  38. package/dist/types/src/capabilities/react-surface.d.ts +5 -0
  39. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  40. package/dist/types/src/components/index.d.ts +0 -1
  41. package/dist/types/src/components/index.d.ts.map +1 -1
  42. package/dist/types/src/containers/BoardContainer/BoardContainer.d.ts +6 -0
  43. package/dist/types/src/containers/BoardContainer/BoardContainer.d.ts.map +1 -0
  44. package/dist/types/src/containers/BoardContainer/BoardContainer.stories.d.ts +54 -0
  45. package/dist/types/src/containers/BoardContainer/BoardContainer.stories.d.ts.map +1 -0
  46. package/dist/types/src/containers/BoardContainer/index.d.ts +2 -0
  47. package/dist/types/src/containers/BoardContainer/index.d.ts.map +1 -0
  48. package/dist/types/src/containers/index.d.ts +3 -0
  49. package/dist/types/src/containers/index.d.ts.map +1 -0
  50. package/dist/types/src/index.d.ts +0 -2
  51. package/dist/types/src/index.d.ts.map +1 -1
  52. package/dist/types/src/plugin.d.ts +3 -0
  53. package/dist/types/src/plugin.d.ts.map +1 -0
  54. package/dist/types/src/translations.d.ts +22 -14
  55. package/dist/types/src/translations.d.ts.map +1 -1
  56. package/dist/types/src/types/Board.d.ts +3 -3
  57. package/dist/types/src/types/Board.d.ts.map +1 -1
  58. package/dist/types/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +92 -56
  60. package/src/BoardPlugin.test.ts +27 -0
  61. package/src/BoardPlugin.tsx +8 -19
  62. package/src/capabilities/create-object.ts +30 -0
  63. package/src/capabilities/index.ts +4 -1
  64. package/src/capabilities/react-surface.tsx +31 -0
  65. package/src/components/index.ts +0 -2
  66. package/src/{components → containers/BoardContainer}/BoardContainer.stories.tsx +21 -20
  67. package/src/{components → containers/BoardContainer}/BoardContainer.tsx +70 -39
  68. package/src/containers/BoardContainer/index.ts +5 -0
  69. package/src/containers/index.ts +7 -0
  70. package/src/index.ts +0 -3
  71. package/src/meta.ts +1 -1
  72. package/src/plugin.ts +9 -0
  73. package/src/translations.ts +12 -11
  74. package/src/types/Board.ts +7 -3
  75. package/dist/lib/browser/chunk-7VKDDGS7.mjs.map +0 -7
  76. package/dist/lib/browser/chunk-DCLJ4HOC.mjs +0 -159
  77. package/dist/lib/browser/chunk-DCLJ4HOC.mjs.map +0 -7
  78. package/dist/lib/browser/index.mjs +0 -69
  79. package/dist/lib/browser/index.mjs.map +0 -7
  80. package/dist/lib/browser/meta.json +0 -1
  81. package/dist/lib/browser/react-surface-3ECZMYLQ.mjs +0 -32
  82. package/dist/lib/browser/react-surface-3ECZMYLQ.mjs.map +0 -7
  83. package/dist/lib/node-esm/chunk-HICZFU5T.mjs.map +0 -7
  84. package/dist/lib/node-esm/chunk-UGVRNKWV.mjs +0 -37
  85. package/dist/lib/node-esm/chunk-UGVRNKWV.mjs.map +0 -7
  86. package/dist/lib/node-esm/index.mjs +0 -70
  87. package/dist/lib/node-esm/index.mjs.map +0 -7
  88. package/dist/lib/node-esm/meta.json +0 -1
  89. package/dist/lib/node-esm/react-surface-W7FPI7BR.mjs +0 -33
  90. package/dist/lib/node-esm/react-surface-W7FPI7BR.mjs.map +0 -7
  91. package/dist/lib/node-esm/types/index.mjs +0 -8
  92. package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
  93. package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
  94. package/dist/types/src/capabilities/react-surface/react-surface.d.ts +0 -5
  95. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
  96. package/dist/types/src/components/BoardContainer.d.ts +0 -6
  97. package/dist/types/src/components/BoardContainer.d.ts.map +0 -1
  98. package/dist/types/src/components/BoardContainer.stories.d.ts +0 -45
  99. package/dist/types/src/components/BoardContainer.stories.d.ts.map +0 -1
  100. package/src/capabilities/react-surface/index.ts +0 -7
  101. package/src/capabilities/react-surface/react-surface.tsx +0 -27
  102. /package/dist/lib/{browser/types/index.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
  103. /package/dist/lib/{node-esm/types → neutral/components}/index.mjs.map +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-board",
3
- "version": "0.8.4-main.3eb6e50203",
3
+ "version": "0.8.4-main.3fbcb4aa9b",
4
4
  "description": "Surface plugin for card baords",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -12,83 +12,119 @@
12
12
  "author": "DXOS.org",
13
13
  "sideEffects": true,
14
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
+ "#components": {
22
+ "source": "./src/components/index.ts",
23
+ "types": "./dist/types/src/components/index.d.ts",
24
+ "default": "./dist/lib/neutral/components/index.mjs"
25
+ },
26
+ "#containers": {
27
+ "source": "./src/containers/index.ts",
28
+ "types": "./dist/types/src/containers/index.d.ts",
29
+ "default": "./dist/lib/neutral/containers/index.mjs"
30
+ },
31
+ "#meta": {
32
+ "source": "./src/meta.ts",
33
+ "types": "./dist/types/src/meta.d.ts",
34
+ "default": "./dist/lib/neutral/meta.mjs"
35
+ },
36
+ "#plugin": {
37
+ "source": "./src/BoardPlugin.tsx",
38
+ "types": "./dist/types/src/BoardPlugin.d.ts",
39
+ "default": "./dist/lib/neutral/BoardPlugin.mjs"
40
+ },
41
+ "#translations": {
42
+ "source": "./src/translations.ts",
43
+ "types": "./dist/types/src/translations.d.ts",
44
+ "default": "./dist/lib/neutral/translations.mjs"
45
+ },
46
+ "#types": {
47
+ "source": "./src/types/index.ts",
48
+ "types": "./dist/types/src/types/index.d.ts",
49
+ "default": "./dist/lib/neutral/types/index.mjs"
50
+ }
51
+ },
15
52
  "exports": {
16
53
  ".": {
17
- "browser": "./dist/lib/browser/index.mjs",
18
- "node": "./dist/lib/node-esm/index.mjs",
19
54
  "source": "./src/index.ts",
20
- "types": "./dist/types/src/index.d.ts"
55
+ "types": "./dist/types/src/index.d.ts",
56
+ "default": "./dist/lib/neutral/index.mjs"
21
57
  },
22
- "./types": {
23
- "browser": "./dist/lib/browser/types/index.mjs",
24
- "node": "./dist/lib/node-esm/types/index.mjs",
25
- "source": "./src/types/index.ts",
26
- "types": "./dist/types/src/types/index.d.ts"
58
+ "./plugin": {
59
+ "source": "./src/plugin.ts",
60
+ "types": "./dist/types/src/plugin.d.ts",
61
+ "default": "./dist/lib/neutral/plugin.mjs"
62
+ },
63
+ "./translations": {
64
+ "source": "./src/translations.ts",
65
+ "types": "./dist/types/src/translations.d.ts",
66
+ "default": "./dist/lib/neutral/translations.mjs"
27
67
  }
28
68
  },
29
69
  "types": "dist/types/src/index.d.ts",
30
- "typesVersions": {
31
- "*": {
32
- "types": [
33
- "dist/types/src/types/index.d.ts"
34
- ]
35
- }
36
- },
37
70
  "files": [
38
71
  "dist",
39
72
  "src"
40
73
  ],
41
74
  "dependencies": {
42
- "effect": "3.19.16",
43
- "@dxos/ai": "0.8.4-main.3eb6e50203",
44
- "@dxos/app-framework": "0.8.4-main.3eb6e50203",
45
- "@dxos/assistant": "0.8.4-main.3eb6e50203",
46
- "@dxos/blueprints": "0.8.4-main.3eb6e50203",
47
- "@dxos/async": "0.8.4-main.3eb6e50203",
48
- "@dxos/client": "0.8.4-main.3eb6e50203",
49
- "@dxos/echo": "0.8.4-main.3eb6e50203",
50
- "@dxos/echo-react": "0.8.4-main.3eb6e50203",
51
- "@dxos/effect": "0.8.4-main.3eb6e50203",
52
- "@dxos/invariant": "0.8.4-main.3eb6e50203",
53
- "@dxos/log": "0.8.4-main.3eb6e50203",
54
- "@dxos/operation": "0.8.4-main.3eb6e50203",
55
- "@dxos/plugin-client": "0.8.4-main.3eb6e50203",
56
- "@dxos/plugin-search": "0.8.4-main.3eb6e50203",
57
- "@dxos/app-toolkit": "0.8.4-main.3eb6e50203",
58
- "@dxos/plugin-space": "0.8.4-main.3eb6e50203",
59
- "@dxos/plugin-graph": "0.8.4-main.3eb6e50203",
60
- "@dxos/random": "0.8.4-main.3eb6e50203",
61
- "@dxos/react-ui": "0.8.4-main.3eb6e50203",
62
- "@dxos/react-ui-attention": "0.8.4-main.3eb6e50203",
63
- "@dxos/react-ui-form": "0.8.4-main.3eb6e50203",
64
- "@dxos/react-ui-board": "0.8.4-main.3eb6e50203",
65
- "@dxos/react-ui-mosaic": "0.8.4-main.3eb6e50203",
66
- "@dxos/react-ui-stack": "0.8.4-main.3eb6e50203",
67
- "@dxos/schema": "0.8.4-main.3eb6e50203",
68
- "@dxos/types": "0.8.4-main.3eb6e50203",
69
- "@dxos/util": "0.8.4-main.3eb6e50203",
70
- "@dxos/react-client": "0.8.4-main.3eb6e50203"
75
+ "@effect-atom/atom": "^0.5.1",
76
+ "@effect-atom/atom-react": "^0.5.0",
77
+ "effect": "3.20.0",
78
+ "@dxos/ai": "0.8.4-main.3fbcb4aa9b",
79
+ "@dxos/assistant": "0.8.4-main.3fbcb4aa9b",
80
+ "@dxos/app-toolkit": "0.8.4-main.3fbcb4aa9b",
81
+ "@dxos/compute": "0.8.4-main.3fbcb4aa9b",
82
+ "@dxos/async": "0.8.4-main.3fbcb4aa9b",
83
+ "@dxos/client": "0.8.4-main.3fbcb4aa9b",
84
+ "@dxos/echo": "0.8.4-main.3fbcb4aa9b",
85
+ "@dxos/effect": "0.8.4-main.3fbcb4aa9b",
86
+ "@dxos/echo-atom": "0.8.4-main.3fbcb4aa9b",
87
+ "@dxos/echo-react": "0.8.4-main.3fbcb4aa9b",
88
+ "@dxos/invariant": "0.8.4-main.3fbcb4aa9b",
89
+ "@dxos/log": "0.8.4-main.3fbcb4aa9b",
90
+ "@dxos/plugin-client": "0.8.4-main.3fbcb4aa9b",
91
+ "@dxos/app-framework": "0.8.4-main.3fbcb4aa9b",
92
+ "@dxos/plugin-graph": "0.8.4-main.3fbcb4aa9b",
93
+ "@dxos/plugin-markdown": "0.8.4-main.3fbcb4aa9b",
94
+ "@dxos/plugin-search": "0.8.4-main.3fbcb4aa9b",
95
+ "@dxos/random": "0.8.4-main.3fbcb4aa9b",
96
+ "@dxos/react-client": "0.8.4-main.3fbcb4aa9b",
97
+ "@dxos/plugin-space": "0.8.4-main.3fbcb4aa9b",
98
+ "@dxos/react-ui-board": "0.8.4-main.3fbcb4aa9b",
99
+ "@dxos/react-ui-form": "0.8.4-main.3fbcb4aa9b",
100
+ "@dxos/react-ui-attention": "0.8.4-main.3fbcb4aa9b",
101
+ "@dxos/react-ui-mosaic": "0.8.4-main.3fbcb4aa9b",
102
+ "@dxos/react-ui-stack": "0.8.4-main.3fbcb4aa9b",
103
+ "@dxos/schema": "0.8.4-main.3fbcb4aa9b",
104
+ "@dxos/react-ui": "0.8.4-main.3fbcb4aa9b",
105
+ "@dxos/types": "0.8.4-main.3fbcb4aa9b",
106
+ "@dxos/util": "0.8.4-main.3fbcb4aa9b"
71
107
  },
72
108
  "devDependencies": {
73
109
  "@types/react": "~19.2.7",
74
110
  "@types/react-dom": "~19.2.3",
75
111
  "react": "~19.2.3",
76
112
  "react-dom": "~19.2.3",
77
- "vite": "7.1.9",
78
- "@dxos/plugin-preview": "0.8.4-main.3eb6e50203",
79
- "@dxos/plugin-testing": "0.8.4-main.3eb6e50203",
80
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.3eb6e50203",
81
- "@dxos/storybook-utils": "0.8.4-main.3eb6e50203",
82
- "@dxos/test-utils": "0.8.4-main.3eb6e50203",
83
- "@dxos/ui-theme": "0.8.4-main.3eb6e50203",
84
- "@dxos/plugin-theme": "0.8.4-main.3eb6e50203"
113
+ "vite": "^8.0.10",
114
+ "@dxos/plugin-preview": "0.8.4-main.3fbcb4aa9b",
115
+ "@dxos/plugin-testing": "0.8.4-main.3fbcb4aa9b",
116
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.3fbcb4aa9b",
117
+ "@dxos/plugin-theme": "0.8.4-main.3fbcb4aa9b",
118
+ "@dxos/storybook-utils": "0.8.4-main.3fbcb4aa9b",
119
+ "@dxos/test-utils": "0.8.4-main.3fbcb4aa9b",
120
+ "@dxos/ui-theme": "0.8.4-main.3fbcb4aa9b"
85
121
  },
86
122
  "peerDependencies": {
87
- "effect": "3.19.16",
123
+ "effect": "3.20.0",
88
124
  "react": "~19.2.3",
89
125
  "react-dom": "~19.2.3",
90
- "@dxos/react-ui": "0.8.4-main.3eb6e50203",
91
- "@dxos/ui-theme": "0.8.4-main.3eb6e50203"
126
+ "@dxos/react-ui": "0.8.4-main.3fbcb4aa9b",
127
+ "@dxos/ui-theme": "0.8.4-main.3fbcb4aa9b"
92
128
  },
93
129
  "publishConfig": {
94
130
  "access": "public"
@@ -0,0 +1,27 @@
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 { BoardPlugin } from '#plugin';
11
+
12
+ import { meta } from './meta';
13
+
14
+ const moduleId = (name: string) => `${meta.id}.module.${name}`;
15
+
16
+ describe('BoardPlugin', () => {
17
+ test('modules activate on the expected events', async ({ expect }) => {
18
+ await using harness = await createComposerTestApp({
19
+ plugins: [ClientPlugin({}), BoardPlugin()],
20
+ });
21
+
22
+ // Modules expected to be active after a normal startup.
23
+ expect(harness.manager.getActive()).toEqual(
24
+ expect.arrayContaining([moduleId('CreateObject'), moduleId('schema'), moduleId('ReactSurface')]),
25
+ );
26
+ });
27
+ });
@@ -2,32 +2,21 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import * as Effect from 'effect/Effect';
6
-
7
5
  import { Plugin } from '@dxos/app-framework';
8
6
  import { AppPlugin } from '@dxos/app-toolkit';
9
- import { type CreateObject } from '@dxos/plugin-space/types';
10
- import { translations as boardTranslations } from '@dxos/react-ui-board';
7
+ import { translations as boardTranslations } from '@dxos/react-ui-board/translations';
11
8
 
12
- import { ReactSurface } from './capabilities';
13
- import { meta } from './meta';
14
- import { translations } from './translations';
15
- import { Board } from './types';
9
+ import { CreateObject, ReactSurface } from '#capabilities';
10
+ import { meta } from '#meta';
11
+ import { translations } from '#translations';
12
+ import { Board } from '#types';
16
13
 
17
14
  export const BoardPlugin = Plugin.define(meta).pipe(
18
- AppPlugin.addMetadataModule({
19
- metadata: {
20
- id: Board.Board.typename,
21
- metadata: {
22
- icon: 'ph--squares-four--regular',
23
- iconHue: 'green',
24
- createObject: ((props) => Effect.sync(() => Board.makeBoard(props))) satisfies CreateObject,
25
- addToCollectionOnCreate: true,
26
- },
27
- },
28
- }),
15
+ AppPlugin.addCreateObjectModule({ activate: CreateObject }),
29
16
  AppPlugin.addSchemaModule({ schema: [Board.Board] }),
30
17
  AppPlugin.addSurfaceModule({ activate: ReactSurface }),
31
18
  AppPlugin.addTranslationsModule({ translations: [...translations, ...boardTranslations] }),
32
19
  Plugin.make,
33
20
  );
21
+
22
+ export default BoardPlugin;
@@ -0,0 +1,30 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import * as Effect from 'effect/Effect';
6
+
7
+ import { Capability } from '@dxos/app-framework';
8
+ import { Operation } from '@dxos/compute';
9
+ import { SpaceOperation } from '@dxos/plugin-space';
10
+ import { SpaceCapabilities } from '@dxos/plugin-space';
11
+
12
+ import { Board } from '#types';
13
+
14
+ export default Capability.makeModule(
15
+ Effect.fnUntraced(function* () {
16
+ return Capability.contributes(SpaceCapabilities.CreateObjectEntry, {
17
+ id: Board.Board.typename,
18
+ createObject: (props, options) =>
19
+ Effect.gen(function* () {
20
+ const object = Board.makeBoard(props);
21
+ return yield* Operation.invoke(SpaceOperation.AddObject, {
22
+ object,
23
+ target: options.target,
24
+ hidden: true,
25
+ targetNodeId: options.targetNodeId,
26
+ });
27
+ }),
28
+ });
29
+ }),
30
+ );
@@ -2,4 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- export * from './react-surface';
5
+ import { Capability } from '@dxos/app-framework';
6
+
7
+ export const CreateObject = Capability.lazy('CreateObject', () => import('./create-object'));
8
+ export const ReactSurface = Capability.lazy('ReactSurface', () => import('./react-surface'));
@@ -0,0 +1,31 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Effect from 'effect/Effect';
6
+ import React from 'react';
7
+
8
+ import { Capabilities, Capability } from '@dxos/app-framework';
9
+ import { Surface } from '@dxos/app-framework/ui';
10
+ import { AppSurface } from '@dxos/app-toolkit/ui';
11
+
12
+ import { BoardContainer } from '#containers';
13
+ import { Board } from '#types';
14
+
15
+ export default Capability.makeModule(() =>
16
+ Effect.succeed(
17
+ Capability.contributes(Capabilities.ReactSurface, [
18
+ Surface.create({
19
+ id: 'root',
20
+ // TODO(wittjosiah): Split into multiple surfaces if this filter proves too strict for non-article roles.
21
+ filter: AppSurface.oneOf(
22
+ AppSurface.object(AppSurface.Article, Board.Board),
23
+ AppSurface.object(AppSurface.Section, Board.Board),
24
+ ),
25
+ component: ({ role, data }) => (
26
+ <BoardContainer role={role} subject={data.subject} attendableId={data.attendableId} />
27
+ ),
28
+ }),
29
+ ]),
30
+ ),
31
+ );
@@ -1,5 +1,3 @@
1
1
  //
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
-
5
- export * from './BoardContainer';
@@ -8,21 +8,22 @@ import React, { useEffect, useState } from 'react';
8
8
 
9
9
  import { withPluginManager } from '@dxos/app-framework/testing';
10
10
  import { Obj } from '@dxos/echo';
11
- import { ClientPlugin } from '@dxos/plugin-client';
12
- import { PreviewPlugin } from '@dxos/plugin-preview';
11
+ import { ClientPlugin } from '@dxos/plugin-client/plugin';
12
+ import { initializeIdentity } from '@dxos/plugin-client/testing';
13
+ import { PreviewPlugin } from '@dxos/plugin-preview/testing';
13
14
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
14
- import { faker } from '@dxos/random';
15
+ import { random } from '@dxos/random';
15
16
  import { Filter, Ref, useQuery, useSpaces } from '@dxos/react-client/echo';
16
- import { withTheme } from '@dxos/react-ui/testing';
17
- import { translations as stackTranslations } from '@dxos/react-ui-stack';
17
+ import { translations as stackTranslations } from '@dxos/react-ui-stack/translations';
18
+ import { withLayout } from '@dxos/react-ui/testing';
18
19
  import { Organization, Person } from '@dxos/types';
19
20
 
20
- import { translations } from '../translations';
21
- import { Board } from '../types';
21
+ import { translations } from '#translations';
22
+ import { Board } from '#types';
22
23
 
23
24
  import { BoardContainer } from './BoardContainer';
24
25
 
25
- faker.seed(0);
26
+ random.seed(0);
26
27
 
27
28
  const createBoard = () =>
28
29
  Obj.make(Board.Board, {
@@ -36,12 +37,12 @@ const createBoard = () =>
36
37
 
37
38
  const createOrg = () =>
38
39
  Obj.make(Organization.Organization, {
39
- name: faker.commerce.productName(),
40
- description: faker.lorem.paragraph(),
41
- image: faker.image.url(),
42
- website: faker.internet.url(),
40
+ name: random.commerce.productName(),
41
+ description: random.lorem.paragraph(),
42
+ image: random.image.url(),
43
+ website: random.internet.url(),
43
44
  // TODO(burdon): Fix.
44
- // status: faker.helpers.arrayElement(Organization.StatusOptions).id,
45
+ // status: random.helpers.arrayElement(Organization.StatusOptions).id,
45
46
  });
46
47
 
47
48
  const DefaultStory = () => {
@@ -61,7 +62,7 @@ const DefaultStory = () => {
61
62
  return null;
62
63
  }
63
64
 
64
- return <BoardContainer role='board' subject={board} />;
65
+ return <BoardContainer role='board' subject={board} attendableId='test' />;
65
66
  };
66
67
 
67
68
  //
@@ -69,10 +70,10 @@ const DefaultStory = () => {
69
70
  //
70
71
 
71
72
  const meta = {
72
- title: 'plugins/plugin-board/Board',
73
+ title: 'plugins/plugin-board/containers/BoardContainer',
73
74
  render: DefaultStory,
74
75
  decorators: [
75
- withTheme(),
76
+ withLayout({ layout: 'fullscreen' }),
76
77
  withPluginManager({
77
78
  plugins: [
78
79
  ...corePlugins(),
@@ -80,18 +81,18 @@ const meta = {
80
81
  types: [Organization.Organization, Person.Person, Board.Board],
81
82
  onClientInitialized: ({ client }) =>
82
83
  Effect.gen(function* () {
83
- yield* Effect.promise(() => client.halo.createIdentity());
84
+ yield* initializeIdentity(client);
84
85
  const space = yield* Effect.promise(() => client.spaces.create());
85
86
  yield* Effect.promise(() => space.waitUntilReady());
86
87
  const board = space.db.add(createBoard());
87
88
 
88
- Obj.change(board, (b) => {
89
+ Obj.update(board, (board) => {
89
90
  // Add some sample items
90
91
  Array.from({ length: 10 }).map(() => {
91
92
  const org = createOrg();
92
93
  space.db.add(org);
93
- b.items.push(Ref.make(org));
94
- b.layout.cells[org.id] = {
94
+ board.items.push(Ref.make(org));
95
+ board.layout.cells[org.id] = {
95
96
  x: Math.floor(Math.random() * 5) - 2,
96
97
  y: Math.floor(Math.random() * 5) - 2,
97
98
  width: 1,
@@ -2,21 +2,25 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { useAtomValue } from '@effect-atom/atom-react';
6
+ import * as Atom from '@effect-atom/atom/Atom';
5
7
  import React, { useCallback, useMemo, useRef, useState } from 'react';
6
8
 
7
9
  import { Surface } from '@dxos/app-framework/ui';
8
- import { type SurfaceComponentProps } from '@dxos/app-toolkit/ui';
10
+ import { AppSurface } from '@dxos/app-toolkit/ui';
9
11
  import { Filter, Obj, Ref } from '@dxos/echo';
10
- import { useObject, useObjects } from '@dxos/echo-react';
12
+ import { AtomObj } from '@dxos/echo-atom';
13
+ import { useObject } from '@dxos/echo-react';
11
14
  import { invariant } from '@dxos/invariant';
15
+ import { Markdown } from '@dxos/plugin-markdown';
12
16
  import { useQuery } from '@dxos/react-client/echo';
13
- import { Layout } from '@dxos/react-ui';
17
+ import { Panel } from '@dxos/react-ui';
14
18
  import { useAttention } from '@dxos/react-ui-attention';
15
19
  import { Board, type BoardController, type BoardRootProps, type Position } from '@dxos/react-ui-board';
16
20
  import { ObjectPicker, type ObjectPickerContentProps } from '@dxos/react-ui-form';
17
21
  import { isNonNullable } from '@dxos/util';
18
22
 
19
- import { type Board as BoardType } from '../types';
23
+ import { type Board as BoardType } from '#types';
20
24
 
21
25
  const DEFAULT_POSITION = { x: 0, y: 0 } satisfies Position;
22
26
 
@@ -24,18 +28,32 @@ type PickerState = {
24
28
  position: Position;
25
29
  };
26
30
 
27
- export type BoardContainerProps = SurfaceComponentProps<BoardType.Board>;
31
+ export type BoardContainerProps = AppSurface.ObjectArticleProps<BoardType.Board>;
28
32
 
29
- export const BoardContainer = ({ role, subject: board }: BoardContainerProps) => {
30
- const controller = useRef<BoardController>(null);
33
+ export const BoardContainer = ({ role, subject: board, attendableId }: BoardContainerProps) => {
34
+ const { hasAttention } = useAttention(attendableId);
35
+ const db = Obj.getDatabase(board);
31
36
  const [boardItems] = useObject(board, 'items');
32
- const items = useObjects(boardItems ?? []);
37
+ const itemsAtom = useMemo(
38
+ () =>
39
+ Atom.make((get) => {
40
+ const result: Obj.Unknown[] = [];
41
+ for (const ref of boardItems ?? []) {
42
+ const obj = get(AtomObj.makeWithReactive(ref));
43
+ if (obj) {
44
+ result.push(obj);
45
+ }
46
+ }
47
+ return result;
48
+ }),
49
+ [boardItems],
50
+ );
51
+ const items = useAtomValue(itemsAtom);
52
+
53
+ const controller = useRef<BoardController>(null);
33
54
  const addTriggerRef = useRef<HTMLButtonElement | null>(null);
34
55
  const [pickerState, setPickerState] = useState<PickerState | null>(null);
35
- const attendableId = Obj.getDXN(board).toString();
36
- const { hasAttention } = useAttention(attendableId);
37
56
 
38
- const db = Obj.getDatabase(board);
39
57
  // TODO(burdon): Use search.
40
58
  const objects = useQuery(db, Filter.everything());
41
59
  const options = useMemo<ObjectPickerContentProps['options']>(
@@ -58,13 +76,22 @@ export const BoardContainer = ({ role, subject: board }: BoardContainerProps) =>
58
76
  );
59
77
 
60
78
  const handleAdd = useCallback<NonNullable<BoardRootProps['onAdd']>>(
61
- async (anchor, position = DEFAULT_POSITION) => {
79
+ async (anchor, position) => {
62
80
  const db = Obj.getDatabase(board);
63
81
  invariant(db);
82
+ // Grid backdrop "+" supplies a position → create a new Markdown document directly.
83
+ // Toolbar "+" omits position → fall back to the picker over existing objects.
84
+ if (position) {
85
+ const doc = db.add(Markdown.make());
86
+ Obj.update(board, (board) => {
87
+ board.items.push(Ref.make(doc));
88
+ board.layout.cells[doc.id.toString()] = position;
89
+ });
90
+ return;
91
+ }
92
+
64
93
  addTriggerRef.current = anchor;
65
- setPickerState({
66
- position,
67
- });
94
+ setPickerState({ position: DEFAULT_POSITION });
68
95
  },
69
96
  [board],
70
97
  );
@@ -74,11 +101,11 @@ export const BoardContainer = ({ role, subject: board }: BoardContainerProps) =>
74
101
  (id) => {
75
102
  // TODO(burdon): Impl. DXN.equals and pass in DXN from `id`.
76
103
  const idx = board.items.findIndex((ref) => ref.dxn.asEchoDXN()?.echoId === id);
77
- Obj.change(board, (b) => {
104
+ Obj.update(board, (board) => {
78
105
  if (idx !== -1) {
79
- b.items.splice(idx, 1);
106
+ board.items.splice(idx, 1);
80
107
  }
81
- delete b.layout.cells[id];
108
+ delete board.layout.cells[id];
82
109
  });
83
110
  },
84
111
  [board],
@@ -87,8 +114,8 @@ export const BoardContainer = ({ role, subject: board }: BoardContainerProps) =>
87
114
  const handleMove = useCallback<NonNullable<BoardRootProps['onMove']>>(
88
115
  (id, position) => {
89
116
  const layout = board.layout.cells[id];
90
- Obj.change(board, (b) => {
91
- b.layout.cells[id] = { ...layout, ...position };
117
+ Obj.update(board, (board) => {
118
+ board.layout.cells[id] = { ...layout, ...position };
92
119
  });
93
120
  },
94
121
  [board],
@@ -107,11 +134,11 @@ export const BoardContainer = ({ role, subject: board }: BoardContainerProps) =>
107
134
  }
108
135
 
109
136
  // Create a reference to the selected object and add it to the board.
110
- Obj.change(board, (b) => {
111
- b.items.push(Ref.make(selectedObject));
137
+ Obj.update(board, (board) => {
138
+ board.items.push(Ref.make(selectedObject));
112
139
 
113
140
  // Set the layout position for the new item.
114
- b.layout.cells[selectedObject.id.toString()] = pickerState.position;
141
+ board.layout.cells[selectedObject.id.toString()] = pickerState.position;
115
142
  });
116
143
 
117
144
  // Close the picker.
@@ -128,22 +155,26 @@ export const BoardContainer = ({ role, subject: board }: BoardContainerProps) =>
128
155
  setPickerState(nextOpen ? { position: DEFAULT_POSITION } : null);
129
156
  }}
130
157
  >
131
- <Layout.Main role={role} toolbar>
132
- <Board.Toolbar disabled={!hasAttention} />
133
- <Board.Container>
134
- <Board.Viewport classNames='border-none'>
135
- <Board.Backdrop />
136
- <Board.Content>
137
- {items?.map((item, index) => (
138
- <Board.Cell item={item} key={index} layout={board.layout?.cells[item.id] ?? { x: 0, y: 0 }}>
139
- <Surface.Surface role='card--content' data={{ subject: item }} limit={1} />
140
- </Board.Cell>
141
- ))}
142
- </Board.Content>
143
- </Board.Viewport>
144
- </Board.Container>
145
- </Layout.Main>
146
- <ObjectPicker.Content options={options} onSelect={handleSelect} classNames='popover-card-width' />
158
+ <Panel.Root role={role}>
159
+ <Panel.Toolbar asChild>
160
+ <Board.Toolbar disabled={!hasAttention} />
161
+ </Panel.Toolbar>
162
+ <Panel.Content asChild>
163
+ <Board.Container>
164
+ <Board.Viewport classNames='border-none'>
165
+ <Board.Backdrop />
166
+ <Board.Content>
167
+ {items?.map((item, index) => (
168
+ <Board.Cell item={item} key={index} layout={board.layout?.cells[item.id] ?? { x: 0, y: 0 }}>
169
+ <Surface.Surface type={AppSurface.Card} data={{ subject: item, editable: true }} limit={1} />
170
+ </Board.Cell>
171
+ ))}
172
+ </Board.Content>
173
+ </Board.Viewport>
174
+ </Board.Container>
175
+ </Panel.Content>
176
+ </Panel.Root>
177
+ <ObjectPicker.Content options={options} onSelect={handleSelect} classNames='dx-card-popover-width' />
147
178
  <ObjectPicker.VirtualTrigger virtualRef={addTriggerRef} />
148
179
  </ObjectPicker.Root>
149
180
  </Board.Root>
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export { BoardContainer as default } from './BoardContainer';
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type ComponentType, lazy } from 'react';
6
+
7
+ export const BoardContainer: ComponentType<any> = lazy(() => import('./BoardContainer'));
package/src/index.ts CHANGED
@@ -2,8 +2,5 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- export * from './components';
6
5
  export * from './meta';
7
6
  export * from './types';
8
-
9
- export * from './BoardPlugin';
package/src/meta.ts CHANGED
@@ -6,7 +6,7 @@ import { type Plugin } from '@dxos/app-framework';
6
6
  import { trim } from '@dxos/util';
7
7
 
8
8
  export const meta: Plugin.Meta = {
9
- id: 'dxos.org/plugin/board',
9
+ id: 'org.dxos.plugin.board',
10
10
  name: 'Board',
11
11
  description: trim`
12
12
  Infinite canvas workspace that combines sticky notes, media, and whiteboarding tools.
package/src/plugin.ts ADDED
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Plugin } from '@dxos/app-framework';
6
+
7
+ import { meta } from './meta';
8
+
9
+ export const BoardPlugin = Plugin.lazy(meta, () => import('#plugin'));