Add visual graph editor for entity interconnections

SVG-based node graph with ELK.js autolayout showing all 13 entity types
and their relationships. Features include:

- Pan/zoom canvas with bounds clamping and dead-zone click detection
- Interactive minimap with viewport rectangle, click-to-pan, drag-to-move,
  and dual resize handles (bottom-left/bottom-right)
- Movable toolbar with drag handle and inline zoom percentage indicator
- Entity-type SVG icons from Lucide icon set with subtype-specific overrides
- Command palette search (/) with keyboard navigation and fly-to
- Node selection with upstream/downstream chain highlighting
- Double-click to zoom-to-card, edit/delete overlay on hover
- Legend panel, orphan node detection, running state indicators
- Full i18n support with languageChanged re-render
- Catmull-Rom-to-cubic bezier edge routing for smooth curves

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 15:01:47 +03:00
parent 42b5ecf1cd
commit bd7a315c2c
14 changed files with 2320 additions and 4 deletions

View File

@@ -157,6 +157,13 @@ import {
updateCalibrationLine, resetCalibrationView,
} from './features/advanced-calibration.js';
// Layer 5.5: graph editor
import {
loadGraphEditor, openGraphSearch, closeGraphSearch,
toggleGraphLegend, toggleGraphMinimap,
graphFitAll, graphZoomIn, graphZoomOut, graphRelayout,
} from './features/graph-editor.js';
// Layer 6: tabs, navigation, command palette, settings
import { switchTab, initTabs, startAutoRefresh, handlePopState } from './features/tabs.js';
import { navigateToCard } from './core/navigation.js';
@@ -455,6 +462,17 @@ Object.assign(window, {
updateCalibrationLine,
resetCalibrationView,
// graph editor
loadGraphEditor,
openGraphSearch,
closeGraphSearch,
toggleGraphLegend,
toggleGraphMinimap,
graphFitAll,
graphZoomIn,
graphZoomOut,
graphRelayout,
// tabs / navigation / command palette
switchTab,
startAutoRefresh,
@@ -488,7 +506,7 @@ document.addEventListener('keydown', (e) => {
// Tab shortcuts: Ctrl+1..4 (skip when typing in inputs)
if (!inInput && e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) {
const tabMap = { '1': 'dashboard', '2': 'automations', '3': 'targets', '4': 'streams' };
const tabMap = { '1': 'dashboard', '2': 'automations', '3': 'targets', '4': 'streams', '5': 'graph' };
const tab = tabMap[e.key];
if (tab) {
e.preventDefault();