Workflow renderer demo
Render step-style cards with custom renderers, connections, and undo/redo.
What happens
This example combines three patterns: app-owned workflow records, board-owned layout, and first-party connections/history. The workflow step fields live in one app model. Board nodes keep only the spatial identity that the canvas needs.
The history plugin records board commands such as dragging, resizing, grouping,
and connection edits. If your product needs undo for workflow status changes,
put that in the workflow model's own command/history path instead of copying the
status into node.text.
The code
app.vue
<script setup lang="ts">
import { computed, ref } from 'vue'
import { asNodeId, createBoardEngine } from '@lupinum/board-core'
import { historyPlugin } from '@lupinum/board-history'
import { connectionsPlugin } from '@lupinum/board-connections'
import { BoardConnectionLayer } from '@lupinum/board-connections/vue'
import StepNode from './StepNode.vue'
const steps = ref([
{
id: 'capture',
status: 'done',
label: 'Capture lead',
summary: 'Collect the request and normalize inputs.',
},
{
id: 'qualify',
status: 'active',
label: 'Qualify',
summary: 'Score, tag, and route the lead.',
},
])
const stepsById = computed(
() => new Map(steps.value.map((step) => [step.id, step])),
)
const engine = createBoardEngine({
plugins: [historyPlugin(), connectionsPlugin({ routing: 'step' })],
})
for (const step of steps.value) {
engine.createNode({
id: asNodeId(step.id),
type: 'text',
text: '',
})
}
</script>
<template>
<BoardRoot :engine="engine" style="height: 100vh">
<template #node:text="{ node, selected }">
<StepNode :step="stepsById.get(node.id)" :selected="selected" />
</template>
<BoardConnectionLayer routing="step" />
</BoardRoot>
</template><script setup lang="ts">
defineProps<{
step?: {
status: 'pending' | 'active' | 'done'
label: string
summary: string
}
selected: boolean
}>()
</script>
<template>
<div class="step-card">
<span>{{ step?.status ?? 'pending' }}</span>
<strong>{{ step?.label ?? 'Missing step' }}</strong>
<p>{{ step?.summary }}</p>
</div>
</template>Try these things
- Select a step, then click Cycle selected step to advance its status.
- Drag a step — the routed edges redraw to stay readable and the engine history records the layout change.
- Press ⌘ + Z to undo the layout change, then ⌘ + Shift + Z to redo it.
- Watch the undo/redo counter in the toolbar update in real time.