@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,145 @@
1
+ ---
2
+ title: "Selection and interaction"
3
+ description: "Selection modes, box selection, and the interaction state machine."
4
+ url: "https://nuxt-board.lupinum.com/docs/build-features/selection-and-keyboard"
5
+ route: "/docs/build-features/selection-and-keyboard"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Selection and interaction
13
+
14
+ > Selection modes, box selection, and the interaction state machine.
15
+
16
+ Selection and interaction are how users communicate with the board. Select nodes to act on them, drag to move, resize from handles, and box-select on empty canvas.
17
+
18
+ > Component omitted: `interaction-state-viz`.
19
+ > This page contains an interactive or site-specific block that has no agent markdown serializer yet.
20
+
21
+ ## Selecting nodes
22
+
23
+ ```ts
24
+ engine.select(nodeId) // replace selection
25
+ engine.select([nodeA, nodeB]) // replace with multiple
26
+ engine.select(nodeId, 'append') // add to selection
27
+ engine.select(nodeId, 'toggle') // flip in/out
28
+ engine.selectAll() // all visible nodes
29
+ engine.clearSelection() // deselect everything
30
+ const selected = engine.getSelection() // NodeId[]
31
+ ```
32
+
33
+ | Mode | Behavior |
34
+ | --- | --- |
35
+ | `'replace'` | Clears existing selection, selects the given IDs (default). |
36
+ | `'append'` | Adds IDs to the existing selection. |
37
+ | `'toggle'` | Flips each ID in/out of the selection. |
38
+
39
+ Keyboard shortcuts: <kbd>
40
+ ⌘
41
+ </kbd>
42
+
43
+ + <kbd>
44
+ A
45
+ </kbd>
46
+
47
+ to select all, <kbd>
48
+ Escape
49
+ </kbd>
50
+
51
+ to deselect.
52
+
53
+ In Vue, use the `useBoardSelection()` composable for reactive access:
54
+
55
+ ```vue
56
+ <script setup lang="ts">
57
+ const selection = useBoardSelection()
58
+ // selection.value is readonly NodeId[]
59
+ </script>
60
+ ```
61
+
62
+ ## Interaction state machine
63
+
64
+ The engine tracks pointer interactions through six modes. Only one mode is active at a time.
65
+
66
+ <accordion>
67
+ <accordion-item title="Idle">
68
+ No interaction is active. This is the default state.
69
+ </accordion-item>
70
+
71
+ <accordion-item title="Panning">
72
+ Triggered by <kbd>
73
+ Space
74
+ </kbd>
75
+
76
+ + drag or middle mouse drag. The camera follows the pointer.
77
+ </accordion-item>
78
+
79
+ <accordion-item title="Dragging nodes">
80
+ Triggered by clicking and dragging a node. If the node is part of a selection, all selected nodes move. Grid and edge snapping produce alignment guides.
81
+ </accordion-item>
82
+
83
+ <accordion-item title="Resizing a node">
84
+ Triggered by dragging a resize handle (`n`, `ne`, `e`, `se`, `s`, `sw`, `w`, `nw`). Hold <kbd>
85
+ Shift
86
+ </kbd>
87
+
88
+ for aspect-ratio locking.
89
+ The default Vue handles are also keyboard-operable: focus a handle and use the
90
+ arrow keys to move its active edge by one grid step, or hold <kbd>
91
+ Shift
92
+ </kbd>
93
+
94
+ for a
95
+ major-grid step.
96
+ </accordion-item>
97
+
98
+ <accordion-item title="Box selection">
99
+ Triggered by clicking on empty canvas and dragging. By default, box selection follows AutoCAD semantics: drag left-to-right for window selection (node must be fully enclosed) and right-to-left for crossing selection (node only needs to intersect the box). Override this with `createBoardEngine({ boxSelect: { behavior: 'contain' | 'intersect' } })`.
100
+ </accordion-item>
101
+
102
+ <accordion-item title="Text editing">
103
+ Triggered by double-clicking a text node, pressing `Enter` with one text node selected, or choosing **Edit** in the selection toolbar. The built-in `BoardNode` shows a `<textarea>` overlay. File, link, and group renderers own any custom editing UI they provide.
104
+ </accordion-item>
105
+ </accordion>
106
+
107
+ ## Pointer event flow
108
+
109
+ When a pointer event hits `BoardRoot`:
110
+
111
+ 1. **Middle mouse / Space** starts panning.
112
+ 2. **Resize handle** selects the node, then starts resizing after the drag threshold.
113
+ 3. **Node** selects the node, then starts dragging after the drag threshold.
114
+ 4. **Empty canvas** clears selection, then starts a box-selection session after the drag threshold.
115
+
116
+ Pointer movement is throttled through `requestAnimationFrame` and translated by `BoardRoot`; the low-level start, update, commit, and cancellation methods are absent from the normal engine API and available only through the unsupported first-party framework ABI. Pointer up commits the session, while pointer cancellation or Escape restores its starting geometry.
117
+
118
+ <note>
119
+ `BoardRoot` handles two-finger pinch gestures on touch devices automatically.
120
+ </note>
121
+
122
+ <collapsible>
123
+ ### Text editing commands
124
+
125
+ `BoardRoot` exclusively owns pointer sessions. Applications can still control
126
+ the meaningful text-edit boundary directly:
127
+
128
+ ```ts
129
+ engine.beginTextEdit(nodeId)
130
+ engine.commitTextEdit(nodeId, 'New text')
131
+ engine.cancelTextEdit()
132
+ ```
133
+ </collapsible>
134
+
135
+ <collapsible>
136
+ ### Interaction events
137
+
138
+ ```ts
139
+ engine.on('interaction:start', (state) => console.log('Started:', state.mode))
140
+ engine.on('interaction:update', (state) => {
141
+ /* high-frequency during drag */
142
+ })
143
+ engine.on('interaction:end', (state) => console.log('Ended:', state.mode))
144
+ ```
145
+ </collapsible>
@@ -0,0 +1,67 @@
1
+ ---
2
+ title: "SSR deterministic scene"
3
+ description: "Seed a board with initial nodes for server-side rendering and hydration."
4
+ url: "https://nuxt-board.lupinum.com/docs/build-features/ssr-and-deterministic-state"
5
+ route: "/docs/build-features/ssr-and-deterministic-state"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # SSR deterministic scene
13
+
14
+ > Seed a board with initial nodes for server-side rendering and hydration.
15
+
16
+ SSR only works if the server and client render the same scene. That means deterministic IDs, deterministic node order, and no random creation during setup.
17
+
18
+ <tabs>
19
+ <tab label="Server" icon="i-lucide-server">
20
+ ```ts
21
+ const engine = createBoardEngine({
22
+ initialNodes: [
23
+ {
24
+ id: asNodeId('welcome'),
25
+ type: 'text',
26
+ x: 100,
27
+ y: 100,
28
+ width: 240,
29
+ height: 160,
30
+ text: 'Node',
31
+ zIndex: 1,
32
+ locked: false,
33
+ visible: true,
34
+ },
35
+ ],
36
+ })
37
+ ```
38
+ </tab>
39
+
40
+ <tab label="Client" icon="i-lucide-monitor">
41
+ ```vue
42
+ <BoardRoot :engine="engine" style="height: 100vh" />
43
+ ```
44
+ </tab>
45
+ </tabs>
46
+
47
+ <steps>
48
+ ### Use deterministic IDs
49
+
50
+ ```ts
51
+ id: asNodeId('welcome')
52
+ ```
53
+
54
+ ### Seed the full node records up front
55
+
56
+ ```ts
57
+ initialNodes: [{ id, type, x, y, width, height, text, zIndex, locked, visible }]
58
+ ```
59
+
60
+ ### Avoid random runtime creation during hydration
61
+
62
+ Do not call `createNode()` in a way that would diverge between server and client.
63
+ </steps>
64
+
65
+ <tip>
66
+ If the scene is bigger, build a deterministic JSON Canvas document once and import that document on both sides.
67
+ </tip>
@@ -0,0 +1,102 @@
1
+ ---
2
+ title: "Theming"
3
+ description: "Customize board appearance with CSS custom properties."
4
+ url: "https://nuxt-board.lupinum.com/docs/build-features/theming"
5
+ route: "/docs/build-features/theming"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Theming
13
+
14
+ > Customize board appearance with CSS custom properties.
15
+
16
+ Theme the board with CSS variables. Keep semantic state, such as node color presets, in engine state.
17
+
18
+ > Component omitted: `theme-playground`.
19
+ > This page contains an interactive or site-specific block that has no agent markdown serializer yet.
20
+
21
+ ## Core tokens
22
+
23
+ | Token | Default | Purpose |
24
+ | --- | --- | --- |
25
+ | `--board-bg` | `#fbfbfd` | Page and panel background token used by the board UI. |
26
+ | `--board-canvas-bg` | `#f5f6fa` | Canvas background behind nodes and overlays. |
27
+ | `--board-fg` | `#14161f` | Text, grid contrast, and general foreground color. |
28
+ | `--board-node-bg` | `#fff` | Default node surface. |
29
+ | `--board-node-border` | `#e4e5ee` | Default node border color. |
30
+ | `--board-accent` | `#6366e8` | Selection accents and snap guides. |
31
+ | `--board-node-color` | `unset` | Resolved border color for a colored node, usually set by the renderer from `node.color`. |
32
+ | `--board-node-tint` | `unset` | Subtle surface tint for colored cards and groups. |
33
+ | `--board-node-color-ring` | `unset` | Selection outline color for a colored node. |
34
+ | `--board-snap-guide-color` | `rgba(99, 102, 232, 0.95)` | Snap guide line color. |
35
+
36
+ ## Light and dark examples
37
+
38
+ <tabs>
39
+ <tab label="Light" icon="i-lucide-sun">
40
+ ```css
41
+ .board-root {
42
+ --board-bg: #f8fafc;
43
+ --board-fg: #0f172a;
44
+ --board-node-bg: #ffffff;
45
+ --board-node-border: rgba(15, 23, 42, 0.14);
46
+ --board-accent: #0ea5e9;
47
+ }
48
+ ```
49
+ </tab>
50
+
51
+ <tab label="Dark" icon="i-lucide-moon">
52
+ ```css
53
+ .board-root {
54
+ --board-bg: #0f172a;
55
+ --board-fg: #e2e8f0;
56
+ --board-node-bg: #162033;
57
+ --board-node-border: rgba(226, 232, 240, 0.12);
58
+ --board-accent: #22d3ee;
59
+ }
60
+ ```
61
+ </tab>
62
+ </tabs>
63
+
64
+ ## Node colors
65
+
66
+ Node colors are stored on `node.color` as preset IDs or six-digit hex colors:
67
+
68
+ ```ts
69
+ engine.updateNode(card.id, { color: '5' })
70
+ engine.updateNode(card.id, { color: undefined })
71
+ ```
72
+
73
+ The built-in renderer resolves those colors into `--board-node-color`, `--board-node-tint`, `--board-node-color-soft`, and `--board-node-color-ring`. Use those variables in custom renderers instead of reading preset IDs directly in CSS.
74
+
75
+ ```css
76
+ .task-card {
77
+ border-color: var(--board-node-color, var(--board-node-border));
78
+ background: var(--board-node-tint, var(--board-node-bg));
79
+ }
80
+ ```
81
+
82
+ The selection toolbar can batch-apply the same preset to selected cards and groups.
83
+
84
+ <collapsible>
85
+ ### Styling specific states
86
+
87
+ ```css
88
+ .board-node.is-selected {
89
+ outline: calc(3px / var(--board-zoom, 1)) solid var(--board-accent);
90
+ }
91
+
92
+ .board-node.is-locked {
93
+ opacity: 0.72;
94
+ }
95
+
96
+ .board-node.is-group {
97
+ background: var(--board-node-tint, var(--board-group-bg));
98
+ }
99
+ ```
100
+
101
+ Use state classes when you want a special selected, locked, or group look without replacing the full renderer.
102
+ </collapsible>
@@ -0,0 +1,124 @@
1
+ ---
2
+ title: "History"
3
+ description: "Record state changes and navigate them with the history plugin."
4
+ url: "https://nuxt-board.lupinum.com/docs/build-features/undo-and-redo"
5
+ route: "/docs/build-features/undo-and-redo"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # History
13
+
14
+ > Record state changes and navigate them with the history plugin.
15
+
16
+ The history plugin stores references to committed structural roots. Undo and redo restore those roots atomically, without inverse actions, replay ordering, or timers. A completed gesture or explicit batch is one deterministic undo step.
17
+
18
+ ## Setup
19
+
20
+ ```ts
21
+ import { createBoardEngine } from '@lupinum/board-core'
22
+ import { historyPlugin } from '@lupinum/board-history'
23
+
24
+ const engine = createBoardEngine({
25
+ plugins: [historyPlugin({ maxSteps: 100 })],
26
+ })
27
+ ```
28
+
29
+ Render the shortcut component inside the matching board root:
30
+
31
+ ```vue
32
+ <script setup lang="ts">
33
+ import { createBoardEngine } from '@lupinum/board-core'
34
+ import { historyPlugin } from '@lupinum/board-history'
35
+ import { BoardHistoryShortcuts } from '@lupinum/board-history/vue'
36
+
37
+ const engine = createBoardEngine({ plugins: [historyPlugin()] })
38
+ </script>
39
+
40
+ <template>
41
+ <BoardRoot :engine="engine">
42
+ <BoardHistoryShortcuts :history="engine.plugins.history" />
43
+ </BoardRoot>
44
+ </template>
45
+ ```
46
+
47
+ ### Plugin options
48
+
49
+ | Option | Type | Default | Purpose |
50
+ | --- | --- | --- | --- |
51
+ | `maxSteps` | `number` | `200` | Maximum undo steps to keep. Older entries are dropped. |
52
+
53
+ ## Undo and redo
54
+
55
+ ```ts
56
+ engine.plugins.history.undo()
57
+ engine.plugins.history.redo()
58
+
59
+ if (engine.plugins.history.canUndo()) {
60
+ /* ... */
61
+ }
62
+
63
+ const state = engine.plugins.history.getState()
64
+ // { undoDepth, redoDepth }
65
+
66
+ engine.plugins.history.clear()
67
+ ```
68
+
69
+ Call `undo()` and `redo()` outside `engine.batch()`. Replay with a retained frame inside a batch throws `BoardConflictError` before changing the document or history. If that error leaves the batch, preceding edits roll back. Group new edits in a batch, then undo or redo the completed batch as one operation.
70
+
71
+ A rejected undo or redo retains its frame. Replay completes its history bookkeeping before notifying listeners, so an edit from a listener starts a new branch and clears redo.
72
+
73
+ History entries are available synchronously after a successful outer command. Read methods such as `canUndo()`, `canRedo()`, and `getState()` never mutate history state.
74
+
75
+ `BoardHistoryShortcuts` handles <kbd>
76
+ ⌘
77
+ </kbd>
78
+
79
+ + <kbd>
80
+ Z
81
+ </kbd>
82
+
83
+ to undo and <kbd>
84
+ ⌘
85
+ </kbd>
86
+
87
+ + <kbd>
88
+ Shift
89
+ </kbd>
90
+
91
+ + <kbd>
92
+ Z
93
+ </kbd>
94
+
95
+ or <kbd>
96
+ ⌘
97
+ </kbd>
98
+
99
+ + <kbd>
100
+ Y
101
+ </kbd>
102
+
103
+ to redo. Pass the matching board's history API through its required `history` prop. It handles only events from its enclosing board.
104
+
105
+ <note>
106
+ When both `historyPlugin` and `connectionsPlugin` are installed, undoing a node deletion also restores its connected edges.
107
+ </note>
108
+
109
+ <collapsible>
110
+ ### Ignored commands
111
+
112
+ History capture follows command metadata emitted by the engine and first-party features. Camera movement, transient interaction setup/teardown, imports, and selection-only commands are ignored; geometry and content mutations are recorded.
113
+ </collapsible>
114
+
115
+ <collapsible>
116
+ ### Events
117
+
118
+ ```ts
119
+ engine.on('history:push', (entry) => console.log('New step:', entry.label))
120
+ engine.on('history:undo', () => console.log('Undo'))
121
+ engine.on('history:redo', () => console.log('Redo'))
122
+ engine.on('history:clear', () => console.log('History cleared'))
123
+ ```
124
+ </collapsible>
@@ -0,0 +1,44 @@
1
+ ---
2
+ title: "Design decisions"
3
+ description: "The constraints Nuxt Board chooses, the problems they solve, and their costs."
4
+ url: "https://nuxt-board.lupinum.com/docs/evaluate/design-decisions"
5
+ route: "/docs/evaluate/design-decisions"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Design decisions
13
+
14
+ > The constraints Nuxt Board chooses, the problems they solve, and their costs.
15
+
16
+ Nuxt Board limits its extension points to keep state ownership and failure behavior understandable.
17
+
18
+ ## One engine owns board state
19
+
20
+ **Decision:** nodes, camera, grid, selection, hierarchy, and plugin slices belong to one engine.
21
+
22
+ **Benefit:** commands, snapshots, events, and persistence cannot silently use different board instances.
23
+
24
+ **Cost:** applications must adapt existing product data at a deliberate boundary instead of mirroring every field into Vue state.
25
+
26
+ ## Commands are the mutation boundary
27
+
28
+ Persistent changes stage a candidate, validate it, and publish only after the outer transaction succeeds. The cost is that direct record mutation is unsupported. The benefit is atomic failure and one observable lifecycle.
29
+
30
+ ## Document and session state are separate
31
+
32
+ Pointer gestures update transient geometry. Completion commits once; cancellation discards the override. Runtime and exported state can differ during a gesture. That difference keeps persistence and history stable.
33
+
34
+ ## JSON Canvas node types are canonical
35
+
36
+ Custom renderers use existing `text`, `file`, `link`, and `group` records. This rules out arbitrary type strings, but prevents a second persistence schema.
37
+
38
+ ## Plugins are first-party infrastructure
39
+
40
+ Connections and history install during engine construction. The unsupported `@lupinum/board-core/internal` ABI exists for separately published first-party packages, not application extensions.
41
+
42
+ ## Nuxt owns no board behavior
43
+
44
+ `@lupinum/nuxt-board` registers imports and styles. Domain invariants stay below the framework boundary.
@@ -0,0 +1,41 @@
1
+ ---
2
+ title: "How Nuxt Board works"
3
+ description: "The complete mental model from application intent to rendered board."
4
+ url: "https://nuxt-board.lupinum.com/docs/evaluate/how-nuxt-board-works"
5
+ route: "/docs/evaluate/how-nuxt-board-works"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # How Nuxt Board works
13
+
14
+ > The complete mental model from application intent to rendered board.
15
+
16
+ Nuxt Board follows one direct flow:
17
+
18
+ ```text
19
+ application intent
20
+ → engine command
21
+ → validated transaction
22
+ → canonical board state
23
+ → subscriptions
24
+ → Vue rendering
25
+ ```
26
+
27
+ `createBoardEngine()` creates the source of truth. Application code changes it through commands. `BoardRoot` subscribes to the engine, renders effective state, and translates pointer and keyboard input through the framework adapter.
28
+
29
+ ## Four boundaries
30
+
31
+ 1. **Commands own persistent mutation.** Guards, validation, plugins, history, subscriptions, and events see the same commit.
32
+ 2. **Session state stays transient.** Drag and resize updates render immediately without rewriting the persisted document on every pointer move.
33
+ 3. **Renderers own presentation.** A renderer receives a node record. It does not create a competing state model.
34
+ 4. **Packages own named capabilities.** Core owns nodes; connections owns edges; history owns undo stacks; Nuxt owns registration.
35
+
36
+ > Component omitted: `engine-command-lab`.
37
+ > This page contains an interactive or site-specific block that has no agent markdown serializer yet.
38
+
39
+ Run a command in the lab. The document changes before queued public events publish. Subscribers never receive an intermediate candidate.
40
+
41
+ The engine is mutable internally so commands remain direct. Its public records and snapshots are immutable contracts. Keep the engine out of Vue deep reactivity; use a stable instance or `shallowRef`.
@@ -0,0 +1,61 @@
1
+ ---
2
+ title: "Why Nuxt Board"
3
+ description: "The product problems Nuxt Board solves and the boundaries it keeps explicit."
4
+ url: "https://nuxt-board.lupinum.com/docs/evaluate/why-nuxt-board"
5
+ route: "/docs/evaluate/why-nuxt-board"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Why Nuxt Board
13
+
14
+ > The product problems Nuxt Board solves and the boundaries it keeps explicit.
15
+
16
+ Nuxt Board gives your application a board model that exists independently of the component tree. That matters when the canvas is part of a product rather than a disposable visualization.
17
+
18
+ ## What it owns
19
+
20
+ The headless engine owns spatial state and behavior: nodes, hierarchy, camera, selection, commands, validation, events, and JSON Canvas persistence. `@lupinum/vue-board` owns DOM interaction and rendering. Optional packages own connections and history.
21
+
22
+ This separation gives application code one mutation path. A command can be guarded, validated, recorded in history, and observed without coordinating several Vue stores.
23
+
24
+ ## What you keep
25
+
26
+ Your product owns business rules, backend execution, and domain data. Custom Vue renderers control how records look. Nuxt Board does not turn a planning card into a task service or a workflow node into an execution engine.
27
+
28
+ ## Why the limits help
29
+
30
+ Nuxt Board supports the JSON Canvas node types: `text`, `file`, `link`, and `group`. Custom renderers change presentation without introducing another node schema. First-party plugins add bounded capabilities instead of an unrestricted extension system.
31
+
32
+ The cost is less speculative flexibility. The benefit is one document model, predictable persistence, and fewer invalid combinations.
33
+
34
+ Choose Nuxt Board when those boundaries match the system you want to maintain.
35
+
36
+ ## Strong fit
37
+
38
+ - Users arrange, resize, group, select, or connect structured records.
39
+ - Board state must exist outside the Vue component tree.
40
+ - Mutations need guards, atomic batches, events, validation, or undo boundaries.
41
+ - Custom Vue components should render a stable document schema.
42
+ - JSON Canvas is a useful persistence or interchange format.
43
+ - Connections, history, and minimaps should remain optional.
44
+
45
+ ## Look elsewhere when
46
+
47
+ - You only need a static graph renderer.
48
+ - Freehand drawing and vector illustration are the primary interaction.
49
+ - You want a complete collaborative whiteboard product rather than a library.
50
+ - Your graph is already modeled around another library's node and edge model.
51
+ - You need backend workflow execution; Nuxt Board only owns the editor surface.
52
+
53
+ Graph-first components such as Vue Flow are a better fit when handles and edges
54
+ define the product. Drawing libraries such as Konva or Fabric are a better fit
55
+ when shapes and low-level scene manipulation are the core abstraction. Complete
56
+ whiteboard SDKs fit products that value a prescribed toolset over a narrow
57
+ Vue-specific engine, while static renderers such as Mermaid fit non-interactive
58
+ diagrams.
59
+
60
+ The deciding question is where canonical state should live. Choose Nuxt Board
61
+ when you want a headless board document changed through explicit commands.
@@ -0,0 +1,101 @@
1
+ ---
2
+ title: "Contributing"
3
+ description: "Set up the repo, run the docs, and make changes that are ready to review."
4
+ url: "https://nuxt-board.lupinum.com/docs/project/contributing"
5
+ route: "/docs/project/contributing"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Contributing
13
+
14
+ > Set up the repo, run the docs, and make changes that are ready to review.
15
+
16
+ Contributions are welcome when they keep the library easier to use, test, and maintain.
17
+
18
+ ## Set up the repository
19
+
20
+ The canonical contribution policy is [CONTRIBUTING.md](https://github.com/lupinum-dev/nuxt-board/blob/main/CONTRIBUTING.md). From the repository root:
21
+
22
+ ```bash
23
+ corepack enable
24
+ pnpm install
25
+ ```
26
+
27
+ Start the playground or docs app:
28
+
29
+ ```bash
30
+ pnpm dev:playground
31
+ pnpm dev:docs
32
+ ```
33
+
34
+ The playground is the fastest place to verify interaction changes. The docs app uses the workspace packages, so examples exercise the same code consumers install.
35
+
36
+ ## Run the checks
37
+
38
+ Run the narrowest relevant check while you work, then the repository gate before handoff:
39
+
40
+ ```bash
41
+ pnpm verify
42
+ ```
43
+
44
+ Use `pnpm test:e2e` for browser interaction or screenshot changes. Maintainers use `pnpm release:verify` only on a release candidate.
45
+
46
+ ## Where docs live
47
+
48
+ Docs content lives in `docs/content`.
49
+
50
+ | Folder | Purpose |
51
+ | --- | --- |
52
+ | `1.evaluate` | Product fit and architecture orientation. |
53
+ | `2.start-building` | Installation and first working board. |
54
+ | `3.understand-the-system` | State, commands, coordinates, and data. |
55
+ | `4.build-features` | Task-focused feature guides. |
56
+ | `5.solutions` | Complete application examples. |
57
+ | `6.reference` | Maintained public API contracts. |
58
+ | `7.project` | Contribution, support, and security. |
59
+
60
+ ## Documentation standards
61
+
62
+ Write each page for one reader and one job. A quickstart should not become a reference page, and a reference page should not become a tutorial.
63
+
64
+ Before shipping docs, check:
65
+
66
+ - The first paragraph says what the page helps the reader do or understand.
67
+ - Examples include imports and setup when needed.
68
+ - Claims about defaults, fields, commands, and behavior match source or tests.
69
+ - Links describe the destination.
70
+ - The page ends with a useful next step.
71
+
72
+ ## When code needs docs
73
+
74
+ Update docs when a change affects:
75
+
76
+ - exported package APIs
77
+ - node, edge, selection, camera, or import/export behavior
78
+ - Nuxt module options or auto-imports
79
+ - default UI behavior
80
+ - migration steps for existing users
81
+ - setup commands or package names
82
+
83
+ For publishable package changes, add a changeset:
84
+
85
+ ```bash
86
+ pnpm changeset
87
+ ```
88
+
89
+ ## Pull request shape
90
+
91
+ A reviewable change has a narrow scope, tests for changed behavior, and docs for user-visible changes.
92
+
93
+ Include screenshots or short recordings for visual interaction changes. For API docs, include the source change and matching reference update together.
94
+
95
+ ## Documentation voice
96
+
97
+ Write in a calm, direct, technically honest voice. Lead with the answer. Name the package that owns a capability. Explain non-obvious constraints through decision, reason, benefit, cost, and consequence.
98
+
99
+ Use product-direct writing for evaluation, principle-led writing for concepts, task-first writing for guides, contract-style writing for reference, and neutral analytical writing for comparisons.
100
+
101
+ Do not hide required facts inside a demo, tab, or accordion. Every interactive lab needs a textual objective, expected result, invariant, and relevant consumer code.