@dxos/plugin-board 0.8.4-main.7996785055 → 0.8.4-main.8baae0fced

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 (106) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/{browser/BoardContainer-ANMIIXMG.mjs → neutral/BoardArticle-6DBQWLP5.mjs} +49 -34
  4. package/dist/lib/neutral/BoardArticle-6DBQWLP5.mjs.map +7 -0
  5. package/dist/lib/neutral/BoardPlugin.mjs +30 -0
  6. package/dist/lib/neutral/BoardPlugin.mjs.map +7 -0
  7. package/dist/lib/neutral/capabilities/index.mjs +11 -0
  8. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  9. package/dist/lib/{browser/chunk-F24SVFD3.mjs → neutral/chunk-BWSKEQIG.mjs} +6 -2
  10. package/dist/lib/neutral/chunk-BWSKEQIG.mjs.map +7 -0
  11. package/dist/lib/{browser/chunk-UWECQOHO.mjs → neutral/chunk-XXGRE3CK.mjs} +1 -1
  12. package/dist/lib/{browser/chunk-UWECQOHO.mjs.map → neutral/chunk-XXGRE3CK.mjs.map} +2 -2
  13. package/dist/lib/neutral/components/index.mjs +1 -0
  14. package/dist/lib/neutral/containers/index.mjs +9 -0
  15. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  16. package/dist/lib/neutral/create-object-OWHPPT3P.mjs +27 -0
  17. package/dist/lib/neutral/create-object-OWHPPT3P.mjs.map +7 -0
  18. package/dist/lib/neutral/index.mjs +12 -0
  19. package/dist/lib/neutral/meta.json +1 -0
  20. package/dist/lib/neutral/meta.mjs +8 -0
  21. package/dist/lib/neutral/plugin.mjs +12 -0
  22. package/dist/lib/neutral/plugin.mjs.map +7 -0
  23. package/dist/lib/neutral/react-surface-M5JHK3ZQ.mjs +26 -0
  24. package/dist/lib/neutral/react-surface-M5JHK3ZQ.mjs.map +7 -0
  25. package/dist/lib/neutral/translations.mjs +29 -0
  26. package/dist/lib/neutral/translations.mjs.map +7 -0
  27. package/dist/lib/{browser → neutral}/types/index.mjs +1 -1
  28. package/dist/lib/neutral/types/index.mjs.map +7 -0
  29. package/dist/types/src/BoardPlugin.d.ts +1 -0
  30. package/dist/types/src/BoardPlugin.d.ts.map +1 -1
  31. package/dist/types/src/BoardPlugin.test.d.ts +2 -0
  32. package/dist/types/src/BoardPlugin.test.d.ts.map +1 -0
  33. package/dist/types/src/capabilities/create-object.d.ts +11 -0
  34. package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
  35. package/dist/types/src/capabilities/index.d.ts +8 -1
  36. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  37. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  38. package/dist/types/src/containers/BoardArticle/BoardArticle.d.ts +6 -0
  39. package/dist/types/src/containers/BoardArticle/BoardArticle.d.ts.map +1 -0
  40. package/dist/types/src/containers/BoardArticle/BoardArticle.stories.d.ts +54 -0
  41. package/dist/types/src/containers/BoardArticle/BoardArticle.stories.d.ts.map +1 -0
  42. package/dist/types/src/containers/BoardArticle/index.d.ts +2 -0
  43. package/dist/types/src/containers/BoardArticle/index.d.ts.map +1 -0
  44. package/dist/types/src/containers/index.d.ts +1 -1
  45. package/dist/types/src/containers/index.d.ts.map +1 -1
  46. package/dist/types/src/index.d.ts +0 -1
  47. package/dist/types/src/index.d.ts.map +1 -1
  48. package/dist/types/src/meta.d.ts.map +1 -1
  49. package/dist/types/src/plugin.d.ts +3 -0
  50. package/dist/types/src/plugin.d.ts.map +1 -0
  51. package/dist/types/src/translations.d.ts +22 -15
  52. package/dist/types/src/translations.d.ts.map +1 -1
  53. package/dist/types/src/types/Board.d.ts.map +1 -1
  54. package/dist/types/tsconfig.tsbuildinfo +1 -1
  55. package/package.json +91 -55
  56. package/src/BoardPlugin.test.ts +27 -0
  57. package/src/BoardPlugin.tsx +8 -31
  58. package/src/capabilities/create-object.ts +30 -0
  59. package/src/capabilities/index.ts +4 -1
  60. package/src/capabilities/react-surface.tsx +31 -0
  61. package/src/containers/{BoardContainer/BoardContainer.stories.tsx → BoardArticle/BoardArticle.stories.tsx} +20 -20
  62. package/src/containers/{BoardContainer/BoardContainer.tsx → BoardArticle/BoardArticle.tsx} +50 -22
  63. package/src/containers/BoardArticle/index.ts +5 -0
  64. package/src/containers/index.ts +1 -1
  65. package/src/index.ts +0 -2
  66. package/src/meta.ts +2 -0
  67. package/src/plugin.ts +9 -0
  68. package/src/translations.ts +12 -12
  69. package/dist/lib/browser/BoardContainer-ANMIIXMG.mjs.map +0 -7
  70. package/dist/lib/browser/chunk-F24SVFD3.mjs.map +0 -7
  71. package/dist/lib/browser/index.mjs +0 -80
  72. package/dist/lib/browser/index.mjs.map +0 -7
  73. package/dist/lib/browser/meta.json +0 -1
  74. package/dist/lib/browser/react-surface-LEGRWMJS.mjs +0 -39
  75. package/dist/lib/browser/react-surface-LEGRWMJS.mjs.map +0 -7
  76. package/dist/lib/node-esm/BoardContainer-FDVFXDZP.mjs +0 -150
  77. package/dist/lib/node-esm/BoardContainer-FDVFXDZP.mjs.map +0 -7
  78. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  79. package/dist/lib/node-esm/chunk-RCYQ2LGJ.mjs +0 -38
  80. package/dist/lib/node-esm/chunk-RCYQ2LGJ.mjs.map +0 -7
  81. package/dist/lib/node-esm/chunk-RIBFHKLN.mjs +0 -21
  82. package/dist/lib/node-esm/chunk-RIBFHKLN.mjs.map +0 -7
  83. package/dist/lib/node-esm/index.mjs +0 -81
  84. package/dist/lib/node-esm/index.mjs.map +0 -7
  85. package/dist/lib/node-esm/meta.json +0 -1
  86. package/dist/lib/node-esm/react-surface-ISI4UNAF.mjs +0 -40
  87. package/dist/lib/node-esm/react-surface-ISI4UNAF.mjs.map +0 -7
  88. package/dist/lib/node-esm/types/index.mjs +0 -9
  89. package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
  90. package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
  91. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
  92. package/dist/types/src/containers/BoardContainer/BoardContainer.d.ts +0 -6
  93. package/dist/types/src/containers/BoardContainer/BoardContainer.d.ts.map +0 -1
  94. package/dist/types/src/containers/BoardContainer/BoardContainer.stories.d.ts +0 -46
  95. package/dist/types/src/containers/BoardContainer/BoardContainer.stories.d.ts.map +0 -1
  96. package/dist/types/src/containers/BoardContainer/index.d.ts +0 -3
  97. package/dist/types/src/containers/BoardContainer/index.d.ts.map +0 -1
  98. package/src/capabilities/react-surface/index.ts +0 -7
  99. package/src/capabilities/react-surface/react-surface.tsx +0 -30
  100. package/src/containers/BoardContainer/index.ts +0 -7
  101. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  102. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
  103. /package/dist/lib/{browser/types → neutral/components}/index.mjs.map +0 -0
  104. /package/dist/lib/{node-esm/types → neutral}/index.mjs.map +0 -0
  105. /package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/meta.mjs.map} +0 -0
  106. /package/dist/types/src/capabilities/{react-surface/react-surface.d.ts → react-surface.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-board",
3
- "version": "0.8.4-main.7996785055",
3
+ "version": "0.8.4-main.8baae0fced",
4
4
  "description": "Surface plugin for card baords",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -8,87 +8,123 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/dxos/dxos"
10
10
  },
11
- "license": "MIT",
11
+ "license": "FSL-1.1-Apache-2.0",
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
54
  "source": "./src/index.ts",
18
- "browser": "./dist/lib/browser/index.mjs",
19
- "node": "./dist/lib/node-esm/index.mjs",
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
- "source": "./src/types/index.ts",
24
- "browser": "./dist/lib/browser/types/index.mjs",
25
- "node": "./dist/lib/node-esm/types/index.mjs",
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": {
75
+ "@effect-atom/atom": "^0.5.1",
76
+ "@effect-atom/atom-react": "^0.5.0",
42
77
  "effect": "3.20.0",
43
- "@dxos/app-toolkit": "0.8.4-main.7996785055",
44
- "@dxos/assistant": "0.8.4-main.7996785055",
45
- "@dxos/ai": "0.8.4-main.7996785055",
46
- "@dxos/blueprints": "0.8.4-main.7996785055",
47
- "@dxos/async": "0.8.4-main.7996785055",
48
- "@dxos/app-framework": "0.8.4-main.7996785055",
49
- "@dxos/echo-react": "0.8.4-main.7996785055",
50
- "@dxos/echo": "0.8.4-main.7996785055",
51
- "@dxos/invariant": "0.8.4-main.7996785055",
52
- "@dxos/client": "0.8.4-main.7996785055",
53
- "@dxos/effect": "0.8.4-main.7996785055",
54
- "@dxos/operation": "0.8.4-main.7996785055",
55
- "@dxos/log": "0.8.4-main.7996785055",
56
- "@dxos/plugin-graph": "0.8.4-main.7996785055",
57
- "@dxos/plugin-search": "0.8.4-main.7996785055",
58
- "@dxos/plugin-space": "0.8.4-main.7996785055",
59
- "@dxos/random": "0.8.4-main.7996785055",
60
- "@dxos/plugin-client": "0.8.4-main.7996785055",
61
- "@dxos/react-ui": "0.8.4-main.7996785055",
62
- "@dxos/react-client": "0.8.4-main.7996785055",
63
- "@dxos/react-ui-board": "0.8.4-main.7996785055",
64
- "@dxos/react-ui-attention": "0.8.4-main.7996785055",
65
- "@dxos/react-ui-form": "0.8.4-main.7996785055",
66
- "@dxos/react-ui-mosaic": "0.8.4-main.7996785055",
67
- "@dxos/react-ui-stack": "0.8.4-main.7996785055",
68
- "@dxos/schema": "0.8.4-main.7996785055",
69
- "@dxos/types": "0.8.4-main.7996785055",
70
- "@dxos/util": "0.8.4-main.7996785055"
78
+ "@dxos/ai": "0.8.4-main.8baae0fced",
79
+ "@dxos/app-framework": "0.8.4-main.8baae0fced",
80
+ "@dxos/assistant": "0.8.4-main.8baae0fced",
81
+ "@dxos/async": "0.8.4-main.8baae0fced",
82
+ "@dxos/compute": "0.8.4-main.8baae0fced",
83
+ "@dxos/client": "0.8.4-main.8baae0fced",
84
+ "@dxos/app-toolkit": "0.8.4-main.8baae0fced",
85
+ "@dxos/echo": "0.8.4-main.8baae0fced",
86
+ "@dxos/echo-react": "0.8.4-main.8baae0fced",
87
+ "@dxos/echo-atom": "0.8.4-main.8baae0fced",
88
+ "@dxos/effect": "0.8.4-main.8baae0fced",
89
+ "@dxos/invariant": "0.8.4-main.8baae0fced",
90
+ "@dxos/plugin-client": "0.8.4-main.8baae0fced",
91
+ "@dxos/log": "0.8.4-main.8baae0fced",
92
+ "@dxos/plugin-graph": "0.8.4-main.8baae0fced",
93
+ "@dxos/plugin-search": "0.8.4-main.8baae0fced",
94
+ "@dxos/plugin-markdown": "0.8.4-main.8baae0fced",
95
+ "@dxos/plugin-space": "0.8.4-main.8baae0fced",
96
+ "@dxos/random": "0.8.4-main.8baae0fced",
97
+ "@dxos/react-client": "0.8.4-main.8baae0fced",
98
+ "@dxos/react-ui": "0.8.4-main.8baae0fced",
99
+ "@dxos/react-ui-mosaic": "0.8.4-main.8baae0fced",
100
+ "@dxos/react-ui-attention": "0.8.4-main.8baae0fced",
101
+ "@dxos/react-ui-form": "0.8.4-main.8baae0fced",
102
+ "@dxos/schema": "0.8.4-main.8baae0fced",
103
+ "@dxos/react-ui-stack": "0.8.4-main.8baae0fced",
104
+ "@dxos/types": "0.8.4-main.8baae0fced",
105
+ "@dxos/util": "0.8.4-main.8baae0fced",
106
+ "@dxos/react-ui-board": "0.8.4-main.8baae0fced"
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.11",
78
- "@dxos/plugin-preview": "0.8.4-main.7996785055",
79
- "@dxos/plugin-testing": "0.8.4-main.7996785055",
80
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.7996785055",
81
- "@dxos/plugin-theme": "0.8.4-main.7996785055",
82
- "@dxos/storybook-utils": "0.8.4-main.7996785055",
83
- "@dxos/ui-theme": "0.8.4-main.7996785055",
84
- "@dxos/test-utils": "0.8.4-main.7996785055"
113
+ "vite": "^8.0.13",
114
+ "@dxos/plugin-preview": "0.8.4-main.8baae0fced",
115
+ "@dxos/plugin-testing": "0.8.4-main.8baae0fced",
116
+ "@dxos/plugin-theme": "0.8.4-main.8baae0fced",
117
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.8baae0fced",
118
+ "@dxos/storybook-utils": "0.8.4-main.8baae0fced",
119
+ "@dxos/test-utils": "0.8.4-main.8baae0fced",
120
+ "@dxos/ui-theme": "0.8.4-main.8baae0fced"
85
121
  },
86
122
  "peerDependencies": {
87
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.7996785055",
91
- "@dxos/ui-theme": "0.8.4-main.7996785055"
126
+ "@dxos/react-ui": "0.8.4-main.8baae0fced",
127
+ "@dxos/ui-theme": "0.8.4-main.8baae0fced"
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,44 +2,21 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import * as Effect from 'effect/Effect';
6
- import * as Option from 'effect/Option';
7
-
8
5
  import { Plugin } from '@dxos/app-framework';
9
6
  import { AppPlugin } from '@dxos/app-toolkit';
10
- import { Annotation } from '@dxos/echo';
11
- import { Operation } from '@dxos/operation';
12
- import { type CreateObject } from '@dxos/plugin-space/types';
13
- import { SpaceOperation } from '@dxos/plugin-space/operations';
14
- import { translations as boardTranslations } from '@dxos/react-ui-board';
7
+ import { translations as boardTranslations } from '@dxos/react-ui-board/translations';
15
8
 
16
- import { ReactSurface } from './capabilities';
17
- import { meta } from './meta';
18
- import { translations } from './translations';
19
- 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';
20
13
 
21
14
  export const BoardPlugin = Plugin.define(meta).pipe(
22
- AppPlugin.addMetadataModule({
23
- metadata: {
24
- id: Board.Board.typename,
25
- metadata: {
26
- icon: Annotation.IconAnnotation.get(Board.Board).pipe(Option.getOrThrow).icon,
27
- iconHue: Annotation.IconAnnotation.get(Board.Board).pipe(Option.getOrThrow).hue ?? 'white',
28
- createObject: ((props, options) =>
29
- Effect.gen(function* () {
30
- const object = Board.makeBoard(props);
31
- return yield* Operation.invoke(SpaceOperation.AddObject, {
32
- object,
33
- target: options.target,
34
- hidden: true,
35
- targetNodeId: options.targetNodeId,
36
- });
37
- })) satisfies CreateObject,
38
- },
39
- },
40
- }),
15
+ AppPlugin.addCreateObjectModule({ activate: CreateObject }),
41
16
  AppPlugin.addSchemaModule({ schema: [Board.Board] }),
42
17
  AppPlugin.addSurfaceModule({ activate: ReactSurface }),
43
18
  AppPlugin.addTranslationsModule({ translations: [...translations, ...boardTranslations] }),
44
19
  Plugin.make,
45
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 { BoardArticle } 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
+ <BoardArticle role={role} subject={data.subject} attendableId={data.attendableId} />
27
+ ),
28
+ }),
29
+ ]),
30
+ ),
31
+ );
@@ -7,23 +7,23 @@ import * as Effect from 'effect/Effect';
7
7
  import React, { useEffect, useState } from 'react';
8
8
 
9
9
  import { withPluginManager } from '@dxos/app-framework/testing';
10
- import { Obj } from '@dxos/echo';
11
- import { ClientPlugin } from '@dxos/plugin-client';
10
+ import { Filter, Obj, Ref } from '@dxos/echo';
11
+ import { ClientPlugin } from '@dxos/plugin-client/testing';
12
12
  import { initializeIdentity } from '@dxos/plugin-client/testing';
13
- import { PreviewPlugin } from '@dxos/plugin-preview';
13
+ import { PreviewPlugin } from '@dxos/plugin-preview/testing';
14
14
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
15
- import { faker } from '@dxos/random';
16
- import { Filter, Ref, useQuery, useSpaces } from '@dxos/react-client/echo';
15
+ import { random } from '@dxos/random';
16
+ import { useQuery, useSpaces } from '@dxos/react-client/echo';
17
+ import { translations as stackTranslations } from '@dxos/react-ui-stack/translations';
17
18
  import { withLayout } from '@dxos/react-ui/testing';
18
- import { translations as stackTranslations } from '@dxos/react-ui-stack';
19
19
  import { Organization, Person } from '@dxos/types';
20
20
 
21
- import { translations } from '../../translations';
22
- import { Board } from '../../types';
21
+ import { translations } from '#translations';
22
+ import { Board } from '#types';
23
23
 
24
- import { BoardContainer } from './BoardContainer';
24
+ import { BoardArticle } from './BoardArticle';
25
25
 
26
- faker.seed(0);
26
+ random.seed(0);
27
27
 
28
28
  const createBoard = () =>
29
29
  Obj.make(Board.Board, {
@@ -37,12 +37,12 @@ const createBoard = () =>
37
37
 
38
38
  const createOrg = () =>
39
39
  Obj.make(Organization.Organization, {
40
- name: faker.commerce.productName(),
41
- description: faker.lorem.paragraph(),
42
- image: faker.image.url(),
43
- website: faker.internet.url(),
40
+ name: random.commerce.productName(),
41
+ description: random.lorem.paragraph(),
42
+ image: random.image.url(),
43
+ website: random.internet.url(),
44
44
  // TODO(burdon): Fix.
45
- // status: faker.helpers.arrayElement(Organization.StatusOptions).id,
45
+ // status: random.helpers.arrayElement(Organization.StatusOptions).id,
46
46
  });
47
47
 
48
48
  const DefaultStory = () => {
@@ -62,7 +62,7 @@ const DefaultStory = () => {
62
62
  return null;
63
63
  }
64
64
 
65
- return <BoardContainer role='board' subject={board} attendableId='test' />;
65
+ return <BoardArticle role='board' subject={board} attendableId='test' />;
66
66
  };
67
67
 
68
68
  //
@@ -70,7 +70,7 @@ const DefaultStory = () => {
70
70
  //
71
71
 
72
72
  const meta = {
73
- title: 'plugins/plugin-board/containers/BoardContainer',
73
+ title: 'plugins/plugin-board/containers/BoardArticle',
74
74
  render: DefaultStory,
75
75
  decorators: [
76
76
  withLayout({ layout: 'fullscreen' }),
@@ -86,13 +86,13 @@ const meta = {
86
86
  yield* Effect.promise(() => space.waitUntilReady());
87
87
  const board = space.db.add(createBoard());
88
88
 
89
- Obj.change(board, (obj) => {
89
+ Obj.update(board, (board) => {
90
90
  // Add some sample items
91
91
  Array.from({ length: 10 }).map(() => {
92
92
  const org = createOrg();
93
93
  space.db.add(org);
94
- obj.items.push(Ref.make(org));
95
- obj.layout.cells[org.id] = {
94
+ board.items.push(Ref.make(org));
95
+ board.layout.cells[org.id] = {
96
96
  x: Math.floor(Math.random() * 5) - 2,
97
97
  y: Math.floor(Math.random() * 5) - 2,
98
98
  width: 1,
@@ -2,13 +2,17 @@
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
17
  import { Panel } from '@dxos/react-ui';
14
18
  import { useAttention } from '@dxos/react-ui-attention';
@@ -16,7 +20,7 @@ import { Board, type BoardController, type BoardRootProps, type Position } from
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,17 +28,32 @@ type PickerState = {
24
28
  position: Position;
25
29
  };
26
30
 
27
- export type BoardContainerProps = SurfaceComponentProps<BoardType.Board>;
31
+ export type BoardArticleProps = AppSurface.ObjectArticleProps<BoardType.Board>;
28
32
 
29
- export const BoardContainer = ({ role, subject: board, attendableId }: BoardContainerProps) => {
30
- const controller = useRef<BoardController>(null);
33
+ export const BoardArticle = ({ role, subject: board, attendableId }: BoardArticleProps) => {
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 { hasAttention } = useAttention(attendableId);
36
56
 
37
- const db = Obj.getDatabase(board);
38
57
  // TODO(burdon): Use search.
39
58
  const objects = useQuery(db, Filter.everything());
40
59
  const options = useMemo<ObjectPickerContentProps['options']>(
@@ -57,13 +76,22 @@ export const BoardContainer = ({ role, subject: board, attendableId }: BoardCont
57
76
  );
58
77
 
59
78
  const handleAdd = useCallback<NonNullable<BoardRootProps['onAdd']>>(
60
- async (anchor, position = DEFAULT_POSITION) => {
79
+ async (anchor, position) => {
61
80
  const db = Obj.getDatabase(board);
62
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
+
63
93
  addTriggerRef.current = anchor;
64
- setPickerState({
65
- position,
66
- });
94
+ setPickerState({ position: DEFAULT_POSITION });
67
95
  },
68
96
  [board],
69
97
  );
@@ -73,11 +101,11 @@ export const BoardContainer = ({ role, subject: board, attendableId }: BoardCont
73
101
  (id) => {
74
102
  // TODO(burdon): Impl. DXN.equals and pass in DXN from `id`.
75
103
  const idx = board.items.findIndex((ref) => ref.dxn.asEchoDXN()?.echoId === id);
76
- Obj.change(board, (obj) => {
104
+ Obj.update(board, (board) => {
77
105
  if (idx !== -1) {
78
- obj.items.splice(idx, 1);
106
+ board.items.splice(idx, 1);
79
107
  }
80
- delete obj.layout.cells[id];
108
+ delete board.layout.cells[id];
81
109
  });
82
110
  },
83
111
  [board],
@@ -86,8 +114,8 @@ export const BoardContainer = ({ role, subject: board, attendableId }: BoardCont
86
114
  const handleMove = useCallback<NonNullable<BoardRootProps['onMove']>>(
87
115
  (id, position) => {
88
116
  const layout = board.layout.cells[id];
89
- Obj.change(board, (obj) => {
90
- obj.layout.cells[id] = { ...layout, ...position };
117
+ Obj.update(board, (board) => {
118
+ board.layout.cells[id] = { ...layout, ...position };
91
119
  });
92
120
  },
93
121
  [board],
@@ -106,11 +134,11 @@ export const BoardContainer = ({ role, subject: board, attendableId }: BoardCont
106
134
  }
107
135
 
108
136
  // Create a reference to the selected object and add it to the board.
109
- Obj.change(board, (obj) => {
110
- obj.items.push(Ref.make(selectedObject));
137
+ Obj.update(board, (board) => {
138
+ board.items.push(Ref.make(selectedObject));
111
139
 
112
140
  // Set the layout position for the new item.
113
- obj.layout.cells[selectedObject.id.toString()] = pickerState.position;
141
+ board.layout.cells[selectedObject.id.toString()] = pickerState.position;
114
142
  });
115
143
 
116
144
  // Close the picker.
@@ -138,7 +166,7 @@ export const BoardContainer = ({ role, subject: board, attendableId }: BoardCont
138
166
  <Board.Content>
139
167
  {items?.map((item, index) => (
140
168
  <Board.Cell item={item} key={index} layout={board.layout?.cells[item.id] ?? { x: 0, y: 0 }}>
141
- <Surface.Surface role='card--content' data={{ subject: item }} limit={1} />
169
+ <Surface.Surface type={AppSurface.Card} data={{ subject: item, editable: true }} limit={1} />
142
170
  </Board.Cell>
143
171
  ))}
144
172
  </Board.Content>
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export { BoardArticle as default } from './BoardArticle';
@@ -4,4 +4,4 @@
4
4
 
5
5
  import { type ComponentType, lazy } from 'react';
6
6
 
7
- export const BoardContainer: ComponentType<any> = lazy(() => import('./BoardContainer'));
7
+ export const BoardArticle: ComponentType<any> = lazy(() => import('./BoardArticle'));
package/src/index.ts CHANGED
@@ -4,5 +4,3 @@
4
4
 
5
5
  export * from './meta';
6
6
  export * from './types';
7
-
8
- export * from './BoardPlugin';
package/src/meta.ts CHANGED
@@ -8,6 +8,7 @@ import { trim } from '@dxos/util';
8
8
  export const meta: Plugin.Meta = {
9
9
  id: 'org.dxos.plugin.board',
10
10
  name: 'Board',
11
+ author: 'DXOS',
11
12
  description: trim`
12
13
  Infinite canvas workspace that combines sticky notes, media, and whiteboarding tools.
13
14
  Arrange and connect ideas freely in a visual space perfect for brainstorming and creative collaboration.
@@ -16,4 +17,5 @@ export const meta: Plugin.Meta = {
16
17
  iconHue: 'green',
17
18
  source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-board',
18
19
  screenshots: [],
20
+ tags: ['labs'],
19
21
  };
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'));