# Visual Graph Editor **Read this file when working on the graph editor** (`static/js/features/graph-editor.js` and related modules). ## Architecture The graph editor renders all entities (devices, templates, sources, clocks, targets, scenes, automations) as SVG nodes connected by edges in a left-to-right layered layout. ### Core modules | File | Responsibility | |---|---| | `js/features/graph-editor.js` | Main orchestrator — toolbar, keyboard, search, filter, add-entity menu, port/node drag, minimap | | `js/core/graph-layout.js` | ELK.js layout, `buildGraph()`, `computePorts()`, entity color/label maps | | `js/core/graph-nodes.js` | SVG node rendering, overlay buttons, per-node color overrides | | `js/core/graph-edges.js` | SVG edge rendering (bezier curves, arrowheads, flow dots) | | `js/core/graph-canvas.js` | Pan/zoom controller with `zoomToPoint()` rAF animation | | `js/core/graph-connections.js` | CONNECTION_MAP — which fields link entity types, drag-connect/detach logic | | `css/graph-editor.css` | All graph-specific styles | ### Data flow 1. `loadGraphEditor()` → `_fetchAllEntities()` fetches all caches in parallel 2. `computeLayout(entities)` builds ELK graph, runs layout → returns `{nodes: Map, edges: Array, bounds}` 3. `computePorts(nodeMap, edges)` assigns port positions and annotates edges with `fromPortY`/`toPortY` 4. Manual position overrides (`_manualPositions`) applied after layout 5. `renderEdges()` + `renderNodes()` paint SVG elements 6. `GraphCanvas` handles pan/zoom via CSS `transform: scale() translate()` ### Edge rendering Edges always use `_defaultBezier()` (port-aware cubic bezier) — ELK edge routing is ignored because it lacks port awareness, causing misaligned bend points. ELK is only used for node positioning. ### Port system Nodes have input ports (left) and output ports (right), colored by edge type. Port types are ordered vertically: `template > picture > colorstrip > value > audio > clock > scene > device > default`. ## Keeping the graph in sync with entity types **CRITICAL:** When adding or modifying entity types in the system, these graph files MUST be updated: ### Adding a new entity type 1. **`graph-layout.js`** — `ENTITY_COLORS`, `ENTITY_LABELS`, `buildGraph()` (add node loop + edge loops) 2. **`graph-layout.js`** — `edgeType()` function if the new type needs a distinct edge color 3. **`graph-nodes.js`** — `KIND_ICONS` (default icon), `SUBTYPE_ICONS` (subtype-specific icons) 4. **`graph-nodes.js`** — `START_STOP_KINDS` or `TEST_KINDS` sets if the entity supports start/stop or test 5. **`graph-connections.js`** — `CONNECTION_MAP` for drag-connect edge creation 6. **`graph-editor.js`** — `ADD_ENTITY_MAP` (add-entity menu entry with window function) 7. **`graph-editor.js`** — `ALL_CACHES` array (for new-entity-focus watcher) 8. **`graph-editor.js`** — `_fetchAllEntities()` (add cache fetch + pass to `computeLayout`) 9. **`core/state.js`** — Add/export the new DataCache 10. **`app.js`** — Import and window-export the add/edit/clone functions ### Adding a new field/connection to an existing entity 1. **`graph-layout.js`** — `buildGraph()` edges section: add `addEdge()` call 2. **`graph-connections.js`** — `CONNECTION_MAP`: add the field entry 3. **`graph-edges.js`** — `EDGE_COLORS` if a new edge type is needed ### Adding a new entity subtype 1. **`graph-nodes.js`** — `SUBTYPE_ICONS[kind]` — add icon for the new subtype 2. **`graph-layout.js`** — `buildGraph()` — ensure `subtype` is extracted from the entity data ## Features & keyboard shortcuts | Key | Action | |---|---| | `/` | Open search | | `F` | Toggle filter | | `F11` | Toggle fullscreen | | `+` | Add entity menu | | `Escape` | Close filter → close search → deselect all | | `Delete` | Delete selected edge or node | | `Arrows / WASD` | Spatial navigation between nodes | | `Ctrl+A` | Select all nodes | ## Node color overrides Per-node colors stored in `localStorage` key `graph_node_colors`. The `getNodeColor(nodeId, kind)` function returns the override or falls back to `ENTITY_COLORS[kind]`. The color bar on the left side of each node is clickable to open a native color picker. ## Filter system The filter bar (toggled with F or toolbar button) filters nodes by name/kind/subtype. Non-matching nodes get the `.graph-filtered-out` CSS class (low opacity, no pointer events). Edges where either endpoint is filtered also dim. Minimap nodes for filtered-out entities become nearly invisible (opacity 0.07). ## Minimap Rendered as a small SVG with colored rects for each node and a viewport rect. Supports drag-to-pan, resize handles, and position persistence in localStorage. ## Node hover FPS tooltip Running `output_target` nodes show a floating HTML tooltip on hover (300ms delay). The tooltip is an absolutely-positioned `
` inside `.graph-container` (not SVG — needed for Chart.js canvas). It displays errors, uptime, and a FPS sparkline (reusing `createFpsSparkline` from `core/chart-utils.js`). The sparkline is seeded from `/api/v1/system/metrics-history` for instant context. **Hover events** use `pointerover`/`pointerout` with `relatedTarget` check to prevent flicker when the cursor moves between child SVG elements within the same `` node. **Node titles** display the full entity name (no truncation). Native SVG `` tooltips are omitted on nodes to avoid conflict with the custom tooltip. ## New entity focus When a user adds an entity via the graph's + menu, a watcher subscribes to all caches, detects the new ID, reloads the graph, and uses `zoomToPoint()` to smoothly fly to the new node with zoom + highlight animation.