Files
wled-screen-controller-mixed/contexts/graph-editor.md
alexei.dolgolyov f2871319cb
Some checks failed
Lint & Test / test (push) Failing after 48s
refactor: comprehensive code quality, security, and release readiness improvements
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.
2026-03-22 00:38:28 +03:00

5.6 KiB

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.tsENTITY_COLORS, ENTITY_LABELS, buildGraph() (add node loop + edge loops)
  2. graph-layout.tsedgeType() function if the new type needs a distinct edge color
  3. graph-nodes.tsKIND_ICONS (default icon), SUBTYPE_ICONS (subtype-specific icons)
  4. graph-nodes.tsSTART_STOP_KINDS or TEST_KINDS sets if the entity supports start/stop or test
  5. graph-connections.tsCONNECTION_MAP for drag-connect edge creation
  6. graph-editor.tsADD_ENTITY_MAP (add-entity menu entry with window function)
  7. graph-editor.tsALL_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.tsbuildGraph() edges section: add addEdge() call
  2. graph-connections.tsCONNECTION_MAP: add the field entry
  3. graph-edges.tsEDGE_COLORS if a new edge type is needed

Adding a new entity subtype

  1. graph-nodes.tsSUBTYPE_ICONS[kind] — add icon for the new subtype
  2. graph-layout.tsbuildGraph() — 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.