Skip to content

mindmap.build / studio architecture

How the canvas stays fast, structured, and local-first

The Studio is not a raw canvas app. It is a React editor that keeps graph state in a reducer, renders nodes as real HTML, draws relationships in SVG, applies one viewport transform to the whole world, and persists every settled change through an IndexedDB-backed sync queue.

Viewportscale + translate
Graph stateNodeMap + edges
PersistenceIndexedDB queue
AI handoffghost nodes
01

Mental Model

The editor separates the work into four planes. The reducer decides what the map is, the layout utilities decide where structured nodes belong, the canvas components decide how that world is drawn, and the session hook decides when local state becomes durable.

Studio.tsx Orchestrates hooks and passes a stable render model to canvas children.
Reducer Applies graph actions, selection, history snapshots, ghost nodes, and layout commits.
Canvas Renders one transformed world with SVG edges, HTML nodes, and screen-space overlays.
Persistence Diffs settled state into IndexedDB and lets SyncManager push batches to the API.
02

State Core

Studio state is graph-shaped, not DOM-shaped. Nodes live in a keyed lookup table for fast traversal, root islands can carry their own tree layout algorithm, and non-hierarchical cross-links ride alongside the parent-child tree as separate edges.

MindMapState

The reducer owns nodes, cross-links, selected node ids, selected edge id, undo history, redo future, active theme, density, and cut/paste clipboard state.

type MindMapState = {
nodes: NodeMap
edges: MapEdge[]
selectedNodeIds: Set<string>
selectedEdgeId: string | null
history: MindMapHistorySnapshot[]
future: MindMapHistorySnapshot[]
}

NodeData

A node stores both graph structure and visual state: parent id, ordered child ids, absolute canvas coordinates, collapse state, root-side placement, rich text, style overrides, draft state, and temporary ghost suggestion metadata.

Structureid, parentId, children, side
Viewx, y, width, color, shape
LifecycleisDraft, isGhost, updatedAt
03

Viewport Math

Nodes keep stable canvas coordinates. Panning and zooming move the containing world with one GPU-friendly transform, while overlays such as toolbars are positioned separately in screen space.

RootBranchIdeazoom 1.2x -180y 64
World transform
transform:
scale(zoom)
translate(viewOffset.x, viewOffset.y)
Cursor-centered zoom
newOffset.x =
mouse.x / newZoom
- mouse.x / oldZoom
+ oldOffset.x
04

Layout Engine

Structured layout is a pure computation over the NodeMap. Root children are split into left and right sides, descendants continue outward on their inherited side, collapsed subtrees are skipped, and multiple root islands are nudged apart on the grid when their bounds overlap.

Risks
Scope
Budget
Launch plan
Milestones
Alpha
Beta
Normalization Direct root children get a stable side. Child order can be inferred from vertical centers when a layout operation needs to reconcile freeform edits.
Subtree measurement Each subtree reports its height and root offset so parents can center themselves against visible children.
Reparent preview Dragging over a target builds a temporary structured tree and renders a ghost slot before the reducer commits the move.
05

Rendering Layers

The visible Studio is layered, but all layers share the same canvas coordinates. SVG gives edges crisp curves and big invisible hit targets for cross-links; HTML gives nodes rich text, focus management, resizing, menus, and accessible controls.

Screen-space overlaysContextual toolbars, move picker, ghost controls, search, onboarding hints.
HTML node layerMindMapNode components positioned with translate(x, y), measured by ResizeObserver.
SVG connection layerTree edges, cross-links, junctions, selected edge affordances, reparent preview.
Canvas backgroundTheme-driven dots, grids, graph paper, or solid color from resolved theme tokens.

ConnectionLine

Edges are cubic Bezier paths. Control points sit halfway between start and end on the x-axis, which makes branches leave and enter nodes horizontally even as the tree grows in either direction.

deltaX = end.x - start.x
cp1 = { x: start.x + deltaX * 0.5, y: start.y }
cp2 = { x: end.x - deltaX * 0.5, y: end.y }
06

Interaction Loop

The interaction hook acts like a small state machine. A pointer can be panning the canvas, dragging a subtree, dragging one node, dragging a selected group, hovering a reparent target, or finishing a click-to-edit sequence.

pointer down Selects or toggles the node, captures drag offset, closes floating panels.
move threshold Converts screen coordinates into canvas coordinates and previews the moving node.
target hover Validates circular dependencies, computes reorder or reparent intent, draws a ghost slot.
pointer up Commits REPARENT_NODE, toggles selection, or opens edit mode for a second click.
Drag offset
offset.x =
mouse.x / zoom
- node.x
- viewOffset.x
Pan offset
viewOffset.x =
mouse.x / zoom
- panStart.x
07

Local-First Sync

Editing feels immediate because React state updates first. Once the user is not dragging or editing text, the session hook diffs current graph state against the previous settled graph, writes local records, and lets the background sync loop handle network timing.

1Reducer state
2Diff nodes and edges
3Save IndexedDB records
4Queue delta events
5Compress updates
6Batch API request

What gets queued

Map creates and updates, node creates, deletes, coalesced node updates, view-node state updates, and edge changes are stored before any network call is required.

When sync runs

SyncManager runs every ten seconds, on reconnect, when the document is hidden, and synchronously before navigating home. Failed requests keep the queue available for retry.

08

Spark Path

Spark suggestions enter the canvas as ghost nodes. The client sends the target node, guided options, and accepted child context; the server assembles ancestry and sibling context for generation; the reducer inserts suggestions without persisting them until the user commits.

Target node
client context
Go Spark context
generated ideas
Ghost nodes
commit
Real nodes

Key files

  • apps/web-client/src/modules/studio/components/Studio.tsx - Studio orchestration.
  • apps/web-client/src/modules/studio/components/StudioCanvas.tsx - transformed canvas container.
  • apps/web-client/src/modules/studio/components/StudioConnectionLayer.tsx - SVG edges and junctions.
  • apps/web-client/src/modules/studio/hooks/useStudioCanvasInteractions.ts - drag, pan, reparent, and edit transitions.
  • apps/web-client/src/modules/studio/store/mind-map-reducer.ts - graph actions and history.
  • apps/web-client/src/shared/lib/layoutUtils.ts - structured tree layout and root island collision resolution.
  • apps/web-client/src/modules/studio/services/sync-manager.ts - queue compression and API sync.
  • apps/server-go/internal/services/mindmap/context.go - Spark context assembly.