@lupinum/board-core 1.0.0-beta.4 → 1.0.0-beta.6
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/CHANGELOG.md +9 -0
- package/README.md +46 -25
- package/dist/agent/AGENTS.md +53 -57
- package/dist/agent/pages/docs/build-features/connections.md +333 -2
- package/dist/agent/pages/docs/build-features/custom-node-renderers.md +366 -2
- package/dist/agent/pages/docs/build-features/read-only-and-command-guards.md +278 -2
- package/dist/agent/pages/docs/build-features/save-and-load.md +264 -2
- package/dist/agent/pages/docs/build-features/selection-and-keyboard.md +257 -2
- package/dist/agent/pages/docs/build-features/theming.md +285 -2
- package/dist/agent/pages/docs/evaluate/how-nuxt-board-works.md +451 -2
- package/dist/agent/pages/docs/project/contributing.md +4 -4
- package/dist/agent/pages/docs/project/support-and-security.md +1 -1
- package/dist/agent/pages/docs/reference/events-and-errors.md +279 -2
- package/dist/agent/pages/docs/reference/package-overview.md +2 -2
- package/dist/agent/pages/docs/solutions/mind-map.md +448 -2
- package/dist/agent/pages/docs/solutions/nuxt-application.md +214 -2
- package/dist/agent/pages/docs/solutions/planning-board.md +281 -2
- package/dist/agent/pages/docs/solutions/read-only-viewer.md +278 -2
- package/dist/agent/pages/docs/solutions/workflow-builder.md +435 -2
- package/dist/agent/pages/docs/start-building/customize-your-first-node.md +381 -2
- package/dist/agent/pages/docs/start-building/installation.md +6 -14
- package/dist/agent/pages/docs/understand-the-system/commands-and-transactions.md +456 -2
- package/dist/agent/pages/docs/understand-the-system/document-and-session-state.md +348 -2
- package/dist/agent/pages/docs/understand-the-system/persistence-and-json-canvas.md +445 -2
- package/package.json +3 -2
- package/dist/agent/manifest.json +0 -320
|
@@ -15,8 +15,285 @@ source: "ginko-content"
|
|
|
15
15
|
|
|
16
16
|
Events tell you what happened. Subscribables tell you what state looks like now. Use events to react to discrete actions, use subscribables to keep your UI in sync.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
## Live example source
|
|
19
|
+
|
|
20
|
+
Use the installation guide to register @lupinum/nuxt-board. These are the actual documentation demo files. Keep their paths under app/. The module auto-imports Board components; install the optional packages named in imports. For the local content components, use components: [{ path: "~/components/content", pathPrefix: false }, "~/components"] in nuxt.config.ts, as this site does, or import them explicitly. createDemoDocument is a local helper, not a package export. DocsLab and the inspector components provide presentation only.
|
|
21
|
+
|
|
22
|
+
### app/components/demos/EventLogger.vue
|
|
23
|
+
|
|
24
|
+
````vue
|
|
25
|
+
<script setup lang="ts">
|
|
26
|
+
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
|
27
|
+
import { createBoardEngine } from '@lupinum/board-core'
|
|
28
|
+
import { createDemoDocument } from '../../utils/demoDocument'
|
|
29
|
+
|
|
30
|
+
const engine = createBoardEngine({
|
|
31
|
+
grid: { size: 20, majorEvery: 5, snap: true, pattern: 'dot' },
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const entries = ref<string[]>([])
|
|
35
|
+
const unsubscribes: Array<() => void> = []
|
|
36
|
+
|
|
37
|
+
function push(message: string) {
|
|
38
|
+
entries.value = [
|
|
39
|
+
`${new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })} ${message}`,
|
|
40
|
+
...entries.value,
|
|
41
|
+
].slice(0, 18)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function seed() {
|
|
45
|
+
engine.loadDocument(
|
|
46
|
+
createDemoDocument({
|
|
47
|
+
camera: { x: 0, y: 0, z: 1 },
|
|
48
|
+
grid: engine.getGridSettings(),
|
|
49
|
+
nodes: [
|
|
50
|
+
{
|
|
51
|
+
id: 'event-a',
|
|
52
|
+
type: 'text',
|
|
53
|
+
x: 70,
|
|
54
|
+
y: 80,
|
|
55
|
+
width: 220,
|
|
56
|
+
height: 100,
|
|
57
|
+
text: 'Node',
|
|
58
|
+
zIndex: 1,
|
|
59
|
+
locked: false,
|
|
60
|
+
visible: true,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'event-b',
|
|
64
|
+
type: 'text',
|
|
65
|
+
x: 360,
|
|
66
|
+
y: 200,
|
|
67
|
+
width: 220,
|
|
68
|
+
height: 100,
|
|
69
|
+
text: 'Node',
|
|
70
|
+
zIndex: 2,
|
|
71
|
+
locked: false,
|
|
72
|
+
visible: true,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
selection: [],
|
|
76
|
+
nextZIndex: 3,
|
|
77
|
+
}),
|
|
78
|
+
{ mode: 'replace' },
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function addNode() {
|
|
83
|
+
const index = engine.getState().nodes.size - 2
|
|
84
|
+
engine.createNode({
|
|
85
|
+
type: 'text',
|
|
86
|
+
x: 120 + (index % 3) * 140,
|
|
87
|
+
y: 80 + (index % 2) * 120,
|
|
88
|
+
width: 220,
|
|
89
|
+
height: 110,
|
|
90
|
+
text: `Observed command ${index + 1}`,
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function deleteSelected() {
|
|
95
|
+
engine.deleteSelected()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function reset() {
|
|
99
|
+
entries.value = []
|
|
100
|
+
seed()
|
|
101
|
+
push('ready')
|
|
102
|
+
await engine.zoomToFit(60, false)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
onMounted(() => {
|
|
106
|
+
unsubscribes.push(
|
|
107
|
+
engine.on('node:created', (node) => push(`node:created ${node.id}`)),
|
|
108
|
+
engine.on('node:updated', (node) => push(`node:updated ${node.id}`)),
|
|
109
|
+
engine.on('node:deleted', (id) => push(`node:deleted ${id}`)),
|
|
110
|
+
engine.on('selection:change', (selected) =>
|
|
111
|
+
push(`selection:change [${selected.join(', ') || 'none'}]`),
|
|
112
|
+
),
|
|
113
|
+
engine.on('interaction:start', (state) =>
|
|
114
|
+
push(`interaction:start ${state.mode}`),
|
|
115
|
+
),
|
|
116
|
+
engine.on('interaction:end', (state) =>
|
|
117
|
+
push(`interaction:end ${state.mode}`),
|
|
118
|
+
),
|
|
119
|
+
engine.on('command:after', (name) => push(`command:after ${name}`)),
|
|
120
|
+
)
|
|
121
|
+
void reset()
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
onBeforeUnmount(() => {
|
|
125
|
+
for (const unsubscribe of unsubscribes) {
|
|
126
|
+
unsubscribe()
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<template>
|
|
132
|
+
<div class="demo-frame">
|
|
133
|
+
<div class="demo-toolbar">
|
|
134
|
+
<button class="demo-danger" @click="reset">Reset</button>
|
|
135
|
+
<button class="demo-primary" @click="addNode">Add node</button>
|
|
136
|
+
<button @click="deleteSelected">Delete selected</button>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<div class="grid gap-0 lg:grid-cols-[minmax(0,1fr)_340px]">
|
|
140
|
+
<BoardRoot :engine="engine" style="height: 360px" />
|
|
141
|
+
|
|
142
|
+
<div
|
|
143
|
+
class="border-t border-default bg-inverted p-4 font-mono text-xs text-primary lg:border-t-0 lg:border-l"
|
|
144
|
+
>
|
|
145
|
+
<p
|
|
146
|
+
class="text-[0.7rem] font-semibold uppercase tracking-[0.28em] text-primary"
|
|
147
|
+
>
|
|
148
|
+
Live event stream
|
|
149
|
+
</p>
|
|
150
|
+
<ul class="mt-3 space-y-2 leading-6">
|
|
151
|
+
<li
|
|
152
|
+
v-for="entry in entries"
|
|
153
|
+
:key="entry"
|
|
154
|
+
class="rounded-md bg-elevated/10 px-3 py-2"
|
|
155
|
+
>
|
|
156
|
+
{{ entry }}
|
|
157
|
+
</li>
|
|
158
|
+
<li
|
|
159
|
+
v-if="entries.length === 0"
|
|
160
|
+
class="rounded-md border border-dashed border-white/10 px-3 py-4 text-dimmed"
|
|
161
|
+
>
|
|
162
|
+
No events yet.
|
|
163
|
+
</li>
|
|
164
|
+
</ul>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</template>
|
|
169
|
+
````
|
|
170
|
+
|
|
171
|
+
### app/utils/demoDocument.ts
|
|
172
|
+
|
|
173
|
+
````ts
|
|
174
|
+
import type {
|
|
175
|
+
BoardNode,
|
|
176
|
+
BoardNodeMetadata,
|
|
177
|
+
Camera,
|
|
178
|
+
GridSettings,
|
|
179
|
+
JsonCanvasDocument,
|
|
180
|
+
JsonCanvasNode,
|
|
181
|
+
NodeId,
|
|
182
|
+
} from '@lupinum/board-core'
|
|
183
|
+
|
|
184
|
+
type DemoNode = Omit<
|
|
185
|
+
Pick<
|
|
186
|
+
BoardNode,
|
|
187
|
+
| 'id'
|
|
188
|
+
| 'type'
|
|
189
|
+
| 'x'
|
|
190
|
+
| 'y'
|
|
191
|
+
| 'width'
|
|
192
|
+
| 'height'
|
|
193
|
+
| 'color'
|
|
194
|
+
| 'text'
|
|
195
|
+
| 'file'
|
|
196
|
+
| 'subpath'
|
|
197
|
+
| 'url'
|
|
198
|
+
| 'label'
|
|
199
|
+
| 'background'
|
|
200
|
+
| 'backgroundStyle'
|
|
201
|
+
| 'zIndex'
|
|
202
|
+
| 'locked'
|
|
203
|
+
| 'visible'
|
|
204
|
+
| 'parentId'
|
|
205
|
+
>,
|
|
206
|
+
'id' | 'parentId'
|
|
207
|
+
> & {
|
|
208
|
+
id: NodeId | string
|
|
209
|
+
parentId?: NodeId | string
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface DemoDocumentInput {
|
|
213
|
+
nodes: readonly DemoNode[]
|
|
214
|
+
camera?: Camera
|
|
215
|
+
grid?: GridSettings
|
|
216
|
+
selection?: readonly (NodeId | string)[]
|
|
217
|
+
nextZIndex?: number
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function toJsonCanvasNode(node: DemoNode): JsonCanvasNode {
|
|
221
|
+
const base = {
|
|
222
|
+
id: node.id as NodeId,
|
|
223
|
+
type: node.type,
|
|
224
|
+
x: node.x,
|
|
225
|
+
y: node.y,
|
|
226
|
+
width: node.width,
|
|
227
|
+
height: node.height,
|
|
228
|
+
...(node.color ? { color: node.color } : {}),
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
switch (node.type) {
|
|
232
|
+
case 'file':
|
|
233
|
+
return {
|
|
234
|
+
...base,
|
|
235
|
+
type: 'file',
|
|
236
|
+
file: node.file ?? '',
|
|
237
|
+
...(node.subpath ? { subpath: node.subpath } : {}),
|
|
238
|
+
}
|
|
239
|
+
case 'link':
|
|
240
|
+
return {
|
|
241
|
+
...base,
|
|
242
|
+
type: 'link',
|
|
243
|
+
url: node.url ?? '',
|
|
244
|
+
}
|
|
245
|
+
case 'group':
|
|
246
|
+
return {
|
|
247
|
+
...base,
|
|
248
|
+
type: 'group',
|
|
249
|
+
...((node.label ?? node.text)
|
|
250
|
+
? { label: node.label ?? node.text }
|
|
251
|
+
: {}),
|
|
252
|
+
...(node.background ? { background: node.background } : {}),
|
|
253
|
+
...(node.backgroundStyle
|
|
254
|
+
? { backgroundStyle: node.backgroundStyle }
|
|
255
|
+
: {}),
|
|
256
|
+
}
|
|
257
|
+
case 'text':
|
|
258
|
+
default:
|
|
259
|
+
return {
|
|
260
|
+
...base,
|
|
261
|
+
type: 'text',
|
|
262
|
+
text: node.text ?? '',
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function toNodeMetadata(node: DemoNode): BoardNodeMetadata {
|
|
268
|
+
return {
|
|
269
|
+
zIndex: node.zIndex,
|
|
270
|
+
locked: node.locked,
|
|
271
|
+
visible: node.visible,
|
|
272
|
+
...(node.parentId ? { parentId: node.parentId as NodeId } : {}),
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function createDemoDocument(
|
|
277
|
+
input: DemoDocumentInput,
|
|
278
|
+
): JsonCanvasDocument {
|
|
279
|
+
return {
|
|
280
|
+
nodes: input.nodes.map(toJsonCanvasNode),
|
|
281
|
+
'x-lupinum-board': {
|
|
282
|
+
...(input.camera ? { camera: input.camera } : {}),
|
|
283
|
+
...(input.grid ? { grid: input.grid } : {}),
|
|
284
|
+
...(input.selection
|
|
285
|
+
? { selection: input.selection.map((id) => id as NodeId) }
|
|
286
|
+
: {}),
|
|
287
|
+
nextZIndex:
|
|
288
|
+
input.nextZIndex ??
|
|
289
|
+
input.nodes.reduce((max, node) => Math.max(max, node.zIndex), 0) + 1,
|
|
290
|
+
nodes: Object.fromEntries(
|
|
291
|
+
input.nodes.map((node) => [node.id, toNodeMetadata(node)]),
|
|
292
|
+
),
|
|
293
|
+
},
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
````
|
|
20
297
|
|
|
21
298
|
## Events
|
|
22
299
|
|
|
@@ -39,8 +39,8 @@ your application directory; for example, resolve
|
|
|
39
39
|
local pages from its index. The snapshot matches the installed package version.
|
|
40
40
|
Resolve it again after an upgrade or rollback. This export is not a JavaScript module.
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
to
|
|
42
|
+
Follow [Set up your coding agent](/raw/docs/start-building/installation.md#set-up-your-coding-agent)
|
|
43
|
+
to point your agent at these pages. Existing project instructions remain authoritative.
|
|
44
44
|
|
|
45
45
|
## Runtime export index
|
|
46
46
|
|