Vancetope — Document Kind canvas
First 2D spatial surface in Vancetope: an area where notes, referenced documents/images, links, and groups can be freely arranged and connected with directed edges — a thinking/arrangement tool (Obsidian Canvas-like), not a freehand whiteboard. See also: app-canvasbook (container app), doc-kind-application §1 Design Principle 4, pointers-channel (live cursor), web-ui §7. Implementation track:
planning/canvas.md.
1. Purpose
A kind: canvas is a surface = a document that holds a graph of Nodes
(at free x/y positions) and directed Edges. It is Vancetope’s answer to “I
want to spatially arrange and connect thoughts”.
Distinction: For a linear rich-text document → kind: workpage; for
workflow states → app: kanban; for a hierarchical outline → kind: mindmap.
Canvas is deliberately only Nodes + Edges — no freehand drawing
(specification/vision.md §7).
This Kind lives in the standalone addon vance-addon-brain-canvas (no
dependency on the workbook addon). When opened standalone, the Canvas view is
read-only; the authoring interface is the app: canvasbook.
2. Data Model
2.1 Top-Level
| Field | Type | Required | Meaning |
|---|---|---|---|
kind |
string = "canvas" |
yes | Top-level discriminator. |
title |
string |
no | Display title. |
description |
string |
no | Free short description. |
canvas |
object |
yes | The graph: nodes + edges. |
Discovery via $meta.kind (documentService.listByKind(tenant, project,
"canvas")). No dedicated collection — CanvasService (vance-addon-brain-canvas)
is layered on DocumentService (data sovereignty). All mutations are a typed
round-trip via CanvasCodec (no field patch on the blob).
2.2 Node Types
Common geometry on each Node: id (persistent, for edge references), x, y,
w, h; optional color (palette index "1"–"6" or hex string), z
(stacking order), parent (see §2.3). Node ids are assigned server-side
(n1, n2, …).
type |
Additional Fields | Rendering |
|---|---|---|
text |
text (Markdown snippet); optional bold, italic, fontSize (s\|m\|l), textColor, author |
yellow sticky note, inline editable |
doc |
ref (vance:-URI to a Document) |
Content rendered by Kind (§3.2) |
link |
href, optional title |
external link card |
group |
optional label |
framed rectangle; container (§2.3) |
2.3 Grouping — Explicitly via parent
A Node belongs to a group if its parent carries the id of a group Node.
If parent is set, x/y are relative to the group’s corner (VueFlow
parenting). This makes membership machine- and LLM-readable — moving a
group moves all children with it; deleting a group releases the children back to
the Canvas root (coordinates become absolute). Nested groups are not planned for
v1 (canvas_validate warns).
2.4 Edges — Directed (Arrows)
id, from (Node ID), to (Node ID); optional fromSide/toSide
(top\|right\|bottom\|left), label, color. Arrow ends fromEnd/toEnd
each none\|arrow — default fromEnd: none, toEnd: arrow (single arrow
from → to). Bidirectional/undirected can thus be expressed.
3. On-Disk Format & Web-UI
3.1 File Format
Recommended extension *.canvas.yaml (or *.canvas.json); $meta.kind: canvas
is mandatory. YAML canonical, JSON 1:1-dual via a common CanvasCodec.
Markdown → KindCodecException.
$meta:
kind: canvas
title: "Architecture Sketch"
canvas:
nodes:
- { id: g1, type: group, x: 60, y: 40, w: 520, h: 320, label: "Research" }
- { id: n1, type: text, parent: g1, x: 20, y: 30, w: 200, h: 120, text: "Core Idea" }
- { id: n2, type: doc, x: 620, y: 60, w: 280, h: 200, ref: "vance:/data/plan.md" }
edges:
- { id: e1, from: n1, to: n2, toEnd: arrow, label: "supported by" }
3.2 Rendering
Editor @vance-addon/canvas based on VueFlow (@vue-flow/core). Custom
Node component per type (sticky note / Doc / Link / group frame). doc Nodes
render the target content by Kind: the host (Cortex) passes its Kind renderer
via provide('vance:embed-component', …) into the addon (Vue singleton across
the federation boundary), so that Records/Mindmap/Chart/… are fully rendered —
just like the Workpage embed. The addon renders images and .md/text itself
(mime- or extension-based) so that documents without a kind also appear
correctly; otherwise, a Kind-aware card. Each doc Node has a ↗-jump to the
Cortex tab.
Editor features: Resize (@vue-flow/node-resizer), Node toolbar
(@vue-flow/node-toolbar: color/font/bold/italic/text color/foreground-background/
delete), in-canvas tool panel, multi-selection, Z-order (no auto-foreground),
inline text edit, Vue dialogs instead of browser prompts, file drag & drop
(any files → upload to <folder>/assets/ → doc Node), connectors on all four
sides.
Live cursor (awareness only, no edit CRDT): the editor sends/displays pointers
via the pointers-channel. Coordinates are transmitted in
Canvas space (zoom-/pan-independent) and rendered at the viewer’s own viewport.
4. LLM Tools
| Tool | Purpose |
|---|---|
canvas_create(path, title?, description?) |
New empty Canvas. |
canvas_node_add(path, node) |
Add Node (text/doc/link/group); returns nodeId. |
canvas_node_update(path, id, patch) |
Patch Node (position/size/color/text/…); id immutable. |
canvas_node_delete(path, id) |
Remove Node + incident edges. |
canvas_edge_add(path, from, to, label?, toEnd?, …) |
Connect edge; returns edgeId. |
canvas_edge_delete(path, id) |
Remove edge. |
canvas_query(path, type?, textContains?) |
Read-only: filter Nodes. |
canvas_validate(path) |
Read-only structural check (§5). |
The graph is LLM-readable as YAML (document_read). Manual: manual_read('canvas').
The Node grammar of the tools uses the same field names as the on-disk format.
5. canvas_validate
Static structural check (analogous to workbook_validate), so an LLM can
validate a generated Canvas before “done”. Read-only. Checks: parse errors,
duplicate Node/Edge ids, edges to non-existent Nodes, parent to unknown/
non-group/self; warnings for empty text Nodes and nested groups. Returns:
{ ok, errors, warnings, findings[] } (findings[].level = error|warning).
6. Anti-Patterns / v1-Scope
- Each element as its own Document. Rejected — the graph is one Doc, heavy
content is external Docs via
vance:reference. - Positions/edges in Markdown. Rejected — graph is YAML/JSON.
- Freehand pen strokes / shapes. Out of scope (Think Tool boundary).
- Editing in the standalone
kindview. Read-only by design; authoring path is theapp: canvasbook. - Not in v1: Multi-user CRDT edit, nested groups, auto-layout, cross-Canvas Node references.