@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.
- package/README.md +24 -0
- package/dist/agent/AGENTS.md +63 -0
- package/dist/agent/manifest.json +320 -0
- package/dist/agent/pages/docs/build-features/connections.md +241 -0
- package/dist/agent/pages/docs/build-features/custom-node-renderers.md +219 -0
- package/dist/agent/pages/docs/build-features/groups-and-nesting.md +172 -0
- package/dist/agent/pages/docs/build-features/performance.md +76 -0
- package/dist/agent/pages/docs/build-features/read-only-and-command-guards.md +52 -0
- package/dist/agent/pages/docs/build-features/save-and-load.md +142 -0
- package/dist/agent/pages/docs/build-features/selection-and-keyboard.md +145 -0
- package/dist/agent/pages/docs/build-features/ssr-and-deterministic-state.md +67 -0
- package/dist/agent/pages/docs/build-features/theming.md +102 -0
- package/dist/agent/pages/docs/build-features/undo-and-redo.md +124 -0
- package/dist/agent/pages/docs/evaluate/design-decisions.md +44 -0
- package/dist/agent/pages/docs/evaluate/how-nuxt-board-works.md +41 -0
- package/dist/agent/pages/docs/evaluate/why-nuxt-board.md +61 -0
- package/dist/agent/pages/docs/project/contributing.md +101 -0
- package/dist/agent/pages/docs/project/support-and-security.md +40 -0
- package/dist/agent/pages/docs/reference/board-core-types.md +82 -0
- package/dist/agent/pages/docs/reference/board-core.md +261 -0
- package/dist/agent/pages/docs/reference/connections.md +531 -0
- package/dist/agent/pages/docs/reference/events-and-errors.md +158 -0
- package/dist/agent/pages/docs/reference/glossary.md +58 -0
- package/dist/agent/pages/docs/reference/history.md +193 -0
- package/dist/agent/pages/docs/reference/minimap.md +157 -0
- package/dist/agent/pages/docs/reference/nuxt-board.md +148 -0
- package/dist/agent/pages/docs/reference/package-overview.md +58 -0
- package/dist/agent/pages/docs/reference/vue-board.md +424 -0
- package/dist/agent/pages/docs/reference/vue-composables.md +293 -0
- package/dist/agent/pages/docs/solutions/mind-map.md +104 -0
- package/dist/agent/pages/docs/solutions/nuxt-application.md +47 -0
- package/dist/agent/pages/docs/solutions/planning-board.md +51 -0
- package/dist/agent/pages/docs/solutions/read-only-viewer.md +61 -0
- package/dist/agent/pages/docs/solutions/workflow-builder.md +124 -0
- package/dist/agent/pages/docs/start-building/add-connections-and-history.md +42 -0
- package/dist/agent/pages/docs/start-building/customize-your-first-node.md +38 -0
- package/dist/agent/pages/docs/start-building/installation.md +76 -0
- package/dist/agent/pages/docs/start-building/your-first-board.md +52 -0
- package/dist/agent/pages/docs/understand-the-system/camera-and-coordinates.md +22 -0
- package/dist/agent/pages/docs/understand-the-system/commands-and-transactions.md +31 -0
- package/dist/agent/pages/docs/understand-the-system/document-and-session-state.md +25 -0
- package/dist/agent/pages/docs/understand-the-system/nodes-and-hierarchy.md +24 -0
- package/dist/agent/pages/docs/understand-the-system/packages-and-plugins.md +29 -0
- package/dist/agent/pages/docs/understand-the-system/persistence-and-json-canvas.md +25 -0
- package/dist/agent/pages/docs/understand-the-system/rendering-and-interaction.md +22 -0
- package/dist/agent/pages/docs/understand-the-system/the-engine.md +28 -0
- package/dist/agent/pages/docs.md +18 -0
- package/dist/engine/transaction.d.ts +2 -2
- package/dist/index.js +18 -7
- package/dist/types.d.ts +2 -2
- package/package.json +3 -2
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Mind map"
|
|
3
|
+
description: "Spatial topic hierarchy with connections, custom renderers, and branch-from-selection."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/solutions/mind-map"
|
|
5
|
+
route: "/docs/solutions/mind-map"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Mind map
|
|
13
|
+
|
|
14
|
+
> Spatial topic hierarchy with connections, custom renderers, and branch-from-selection.
|
|
15
|
+
|
|
16
|
+
> Component omitted: `mind-map-demo`.
|
|
17
|
+
> This page contains an interactive or site-specific block that has no agent markdown serializer yet.
|
|
18
|
+
|
|
19
|
+
## What happens
|
|
20
|
+
|
|
21
|
+
The root topic sits at the center with branches radiating outward. Each topic is a custom renderer that styles itself based on depth (root, branch, leaf). Connections use bezier routing to draw the edges between parent and child topics.
|
|
22
|
+
|
|
23
|
+
Select any topic and click "Add branch" to grow the map from that point. The new node auto-connects to its parent using the connections plugin. History tracks every change so you can undo freely.
|
|
24
|
+
|
|
25
|
+
Topic text stays as topic text. The renderer gets depth from the connection
|
|
26
|
+
graph, so branch styling is derived from the map structure instead of hidden in
|
|
27
|
+
the first line of `node.text`.
|
|
28
|
+
|
|
29
|
+
## The code
|
|
30
|
+
|
|
31
|
+
<code-tree defaultValue="app.vue">
|
|
32
|
+
```vue [app.vue]
|
|
33
|
+
<script setup lang="ts">
|
|
34
|
+
import { computed } from 'vue'
|
|
35
|
+
import { asNodeId, createBoardEngine } from '@lupinum/board-core'
|
|
36
|
+
import { connectionsPlugin } from '@lupinum/board-connections'
|
|
37
|
+
import { BoardConnectionLayer } from '@lupinum/board-connections/vue'
|
|
38
|
+
import { historyPlugin } from '@lupinum/board-history'
|
|
39
|
+
import TopicNode from './TopicNode.vue'
|
|
40
|
+
|
|
41
|
+
const ROOT_ID = asNodeId('root')
|
|
42
|
+
const engine = createBoardEngine({
|
|
43
|
+
grid: { size: 20, snap: true, pattern: 'dot' },
|
|
44
|
+
plugins: [historyPlugin(), connectionsPlugin({ routing: 'bezier' })],
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const topicDepthsById = computed(() => {
|
|
48
|
+
const depths = new Map([[ROOT_ID, 0]])
|
|
49
|
+
for (const edge of engine.plugins.connections.getEdges()) {
|
|
50
|
+
const fromDepth = depths.get(edge.from)
|
|
51
|
+
if (fromDepth !== undefined) {
|
|
52
|
+
depths.set(edge.to, fromDepth + 1)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return depths
|
|
56
|
+
})
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<template>
|
|
60
|
+
<BoardRoot :engine="engine" style="height: 100vh">
|
|
61
|
+
<template #node:text="{ node, selected }">
|
|
62
|
+
<TopicNode
|
|
63
|
+
:node="node"
|
|
64
|
+
:selected="selected"
|
|
65
|
+
:depth="topicDepthsById.get(node.id) ?? 2"
|
|
66
|
+
/>
|
|
67
|
+
</template>
|
|
68
|
+
<BoardConnectionLayer routing="bezier" />
|
|
69
|
+
</BoardRoot>
|
|
70
|
+
</template>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```vue [TopicNode.vue]
|
|
74
|
+
<script setup lang="ts">
|
|
75
|
+
import { computed } from 'vue'
|
|
76
|
+
|
|
77
|
+
const props = defineProps<{
|
|
78
|
+
node: { text?: string }
|
|
79
|
+
depth: number
|
|
80
|
+
}>()
|
|
81
|
+
|
|
82
|
+
const topic = computed(() => {
|
|
83
|
+
const [title = 'Topic', detail = ''] = props.node.text?.split('\n') ?? []
|
|
84
|
+
return { title, detail }
|
|
85
|
+
})
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<template>
|
|
89
|
+
<div class="topic-card" :class="depthClass(depth)">
|
|
90
|
+
<strong>{{ topic.title }}</strong>
|
|
91
|
+
<p>
|
|
92
|
+
{{ topic.detail }}
|
|
93
|
+
</p>
|
|
94
|
+
</div>
|
|
95
|
+
</template>
|
|
96
|
+
```
|
|
97
|
+
</code-tree>
|
|
98
|
+
|
|
99
|
+
## Try these things
|
|
100
|
+
|
|
101
|
+
- Select the root topic, then add a branch. The new node connects automatically.
|
|
102
|
+
- Drag a branch away from the center. The bezier edge stretches to follow.
|
|
103
|
+
- Undo a few times to watch the map shrink back.
|
|
104
|
+
- Zoom out to see the full map structure.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Nuxt auto-imports"
|
|
3
|
+
description: "Use board components and composables without manual imports via the Nuxt module."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/solutions/nuxt-application"
|
|
5
|
+
route: "/docs/solutions/nuxt-application"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Nuxt auto-imports
|
|
13
|
+
|
|
14
|
+
> Use board components and composables without manual imports via the Nuxt module.
|
|
15
|
+
|
|
16
|
+
> Component omitted: `nuxt-auto-imports-demo`.
|
|
17
|
+
> This page contains an interactive or site-specific block that has no agent markdown serializer yet.
|
|
18
|
+
|
|
19
|
+
## What happens
|
|
20
|
+
|
|
21
|
+
This is the same board model you would build in Vue, but the Nuxt module auto-imports the component and composable layer. That removes boilerplate without changing the engine surface.
|
|
22
|
+
|
|
23
|
+
## The code
|
|
24
|
+
|
|
25
|
+
<code-tree defaultValue="app.vue">
|
|
26
|
+
```ts [nuxt.config.ts]
|
|
27
|
+
export default defineNuxtConfig({
|
|
28
|
+
modules: ['@lupinum/nuxt-board'],
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```vue [app.vue]
|
|
33
|
+
<script setup lang="ts">
|
|
34
|
+
const engine = createBoardEngine()
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<template>
|
|
38
|
+
<BoardRoot :engine="engine" style="height: 100vh" />
|
|
39
|
+
</template>
|
|
40
|
+
```
|
|
41
|
+
</code-tree>
|
|
42
|
+
|
|
43
|
+
## Try these things
|
|
44
|
+
|
|
45
|
+
- Add a node from the demo toolbar.
|
|
46
|
+
- Confirm the board works without any explicit imports in the example source.
|
|
47
|
+
- Compare it with the [Quick Start](/raw/docs/start-building/your-first-board.md) tabs.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Planning board"
|
|
3
|
+
description: "A practical planning surface with notes, grouping, and zoom-to-fit."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/solutions/planning-board"
|
|
5
|
+
route: "/docs/solutions/planning-board"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Planning board
|
|
13
|
+
|
|
14
|
+
> A practical planning surface with notes, grouping, and zoom-to-fit.
|
|
15
|
+
|
|
16
|
+
> Component omitted: `basic-board-demo`.
|
|
17
|
+
> This page contains an interactive or site-specific block that has no agent markdown serializer yet.
|
|
18
|
+
|
|
19
|
+
## What happens
|
|
20
|
+
|
|
21
|
+
This planning surface uses a seeded document, direct engine commands, grouping, and the default interaction model. The application can add domain renderers later without replacing its board document.
|
|
22
|
+
|
|
23
|
+
## The code
|
|
24
|
+
|
|
25
|
+
<code-tree defaultValue="app.vue">
|
|
26
|
+
```ts [app.vue]
|
|
27
|
+
const engine = createBoardEngine({
|
|
28
|
+
grid: { size: 20, majorEvery: 5, snap: true, pattern: 'line' },
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
engine.loadDocument(document, { mode: 'replace' })
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```ts [toolbar.ts]
|
|
35
|
+
engine.createNode({ type: 'text', text: 'New note' })
|
|
36
|
+
engine.zoomToFit(72, false)
|
|
37
|
+
```
|
|
38
|
+
</code-tree>
|
|
39
|
+
|
|
40
|
+
## Try these things
|
|
41
|
+
|
|
42
|
+
- Click **Add note** and drag the new node around. It snaps to the grid.
|
|
43
|
+
- Hold <kbd>
|
|
44
|
+
Shift
|
|
45
|
+
</kbd>
|
|
46
|
+
and click two nodes, then click **Group selection**.
|
|
47
|
+
- Double-click a node to edit its text. Press <kbd>
|
|
48
|
+
Escape
|
|
49
|
+
</kbd>
|
|
50
|
+
to cancel.
|
|
51
|
+
- Spread nodes apart, then click **Zoom to fit** to recenter.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Read-only viewer"
|
|
3
|
+
description: "Display a board for viewing without allowing edits by using a command guard."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/solutions/read-only-viewer"
|
|
5
|
+
route: "/docs/solutions/read-only-viewer"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Read-only viewer
|
|
13
|
+
|
|
14
|
+
> Display a board for viewing without allowing edits by using a command guard.
|
|
15
|
+
|
|
16
|
+
> Component omitted: `read-only-toggle-demo`.
|
|
17
|
+
> This page contains an interactive or site-specific block that has no agent markdown serializer yet.
|
|
18
|
+
|
|
19
|
+
## What happens
|
|
20
|
+
|
|
21
|
+
This example uses `addCommandGuard` to register a command guard. The board still renders and the camera still moves, but the edit commands used by the demo exit early when read-only mode is enabled.
|
|
22
|
+
|
|
23
|
+
## The code
|
|
24
|
+
|
|
25
|
+
<steps>
|
|
26
|
+
### Load the scene
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
const engine = createBoardEngine()
|
|
30
|
+
engine.loadDocument(JSON.parse(savedBoardJson), { mode: 'replace' })
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Block mutation commands
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
const blocked = new Set([
|
|
37
|
+
'beginNodeDrag',
|
|
38
|
+
'beginResize',
|
|
39
|
+
'beginTextEdit',
|
|
40
|
+
'createNode',
|
|
41
|
+
'updateNode',
|
|
42
|
+
'deleteSelected',
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
const stopReadOnly = engine.addCommandGuard(({ name }) =>
|
|
46
|
+
blocked.has(name) ? 'Board is read-only.' : true,
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Remove the guard to restore editing
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
stopReadOnly()
|
|
54
|
+
```
|
|
55
|
+
</steps>
|
|
56
|
+
|
|
57
|
+
## Try these things
|
|
58
|
+
|
|
59
|
+
- Leave the demo in read-only mode and try dragging or deleting a node.
|
|
60
|
+
- Toggle back to edit mode and repeat the same action.
|
|
61
|
+
- Watch the blocked command counter increment when a guard rejects a mutation.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Workflow renderer demo"
|
|
3
|
+
description: "Render step-style cards with custom renderers, connections, and undo/redo."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/solutions/workflow-builder"
|
|
5
|
+
route: "/docs/solutions/workflow-builder"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Workflow renderer demo
|
|
13
|
+
|
|
14
|
+
> Render step-style cards with custom renderers, connections, and undo/redo.
|
|
15
|
+
|
|
16
|
+
> Component omitted: `workflow-renderer-demo`.
|
|
17
|
+
> This page contains an interactive or site-specific block that has no agent markdown serializer yet.
|
|
18
|
+
|
|
19
|
+
## What happens
|
|
20
|
+
|
|
21
|
+
This example combines three patterns: app-owned workflow records, board-owned
|
|
22
|
+
layout, and first-party connections/history. The workflow step fields live in
|
|
23
|
+
one app model. Board nodes keep only the spatial identity that the canvas needs.
|
|
24
|
+
|
|
25
|
+
The history plugin records board commands such as dragging, resizing, grouping,
|
|
26
|
+
and connection edits. If your product needs undo for workflow status changes,
|
|
27
|
+
put that in the workflow model's own command/history path instead of copying the
|
|
28
|
+
status into `node.text`.
|
|
29
|
+
|
|
30
|
+
## The code
|
|
31
|
+
|
|
32
|
+
<code-tree defaultValue="app.vue">
|
|
33
|
+
```vue [app.vue]
|
|
34
|
+
<script setup lang="ts">
|
|
35
|
+
import { computed, ref } from 'vue'
|
|
36
|
+
import { asNodeId, createBoardEngine } from '@lupinum/board-core'
|
|
37
|
+
import { historyPlugin } from '@lupinum/board-history'
|
|
38
|
+
import { connectionsPlugin } from '@lupinum/board-connections'
|
|
39
|
+
import { BoardConnectionLayer } from '@lupinum/board-connections/vue'
|
|
40
|
+
import StepNode from './StepNode.vue'
|
|
41
|
+
|
|
42
|
+
const steps = ref([
|
|
43
|
+
{
|
|
44
|
+
id: 'capture',
|
|
45
|
+
status: 'done',
|
|
46
|
+
label: 'Capture lead',
|
|
47
|
+
summary: 'Collect the request and normalize inputs.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'qualify',
|
|
51
|
+
status: 'active',
|
|
52
|
+
label: 'Qualify',
|
|
53
|
+
summary: 'Score, tag, and route the lead.',
|
|
54
|
+
},
|
|
55
|
+
])
|
|
56
|
+
const stepsById = computed(
|
|
57
|
+
() => new Map(steps.value.map((step) => [step.id, step])),
|
|
58
|
+
)
|
|
59
|
+
const engine = createBoardEngine({
|
|
60
|
+
plugins: [historyPlugin(), connectionsPlugin({ routing: 'step' })],
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
for (const step of steps.value) {
|
|
64
|
+
engine.createNode({
|
|
65
|
+
id: asNodeId(step.id),
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: '',
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<template>
|
|
73
|
+
<BoardRoot :engine="engine" style="height: 100vh">
|
|
74
|
+
<template #node:text="{ node, selected }">
|
|
75
|
+
<StepNode :step="stepsById.get(node.id)" :selected="selected" />
|
|
76
|
+
</template>
|
|
77
|
+
<BoardConnectionLayer routing="step" />
|
|
78
|
+
</BoardRoot>
|
|
79
|
+
</template>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```vue [StepNode.vue]
|
|
83
|
+
<script setup lang="ts">
|
|
84
|
+
defineProps<{
|
|
85
|
+
step?: {
|
|
86
|
+
status: 'pending' | 'active' | 'done'
|
|
87
|
+
label: string
|
|
88
|
+
summary: string
|
|
89
|
+
}
|
|
90
|
+
selected: boolean
|
|
91
|
+
}>()
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<template>
|
|
95
|
+
<div class="step-card">
|
|
96
|
+
<span>{{ step?.status ?? 'pending' }}</span>
|
|
97
|
+
<strong>{{ step?.label ?? 'Missing step' }}</strong>
|
|
98
|
+
<p>{{ step?.summary }}</p>
|
|
99
|
+
</div>
|
|
100
|
+
</template>
|
|
101
|
+
```
|
|
102
|
+
</code-tree>
|
|
103
|
+
|
|
104
|
+
## Try these things
|
|
105
|
+
|
|
106
|
+
- Select a step, then click **Cycle selected step** to advance its status.
|
|
107
|
+
- Drag a step — the routed edges redraw to stay readable and the engine history records the layout change.
|
|
108
|
+
- Press <kbd>
|
|
109
|
+
⌘
|
|
110
|
+
</kbd>
|
|
111
|
+
+ <kbd>
|
|
112
|
+
Z
|
|
113
|
+
</kbd>
|
|
114
|
+
to undo the layout change, then <kbd>
|
|
115
|
+
⌘
|
|
116
|
+
</kbd>
|
|
117
|
+
+ <kbd>
|
|
118
|
+
Shift
|
|
119
|
+
</kbd>
|
|
120
|
+
+ <kbd>
|
|
121
|
+
Z
|
|
122
|
+
</kbd>
|
|
123
|
+
to redo it.
|
|
124
|
+
- Watch the undo/redo counter in the toolbar update in real time.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Add connections and history"
|
|
3
|
+
description: "Install edges and undo without expanding the core package."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/start-building/add-connections-and-history"
|
|
5
|
+
route: "/docs/start-building/add-connections-and-history"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Add connections and history
|
|
13
|
+
|
|
14
|
+
> Install edges and undo without expanding the core package.
|
|
15
|
+
|
|
16
|
+
Install both plugins when constructing the engine:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { connectionsPlugin } from '@lupinum/board-connections'
|
|
20
|
+
import { historyPlugin } from '@lupinum/board-history'
|
|
21
|
+
|
|
22
|
+
const engine = createBoardEngine({
|
|
23
|
+
plugins: [connectionsPlugin(), historyPlugin()],
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Render connections under the matching `BoardRoot`:
|
|
28
|
+
|
|
29
|
+
```vue
|
|
30
|
+
<BoardRoot :engine="engine" style="height: 100vh">
|
|
31
|
+
<BoardConnectionLayer />
|
|
32
|
+
</BoardRoot>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Create an edge through its installed API and undo through history:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
engine.plugins.connections.createEdge({ from: source.id, to: target.id })
|
|
39
|
+
engine.plugins.history.undo()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Plugin names must be unique. Construction throws `BoardConflictError` before installation when the tuple contains duplicate names.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Customize your first node"
|
|
3
|
+
description: "Render product-specific Vue content without creating another document schema."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/start-building/customize-your-first-node"
|
|
5
|
+
route: "/docs/start-building/customize-your-first-node"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Customize your first node
|
|
13
|
+
|
|
14
|
+
> Render product-specific Vue content without creating another document schema.
|
|
15
|
+
|
|
16
|
+
Register a Vue component for an existing JSON Canvas node type.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import type { BoardRendererRegistry } from '@lupinum/vue-board'
|
|
20
|
+
import TaskCard from './TaskCard.vue'
|
|
21
|
+
|
|
22
|
+
const renderers: BoardRendererRegistry = {
|
|
23
|
+
text: TaskCard,
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```vue
|
|
28
|
+
<BoardRoot :engine="engine" :renderers="renderers" style="height: 100vh" />
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The renderer receives the node and selection state. Store canonical content in direct node fields such as `text`; do not introduce a nested content mirror.
|
|
32
|
+
|
|
33
|
+
> Component omitted: `renderer-lab`.
|
|
34
|
+
> This page contains an interactive or site-specific block that has no agent markdown serializer yet.
|
|
35
|
+
|
|
36
|
+
Switch the renderer in the lab. The card changes while the record keeps the same ID, type, text, and color.
|
|
37
|
+
|
|
38
|
+
For a complete renderer contract, editing hooks, slots, and LOD behavior, read [Custom node renderers](/raw/docs/build-features/custom-node-renderers.md).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Installation"
|
|
3
|
+
description: "Install the smallest Nuxt Board package set for Vue or Nuxt."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/start-building/installation"
|
|
5
|
+
route: "/docs/start-building/installation"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Installation
|
|
13
|
+
|
|
14
|
+
> Install the smallest Nuxt Board package set for Vue or Nuxt.
|
|
15
|
+
|
|
16
|
+
## Use a coding agent
|
|
17
|
+
|
|
18
|
+
A coding agent is a development tool that can inspect and change your project.
|
|
19
|
+
|
|
20
|
+
Copy this task prompt into your application agent:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
Use the installed Nuxt Board packages to implement my requested feature.
|
|
24
|
+
Read the application's instructions first. Resolve @lupinum/board-core/agent-docs
|
|
25
|
+
and each installed Board package’s agent-docs entry from this application
|
|
26
|
+
directory. Read the relevant local pages.
|
|
27
|
+
Preserve the existing AGENTS.md. If it has no equivalent guidance, append
|
|
28
|
+
one short note to resolve installed package docs before integration work
|
|
29
|
+
and after dependency changes. Do not install a consumer skill.
|
|
30
|
+
Check the completed feature using this project's normal commands.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The installed documentation matches the package version. If an older version
|
|
34
|
+
has no documentation export, use its README, types and matching release docs.
|
|
35
|
+
|
|
36
|
+
## Vue
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm add @lupinum/board-core @lupinum/vue-board
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Import the stylesheet once in the application entry or root component:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import '@lupinum/vue-board/style.css'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Nuxt Board requires Vue 3.5 and Node 20.19 or newer for the supported toolchain.
|
|
49
|
+
|
|
50
|
+
## Nuxt
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pnpm add @lupinum/nuxt-board @lupinum/board-core @lupinum/vue-board
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
export default defineNuxtConfig({
|
|
58
|
+
modules: ['@lupinum/nuxt-board'],
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The module registers styles and optional component/composable auto-imports. It owns no board behavior.
|
|
63
|
+
|
|
64
|
+
Vue and Nuxt use the same engine. For SSR, use deterministic IDs and complete
|
|
65
|
+
initial records; do not create random initial nodes during hydration. Browser-only
|
|
66
|
+
measurements remain session state and are populated after mount.
|
|
67
|
+
|
|
68
|
+
## Optional packages
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pnpm add @lupinum/board-connections @lupinum/board-history
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Import the minimap from `@lupinum/vue-board/minimap`; it is a subpath, not a separate package.
|
|
75
|
+
|
|
76
|
+
Install optional packages only when the product needs their capability. Continue with [Your first board](/raw/docs/start-building/your-first-board.md).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Your first board"
|
|
3
|
+
description: "Create, populate, and mount a working board with one canonical example."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/start-building/your-first-board"
|
|
5
|
+
route: "/docs/start-building/your-first-board"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Your first board
|
|
13
|
+
|
|
14
|
+
> Create, populate, and mount a working board with one canonical example.
|
|
15
|
+
|
|
16
|
+
Create one stable engine and pass it to `BoardRoot`.
|
|
17
|
+
|
|
18
|
+
```vue
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
import { createBoardEngine } from '@lupinum/board-core'
|
|
21
|
+
import { BoardRoot } from '@lupinum/vue-board'
|
|
22
|
+
import '@lupinum/vue-board/style.css'
|
|
23
|
+
|
|
24
|
+
const engine = createBoardEngine({
|
|
25
|
+
grid: { size: 20, snap: true },
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
engine.createNode({
|
|
29
|
+
type: 'text',
|
|
30
|
+
x: 80,
|
|
31
|
+
y: 80,
|
|
32
|
+
width: 260,
|
|
33
|
+
height: 140,
|
|
34
|
+
color: '5',
|
|
35
|
+
text: 'Review onboarding',
|
|
36
|
+
})
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<BoardRoot :engine="engine" style="height: 100vh" />
|
|
41
|
+
</template>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The container needs a real height. `BoardRoot` cannot infer space from an auto-sized empty parent.
|
|
45
|
+
|
|
46
|
+
Try dragging, resizing, selecting, panning, and zooming. `createNode()` selects the new node by default.
|
|
47
|
+
|
|
48
|
+
## What happened
|
|
49
|
+
|
|
50
|
+
The engine created and selected a JSON Canvas text record. `BoardRoot` subscribed to the engine and rendered the record with the default renderer. Pointer interactions update the same engine through the framework adapter.
|
|
51
|
+
|
|
52
|
+
Keep the engine instance stable after mount. Load new contents with `engine.loadDocument()` instead of replacing the engine prop.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Camera and coordinates"
|
|
3
|
+
description: "Pan, zoom, viewport measurements, and board-space conversion."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/understand-the-system/camera-and-coordinates"
|
|
5
|
+
route: "/docs/understand-the-system/camera-and-coordinates"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Camera and coordinates
|
|
13
|
+
|
|
14
|
+
> Pan, zoom, viewport measurements, and board-space conversion.
|
|
15
|
+
|
|
16
|
+
The camera stores translation `x`, `y`, and zoom `z`. Nodes remain in board coordinates. `BoardRoot` applies the camera transform and owns viewport measurement.
|
|
17
|
+
|
|
18
|
+
Use engine camera commands such as `panBy`, `zoomTo`, and `zoomToFit`. Use the exported math helpers when application UI must convert between screen and board space.
|
|
19
|
+
|
|
20
|
+
Viewport size is runtime session state because it depends on the mounted DOM. It does not belong in the persisted JSON Canvas document.
|
|
21
|
+
|
|
22
|
+
Rendering can derive visible bounds from the camera and viewport, then cull nodes outside the margin. Camera changes publish through their focused subscribable and events.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Commands and transactions"
|
|
3
|
+
description: "How changes are guarded, staged, validated, committed, and published."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/understand-the-system/commands-and-transactions"
|
|
5
|
+
route: "/docs/understand-the-system/commands-and-transactions"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Commands and transactions
|
|
13
|
+
|
|
14
|
+
> How changes are guarded, staged, validated, committed, and published.
|
|
15
|
+
|
|
16
|
+
Every persistent command joins one outer transaction.
|
|
17
|
+
|
|
18
|
+
1. Command guards inspect an immutable description.
|
|
19
|
+
2. Writable roots and plugin slices are staged.
|
|
20
|
+
3. The command and nested commands update the candidate.
|
|
21
|
+
4. Core and plugin invariants validate the candidate.
|
|
22
|
+
5. Commit projectors finalize history and plugin effects.
|
|
23
|
+
6. Subscriptions publish.
|
|
24
|
+
7. Public events publish.
|
|
25
|
+
|
|
26
|
+
> Component omitted: `failed-transaction-lab`.
|
|
27
|
+
> This page contains an interactive or site-specific block that has no agent markdown serializer yet.
|
|
28
|
+
|
|
29
|
+
Run the failing batch. The first nested update is staged, but the duplicate ID rejects the candidate. State, plugin slices, queued events, subscriptions, and history remain unchanged.
|
|
30
|
+
|
|
31
|
+
Subscriber callbacks receive the value from before the outer command as `prev`, even when nested commands updated the same concept several times.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Document and session state"
|
|
3
|
+
description: "Why rendered state and exported state can differ during a gesture."
|
|
4
|
+
url: "https://nuxt-board.lupinum.com/docs/understand-the-system/document-and-session-state"
|
|
5
|
+
route: "/docs/understand-the-system/document-and-session-state"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Document and session state
|
|
13
|
+
|
|
14
|
+
> Why rendered state and exported state can differ during a gesture.
|
|
15
|
+
|
|
16
|
+
Committed document state contains the records a successful command can persist. Session state contains browser measurements, active interaction, snap guides, clipboard data, and transient geometry.
|
|
17
|
+
|
|
18
|
+
> Component omitted: `document-session-lab`.
|
|
19
|
+
> This page contains an interactive or site-specific block that has no agent markdown serializer yet.
|
|
20
|
+
|
|
21
|
+
During a drag, `$nodes` exposes effective geometry: committed nodes plus transient overrides. `exportDocument()` continues to return committed geometry. Releasing the pointer commits the final position once. Escape or pointer cancellation discards the override.
|
|
22
|
+
|
|
23
|
+
This distinction prevents every pointer move from becoming a document transaction or history entry. The trade-off is intentional: runtime and exported coordinates may differ while the gesture is active.
|
|
24
|
+
|
|
25
|
+
Persist `exportDocument()`, not `getState()`, Vue refs, or DOM state.
|