@dxos/plugin-board 0.12.0-next-73daef452f1464b62efdbca75fb0067c1066f264 → 0.13.0
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.
- package/PLUGIN.mdl +58 -9
- package/dist/lib/Board.mjs +4 -5
- package/dist/lib/Board.mjs.map +1 -1
- package/dist/lib/capabilities.browser.mjs +21 -0
- package/dist/lib/capabilities.browser.mjs.map +1 -0
- package/dist/lib/capabilities.mjs +1 -3
- package/dist/lib/capabilities.mjs.map +1 -1
- package/dist/lib/capabilities.node.mjs.map +1 -1
- package/dist/lib/capabilities.tauri.mjs +21 -0
- package/dist/lib/capabilities.tauri.mjs.map +1 -0
- package/dist/lib/capabilities.workerd.mjs.map +1 -1
- package/dist/lib/chunk-BoardArticle.mjs +68 -87
- package/dist/lib/chunk-BoardArticle.mjs.map +1 -1
- package/dist/lib/chunk-PLUGIN.mjs +6 -0
- package/dist/lib/chunk-PLUGIN.mjs.map +1 -0
- package/dist/lib/chunk-react-surface.mjs +2 -2
- package/dist/lib/chunk-react-surface.mjs.map +1 -1
- package/dist/lib/containers.mjs.map +1 -1
- package/dist/lib/index.mjs +0 -1
- package/dist/lib/meta.mjs.map +1 -1
- package/dist/lib/translations.mjs.map +1 -1
- package/dist/types/src/capabilities/gen/browser.d.ts +6 -0
- package/dist/types/src/capabilities/gen/browser.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/node.d.ts.map +1 -1
- package/dist/types/src/capabilities/gen/tauri.d.ts +6 -0
- package/dist/types/src/capabilities/gen/tauri.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/workerd.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/containers/BoardArticle/BoardArticle.d.ts +1 -1
- package/dist/types/src/containers/BoardArticle/BoardArticle.d.ts.map +1 -1
- package/dist/types/src/containers/BoardArticle/index.d.ts +1 -1
- package/dist/types/src/containers/BoardArticle/index.d.ts.map +1 -1
- package/dist/types/src/containers/index.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Board.d.ts +2 -2
- package/dist/types/src/types/Board.d.ts.map +1 -1
- package/dist/types/src/types/index.d.ts +2 -2
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +34 -32
- package/src/capabilities/gen/browser.ts +31 -0
- package/src/capabilities/gen/node.ts +2 -2
- package/src/capabilities/gen/tauri.ts +31 -0
- package/src/capabilities/gen/workerd.ts +2 -2
- package/src/capabilities/index.ts +3 -3
- package/src/capabilities/react-surface.ts +2 -2
- package/src/containers/BoardArticle/BoardArticle.stories.tsx +5 -5
- package/src/containers/BoardArticle/BoardArticle.tsx +81 -86
- package/src/containers/BoardArticle/index.ts +1 -1
- package/src/containers/index.ts +1 -1
- package/src/index.ts +1 -2
- package/src/meta.ts +1 -1
- package/src/plugin.test.ts +2 -2
- package/src/translations.ts +2 -2
- package/src/types/Board.ts +4 -4
- package/src/types/index.ts +2 -2
package/PLUGIN.mdl
CHANGED
|
@@ -16,9 +16,10 @@ Each extension is defined in the Appendix or resolved via its URI.
|
|
|
16
16
|
|-------------|--------------------------------|
|
|
17
17
|
| `type` | `org.dxos.mdl.type@1.0` |
|
|
18
18
|
| `feat` | `org.dxos.mdl.feat@1.0` |
|
|
19
|
-
| `
|
|
19
|
+
| `scenario` | `org.dxos.mdl.scenario@1.0` |
|
|
20
20
|
| `component` | `org.dxos.mdl.component@1.0` |
|
|
21
21
|
| `op` | `org.dxos.mdl.op@1.0` |
|
|
22
|
+
| `test` | `org.dxos.mdl.test@1.0` |
|
|
22
23
|
|
|
23
24
|
## Types
|
|
24
25
|
|
|
@@ -213,7 +214,7 @@ feat F-6: Attention and Focus
|
|
|
213
214
|
## Acceptance
|
|
214
215
|
|
|
215
216
|
```mdl
|
|
216
|
-
|
|
217
|
+
scenario T-1: New board is empty
|
|
217
218
|
given: a new Board has been created
|
|
218
219
|
then:
|
|
219
220
|
- board.items is empty
|
|
@@ -221,7 +222,7 @@ test T-1: New board is empty
|
|
|
221
222
|
```
|
|
222
223
|
|
|
223
224
|
```mdl
|
|
224
|
-
|
|
225
|
+
scenario T-2: Add item via grid backdrop
|
|
225
226
|
given: an empty board is displayed
|
|
226
227
|
when: user clicks the "+" hotspot at position (100, 200)
|
|
227
228
|
then:
|
|
@@ -231,7 +232,7 @@ test T-2: Add item via grid backdrop
|
|
|
231
232
|
```
|
|
232
233
|
|
|
233
234
|
```mdl
|
|
234
|
-
|
|
235
|
+
scenario T-3: Add existing object via picker
|
|
235
236
|
given: the space contains a Document with label "Meeting notes"
|
|
236
237
|
when: user opens the ObjectPicker from the toolbar and selects "Meeting notes"
|
|
237
238
|
then:
|
|
@@ -241,7 +242,7 @@ test T-3: Add existing object via picker
|
|
|
241
242
|
```
|
|
242
243
|
|
|
243
244
|
```mdl
|
|
244
|
-
|
|
245
|
+
scenario T-4: Remove item
|
|
245
246
|
given: board has one item
|
|
246
247
|
when: user deletes the cell
|
|
247
248
|
then:
|
|
@@ -251,7 +252,7 @@ test T-4: Remove item
|
|
|
251
252
|
```
|
|
252
253
|
|
|
253
254
|
```mdl
|
|
254
|
-
|
|
255
|
+
scenario T-5: Move item
|
|
255
256
|
given: board has one item at position { x: 0, y: 0 }
|
|
256
257
|
when: user drags it to { x: 300, y: 150 }
|
|
257
258
|
then:
|
|
@@ -260,13 +261,61 @@ test T-5: Move item
|
|
|
260
261
|
```
|
|
261
262
|
|
|
262
263
|
```mdl
|
|
263
|
-
|
|
264
|
+
scenario T-6: Toolbar gated by attention
|
|
264
265
|
given: board is rendered without focus
|
|
265
266
|
then: Board.Toolbar is disabled
|
|
266
267
|
when: board gains focus
|
|
267
268
|
then: Board.Toolbar is interactive
|
|
268
269
|
```
|
|
269
270
|
|
|
271
|
+
## QA
|
|
272
|
+
|
|
273
|
+
Executable tests. Each step carries a human instruction (`do`) and, where the step maps to an
|
|
274
|
+
invocable operation, an agent binding (`invoke`). Report failures by test and step number.
|
|
275
|
+
|
|
276
|
+
None of the four `op` blocks above have a runtime counterpart: `createBoard` is `Board.makeBoard`
|
|
277
|
+
(a local factory, not an `Operation.make`) placed via the generic `space.addObject`, and
|
|
278
|
+
`addItem` / `removeItem` / `moveItem` are component actions that call `Obj.update` directly —
|
|
279
|
+
there is nothing to `invoke` for any of them. Every test below is `actors: human` for that reason.
|
|
280
|
+
|
|
281
|
+
```mdl
|
|
282
|
+
test QA-1: Create a board, add and move an item, remove it
|
|
283
|
+
desc: The core board round-trip — create, add an existing object to the canvas, drag it, remove it.
|
|
284
|
+
actors: human
|
|
285
|
+
covers: [F-1.1, F-1.2, F-1.3, F-2.2, F-2.3, F-3.1, F-3.2, F-4.1, F-4.2]
|
|
286
|
+
status: unverified
|
|
287
|
+
given:
|
|
288
|
+
- an identity exists
|
|
289
|
+
- space: a space is open
|
|
290
|
+
- collection: a collection in that space, to create into
|
|
291
|
+
- a Document named "Meeting notes" exists in the space, for the ObjectPicker step
|
|
292
|
+
- no Board named "QA Board" exists — ABORT rather than deleting one this test did not create
|
|
293
|
+
steps:
|
|
294
|
+
- name: Create the board
|
|
295
|
+
do: In the navtree, click + on a collection and choose Board. Name it "QA Board".
|
|
296
|
+
expect: a Board appears under the collection, with an empty canvas
|
|
297
|
+
note: NO `invoke` — `Board.makeBoard` is a local factory, not an operation.
|
|
298
|
+
- name: Add "Meeting notes" via the picker
|
|
299
|
+
do: With no position given, click the toolbar "+"; the ObjectPicker opens listing space
|
|
300
|
+
objects other than the board. Select "Meeting notes".
|
|
301
|
+
expect: a cell for "Meeting notes" appears on the canvas at the default position (0, 0),
|
|
302
|
+
and the picker closes
|
|
303
|
+
note: NO `invoke` — `addItem` mutates `board.items`/`board.layout` directly in the component.
|
|
304
|
+
- name: Drag the cell to a new position
|
|
305
|
+
do: Drag the "Meeting notes" cell to a visibly different spot on the grid.
|
|
306
|
+
expect: the cell renders at the new position; no other cell moves
|
|
307
|
+
note: NO `invoke` — `moveItem` is a drag handler, not an operation.
|
|
308
|
+
after:
|
|
309
|
+
- name: Delete the board
|
|
310
|
+
do: Right-click "QA Board" in the navtree and choose Delete.
|
|
311
|
+
expect: the row leaves the navtree and its plank closes if it was open
|
|
312
|
+
note: |
|
|
313
|
+
NO `invoke` — the board is created by a UI gesture in step 1, which has no operation to
|
|
314
|
+
`capture` it from, so there is no bound value for `space.removeObjects` to name; deletion
|
|
315
|
+
stays human-only like the rest of this test. `space.removeObjects` deletes the Board object
|
|
316
|
+
itself; "Meeting notes" is unaffected — the board only referenced it.
|
|
317
|
+
```
|
|
318
|
+
|
|
270
319
|
---
|
|
271
320
|
|
|
272
321
|
## Appendix: Extension Definitions
|
|
@@ -296,8 +345,8 @@ ext feat
|
|
|
296
345
|
```
|
|
297
346
|
|
|
298
347
|
```mdl
|
|
299
|
-
ext
|
|
300
|
-
uri: org.dxos.mdl.
|
|
348
|
+
ext scenario
|
|
349
|
+
uri: org.dxos.mdl.scenario@1.0
|
|
301
350
|
desc: An acceptance scenario expressed as given / when / then steps.
|
|
302
351
|
fields:
|
|
303
352
|
given?: Step | Step[]
|
package/dist/lib/Board.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
|
|
2
2
|
import { Annotation, DXN, Obj, Ref, Type } from "@dxos/echo";
|
|
3
3
|
import * as Schema from "effect/Schema";
|
|
4
|
-
import { FormInputAnnotation, LabelAnnotation } from "@dxos/echo/Annotation";
|
|
5
4
|
import { BoardLayout, defaultLayout } from "@dxos/react-ui-board/types";
|
|
6
5
|
//#region src/types/Board.ts
|
|
7
6
|
var Board_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -13,12 +12,12 @@ var Board_exports = /* @__PURE__ */ __exportAll({
|
|
|
13
12
|
*/
|
|
14
13
|
var Board = class extends Type.makeObject(DXN.make("org.dxos.type.board", "0.1.0"))(Schema.Struct({
|
|
15
14
|
name: Schema.String.pipe(Schema.optional),
|
|
16
|
-
items: Ref.Ref(Obj.Unknown).pipe(Schema.Array, FormInputAnnotation.set(false)),
|
|
17
|
-
layout: BoardLayout.pipe(FormInputAnnotation.set(false))
|
|
18
|
-
}).pipe(LabelAnnotation.set(["name"]), Annotation.IconAnnotation.set({
|
|
15
|
+
items: Ref.Ref(Obj.Unknown).pipe(Schema.Array, Annotation.FormInputAnnotation.set(false)),
|
|
16
|
+
layout: BoardLayout.pipe(Annotation.FormInputAnnotation.set(false))
|
|
17
|
+
}).pipe(Annotation.LabelAnnotation.set(["name"]), Annotation.IconAnnotation.set({
|
|
19
18
|
icon: "ph--squares-four--regular",
|
|
20
19
|
hue: "green"
|
|
21
|
-
}))) {};
|
|
20
|
+
}), Annotation.UserType.set())) {};
|
|
22
21
|
var makeBoard = (props = {}) => Obj.make(Board, {
|
|
23
22
|
items: [],
|
|
24
23
|
layout: defaultLayout,
|
package/dist/lib/Board.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Board.mjs","names":[],"sources":["../../src/types/Board.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { Annotation, DXN, Obj, Ref, Type } from '@dxos/echo';\nimport {
|
|
1
|
+
{"version":3,"file":"Board.mjs","names":[],"sources":["../../src/types/Board.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { Annotation, DXN, Obj, Ref, Type } from '@dxos/echo';\nimport { BoardLayout, defaultLayout } from '@dxos/react-ui-board/types';\n\n/**\n * Board and layout.\n */\nexport class Board extends Type.makeObject<Board>(DXN.make('org.dxos.type.board', '0.1.0'))(\n Schema.Struct({\n name: Schema.String.pipe(Schema.optional),\n items: Ref.Ref(Obj.Unknown).pipe(Schema.Array, Annotation.FormInputAnnotation.set(false)),\n layout: BoardLayout.pipe(Annotation.FormInputAnnotation.set(false)),\n }).pipe(\n Annotation.LabelAnnotation.set(['name']),\n Annotation.IconAnnotation.set({ icon: 'ph--squares-four--regular', hue: 'green' }),\n Annotation.UserType.set(),\n ),\n) {}\n\nexport const makeBoard = (props: Partial<Obj.MakeProps<typeof Board>> = {}) =>\n Obj.make(Board, {\n items: [],\n layout: defaultLayout,\n ...props,\n });\n"],"mappings":";;;;;;;;;;;;AAYA,IAAa,QAAb,cAA2B,KAAK,WAAkB,IAAI,KAAK,uBAAuB,OAAO,CAAC,CAAC,CACzF,OAAO,OAAO;CACZ,MAAM,OAAO,OAAO,KAAK,OAAO,QAAQ;CACxC,OAAO,IAAI,IAAI,IAAI,OAAO,CAAC,CAAC,KAAK,OAAO,OAAO,WAAW,oBAAoB,IAAI,KAAK,CAAC;CACxF,QAAQ,YAAY,KAAK,WAAW,oBAAoB,IAAI,KAAK,CAAC;AACpE,CAAC,CAAC,CAAC,KACD,WAAW,gBAAgB,IAAI,CAAC,MAAM,CAAC,GACvC,WAAW,eAAe,IAAI;CAAE,MAAM;CAA6B,KAAK;AAAQ,CAAC,GACjF,WAAW,SAAS,IAAI,CAC1B,CACF,CAAC,CAAC,CAAC;AAEH,IAAa,aAAa,QAA8C,CAAC,MACvE,IAAI,KAAK,OAAO;CACd,OAAO,CAAC;CACR,QAAQ;CACR,GAAG;AACL,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { t as PLUGIN_default } from "./chunk-PLUGIN.mjs";
|
|
2
|
+
import * as AppCapability from "@dxos/app-toolkit/AppCapability";
|
|
3
|
+
import * as SpaceCapability from "@dxos/plugin-space/SpaceCapability";
|
|
4
|
+
import { translations } from "@dxos/react-ui-board/translations";
|
|
5
|
+
import { translations as translations$1 } from "#translations";
|
|
6
|
+
import { meta } from "#meta";
|
|
7
|
+
//#region src/capabilities/gen/browser.ts
|
|
8
|
+
var CreateObject = SpaceCapability.createObject(() => import("./chunk-create-object.mjs"));
|
|
9
|
+
var PluginAsset = AppCapability.pluginAsset({
|
|
10
|
+
pluginId: meta.profile.key,
|
|
11
|
+
path: "PLUGIN.mdl",
|
|
12
|
+
content: PLUGIN_default,
|
|
13
|
+
mimeType: "application/x-mdl"
|
|
14
|
+
});
|
|
15
|
+
var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: ["org.dxos.role.article", "org.dxos.role.section"] });
|
|
16
|
+
var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
|
|
17
|
+
var Translations = AppCapability.translations([...translations$1, ...translations]);
|
|
18
|
+
//#endregion
|
|
19
|
+
export { CreateObject, PluginAsset, ReactSurface, Schema, Translations };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=capabilities.browser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.browser.mjs","names":[],"sources":["../../src/capabilities/gen/browser.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'browser' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\nimport { translations as boardTranslations } from '@dxos/react-ui-board/translations';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\nimport pluginSpec from '../../../PLUGIN.mdl?raw';\n\nexport const CreateObject = SpaceCapability.createObject(() => import('../create-object.ts'));\n\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\n\nexport const ReactSurface = AppCapability.surface(() => import('../react-surface.ts'), {\n roles: ['org.dxos.role.article', 'org.dxos.role.section'],\n});\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations([...translations, ...boardTranslations]);\n"],"mappings":";;;;;;;AAeA,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,4BAAsB;AAE5F,IAAa,cAAc,cAAc,YAAY;CACnD,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC;AAED,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAwB,EACrF,OAAO,CAAC,yBAAyB,uBAAuB,EAC1D,CAAC;AAED,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,CAAC,GAAG,gBAAc,GAAG,YAAiB,CAAC"}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
import { t as PLUGIN_default } from "./chunk-PLUGIN.mjs";
|
|
1
2
|
import * as AppCapability from "@dxos/app-toolkit/AppCapability";
|
|
2
3
|
import * as SpaceCapability from "@dxos/plugin-space/SpaceCapability";
|
|
3
4
|
import { translations } from "@dxos/react-ui-board/translations";
|
|
4
5
|
import { translations as translations$1 } from "#translations";
|
|
5
6
|
import { meta } from "#meta";
|
|
6
|
-
//#region PLUGIN.mdl?raw
|
|
7
|
-
var PLUGIN_default = "---\nid: org.dxos.plugin.board\nname: BoardPlugin\nversion: 0.1.0\n---\n\nA spatial canvas plugin for `DXOS` Composer that lets users arrange any ECHO objects on an infinite two-dimensional grid.\nItems can be placed freely, moved by dragging, and removed from the canvas without deleting the underlying objects.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype Position\n fields:\n x: number\n y: number\n w?: number\n h?: number\n```\n\n```mdl\ntype BoardLayout\n fields:\n cells: { [id: string]: Position }\n```\n\n```mdl\ntype Board\n desc: Root ECHO object (typename org.dxos.type.board).\n fields:\n name?: string\n items: Ref[] # ordered list of references to ECHO objects on the canvas\n layout: BoardLayout # position of every item keyed by object id\n```\n\n## Components\n\n```mdl\ncomponent BoardArticle\n desc: Full-canvas surface rendered for article and section roles.\n props:\n role: string # AppSurface role (article | section)\n subject: Board # ECHO Board object\n attendableId?: string # attention tracking id\n state:\n items: Obj.Unknown[] # resolved ECHO objects from board.items refs\n pickerState?: PickerState # null when picker closed; Position when open\n slots:\n toolbar?: ReactNode # Board.Toolbar (disabled when not focused)\n cell?: ReactNode # one Surface.Surface per item (Card role)\n actions:\n addItem(anchor, position?) → void\n deleteItem(id: string) → void\n moveItem(id: string, position: Position) → void\n selectFromPicker(id: string) → void\n emits:\n onAdd(anchor: HTMLButtonElement, position?: Position)\n onDelete(id: string)\n onMove(id: string, position: Position)\n layout: |\n ┌────────────────────────────────────┐\n │ Board.Toolbar (attention-gated) │\n ├────────────────────────────────────┤\n │ Board.Viewport │\n │ ┌──────────────────────────────┐ │\n │ │ Board.Backdrop (+) │ │\n │ │ │ │\n │ │ ┌──────┐ ┌──────┐ │ │\n │ │ │ Cell │ │ Cell │ … │ │\n │ │ └──────┘ └──────┘ │ │\n │ └──────────────────────────────┘ │\n ├────────────────────────────────────┤\n │ ObjectPicker (popover, when open) │\n └────────────────────────────────────┘\n```\n\n## Operations\n\n```mdl\nop createBoard\n desc: Creates a new Board ECHO object and adds it to the target space.\n input:\n name?: string\n target?: Space\n targetNodeId?: string\n output: Board\n effects: [echo:write]\n note: Delegates to SpaceOperation.AddObject with hidden=true.\n```\n\n```mdl\nop addItem\n desc: Adds an ECHO object reference to the board at the given grid position.\n input:\n board: Board\n object: Obj.Unknown\n position: Position\n output: Board\n effects: [echo:write]\n note: |\n When a grid \"+\" is clicked with a position, a new Markdown document is\n created inline. When the toolbar \"+\" is clicked without a position, an\n ObjectPicker is shown and the user selects an existing object.\n```\n\n```mdl\nop removeItem\n desc: Removes an item reference and its layout entry from the board.\n input:\n board: Board\n id: string\n output: Board\n effects: [echo:write]\n note: Does not delete the underlying ECHO object; only removes it from the canvas.\n```\n\n```mdl\nop moveItem\n desc: Updates the grid position for an existing board item.\n input:\n board: Board\n id: string\n position: Position\n output: Board\n effects: [echo:write]\n```\n\n## Features\n\n```mdl\nfeat F-1: Create Board\n\n req F-1.1: A new Board is created with an empty items list and an empty layout.\n req F-1.2: The Board is added to the active space using SpaceOperation.AddObject.\n req F-1.3: The Board appears in the space object list immediately after creation.\n```\n\n```mdl\nfeat F-2: Add Item to Board\n\n req F-2.1:\n when: user clicks \"+\" on the grid backdrop at a specific position\n then: a new Markdown document is created and placed at that position\n\n req F-2.2:\n when: user clicks \"+\" on the toolbar (no position)\n then: ObjectPicker opens, listing all space objects that are not the board itself\n\n req F-2.3:\n when: user selects an object from the ObjectPicker\n then: a Ref to that object is appended to board.items; layout entry created at default position\n\n req F-2.4: Objects are sorted alphabetically by label in the ObjectPicker.\n```\n\n```mdl\nfeat F-3: Remove Item from Board\n\n req F-3.1:\n when: user deletes a cell\n then: the Ref is removed from board.items and the layout entry is deleted\n note: the referenced ECHO object is not deleted\n\n req F-3.2: Board is updated atomically via Obj.update.\n```\n\n```mdl\nfeat F-4: Move Item on Board\n\n req F-4.1:\n when: user drags a card to a new position\n then: board.layout.cells[id] is updated with the new Position\n\n req F-4.2: Other items are not affected by a move operation.\n```\n\n```mdl\nfeat F-5: Render Items\n\n req F-5.1: Each item in board.items is rendered as a Surface.Surface with the Card role.\n req F-5.2:\n when: an item's Ref cannot be resolved (deleted object)\n then: the cell is silently omitted from the rendered canvas\n\n req F-5.3: Items are rendered in board.items order; z-ordering follows index.\n```\n\n```mdl\nfeat F-6: Attention and Focus\n\n req F-6.1:\n when: the Board surface does not have attention\n then: Board.Toolbar is rendered in disabled state; drag and add interactions are suppressed\n\n req F-6.2:\n when: the Board surface gains attention\n then: Board.Toolbar becomes interactive\n```\n\n## Acceptance\n\n```mdl\ntest T-1: New board is empty\n given: a new Board has been created\n then:\n - board.items is empty\n - board.layout.cells is empty\n```\n\n```mdl\ntest T-2: Add item via grid backdrop\n given: an empty board is displayed\n when: user clicks the \"+\" hotspot at position (100, 200)\n then:\n - a Markdown document is created in the space\n - board.items contains a Ref to the new document\n - board.layout.cells[doc.id] === { x: 100, y: 200 }\n```\n\n```mdl\ntest T-3: Add existing object via picker\n given: the space contains a Document with label \"Meeting notes\"\n when: user opens the ObjectPicker from the toolbar and selects \"Meeting notes\"\n then:\n - board.items contains a Ref to the Document\n - board.layout.cells[doc.id] === { x: 0, y: 0 }\n - picker is closed\n```\n\n```mdl\ntest T-4: Remove item\n given: board has one item\n when: user deletes the cell\n then:\n - board.items is empty\n - board.layout.cells is empty\n - the referenced ECHO object still exists in the space\n```\n\n```mdl\ntest T-5: Move item\n given: board has one item at position { x: 0, y: 0 }\n when: user drags it to { x: 300, y: 150 }\n then:\n - board.layout.cells[item.id] === { x: 300, y: 150 }\n - board.items length is unchanged\n```\n\n```mdl\ntest T-6: Toolbar gated by attention\n given: board is rendered without focus\n then: Board.Toolbar is disabled\n when: board gains focus\n then: Board.Toolbar is interactive\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive — the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n";
|
|
8
|
-
//#endregion
|
|
9
7
|
//#region src/capabilities/index.ts
|
|
10
8
|
var CreateObject = SpaceCapability.createObject(() => import("./chunk-create-object.mjs"));
|
|
11
9
|
var PluginAsset = AppCapability.pluginAsset({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities.mjs","names":[],"sources":["../../src/capabilities/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\nimport { translations as boardTranslations } from '@dxos/react-ui-board/translations';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../../PLUGIN.mdl?raw';\n\nexport const CreateObject = SpaceCapability.createObject(() => import('./create-object'));\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\nexport const ReactSurface = AppCapability.surface(() => import('./react-surface'), {\n roles: ['org.dxos.role.article', 'org.dxos.role.section'],\n});\nexport const Schema = AppCapability.schema(() => import('./schema'));\nexport const Translations = AppCapability.translations([...translations, ...boardTranslations]);\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"capabilities.mjs","names":[],"sources":["../../src/capabilities/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\nimport { translations as boardTranslations } from '@dxos/react-ui-board/translations';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../../PLUGIN.mdl?raw';\n\nexport const CreateObject = SpaceCapability.createObject(() => import('./create-object.ts'));\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\nexport const ReactSurface = AppCapability.surface(() => import('./react-surface.ts'), {\n roles: ['org.dxos.role.article', 'org.dxos.role.section'],\n});\nexport const Schema = AppCapability.schema(() => import('./schema.ts'));\nexport const Translations = AppCapability.translations([...translations, ...boardTranslations]);\n"],"mappings":";;;;;;;AAcA,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,4BAAqB;AAC3F,IAAa,cAAc,cAAc,YAAY;CACnD,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC;AACD,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAuB,EACpF,OAAO,CAAC,yBAAyB,uBAAuB,EAC1D,CAAC;AACD,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAc;AACtE,IAAa,eAAe,cAAc,aAAa,CAAC,GAAG,gBAAc,GAAG,YAAiB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities.node.mjs","names":[],"sources":["../../src/capabilities/gen/node.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'node' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\nimport { translations as boardTranslations } from '@dxos/react-ui-board/translations';\n\nimport { translations } from '#translations';\n\nexport const CreateObject = SpaceCapability.createObject(() => import('../create-object'));\n\nexport const Schema = AppCapability.schema(() => import('../schema'));\n\nexport const Translations = AppCapability.translations([...translations, ...boardTranslations]);\n\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;;;AAYA,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,
|
|
1
|
+
{"version":3,"file":"capabilities.node.mjs","names":[],"sources":["../../src/capabilities/gen/node.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'node' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\nimport { translations as boardTranslations } from '@dxos/react-ui-board/translations';\n\nimport { translations } from '#translations';\n\nexport const CreateObject = SpaceCapability.createObject(() => import('../create-object.ts'));\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations([...translations, ...boardTranslations]);\n\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;;;AAYA,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,4BAAsB;AAE5F,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,CAAC,GAAG,gBAAc,GAAG,YAAiB,CAAC;AAE9F,IAAa,cAAc,KAAA;AAC3B,IAAa,eAAe,KAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { t as PLUGIN_default } from "./chunk-PLUGIN.mjs";
|
|
2
|
+
import * as AppCapability from "@dxos/app-toolkit/AppCapability";
|
|
3
|
+
import * as SpaceCapability from "@dxos/plugin-space/SpaceCapability";
|
|
4
|
+
import { translations } from "@dxos/react-ui-board/translations";
|
|
5
|
+
import { translations as translations$1 } from "#translations";
|
|
6
|
+
import { meta } from "#meta";
|
|
7
|
+
//#region src/capabilities/gen/tauri.ts
|
|
8
|
+
var CreateObject = SpaceCapability.createObject(() => import("./chunk-create-object.mjs"));
|
|
9
|
+
var PluginAsset = AppCapability.pluginAsset({
|
|
10
|
+
pluginId: meta.profile.key,
|
|
11
|
+
path: "PLUGIN.mdl",
|
|
12
|
+
content: PLUGIN_default,
|
|
13
|
+
mimeType: "application/x-mdl"
|
|
14
|
+
});
|
|
15
|
+
var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: ["org.dxos.role.article", "org.dxos.role.section"] });
|
|
16
|
+
var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
|
|
17
|
+
var Translations = AppCapability.translations([...translations$1, ...translations]);
|
|
18
|
+
//#endregion
|
|
19
|
+
export { CreateObject, PluginAsset, ReactSurface, Schema, Translations };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=capabilities.tauri.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.tauri.mjs","names":[],"sources":["../../src/capabilities/gen/tauri.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'tauri' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\nimport { translations as boardTranslations } from '@dxos/react-ui-board/translations';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\nimport pluginSpec from '../../../PLUGIN.mdl?raw';\n\nexport const CreateObject = SpaceCapability.createObject(() => import('../create-object.ts'));\n\nexport const PluginAsset = AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n});\n\nexport const ReactSurface = AppCapability.surface(() => import('../react-surface.ts'), {\n roles: ['org.dxos.role.article', 'org.dxos.role.section'],\n});\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations([...translations, ...boardTranslations]);\n"],"mappings":";;;;;;;AAeA,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,4BAAsB;AAE5F,IAAa,cAAc,cAAc,YAAY;CACnD,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC;AAED,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAwB,EACrF,OAAO,CAAC,yBAAyB,uBAAuB,EAC1D,CAAC;AAED,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,CAAC,GAAG,gBAAc,GAAG,YAAiB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities.workerd.mjs","names":[],"sources":["../../src/capabilities/gen/workerd.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'workerd' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\nimport { translations as boardTranslations } from '@dxos/react-ui-board/translations';\n\nimport { translations } from '#translations';\n\nexport const CreateObject = SpaceCapability.createObject(() => import('../create-object'));\n\nexport const Schema = AppCapability.schema(() => import('../schema'));\n\nexport const Translations = AppCapability.translations([...translations, ...boardTranslations]);\n\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;;;AAYA,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,
|
|
1
|
+
{"version":3,"file":"capabilities.workerd.mjs","names":[],"sources":["../../src/capabilities/gen/workerd.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'workerd' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\nimport { translations as boardTranslations } from '@dxos/react-ui-board/translations';\n\nimport { translations } from '#translations';\n\nexport const CreateObject = SpaceCapability.createObject(() => import('../create-object.ts'));\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations([...translations, ...boardTranslations]);\n\nexport const PluginAsset = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;;;AAYA,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,4BAAsB;AAE5F,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,CAAC,GAAG,gBAAc,GAAG,YAAiB,CAAC;AAE9F,IAAa,cAAc,KAAA;AAC3B,IAAa,eAAe,KAAA"}
|
|
@@ -2,18 +2,21 @@ import { translationKey } from "@dxos/react-ui-board/translations";
|
|
|
2
2
|
import { useCallback, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { isNonNullable } from "@dxos/util";
|
|
4
4
|
import { Filter, Obj, Ref } from "@dxos/echo";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as Surface from "@dxos/app-framework/Surface";
|
|
6
|
+
import * as AppSurface from "@dxos/app-toolkit/AppSurface";
|
|
7
7
|
import { useAtomValue } from "@effect/atom-react/Hooks";
|
|
8
|
-
import * as Atom from "effect/
|
|
8
|
+
import * as Atom from "effect/reactivity/Atom";
|
|
9
9
|
import { useObject, useQuery } from "@dxos/echo-react";
|
|
10
10
|
import { invariant } from "@dxos/invariant";
|
|
11
11
|
import { EID } from "@dxos/keys";
|
|
12
12
|
import * as Markdown from "@dxos/plugin-markdown/Markdown";
|
|
13
|
-
import { Panel, Toolbar, useTranslation } from "@dxos/react-ui";
|
|
14
13
|
import { useAttention } from "@dxos/react-ui-attention";
|
|
15
14
|
import { Board, resizeToFit } from "@dxos/react-ui-board";
|
|
16
15
|
import { ObjectPicker } from "@dxos/react-ui-form";
|
|
16
|
+
import * as Button from "@dxos/react-ui/Button";
|
|
17
|
+
import * as Hooks from "@dxos/react-ui/Hooks";
|
|
18
|
+
import * as Panel from "@dxos/react-ui/Panel";
|
|
19
|
+
import * as Toolbar from "@dxos/react-ui/Toolbar";
|
|
17
20
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18
21
|
//#region src/containers/BoardArticle/BoardArticle.tsx
|
|
19
22
|
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-board/src/containers/BoardArticle/BoardArticle.tsx";
|
|
@@ -33,7 +36,7 @@ var normalizeCells = (cells) => {
|
|
|
33
36
|
}]));
|
|
34
37
|
};
|
|
35
38
|
var BoardArticle = ({ role, subject: board, attendableId }) => {
|
|
36
|
-
const { t } = useTranslation(translationKey);
|
|
39
|
+
const { t } = Hooks.useTranslation(translationKey);
|
|
37
40
|
const { hasAttention } = useAttention(attendableId);
|
|
38
41
|
const db = Obj.getDatabase(board);
|
|
39
42
|
const [boardItems] = useObject(board, "items");
|
|
@@ -47,8 +50,6 @@ var BoardArticle = ({ role, subject: board, attendableId }) => {
|
|
|
47
50
|
}), [boardItems]);
|
|
48
51
|
const items = useAtomValue(itemsAtom);
|
|
49
52
|
const controller = useRef(null);
|
|
50
|
-
const addTriggerRef = useRef(null);
|
|
51
|
-
const [pickerState, setPickerState] = useState(null);
|
|
52
53
|
const [zoom, setZoom] = useState(1);
|
|
53
54
|
const layout = useMemo(() => ({ items: normalizeCells(board.layout.cells) }), [board.layout.cells]);
|
|
54
55
|
const bounds = useMemo(() => ({
|
|
@@ -74,7 +75,7 @@ var BoardArticle = ({ role, subject: board, attendableId }) => {
|
|
|
74
75
|
invariant(db, void 0, {
|
|
75
76
|
"~LogMeta": "~LogMeta",
|
|
76
77
|
F: __dxlog_file,
|
|
77
|
-
L:
|
|
78
|
+
L: 112,
|
|
78
79
|
S: void 0,
|
|
79
80
|
A: ["db", ""]
|
|
80
81
|
});
|
|
@@ -95,93 +96,73 @@ var BoardArticle = ({ role, subject: board, attendableId }) => {
|
|
|
95
96
|
});
|
|
96
97
|
}, [board]);
|
|
97
98
|
const handleSelect = useCallback((id) => {
|
|
98
|
-
const position =
|
|
99
|
+
const position = DEFAULT_POSITION;
|
|
99
100
|
const selected = objects.find((obj) => obj.id === id);
|
|
100
101
|
if (!Obj.isObject(selected)) return;
|
|
101
102
|
Obj.update(board, (board) => {
|
|
102
103
|
board.items.push(Ref.make(selected));
|
|
103
104
|
board.layout.cells[selected.id.toString()] = position;
|
|
104
105
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
106
|
+
}, [objects, board]);
|
|
107
|
+
return /* @__PURE__ */ jsx(Board.Root, {
|
|
108
|
+
ref: controller,
|
|
109
|
+
layout,
|
|
110
|
+
bounds,
|
|
111
|
+
mode: "float",
|
|
112
|
+
resolver: resizeToFit,
|
|
113
|
+
zoom,
|
|
114
|
+
onChange: handleChange,
|
|
115
|
+
onAdd: handleAdd,
|
|
116
|
+
onDelete: handleDelete,
|
|
117
|
+
children: /* @__PURE__ */ jsxs(Panel.Root, {
|
|
118
|
+
role,
|
|
119
|
+
children: [/* @__PURE__ */ jsx(Panel.Header, { children: /* @__PURE__ */ jsxs(Toolbar.Root, { children: [
|
|
120
|
+
/* @__PURE__ */ jsx(Button.Root, {
|
|
121
|
+
icon: "ph--crosshair--regular",
|
|
122
|
+
iconOnly: true,
|
|
123
|
+
label: t("move-to-center.button"),
|
|
124
|
+
disabled: !hasAttention,
|
|
125
|
+
onClick: () => controller.current?.center()
|
|
126
|
+
}),
|
|
127
|
+
/* @__PURE__ */ jsx(Button.Root, {
|
|
128
|
+
icon: zoom < 1 ? "ph--arrows-in--regular" : "ph--arrows-out--regular",
|
|
129
|
+
iconOnly: true,
|
|
130
|
+
label: t("toggle-zoom.button"),
|
|
131
|
+
disabled: !hasAttention,
|
|
132
|
+
onClick: () => setZoom((value) => value < 1 ? 1 : .5)
|
|
133
|
+
}),
|
|
134
|
+
/* @__PURE__ */ jsx(ObjectPicker, {
|
|
135
|
+
options,
|
|
136
|
+
onSelect: handleSelect,
|
|
137
|
+
trigger: /* @__PURE__ */ jsx(Button.Root, {
|
|
138
|
+
icon: "ph--plus--regular",
|
|
139
|
+
iconOnly: true,
|
|
140
|
+
label: t("add-object.button"),
|
|
141
|
+
disabled: !hasAttention
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
] }) }), /* @__PURE__ */ jsx(Panel.Body, {
|
|
145
|
+
asChild: true,
|
|
146
|
+
children: /* @__PURE__ */ jsxs(Board.Container, {
|
|
147
|
+
classNames: "dx-cover",
|
|
148
|
+
children: [/* @__PURE__ */ jsxs(Board.Viewport, { children: [/* @__PURE__ */ jsx(Board.Backdrop, {}), /* @__PURE__ */ jsx(Board.Content, { children: items?.map((item) => {
|
|
149
|
+
const itemLayout = layout.items[item.id];
|
|
150
|
+
return itemLayout ? /* @__PURE__ */ jsx(Board.Cell, {
|
|
151
|
+
item,
|
|
152
|
+
layout: itemLayout,
|
|
153
|
+
children: /* @__PURE__ */ jsx(Surface.Surface, {
|
|
154
|
+
type: AppSurface.CardContent,
|
|
155
|
+
data: {
|
|
156
|
+
subject: item,
|
|
157
|
+
editable: true
|
|
158
|
+
},
|
|
159
|
+
limit: 1
|
|
153
160
|
})
|
|
154
|
-
|
|
155
|
-
}), /* @__PURE__ */ jsx(
|
|
156
|
-
asChild: true,
|
|
157
|
-
children: /* @__PURE__ */ jsxs(Board.Container, {
|
|
158
|
-
classNames: "absolute inset-0",
|
|
159
|
-
children: [/* @__PURE__ */ jsxs(Board.Viewport, { children: [/* @__PURE__ */ jsx(Board.Backdrop, {}), /* @__PURE__ */ jsx(Board.Content, { children: items?.map((item) => {
|
|
160
|
-
const itemLayout = layout.items[item.id];
|
|
161
|
-
return itemLayout ? /* @__PURE__ */ jsx(Board.Cell, {
|
|
162
|
-
item,
|
|
163
|
-
layout: itemLayout,
|
|
164
|
-
children: /* @__PURE__ */ jsx(Surface.Surface, {
|
|
165
|
-
type: AppSurface.CardContent,
|
|
166
|
-
data: {
|
|
167
|
-
subject: item,
|
|
168
|
-
editable: true
|
|
169
|
-
},
|
|
170
|
-
limit: 1
|
|
171
|
-
})
|
|
172
|
-
}, item.id) : null;
|
|
173
|
-
}) })] }), /* @__PURE__ */ jsx(Board.Map, { classNames: "absolute bottom-2 right-2 z-10 w-40" })]
|
|
174
|
-
})
|
|
175
|
-
})]
|
|
161
|
+
}, item.id) : null;
|
|
162
|
+
}) })] }), /* @__PURE__ */ jsx(Board.Map, { classNames: "absolute bottom-2 right-2 z-10 w-40" })]
|
|
176
163
|
})
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
options,
|
|
180
|
-
onSelect: handleSelect,
|
|
181
|
-
classNames: "dx-card-popover-width"
|
|
182
|
-
}),
|
|
183
|
-
/* @__PURE__ */ jsx(ObjectPicker.VirtualTrigger, { virtualRef: addTriggerRef })
|
|
184
|
-
]
|
|
164
|
+
})]
|
|
165
|
+
})
|
|
185
166
|
});
|
|
186
167
|
};
|
|
187
168
|
BoardArticle.displayName = "BoardArticle";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-BoardArticle.mjs","names":[],"sources":["../../src/containers/BoardArticle/BoardArticle.tsx"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { useAtomValue } from '@effect/atom-react/Hooks';\nimport * as Atom from 'effect/unstable/reactivity/Atom';\nimport React, { useCallback, useMemo, useRef, useState } from 'react';\n\nimport { Surface } from '@dxos/app-framework/ui';\nimport { AppSurface } from '@dxos/app-toolkit/ui';\nimport { Filter, Obj, Ref } from '@dxos/echo';\nimport { useObject, useQuery } from '@dxos/echo-react';\nimport { invariant } from '@dxos/invariant';\nimport { EID } from '@dxos/keys';\nimport * as Markdown from '@dxos/plugin-markdown/Markdown';\nimport { Panel, Toolbar, useTranslation } from '@dxos/react-ui';\nimport { useAttention } from '@dxos/react-ui-attention';\nimport {\n Board as BoardComponent,\n type BoardController,\n type BoardRootProps,\n type Layout,\n resizeToFit,\n} from '@dxos/react-ui-board';\nimport { translationKey } from '@dxos/react-ui-board/translations';\nimport { ObjectPicker, type ObjectPickerContentProps } from '@dxos/react-ui-form';\nimport { isNonNullable } from '@dxos/util';\n\nimport { Board } from '#types';\n\ntype Position = { x: number; y: number };\n\nconst DEFAULT_POSITION: Position = { x: 0, y: 0 };\n\n// Legacy boards stored cells with a centre origin (signed coords); the engine is 0-based, so shift all\n// cells to non-negative before handing them to the component. Freshly-created boards are already 0-based.\nconst normalizeCells = (cells: Board.Board['layout']['cells']): Layout['items'] => {\n const values = Object.values(cells);\n const minX = Math.min(0, ...values.map((cell) => cell.x));\n const minY = Math.min(0, ...values.map((cell) => cell.y));\n if (minX === 0 && minY === 0) {\n return cells;\n }\n return Object.fromEntries(\n Object.entries(cells).map(([id, cell]) => [id, { ...cell, x: cell.x - minX, y: cell.y - minY }]),\n );\n};\n\nexport type BoardArticleProps = AppSurface.ObjectArticleProps<Board.Board>;\n\nexport const BoardArticle = ({ role, subject: board, attendableId }: BoardArticleProps) => {\n const { t } = useTranslation(translationKey);\n const { hasAttention } = useAttention(attendableId);\n const db = Obj.getDatabase(board);\n const [boardItems] = useObject(board, 'items');\n const itemsAtom = useMemo(\n () =>\n Atom.make((get) => {\n const result: Obj.Unknown[] = [];\n for (const ref of boardItems ?? []) {\n const obj = get(Obj.atomReactive(ref));\n if (obj) {\n result.push(obj);\n }\n }\n return result;\n }),\n [boardItems],\n );\n const items = useAtomValue(itemsAtom);\n\n const controller = useRef<BoardController>(null);\n const addTriggerRef = useRef<HTMLButtonElement | null>(null);\n const [pickerState, setPickerState] = useState<{ position: Position } | null>(null);\n const [zoom, setZoom] = useState(1);\n\n const layout = useMemo<Layout>(() => ({ items: normalizeCells(board.layout.cells) }), [board.layout.cells]);\n const bounds = useMemo(\n () => ({ columns: board.layout.size.width, rows: board.layout.size.height }),\n [board.layout.size.width, board.layout.size.height],\n );\n\n // TODO(burdon): Use search.\n const objects = useQuery(db, Filter.everything());\n const options = useMemo<ObjectPickerContentProps['options']>(\n () =>\n objects\n .filter((obj) => obj.id !== board.id)\n .map((obj) => {\n const label = Obj.getLabel(obj);\n return label ? { id: obj.id, label, hue: 'neutral' as const } : undefined;\n })\n .filter(isNonNullable)\n .sort(({ label: a }, { label: b }) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase())),\n [objects, board.id],\n );\n\n const handleChange = useCallback<NonNullable<BoardRootProps['onChange']>>(\n (next) => {\n Obj.update(board, (board) => {\n board.layout.cells = next.items;\n });\n },\n [board],\n );\n\n // Backdrop \"+\" supplies a position → create a new Markdown document directly.\n const handleAdd = useCallback<NonNullable<BoardRootProps['onAdd']>>(\n (position) => {\n const db = Obj.getDatabase(board);\n invariant(db);\n const doc = db.add(Markdown.make());\n Obj.update(board, (board) => {\n board.items.push(Ref.make(doc));\n board.layout.cells[doc.id.toString()] = position;\n });\n },\n [board],\n );\n\n // TODO(burdon): Use intents so can be undone.\n const handleDelete = useCallback<NonNullable<BoardRootProps['onDelete']>>(\n (id) => {\n // TODO(burdon): Impl. DXN.equals and pass in DXN from `id`.\n const idx = board.items.findIndex((ref) => {\n const echoUri = EID.tryParse(ref.uri);\n return (echoUri ? EID.getEntityId(echoUri) : undefined) === id;\n });\n Obj.update(board, (board) => {\n if (idx !== -1) {\n board.items.splice(idx, 1);\n }\n delete board.layout.cells[id];\n });\n },\n [board],\n );\n\n // Toolbar \"+\" adds an existing object via the picker.\n const handleSelect = useCallback<NonNullable<ObjectPickerContentProps['onSelect']>>(\n (id) => {\n const position = pickerState?.position ?? DEFAULT_POSITION;\n const selected = objects.find((obj) => obj.id === id);\n if (!Obj.isObject(selected)) {\n return;\n }\n Obj.update(board, (board) => {\n board.items.push(Ref.make(selected));\n board.layout.cells[selected.id.toString()] = position;\n });\n setPickerState(null);\n },\n [pickerState, objects, board],\n );\n\n return (\n <ObjectPicker.Root\n open={!!pickerState}\n onOpenChange={(next: boolean) => setPickerState(next ? { position: DEFAULT_POSITION } : null)}\n >\n <BoardComponent.Root\n ref={controller}\n layout={layout}\n bounds={bounds}\n mode='float'\n resolver={resizeToFit}\n zoom={zoom}\n onChange={handleChange}\n onAdd={handleAdd}\n onDelete={handleDelete}\n >\n <Panel.Root role={role}>\n {/* TODO(burdon): Migrate to Menu.Root + useMenuActions (threading attendableId). */}\n <Panel.Toolbar asChild>\n <Toolbar.Root>\n <Toolbar.IconButton\n icon='ph--crosshair--regular'\n iconOnly\n label={t('move-to-center.button')}\n disabled={!hasAttention}\n onClick={() => controller.current?.center()}\n />\n <Toolbar.IconButton\n icon={zoom < 1 ? 'ph--arrows-in--regular' : 'ph--arrows-out--regular'}\n iconOnly\n label={t('toggle-zoom.button')}\n disabled={!hasAttention}\n onClick={() => setZoom((value) => (value < 1 ? 1 : 0.5))}\n />\n <Toolbar.IconButton\n icon='ph--plus--regular'\n iconOnly\n label={t('add-object.button')}\n disabled={!hasAttention}\n onClick={(event) => {\n addTriggerRef.current = event.currentTarget as HTMLButtonElement;\n setPickerState({ position: DEFAULT_POSITION });\n }}\n />\n </Toolbar.Root>\n </Panel.Toolbar>\n <Panel.Content asChild>\n <BoardComponent.Container classNames='absolute inset-0'>\n <BoardComponent.Viewport>\n <BoardComponent.Backdrop />\n <BoardComponent.Content>\n {items?.map((item) => {\n const itemLayout = layout.items[item.id];\n return itemLayout ? (\n <BoardComponent.Cell item={item} key={item.id} layout={itemLayout}>\n <Surface.Surface\n type={AppSurface.CardContent}\n data={{ subject: item, editable: true }}\n limit={1}\n />\n </BoardComponent.Cell>\n ) : null;\n })}\n </BoardComponent.Content>\n </BoardComponent.Viewport>\n {/* Overview map (outlines the visible region), pinned to the corner over the board. */}\n <BoardComponent.Map classNames='absolute bottom-2 right-2 z-10 w-40' />\n </BoardComponent.Container>\n </Panel.Content>\n </Panel.Root>\n </BoardComponent.Root>\n <ObjectPicker.Content options={options} onSelect={handleSelect} classNames='dx-card-popover-width' />\n <ObjectPicker.VirtualTrigger virtualRef={addTriggerRef} />\n </ObjectPicker.Root>\n );\n};\n\nBoardArticle.displayName = 'BoardArticle';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chunk-BoardArticle.mjs","names":[],"sources":["../../src/containers/BoardArticle/BoardArticle.tsx"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { useAtomValue } from '@effect/atom-react/Hooks';\nimport * as Atom from 'effect/reactivity/Atom';\nimport React, { useCallback, useMemo, useRef, useState } from 'react';\n\nimport * as Surface from '@dxos/app-framework/Surface';\nimport * as AppSurface from '@dxos/app-toolkit/AppSurface';\nimport { Filter, Obj, Ref } from '@dxos/echo';\nimport { useObject, useQuery } from '@dxos/echo-react';\nimport { invariant } from '@dxos/invariant';\nimport { EID } from '@dxos/keys';\nimport * as Markdown from '@dxos/plugin-markdown/Markdown';\nimport { useAttention } from '@dxos/react-ui-attention';\nimport {\n Board as BoardComponent,\n type BoardController,\n type BoardRootProps,\n type Layout,\n resizeToFit,\n} from '@dxos/react-ui-board';\nimport { translationKey } from '@dxos/react-ui-board/translations';\nimport { ObjectPicker, type ObjectPickerProps } from '@dxos/react-ui-form';\nimport * as Button from '@dxos/react-ui/Button';\nimport * as Hooks from '@dxos/react-ui/Hooks';\nimport * as Panel from '@dxos/react-ui/Panel';\nimport * as Toolbar from '@dxos/react-ui/Toolbar';\nimport { isNonNullable } from '@dxos/util';\n\nimport { Board } from '#types';\n\ntype Position = { x: number; y: number };\n\nconst DEFAULT_POSITION: Position = { x: 0, y: 0 };\n\n// Legacy boards stored cells with a centre origin (signed coords); the engine is 0-based, so shift all\n// cells to non-negative before handing them to the component. Freshly-created boards are already 0-based.\nconst normalizeCells = (cells: Board.Board['layout']['cells']): Layout['items'] => {\n const values = Object.values(cells);\n const minX = Math.min(0, ...values.map((cell) => cell.x));\n const minY = Math.min(0, ...values.map((cell) => cell.y));\n if (minX === 0 && minY === 0) {\n return cells;\n }\n return Object.fromEntries(\n Object.entries(cells).map(([id, cell]) => [id, { ...cell, x: cell.x - minX, y: cell.y - minY }]),\n );\n};\n\nexport type BoardArticleProps = AppSurface.ObjectArticleProps<Board.Board>;\n\nexport const BoardArticle = ({ role, subject: board, attendableId }: BoardArticleProps) => {\n const { t } = Hooks.useTranslation(translationKey);\n const { hasAttention } = useAttention(attendableId);\n const db = Obj.getDatabase(board);\n const [boardItems] = useObject(board, 'items');\n const itemsAtom = useMemo(\n () =>\n Atom.make((get) => {\n const result: Obj.Unknown[] = [];\n for (const ref of boardItems ?? []) {\n const obj = get(Obj.atomReactive(ref));\n if (obj) {\n result.push(obj);\n }\n }\n return result;\n }),\n [boardItems],\n );\n const items = useAtomValue(itemsAtom);\n\n const controller = useRef<BoardController>(null);\n const [zoom, setZoom] = useState(1);\n\n const layout = useMemo<Layout>(() => ({ items: normalizeCells(board.layout.cells) }), [board.layout.cells]);\n const bounds = useMemo(\n () => ({ columns: board.layout.size.width, rows: board.layout.size.height }),\n [board.layout.size.width, board.layout.size.height],\n );\n\n // TODO(burdon): Use search.\n const objects = useQuery(db, Filter.everything());\n const options = useMemo<ObjectPickerProps['options']>(\n () =>\n objects\n .filter((obj) => obj.id !== board.id)\n .map((obj) => {\n const label = Obj.getLabel(obj);\n return label ? { id: obj.id, label, hue: 'neutral' as const } : undefined;\n })\n .filter(isNonNullable)\n .sort(({ label: a }, { label: b }) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase())),\n [objects, board.id],\n );\n\n const handleChange = useCallback<NonNullable<BoardRootProps['onChange']>>(\n (next) => {\n Obj.update(board, (board) => {\n board.layout.cells = next.items;\n });\n },\n [board],\n );\n\n // Backdrop \"+\" supplies a position → create a new Markdown document directly.\n const handleAdd = useCallback<NonNullable<BoardRootProps['onAdd']>>(\n (position) => {\n const db = Obj.getDatabase(board);\n invariant(db);\n const doc = db.add(Markdown.make());\n Obj.update(board, (board) => {\n board.items.push(Ref.make(doc));\n board.layout.cells[doc.id.toString()] = position;\n });\n },\n [board],\n );\n\n // TODO(burdon): Use intents so can be undone.\n const handleDelete = useCallback<NonNullable<BoardRootProps['onDelete']>>(\n (id) => {\n // TODO(burdon): Impl. DXN.equals and pass in DXN from `id`.\n const idx = board.items.findIndex((ref) => {\n const echoUri = EID.tryParse(ref.uri);\n return (echoUri ? EID.getEntityId(echoUri) : undefined) === id;\n });\n Obj.update(board, (board) => {\n if (idx !== -1) {\n board.items.splice(idx, 1);\n }\n delete board.layout.cells[id];\n });\n },\n [board],\n );\n\n // Toolbar \"+\" adds an existing object via the picker.\n const handleSelect = useCallback(\n (id: string | undefined) => {\n const position = DEFAULT_POSITION;\n const selected = objects.find((obj) => obj.id === id);\n if (!Obj.isObject(selected)) {\n return;\n }\n Obj.update(board, (board) => {\n board.items.push(Ref.make(selected));\n board.layout.cells[selected.id.toString()] = position;\n });\n },\n [objects, board],\n );\n\n return (\n <BoardComponent.Root\n ref={controller}\n layout={layout}\n bounds={bounds}\n mode='float'\n resolver={resizeToFit}\n zoom={zoom}\n onChange={handleChange}\n onAdd={handleAdd}\n onDelete={handleDelete}\n >\n <Panel.Root role={role}>\n {/* TODO(burdon): Migrate to Menu.Root + useMenuActions (threading attendableId). */}\n <Panel.Header>\n <Toolbar.Root>\n <Button.Root\n icon='ph--crosshair--regular'\n iconOnly\n label={t('move-to-center.button')}\n disabled={!hasAttention}\n onClick={() => controller.current?.center()}\n />\n <Button.Root\n icon={zoom < 1 ? 'ph--arrows-in--regular' : 'ph--arrows-out--regular'}\n iconOnly\n label={t('toggle-zoom.button')}\n disabled={!hasAttention}\n onClick={() => setZoom((value) => (value < 1 ? 1 : 0.5))}\n />\n <ObjectPicker\n options={options}\n onSelect={handleSelect}\n trigger={\n <Button.Root\n icon='ph--plus--regular'\n iconOnly\n label={t('add-object.button')}\n disabled={!hasAttention}\n />\n }\n />\n </Toolbar.Root>\n </Panel.Header>\n <Panel.Body asChild>\n <BoardComponent.Container classNames='dx-cover'>\n <BoardComponent.Viewport>\n <BoardComponent.Backdrop />\n <BoardComponent.Content>\n {items?.map((item) => {\n const itemLayout = layout.items[item.id];\n return itemLayout ? (\n <BoardComponent.Cell item={item} key={item.id} layout={itemLayout}>\n <Surface.Surface\n type={AppSurface.CardContent}\n data={{ subject: item, editable: true }}\n limit={1}\n />\n </BoardComponent.Cell>\n ) : null;\n })}\n </BoardComponent.Content>\n </BoardComponent.Viewport>\n {/* Overview map (outlines the visible region), pinned to the corner over the board. */}\n <BoardComponent.Map classNames='absolute bottom-2 right-2 z-10 w-40' />\n </BoardComponent.Container>\n </Panel.Body>\n </Panel.Root>\n </BoardComponent.Root>\n );\n};\n\nBoardArticle.displayName = 'BoardArticle';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region PLUGIN.mdl?raw
|
|
2
|
+
var PLUGIN_default = "---\nid: org.dxos.plugin.board\nname: BoardPlugin\nversion: 0.1.0\n---\n\nA spatial canvas plugin for `DXOS` Composer that lets users arrange any ECHO objects on an infinite two-dimensional grid.\nItems can be placed freely, moved by dragging, and removed from the canvas without deleting the underlying objects.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `scenario` | `org.dxos.mdl.scenario@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n\n## Types\n\n```mdl\ntype Position\n fields:\n x: number\n y: number\n w?: number\n h?: number\n```\n\n```mdl\ntype BoardLayout\n fields:\n cells: { [id: string]: Position }\n```\n\n```mdl\ntype Board\n desc: Root ECHO object (typename org.dxos.type.board).\n fields:\n name?: string\n items: Ref[] # ordered list of references to ECHO objects on the canvas\n layout: BoardLayout # position of every item keyed by object id\n```\n\n## Components\n\n```mdl\ncomponent BoardArticle\n desc: Full-canvas surface rendered for article and section roles.\n props:\n role: string # AppSurface role (article | section)\n subject: Board # ECHO Board object\n attendableId?: string # attention tracking id\n state:\n items: Obj.Unknown[] # resolved ECHO objects from board.items refs\n pickerState?: PickerState # null when picker closed; Position when open\n slots:\n toolbar?: ReactNode # Board.Toolbar (disabled when not focused)\n cell?: ReactNode # one Surface.Surface per item (Card role)\n actions:\n addItem(anchor, position?) → void\n deleteItem(id: string) → void\n moveItem(id: string, position: Position) → void\n selectFromPicker(id: string) → void\n emits:\n onAdd(anchor: HTMLButtonElement, position?: Position)\n onDelete(id: string)\n onMove(id: string, position: Position)\n layout: |\n ┌────────────────────────────────────┐\n │ Board.Toolbar (attention-gated) │\n ├────────────────────────────────────┤\n │ Board.Viewport │\n │ ┌──────────────────────────────┐ │\n │ │ Board.Backdrop (+) │ │\n │ │ │ │\n │ │ ┌──────┐ ┌──────┐ │ │\n │ │ │ Cell │ │ Cell │ … │ │\n │ │ └──────┘ └──────┘ │ │\n │ └──────────────────────────────┘ │\n ├────────────────────────────────────┤\n │ ObjectPicker (popover, when open) │\n └────────────────────────────────────┘\n```\n\n## Operations\n\n```mdl\nop createBoard\n desc: Creates a new Board ECHO object and adds it to the target space.\n input:\n name?: string\n target?: Space\n targetNodeId?: string\n output: Board\n effects: [echo:write]\n note: Delegates to SpaceOperation.AddObject with hidden=true.\n```\n\n```mdl\nop addItem\n desc: Adds an ECHO object reference to the board at the given grid position.\n input:\n board: Board\n object: Obj.Unknown\n position: Position\n output: Board\n effects: [echo:write]\n note: |\n When a grid \"+\" is clicked with a position, a new Markdown document is\n created inline. When the toolbar \"+\" is clicked without a position, an\n ObjectPicker is shown and the user selects an existing object.\n```\n\n```mdl\nop removeItem\n desc: Removes an item reference and its layout entry from the board.\n input:\n board: Board\n id: string\n output: Board\n effects: [echo:write]\n note: Does not delete the underlying ECHO object; only removes it from the canvas.\n```\n\n```mdl\nop moveItem\n desc: Updates the grid position for an existing board item.\n input:\n board: Board\n id: string\n position: Position\n output: Board\n effects: [echo:write]\n```\n\n## Features\n\n```mdl\nfeat F-1: Create Board\n\n req F-1.1: A new Board is created with an empty items list and an empty layout.\n req F-1.2: The Board is added to the active space using SpaceOperation.AddObject.\n req F-1.3: The Board appears in the space object list immediately after creation.\n```\n\n```mdl\nfeat F-2: Add Item to Board\n\n req F-2.1:\n when: user clicks \"+\" on the grid backdrop at a specific position\n then: a new Markdown document is created and placed at that position\n\n req F-2.2:\n when: user clicks \"+\" on the toolbar (no position)\n then: ObjectPicker opens, listing all space objects that are not the board itself\n\n req F-2.3:\n when: user selects an object from the ObjectPicker\n then: a Ref to that object is appended to board.items; layout entry created at default position\n\n req F-2.4: Objects are sorted alphabetically by label in the ObjectPicker.\n```\n\n```mdl\nfeat F-3: Remove Item from Board\n\n req F-3.1:\n when: user deletes a cell\n then: the Ref is removed from board.items and the layout entry is deleted\n note: the referenced ECHO object is not deleted\n\n req F-3.2: Board is updated atomically via Obj.update.\n```\n\n```mdl\nfeat F-4: Move Item on Board\n\n req F-4.1:\n when: user drags a card to a new position\n then: board.layout.cells[id] is updated with the new Position\n\n req F-4.2: Other items are not affected by a move operation.\n```\n\n```mdl\nfeat F-5: Render Items\n\n req F-5.1: Each item in board.items is rendered as a Surface.Surface with the Card role.\n req F-5.2:\n when: an item's Ref cannot be resolved (deleted object)\n then: the cell is silently omitted from the rendered canvas\n\n req F-5.3: Items are rendered in board.items order; z-ordering follows index.\n```\n\n```mdl\nfeat F-6: Attention and Focus\n\n req F-6.1:\n when: the Board surface does not have attention\n then: Board.Toolbar is rendered in disabled state; drag and add interactions are suppressed\n\n req F-6.2:\n when: the Board surface gains attention\n then: Board.Toolbar becomes interactive\n```\n\n## Acceptance\n\n```mdl\nscenario T-1: New board is empty\n given: a new Board has been created\n then:\n - board.items is empty\n - board.layout.cells is empty\n```\n\n```mdl\nscenario T-2: Add item via grid backdrop\n given: an empty board is displayed\n when: user clicks the \"+\" hotspot at position (100, 200)\n then:\n - a Markdown document is created in the space\n - board.items contains a Ref to the new document\n - board.layout.cells[doc.id] === { x: 100, y: 200 }\n```\n\n```mdl\nscenario T-3: Add existing object via picker\n given: the space contains a Document with label \"Meeting notes\"\n when: user opens the ObjectPicker from the toolbar and selects \"Meeting notes\"\n then:\n - board.items contains a Ref to the Document\n - board.layout.cells[doc.id] === { x: 0, y: 0 }\n - picker is closed\n```\n\n```mdl\nscenario T-4: Remove item\n given: board has one item\n when: user deletes the cell\n then:\n - board.items is empty\n - board.layout.cells is empty\n - the referenced ECHO object still exists in the space\n```\n\n```mdl\nscenario T-5: Move item\n given: board has one item at position { x: 0, y: 0 }\n when: user drags it to { x: 300, y: 150 }\n then:\n - board.layout.cells[item.id] === { x: 300, y: 150 }\n - board.items length is unchanged\n```\n\n```mdl\nscenario T-6: Toolbar gated by attention\n given: board is rendered without focus\n then: Board.Toolbar is disabled\n when: board gains focus\n then: Board.Toolbar is interactive\n```\n\n## QA\n\nExecutable tests. Each step carries a human instruction (`do`) and, where the step maps to an\ninvocable operation, an agent binding (`invoke`). Report failures by test and step number.\n\nNone of the four `op` blocks above have a runtime counterpart: `createBoard` is `Board.makeBoard`\n(a local factory, not an `Operation.make`) placed via the generic `space.addObject`, and\n`addItem` / `removeItem` / `moveItem` are component actions that call `Obj.update` directly —\nthere is nothing to `invoke` for any of them. Every test below is `actors: human` for that reason.\n\n```mdl\ntest QA-1: Create a board, add and move an item, remove it\n desc: The core board round-trip — create, add an existing object to the canvas, drag it, remove it.\n actors: human\n covers: [F-1.1, F-1.2, F-1.3, F-2.2, F-2.3, F-3.1, F-3.2, F-4.1, F-4.2]\n status: unverified\n given:\n - an identity exists\n - space: a space is open\n - collection: a collection in that space, to create into\n - a Document named \"Meeting notes\" exists in the space, for the ObjectPicker step\n - no Board named \"QA Board\" exists — ABORT rather than deleting one this test did not create\n steps:\n - name: Create the board\n do: In the navtree, click + on a collection and choose Board. Name it \"QA Board\".\n expect: a Board appears under the collection, with an empty canvas\n note: NO `invoke` — `Board.makeBoard` is a local factory, not an operation.\n - name: Add \"Meeting notes\" via the picker\n do: With no position given, click the toolbar \"+\"; the ObjectPicker opens listing space\n objects other than the board. Select \"Meeting notes\".\n expect: a cell for \"Meeting notes\" appears on the canvas at the default position (0, 0),\n and the picker closes\n note: NO `invoke` — `addItem` mutates `board.items`/`board.layout` directly in the component.\n - name: Drag the cell to a new position\n do: Drag the \"Meeting notes\" cell to a visibly different spot on the grid.\n expect: the cell renders at the new position; no other cell moves\n note: NO `invoke` — `moveItem` is a drag handler, not an operation.\n after:\n - name: Delete the board\n do: Right-click \"QA Board\" in the navtree and choose Delete.\n expect: the row leaves the navtree and its plank closes if it was open\n note: |\n NO `invoke` — the board is created by a UI gesture in step 1, which has no operation to\n `capture` it from, so there is no bound value for `space.removeObjects` to name; deletion\n stays human-only like the rest of this test. `space.removeObjects` deletes the Board object\n itself; \"Meeting notes\" is unaffected — the board only referenced it.\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive — the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next scenario\n uri: org.dxos.mdl.scenario@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n";
|
|
3
|
+
//#endregion
|
|
4
|
+
export { PLUGIN_default as t };
|
|
5
|
+
|
|
6
|
+
//# sourceMappingURL=chunk-PLUGIN.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-PLUGIN.mjs","names":[],"sources":[],"mappings":""}
|
|
@@ -2,8 +2,8 @@ import { Board } from "#types";
|
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import * as Capability from "@dxos/app-framework/Capability";
|
|
4
4
|
import * as Capabilities from "@dxos/app-framework/Capabilities";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as Surface from "@dxos/app-framework/Surface";
|
|
6
|
+
import * as AppSurface from "@dxos/app-toolkit/AppSurface";
|
|
7
7
|
import { BoardArticle } from "#containers";
|
|
8
8
|
//#region src/capabilities/react-surface.ts
|
|
9
9
|
var react_surface_default = Capability.makeModule(() => Effect.succeed(Capability.contribute(Capabilities.ReactSurface, [Surface.create({
|