@lupinum/board-core 1.0.0-beta.2 → 1.0.0-beta.4

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 (51) hide show
  1. package/README.md +24 -0
  2. package/dist/agent/AGENTS.md +63 -0
  3. package/dist/agent/manifest.json +320 -0
  4. package/dist/agent/pages/docs/build-features/connections.md +241 -0
  5. package/dist/agent/pages/docs/build-features/custom-node-renderers.md +219 -0
  6. package/dist/agent/pages/docs/build-features/groups-and-nesting.md +172 -0
  7. package/dist/agent/pages/docs/build-features/performance.md +76 -0
  8. package/dist/agent/pages/docs/build-features/read-only-and-command-guards.md +52 -0
  9. package/dist/agent/pages/docs/build-features/save-and-load.md +142 -0
  10. package/dist/agent/pages/docs/build-features/selection-and-keyboard.md +145 -0
  11. package/dist/agent/pages/docs/build-features/ssr-and-deterministic-state.md +67 -0
  12. package/dist/agent/pages/docs/build-features/theming.md +102 -0
  13. package/dist/agent/pages/docs/build-features/undo-and-redo.md +124 -0
  14. package/dist/agent/pages/docs/evaluate/design-decisions.md +44 -0
  15. package/dist/agent/pages/docs/evaluate/how-nuxt-board-works.md +41 -0
  16. package/dist/agent/pages/docs/evaluate/why-nuxt-board.md +61 -0
  17. package/dist/agent/pages/docs/project/contributing.md +101 -0
  18. package/dist/agent/pages/docs/project/support-and-security.md +40 -0
  19. package/dist/agent/pages/docs/reference/board-core-types.md +82 -0
  20. package/dist/agent/pages/docs/reference/board-core.md +261 -0
  21. package/dist/agent/pages/docs/reference/connections.md +531 -0
  22. package/dist/agent/pages/docs/reference/events-and-errors.md +158 -0
  23. package/dist/agent/pages/docs/reference/glossary.md +58 -0
  24. package/dist/agent/pages/docs/reference/history.md +193 -0
  25. package/dist/agent/pages/docs/reference/minimap.md +157 -0
  26. package/dist/agent/pages/docs/reference/nuxt-board.md +148 -0
  27. package/dist/agent/pages/docs/reference/package-overview.md +58 -0
  28. package/dist/agent/pages/docs/reference/vue-board.md +424 -0
  29. package/dist/agent/pages/docs/reference/vue-composables.md +293 -0
  30. package/dist/agent/pages/docs/solutions/mind-map.md +104 -0
  31. package/dist/agent/pages/docs/solutions/nuxt-application.md +47 -0
  32. package/dist/agent/pages/docs/solutions/planning-board.md +51 -0
  33. package/dist/agent/pages/docs/solutions/read-only-viewer.md +61 -0
  34. package/dist/agent/pages/docs/solutions/workflow-builder.md +124 -0
  35. package/dist/agent/pages/docs/start-building/add-connections-and-history.md +42 -0
  36. package/dist/agent/pages/docs/start-building/customize-your-first-node.md +38 -0
  37. package/dist/agent/pages/docs/start-building/installation.md +76 -0
  38. package/dist/agent/pages/docs/start-building/your-first-board.md +52 -0
  39. package/dist/agent/pages/docs/understand-the-system/camera-and-coordinates.md +22 -0
  40. package/dist/agent/pages/docs/understand-the-system/commands-and-transactions.md +31 -0
  41. package/dist/agent/pages/docs/understand-the-system/document-and-session-state.md +25 -0
  42. package/dist/agent/pages/docs/understand-the-system/nodes-and-hierarchy.md +24 -0
  43. package/dist/agent/pages/docs/understand-the-system/packages-and-plugins.md +29 -0
  44. package/dist/agent/pages/docs/understand-the-system/persistence-and-json-canvas.md +25 -0
  45. package/dist/agent/pages/docs/understand-the-system/rendering-and-interaction.md +22 -0
  46. package/dist/agent/pages/docs/understand-the-system/the-engine.md +28 -0
  47. package/dist/agent/pages/docs.md +18 -0
  48. package/dist/engine/transaction.d.ts +2 -2
  49. package/dist/index.js +18 -7
  50. package/dist/types.d.ts +2 -2
  51. package/package.json +3 -2
@@ -0,0 +1,424 @@
1
+ ---
2
+ title: "@lupinum/vue-board"
3
+ description: "Vue 3 components for rendering interactive boards."
4
+ url: "https://nuxt-board.lupinum.com/docs/reference/vue-board"
5
+ route: "/docs/reference/vue-board"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # @lupinum/vue-board
13
+
14
+ > Vue 3 components for rendering interactive boards.
15
+
16
+ ## Install
17
+
18
+ <code-group>
19
+ ```bash [pnpm]
20
+ pnpm add @lupinum/vue-board @lupinum/board-core
21
+ ```
22
+
23
+ ```bash [npm]
24
+ npm install @lupinum/vue-board @lupinum/board-core
25
+ ```
26
+ </code-group>
27
+
28
+ ---
29
+
30
+ ## BoardRoot
31
+
32
+ The root component that renders the board. It subscribes to the engine, handles all pointer/keyboard events, and provides context to child components and composables.
33
+
34
+ ### Props
35
+
36
+ | Name | Type | Default | Description |
37
+ | --- | --- | --- | --- |
38
+ | `engine` | `BoardEngine` | Auto-created | The board engine instance. If omitted, a default engine is created internally. |
39
+ | `cullMargin` | `number` | `200` | World-space margin around the viewport for node culling. Nodes outside this margin are not rendered. |
40
+ | `grid` | `boolean \| BoardGridOptions` | `true` | Grid configuration. `true` uses engine defaults, `false` hides the grid, or pass an object for fine control. |
41
+ | `selectionToolbar` | `boolean` | `true` | Show the bundled selection toolbar. |
42
+ | `snapGuides` | `boolean` | `true` | Show alignment guides during drag and resize. |
43
+ | `boxSelect` | `boolean` | `true` | Show the bundled box-selection overlay. |
44
+ | `renderers` | `BoardRendererRegistry` | `{}` | Maps supported JSON Canvas node types to Vue components for custom rendering. |
45
+ | `fallbackRenderer` | `Component \| null` | `null` | Fallback renderer used when no type-specific renderer or slot matches. |
46
+
47
+ Treat the `engine` instance as stable after mount. To replace board contents, keep the same engine and load the new document through engine commands such as `loadDocument`.
48
+
49
+ ### Emits
50
+
51
+ | Event | Payload | Description |
52
+ | --- | --- | --- |
53
+ | `ready` | `engine: BoardEngine` | Fired after the root element is mounted and the viewport is measured. |
54
+
55
+ ### Slots
56
+
57
+ #### default
58
+
59
+ The default slot renders after the board viewport, on top of the canvas. It receives the engine, runtime state, and debug state.
60
+
61
+ ```vue
62
+ <BoardRoot :engine="engine">
63
+ <template #default="{ engine, state, debugState }">
64
+ <MyToolbar :engine="engine" />
65
+ <MyMinimap />
66
+ </template>
67
+ </BoardRoot>
68
+ ```
69
+
70
+ | Prop | Type | Description |
71
+ | --- | --- | --- |
72
+ | `engine` | `BoardEngine` | The engine instance. |
73
+ | `state` | `BoardState` | Current immutable runtime state. |
74
+ | `debugState` | `object` | Debug info: camera, grid, selection, interaction, visibleNodeCount, last 20 trace entries. |
75
+
76
+ #### viewport
77
+
78
+ Renders inside the viewport transform layer (world-space). Use this for custom overlays that should move with the canvas.
79
+
80
+ ```vue
81
+ <BoardRoot :engine="engine">
82
+ <template #viewport>
83
+ <MyWorldOverlay />
84
+ </template>
85
+ </BoardRoot>
86
+ ```
87
+
88
+ #### node
89
+
90
+ Fallback slot for node content when no type-specific slot or renderer matches.
91
+
92
+ ```vue
93
+ <BoardRoot :engine="engine">
94
+ <template #node="{ node, selected, editing, beginEdit, commitText }">
95
+ <div>{{ node.text }}</div>
96
+ </template>
97
+ </BoardRoot>
98
+ ```
99
+
100
+ | Prop | Type | Description |
101
+ | --- | --- | --- |
102
+ | `node` | `BoardNode` | The node being rendered. |
103
+ | `selected` | `boolean` | Whether the node is selected. |
104
+ | `editing` | `boolean` | Whether the node is in text editing mode. |
105
+ | `beginEdit` | `() => void` | Start editing for a text node; otherwise no-op. |
106
+ | `commitText` | `(text: string) => void` | Commit a text-node edit; otherwise no-op. |
107
+
108
+ #### node:{type}
109
+
110
+ Type-specific named slot. Takes precedence over the `node` slot and renderer registry.
111
+
112
+ ```vue
113
+ <BoardRoot :engine="engine">
114
+ <template #node:file="{ node, selected }">
115
+ <img :src="node.file" :alt="node.label" />
116
+ </template>
117
+ <template #node:text="{ node }">
118
+ <p>{{ node.text }}</p>
119
+ </template>
120
+ </BoardRoot>
121
+ ```
122
+
123
+ #### handle
124
+
125
+ Custom resize handle slot. Rendered for each of the 8 compass handles when a node is selected.
126
+
127
+ ```vue
128
+ <BoardRoot :engine="engine">
129
+ <template #handle="{ node, handle }">
130
+ <div class="my-handle" :data-resize="handle">●</div>
131
+ </template>
132
+ </BoardRoot>
133
+ ```
134
+
135
+ #### box-select
136
+
137
+ Custom box selection overlay.
138
+
139
+ ```vue
140
+ <BoardRoot :engine="engine">
141
+ <template #box-select="{ bounds }">
142
+ <div class="my-box-select" :style="boxStyle(bounds)" />
143
+ </template>
144
+ </BoardRoot>
145
+ ```
146
+
147
+ ### CSS custom properties
148
+
149
+ Set these on `.board-root` to theme the entire board:
150
+
151
+ | Property | Default | Description |
152
+ | --- | --- | --- |
153
+ | `--board-bg` | `#fbfbfd` | Board UI background token. |
154
+ | `--board-canvas-bg` | `#f5f6fa` | Canvas background color. |
155
+ | `--board-fg` | `#14161f` | Board foreground / text color. |
156
+ | `--board-node-bg` | `#fff` | Node background color. |
157
+ | `--board-node-border` | `#e4e5ee` | Node border color. |
158
+ | `--board-node-color` | unset | Resolved border color for a node with `node.color`. |
159
+ | `--board-node-tint` | unset | Resolved tinted background for a node with `node.color`. |
160
+ | `--board-node-color-soft` | unset | Softer resolved color for group borders and subtle UI surfaces. |
161
+ | `--board-node-color-ring` | unset | Resolved selection outline color for a node with `node.color`. |
162
+ | `--board-accent` | `#6366e8` | Accent color (snap guides, selections). |
163
+ | `--board-edge-color` | `rgba(91, 96, 117, 0.72)` | Default edge color. |
164
+ | `--board-handle-shadow` | `rgba(9, 14, 28, 0.1)` | Shadow on resize handles. |
165
+ | `--board-zoom` | _(auto)_ | Set automatically by `BoardViewport` to the current zoom level. |
166
+
167
+ ```css
168
+ /* Dark theme example */
169
+ .board-root {
170
+ --board-bg: #1e293b;
171
+ --board-fg: #f1f5f9;
172
+ --board-node-bg: #334155;
173
+ --board-node-border: rgba(241, 245, 249, 0.15);
174
+ --board-accent: #22d3ee;
175
+ }
176
+ ```
177
+
178
+ ### Level of detail (LOD)
179
+
180
+ `BoardRoot` automatically adjusts rendering based on screen-space size:
181
+
182
+ | LOD | Condition | Rendering |
183
+ | --- | --- | --- |
184
+ | `full` | Selected, or screen size >= 96px | Full node content, handles, custom renderers. |
185
+ | `simple` | Screen size 6–96px | Minimal placeholder that preserves color and group shape. |
186
+ | `hidden` | Screen size < 6px | Not rendered at all. |
187
+
188
+ ### Keyboard shortcuts
189
+
190
+ `BoardRoot` handles these keyboard shortcuts out of the box:
191
+
192
+ | Key | Action |
193
+ | --- | --- |
194
+ | `Escape` | Clear selection and end interaction. |
195
+ | `Delete` / `Backspace` | Delete selected nodes. |
196
+ | `Enter` | Begin text editing on the selected text node. |
197
+ | `Ctrl/Cmd + A` | Select all visible nodes. |
198
+ | `Ctrl/Cmd + D` | Duplicate selected nodes. |
199
+ | `Ctrl/Cmd + C` | Copy selected nodes. |
200
+ | `Ctrl/Cmd + V` | Paste from clipboard. |
201
+ | `Ctrl/Cmd + 0` | Zoom to 100%. |
202
+ | `Ctrl/Cmd + 1` | Zoom to fit all nodes. |
203
+ | `Arrow keys` | Nudge selected nodes by grid step. |
204
+ | `Shift + Arrow keys` | Nudge by major grid step. |
205
+ | `Alt/Option + drag` | Duplicate the current selection and drag the clone. |
206
+ | `Shift + drag` | Lock dragging to the dominant axis. |
207
+ | `Space` while dragging/resizing | Temporarily bypass snapping while the pointer is active. |
208
+ | `Space + drag` from idle | Pan the canvas. |
209
+ | `Middle mouse drag` | Pan the canvas. |
210
+ | `Scroll wheel` | Pan the canvas. |
211
+ | `Shift + scroll` | Pan horizontally. |
212
+ | `Ctrl/Cmd + scroll` | Zoom at cursor. |
213
+ | `Space + scroll` | Zoom at cursor. |
214
+ | `Double-click empty area` | Create a new text node. |
215
+ | `Double-click text node` | Begin text editing. |
216
+ | `Pinch (touch)` | Zoom in/out. |
217
+
218
+ Undo and redo shortcuts are opt-in through `BoardHistoryShortcuts` from
219
+ `@lupinum/board-history/vue`. Render it inside the owning `BoardRoot` and pass
220
+ `:history="engine.plugins.history"`.
221
+
222
+ ---
223
+
224
+ ## BoardSelectionToolbar
225
+
226
+ Floating toolbar rendered by `BoardRoot` for selected nodes. It is also exported for custom board shells.
227
+
228
+ The toolbar appears above the selected node bounds when the board is idle. It hides during node drag, resize, and text editing.
229
+
230
+ Built-in actions:
231
+
232
+ | Action | Behavior |
233
+ | --- | --- |
234
+ | Remove | Deletes unlocked selected nodes. |
235
+ | Set colour | Opens the preset palette and applies the chosen color to every unlocked selected node. |
236
+ | Zoom to selection | Fits the selected nodes in the viewport. |
237
+ | Edit | Starts text editing for a single selected text node. |
238
+
239
+ Palette options apply `BoardColorPreset` IDs, which are one supported form of `BoardNode.color`.
240
+
241
+ ---
242
+
243
+ ## Node color helpers
244
+
245
+ `@lupinum/vue-board` exports the shared color helpers for custom renderers.
246
+
247
+ ```ts
248
+ import {
249
+ BOARD_COLOR_PRESETS,
250
+ colorForPreset,
251
+ resolveNodeColorStyle,
252
+ } from '@lupinum/vue-board'
253
+ ```
254
+
255
+ Use `resolveNodeColorStyle(node.color)` when custom wrappers need the same CSS variables as the built-in renderer.
256
+
257
+ ```vue
258
+ <template>
259
+ <article class="task-card" :style="resolveNodeColorStyle(node.color)">
260
+ {{ node.text }}
261
+ </article>
262
+ </template>
263
+ ```
264
+
265
+ `resolveNodeColorStyle()` returns an empty style object when the node has no color.
266
+
267
+ ---
268
+
269
+ ## BoardViewport
270
+
271
+ Applies the camera transform (scale + translate) to child content. All children render in world-space coordinates. Used internally by `BoardRoot`.
272
+
273
+ ### CSS
274
+
275
+ ```css
276
+ .board-viewport {
277
+ position: absolute;
278
+ inset: 0;
279
+ transform-origin: 0 0;
280
+ will-change: transform;
281
+ }
282
+ ```
283
+
284
+ ---
285
+
286
+ ## BoardNode
287
+
288
+ Renders a single node with selection state, editing support, resize handles, and slot-based content.
289
+
290
+ ### Props
291
+
292
+ | Name | Type | Default | Description |
293
+ | --- | --- | --- | --- |
294
+ | `node` | `BoardNode` | _required_ | The node data to render. |
295
+ | `selected` | `boolean` | _required_ | Whether the node is currently selected. |
296
+ | `editing` | `boolean` | _required_ | Whether the node is in text editing mode. |
297
+ | `customRenderer` | `boolean` | `undefined` | Hint that a custom renderer is provided. Falls back to slot detection when omitted. |
298
+
299
+ ### Slots
300
+
301
+ #### default
302
+
303
+ Custom node content. When provided, replaces the built-in text display.
304
+
305
+ | Prop | Type | Description |
306
+ | --- | --- | --- |
307
+ | `node` | `BoardNode` | The node data. |
308
+ | `selected` | `boolean` | Selection state. |
309
+ | `editing` | `boolean` | Editing state. |
310
+ | `beginEdit` | `() => void` | Start editing for a text node; otherwise no-op. |
311
+ | `commitText` | `(text: string) => void` | Commit a text-node edit; otherwise no-op. |
312
+
313
+ #### handle
314
+
315
+ Custom resize handle. Rendered for each of the 8 compass directions when the node is selected and not editing or locked.
316
+
317
+ | Prop | Type | Description |
318
+ | --- | --- | --- |
319
+ | `node` | `BoardNode` | The node data. |
320
+ | `handle` | `ResizeHandle` | Which handle (`'n'`, `'ne'`, `'e'`, `'se'`, `'s'`, `'sw'`, `'w'`, `'nw'`). |
321
+
322
+ ### CSS classes
323
+
324
+ | Class | Condition | Description |
325
+ | --- | --- | --- |
326
+ | `.board-node` | Always | Base node styling. |
327
+ | `.is-selected` | `selected === true` | Selection border and ring styling. |
328
+ | `.is-editing` | `editing === true` | Text editing mode. |
329
+ | `.is-locked` | `node.locked === true` | Locked state hook for custom styling. |
330
+ | `.is-group` | `node.type === 'group'` | Group frame styling. |
331
+
332
+ ### Built-in text editing
333
+
334
+ When no custom renderer is provided and the node type is `'text'`, `BoardNode` renders:
335
+
336
+ - A `<textarea>` when `editing` is true (auto-focused, auto-selected)
337
+ - A `<div>` with the content text when not editing
338
+
339
+ The textarea commits on `blur`, `Cmd/Ctrl + Enter`, and cancels on `Escape`.
340
+
341
+ ---
342
+
343
+ ## BoardNodeHandle
344
+
345
+ Renders a single resize handle square at the appropriate position on a node.
346
+
347
+ ### Props
348
+
349
+ | Name | Type | Description |
350
+ | --- | --- | --- |
351
+ | `handle` | `ResizeHandle` | Which handle to render (`'n'`, `'ne'`, `'e'`, `'se'`, `'s'`, `'sw'`, `'w'`, `'nw'`). |
352
+
353
+ The default handle uses an 18px hit target and a 7px visible dot, both scaled by `1 / zoom`. It uses `data-resize` attributes for pointer event detection.
354
+
355
+ ---
356
+
357
+ ## BoardGrid
358
+
359
+ Renders the grid pattern as CSS background gradients. Supports `dot`, `line`, `cross`, and `none` patterns. Automatically adjusts opacity based on zoom level.
360
+
361
+ The grid fades at edges when `fadeEdges` is enabled (via CSS mask-image).
362
+
363
+ ---
364
+
365
+ ## BoardBoxSelect
366
+
367
+ Renders the box selection rectangle during a box-select interaction. The default overlay uses a solid blue window style for contain-mode selection and a dashed teal crossing style for intersecting selection.
368
+
369
+ ### Slots
370
+
371
+ #### default
372
+
373
+ Custom box selection rendering.
374
+
375
+ | Prop | Type | Description |
376
+ | --- | --- | --- |
377
+ | `bounds` | `Bounds` | Screen-space bounds of the selection rectangle. |
378
+ | `mode` | `'window' \| 'crossing'` | Active box-select mode for custom styling. |
379
+
380
+ ---
381
+
382
+ ## BoardSnapGuides
383
+
384
+ Renders alignment snap guides during drag and resize operations. Converts world-space guide data to screen-space lines using the current camera transform.
385
+
386
+ - Vertical guides (x-axis) render as 1px wide lines in `--board-snap-guide-color`, falling back to `--board-accent`.
387
+ - Horizontal guides (y-axis) render as 1px tall lines in `--board-snap-guide-color`, falling back to `--board-accent`.
388
+
389
+ ---
390
+
391
+ ## Types
392
+
393
+ ### BoardGridOptions
394
+
395
+ Presentation-only grid options passed to the `grid` prop on `BoardRoot`. Configure size, snapping, thresholds, and pattern through `createBoardEngine({ grid })` or `engine.updateGridSettings()` so rendering and interaction use one canonical value.
396
+
397
+ ```ts
398
+ interface BoardGridOptions {
399
+ visible?: boolean // show/hide the grid (default: true)
400
+ minorOpacity?: number // minor grid line opacity (default: 0.14)
401
+ majorOpacity?: number // major grid line opacity (default: 0.18)
402
+ fadeEdges?: boolean // fade grid at viewport edges (default: true)
403
+ }
404
+ ```
405
+
406
+ ### BoardRendererRegistry
407
+
408
+ Maps supported JSON Canvas node types to Vue components.
409
+
410
+ ```ts
411
+ type BoardRendererRegistry = Partial<Record<JsonCanvasNodeType, Component>>
412
+ ```
413
+
414
+ ```ts
415
+ import TextCard from './TextCard.vue'
416
+ import GroupCard from './GroupCard.vue'
417
+
418
+ const renderers: BoardRendererRegistry = {
419
+ text: TextCard,
420
+ group: GroupCard,
421
+ }
422
+ ```
423
+
424
+ ---
@@ -0,0 +1,293 @@
1
+ ---
2
+ title: "Vue composables"
3
+ description: "Reactive composables for accessing board state inside Vue components."
4
+ url: "https://nuxt-board.lupinum.com/docs/reference/vue-composables"
5
+ route: "/docs/reference/vue-composables"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Vue composables
13
+
14
+ > Reactive composables for accessing board state inside Vue components.
15
+
16
+ All composables must be called inside a component that is a descendant of `<BoardRoot>`. They inject the board engine context via Vue's `provide`/`inject` system.
17
+
18
+ ## useBoardEngine
19
+
20
+ Returns the full board engine context. This is the primary composable — all other composables are thin wrappers over this.
21
+
22
+ ```ts
23
+ function useBoardEngine(): BoardEngineContext
24
+ ```
25
+
26
+ ### Return type
27
+
28
+ | Property | Type | Description |
29
+ | --- | --- | --- |
30
+ | `engine` | `BoardEngine` | The engine instance for calling commands. |
31
+ | `rootElement` | `Ref<HTMLElement \| null>` | The `BoardRoot` DOM element. |
32
+ | `viewportSize` | `Ref<Point>` | Current viewport dimensions in pixels. |
33
+ | `renderers` | `ShallowRef<BoardRendererRegistry>` | Registered custom renderers. |
34
+ | `resolvedGrid` | `ComputedRef<ResolvedBoardGridOptions>` | Resolved grid options (engine defaults merged with `BoardRoot` props). |
35
+ | `toLocalPoint` | `(clientX, clientY) => Point` | Converts page coordinates to board-local coordinates. |
36
+ | `$camera` | `ShallowRef<Camera>` | Reactive camera state. |
37
+ | `$grid` | `ShallowRef<GridSettings>` | Reactive grid state. |
38
+ | `$nodes` | `ShallowRef<ReadonlyMap<NodeId, BoardNode>>` | Reactive node map. |
39
+ | `$selection` | `ShallowRef<ReadonlySet<NodeId>>` | Reactive selection set. |
40
+ | `$interaction` | `ShallowRef<InteractionState>` | Reactive interaction state. |
41
+ | `$snapGuides` | `ShallowRef<readonly SnapGuide[]>` | Reactive snap guides. |
42
+
43
+ ```vue
44
+ <script setup lang="ts">
45
+ import { useBoardEngine } from '@lupinum/vue-board'
46
+
47
+ const { engine, $camera, $nodes } = useBoardEngine()
48
+
49
+ // Read the current camera
50
+ console.log($camera.value.z) // zoom level
51
+
52
+ // Call engine commands
53
+ engine.selectAll()
54
+ </script>
55
+ ```
56
+
57
+ ---
58
+
59
+ ## useBoardCamera
60
+
61
+ Returns a computed ref of the current camera state.
62
+
63
+ ```ts
64
+ function useBoardCamera(): ComputedRef<Camera>
65
+ ```
66
+
67
+ ```vue
68
+ <script setup lang="ts">
69
+ import { useBoardCamera } from '@lupinum/vue-board'
70
+
71
+ const camera = useBoardCamera()
72
+ </script>
73
+
74
+ <template>
75
+ <div>Zoom: {{ Math.round(camera.z * 100) }}%</div>
76
+ </template>
77
+ ```
78
+
79
+ ---
80
+
81
+ ## useBoardNodes
82
+
83
+ Returns a computed ref of the node map.
84
+
85
+ ```ts
86
+ function useBoardNodes(): ComputedRef<ReadonlyMap<NodeId, BoardNode>>
87
+ ```
88
+
89
+ ```vue
90
+ <script setup lang="ts">
91
+ import { useBoardNodes } from '@lupinum/vue-board'
92
+
93
+ const nodes = useBoardNodes()
94
+ const nodeCount = computed(() => nodes.value.size)
95
+ </script>
96
+ ```
97
+
98
+ ---
99
+
100
+ ## useBoardSelection
101
+
102
+ Returns a computed ref of the currently selected node IDs as an array.
103
+
104
+ ```ts
105
+ function useBoardSelection(): ComputedRef<readonly NodeId[]>
106
+ ```
107
+
108
+ ```vue
109
+ <script setup lang="ts">
110
+ import { useBoardSelection } from '@lupinum/vue-board'
111
+
112
+ const selection = useBoardSelection()
113
+ </script>
114
+
115
+ <template>
116
+ <div>{{ selection.length }} selected</div>
117
+ </template>
118
+ ```
119
+
120
+ ---
121
+
122
+ ## useBoardInteraction
123
+
124
+ Returns a computed ref of the current interaction state.
125
+
126
+ ```ts
127
+ function useBoardInteraction(): ComputedRef<InteractionState>
128
+ ```
129
+
130
+ ```vue
131
+ <script setup lang="ts">
132
+ import { useBoardInteraction } from '@lupinum/vue-board'
133
+
134
+ const interaction = useBoardInteraction()
135
+ const isDragging = computed(() => interaction.value.mode === 'dragging-nodes')
136
+ </script>
137
+ ```
138
+
139
+ ---
140
+
141
+ ## useBoardVisibleBounds
142
+
143
+ Returns a computed ref of the current viewport bounds in world-space coordinates.
144
+
145
+ ```ts
146
+ function useBoardVisibleBounds(): ComputedRef<Bounds>
147
+ ```
148
+
149
+ ```vue
150
+ <script setup lang="ts">
151
+ import { useBoardVisibleBounds } from '@lupinum/vue-board'
152
+
153
+ const bounds = useBoardVisibleBounds()
154
+ // bounds.value = { minX, minY, maxX, maxY }
155
+ </script>
156
+ ```
157
+
158
+ ---
159
+
160
+ ## useBoardVisibleNodes
161
+
162
+ Returns a computed ref of all visible nodes within the viewport, with an optional culling margin.
163
+
164
+ ```ts
165
+ function useBoardVisibleNodes(
166
+ margin?: number,
167
+ ): ComputedRef<readonly BoardNode[]>
168
+ ```
169
+
170
+ | Parameter | Type | Default | Description |
171
+ | --- | --- | --- | --- |
172
+ | `margin` | `number` | `200` | Extra world-space margin around the viewport for culling. |
173
+
174
+ ```vue
175
+ <script setup lang="ts">
176
+ import { useBoardVisibleNodes } from '@lupinum/vue-board'
177
+
178
+ const visible = useBoardVisibleNodes(300) // 300 world-unit margin
179
+ </script>
180
+
181
+ <template>
182
+ <div>{{ visible.length }} nodes visible</div>
183
+ </template>
184
+ ```
185
+
186
+ ---
187
+
188
+ ## useBoardGridStyle
189
+
190
+ Returns a computed CSS variable object for rendering the grid. Used internally by `BoardGrid` but available for custom grid implementations.
191
+
192
+ ```ts
193
+ function useBoardGridStyle(): ComputedRef<Record<string, string>>
194
+ ```
195
+
196
+ ### Returned CSS variables
197
+
198
+ | Variable | Description |
199
+ | --- | --- |
200
+ | `--grid-minor-size` | Screen-space minor grid step. |
201
+ | `--grid-major-size` | Screen-space major grid step. |
202
+ | `--grid-minor-x` | Minor grid horizontal offset. |
203
+ | `--grid-minor-y` | Minor grid vertical offset. |
204
+ | `--grid-major-x` | Major grid horizontal offset. |
205
+ | `--grid-major-y` | Major grid vertical offset. |
206
+ | `--grid-minor-color` | Minor grid line color (with computed opacity). |
207
+ | `--grid-major-color` | Major grid line color (with computed opacity). |
208
+ | `--grid-mask-image` | Edge fade mask (radial gradient or `none`). |
209
+
210
+ Grid opacity automatically reduces at low zoom levels (minor lines fade below 12px screen step, disappear below 6px).
211
+
212
+ ---
213
+
214
+ ## useBoardNode
215
+
216
+ Returns reactive state and actions for a single node. Ideal for custom renderer components.
217
+
218
+ ```ts
219
+ function useBoardNode(id: MaybeRefOrGetter<NodeId>): {
220
+ node: ComputedRef<BoardNode | null>
221
+ selected: ComputedRef<boolean>
222
+ editing: ComputedRef<boolean>
223
+ locked: ComputedRef<boolean>
224
+ style: ComputedRef<CSSProperties>
225
+ beginEdit: () => void
226
+ commitText: (text: string) => void
227
+ startDrag: (event: PointerEvent) => void
228
+ startResize: (handle: ResizeHandle, event: PointerEvent) => void
229
+ }
230
+ ```
231
+
232
+ ### Return type
233
+
234
+ | Property | Type | Description |
235
+ | --- | --- | --- |
236
+ | `node` | `ComputedRef<BoardNode \| null>` | The node, or `null` after it is removed. |
237
+ | `selected` | `ComputedRef<boolean>` | Whether the node is selected. |
238
+ | `editing` | `ComputedRef<boolean>` | Whether the node is being text-edited. |
239
+ | `locked` | `ComputedRef<boolean>` | Whether the node is locked. |
240
+ | `style` | `ComputedRef<CSSProperties>` | CSS position/size/zIndex for absolute positioning. |
241
+ | `beginEdit` | `() => void` | Start text editing when this is a text node. |
242
+ | `commitText` | `(text: string) => void` | Commit text when this is a text node. |
243
+ | `startDrag` | `(event: PointerEvent) => void` | Begin dragging this node from a pointer event. |
244
+ | `startResize` | `(handle, event) => void` | Begin resizing this node from a handle and pointer event. |
245
+
246
+ ```vue
247
+ <script setup lang="ts">
248
+ import { useBoardNode } from '@lupinum/vue-board'
249
+ import type { NodeId } from '@lupinum/board-core'
250
+
251
+ const props = defineProps<{ nodeId: NodeId }>()
252
+ const { node, selected, style, beginEdit } = useBoardNode(() => props.nodeId)
253
+ </script>
254
+
255
+ <template>
256
+ <div v-if="node" :style="style" :class="{ selected }" @dblclick="beginEdit">
257
+ {{ node.text }}
258
+ </div>
259
+ </template>
260
+ ```
261
+
262
+ ---
263
+
264
+ ## useBoardBoxSelectBounds
265
+
266
+ Returns a computed ref of the current box selection bounds in screen-space, or `null` if no box selection is active.
267
+
268
+ ```ts
269
+ function useBoardBoxSelectBounds(): ComputedRef<Bounds | null>
270
+ ```
271
+
272
+ ```vue
273
+ <script setup lang="ts">
274
+ import { useBoardBoxSelectBounds } from '@lupinum/vue-board'
275
+
276
+ const bounds = useBoardBoxSelectBounds()
277
+ </script>
278
+
279
+ <template>
280
+ <div
281
+ v-if="bounds"
282
+ class="custom-box-select"
283
+ :style="{
284
+ left: bounds.minX + 'px',
285
+ top: bounds.minY + 'px',
286
+ width: bounds.maxX - bounds.minX + 'px',
287
+ height: bounds.maxY - bounds.minY + 'px',
288
+ }"
289
+ />
290
+ </template>
291
+ ```
292
+
293
+ ---