Some checks failed
Lint & Test / test (push) Failing after 48s
Security: tighten CORS defaults, add webhook rate limiting, fix XSS in automations, guard WebSocket JSON.parse, validate ADB address input, seal debug exception leak, URL-encode WS tokens, CSS.escape in selectors. Code quality: add Pydantic models for brightness/power endpoints, fix thread safety and name uniqueness in DeviceStore, immutable update pattern, split 6 oversized files into 16 focused modules, enable TypeScript strictNullChecks (741→102 errors), type state variables, add dom-utils helper, migrate 3 modules from inline onclick to event delegation, ProcessorDependencies dataclass. Performance: async store saves, health endpoint log level, command palette debounce, optimized entity-events comparison, fix service worker precache list. Testing: expand from 45 to 293 passing tests — add store tests (141), route tests (25), core logic tests (42), E2E flow tests (33), organize into tests/api/, tests/storage/, tests/core/, tests/e2e/. DevOps: CI test pipeline, pre-commit config, Dockerfile multi-stage build with non-root user and health check, docker-compose improvements, version bump to 0.2.0. Docs: rewrite CLAUDE.md (202→56 lines), server/CLAUDE.md (212→76), create contexts/server-operations.md, fix .js→.ts references, fix env var prefix in README, rewrite INSTALLATION.md, add CONTRIBUTING.md and .env.example.
102 lines
5.6 KiB
Markdown
102 lines
5.6 KiB
Markdown
# Visual Graph Editor
|
|
|
|
**Read this file when working on the graph editor** (`static/js/features/graph-editor.ts` 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.ts` | Main orchestrator — toolbar, keyboard, search, filter, add-entity menu, port/node drag, minimap |
|
|
| `js/core/graph-layout.ts` | ELK.js layout, `buildGraph()`, `computePorts()`, entity color/label maps |
|
|
| `js/core/graph-nodes.ts` | SVG node rendering, overlay buttons, per-node color overrides |
|
|
| `js/core/graph-edges.ts` | SVG edge rendering (bezier curves, arrowheads, flow dots) |
|
|
| `js/core/graph-canvas.ts` | Pan/zoom controller with `zoomToPoint()` rAF animation |
|
|
| `js/core/graph-connections.ts` | 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.ts`** — `ENTITY_COLORS`, `ENTITY_LABELS`, `buildGraph()` (add node loop + edge loops)
|
|
2. **`graph-layout.ts`** — `edgeType()` function if the new type needs a distinct edge color
|
|
3. **`graph-nodes.ts`** — `KIND_ICONS` (default icon), `SUBTYPE_ICONS` (subtype-specific icons)
|
|
4. **`graph-nodes.ts`** — `START_STOP_KINDS` or `TEST_KINDS` sets if the entity supports start/stop or test
|
|
5. **`graph-connections.ts`** — `CONNECTION_MAP` for drag-connect edge creation
|
|
6. **`graph-editor.ts`** — `ADD_ENTITY_MAP` (add-entity menu entry with window function)
|
|
7. **`graph-editor.ts`** — `ALL_CACHES` array (for new-entity-focus watcher)
|
|
8. **`graph-editor.ts`** — `_fetchAllEntities()` (add cache fetch + pass to `computeLayout`)
|
|
9. **`core/state.ts`** — Add/export the new DataCache
|
|
10. **`app.ts`** — Import and window-export the add/edit/clone functions
|
|
|
|
### Adding a new field/connection to an existing entity
|
|
|
|
1. **`graph-layout.ts`** — `buildGraph()` edges section: add `addEdge()` call
|
|
2. **`graph-connections.ts`** — `CONNECTION_MAP`: add the field entry
|
|
3. **`graph-edges.ts`** — `EDGE_COLORS` if a new edge type is needed
|
|
|
|
### Adding a new entity subtype
|
|
|
|
1. **`graph-nodes.ts`** — `SUBTYPE_ICONS[kind]` — add icon for the new subtype
|
|
2. **`graph-layout.ts`** — `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 `<div class="graph-node-tooltip">` inside `.graph-container` (not SVG — needed for Chart.js canvas). It displays errors, uptime, and a FPS sparkline (reusing `createFpsSparkline` from `core/chart-utils.ts`). 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 `<g>` node.
|
|
|
|
**Node titles** display the full entity name (no truncation). Native SVG `<title>` 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.
|