Files
fusion/plugins/fusion-plugin-dependency-graph/README.md
Fusion d7302b3325 feat(FN-3916): fix bundled Dependency Graph view load path
Fixed the bundled Dependency Graph view load path in the dashboard app, adding regression tests across the App and plugin registration test suites, and documenting the bundled loading contract in the plugin README.

Fusion-Task-Id: FN-3916
2026-05-10 01:25:27 -07:00

6.8 KiB

fusion-plugin-dependency-graph

Plugin-provided top-level Graph dashboard view for Fusion.

Host registration and bundled loading

The dependency graph view is registered as a bundled plugin view in the dashboard. This means:

  • registerBundledPluginViews() (called at dashboard startup) registers the view component in the static plugin view registry under key plugin:fusion-plugin-dependency-graph:graph.
  • The view component is resolved via a literal-specifier lazy import (import("@fusion-plugin-examples/dependency-graph/dashboard-view")) — this is critical for Vite/esbuild to emit a production chunk.
  • In Vite dev/build and Vitest, @fusion-plugin-examples/dependency-graph/dashboard-view is aliased to plugins/fusion-plugin-dependency-graph/src/dashboard-view.tsx (source, not dist/). This avoids stale dist artifacts breaking the dashboard view.
  • The App.tsx graph route resolves to the bundled view via isPluginViewRegistered fallback, so the graph view works even when the plugin is not installed/loaded through the PluginLoader API (e.g. fresh DB).
  • The canonical route ID is plugin:fusion-plugin-dependency-graph:graph.

Rendering approach

  • Filtering: includes triage, todo, in-progress, in-review; excludes done, archived
  • Graph build: edges are resolved only from task.dependencies as source=dependent, target=dependency
  • Orphan dependency handling: if a visible task depends on an excluded/missing dependency (for example done/archived after filtering), the missing edge is silently dropped and graph rendering continues without broken connectors
  • Auto-layout: Sugiyama-style layered layout (computeAutoLayout) groups nodes by dependency depth and spaces layers consistently
  • Edge drawing: SVG bezier curves from source bottom-center to target top-center, with arrowheads showing dependent → dependency direction
  • Interaction: drag-to-pan canvas background, drag-to-reposition nodes, cursor-centered wheel zoom, pinch-to-zoom with stationary midpoint, keyboard shortcuts, zoom toolbar, reset, and fit-to-graph
  • Fit-to-graph: computes node bounding box with layout node dimensions and applies zoom/pan so the graph fits in viewport with padding
  • Initial auto-fit: when no saved scoped positions exist, the first non-empty render auto-fits once; subsequent updates preserve user navigation state
  • Position persistence: dragged node positions are stored per project in browser localStorage and restored on reload
  • Animated transitions: fit/reset operations animate transform (var(--transition-normal)), while continuous drag/wheel/pinch stays transition-free for responsiveness
  • Node rendering: each graph node renders the real dashboard TaskCard via GraphTaskNode (no duplicated card markup)
  • Task detail integration: a primary non-drag click on a graph node surface opens the native dashboard task detail modal exactly once through host context (openTaskDetail), matching board/list behavior
  • In-progress behavior: steps are visible by default and active-task glow (agent-active) is preserved because node cards reuse TaskCard directly
  • Active-state indicator bar: active nodes render a compact top bar (.graph-task-active-indicator) with the current execution status label (for example Executing, Planning) and pulsing --in-progress emphasis
  • Current-step highlighting: active nodes set data-current-step for valid native step indices so CSS selectors highlight the currently executing .card-step-item and pulse its step dot
  • Zoom-out differentiation: .graph-task-node--active adds amplified glow and subtle scale/border tint so active nodes remain distinguishable at reduced zoom levels
  • In-review visual treatment: in-review nodes get a static .graph-task-node--in-review left accent in --in-review to distinguish waiting-review work from active execution nodes
  • Graph node classes: .graph-task-node, .graph-task-node--active, .graph-task-node--in-review, .graph-task-node--highlighted, .graph-task-node--dimmed, .graph-node--highlighted, and .graph-node--dimmed are available for graph-specific layering/highlight states while card internals remain owned by TaskCard.css
  • Graph edge classes: .graph-edge--highlighted and .graph-edge--dimmed are applied during dependency-chain emphasis states
  • Drag behavior: graph nodes pass disableDrag={true} to TaskCard so card-level HTML5 drag does not conflict with canvas pan/zoom

Position persistence

  • Canonical base key: fusion-plugin-dependency-graph:positions
  • Storage key format: kb:${projectId}:fusion-plugin-dependency-graph:positions (falls back to fusion-plugin-dependency-graph:positions when no project is selected)
  • Read path: positions load on graph mount and whenever projectId changes, then merge with fresh auto-layout so new tasks still receive layout defaults
  • Write path: positions persist on drag end only (not on every drag frame), filtered to currently visible tasks for stale cleanup
  • Reset behavior: Fit to graph / Reset view clear persisted positions and re-apply auto-layout
  • Implementation detail: the plugin now reuses dashboard projectStorage helpers (getScopedItem / setScopedItem / removeScopedItem) instead of duplicating scoped localStorage logic

Dependency chain highlighting

  • Hover a node to highlight the full transitive upstream + downstream chain for that task.
  • Click a node to persist selection highlighting until the same node is clicked again or the canvas pane is clicked; this same click also opens task detail once through the host detail callback.
  • Priority: hover state overrides selected state; when hover leaves, selected highlighting reappears.
  • Dimming: when a chain is active, unrelated nodes and edges are dimmed.
  • Neutral state: when nothing is hovered/selected, no highlight/dim classes are applied.
  • Drag suppression: drag movements above the node drag threshold suppress the post-drag click, preventing accidental detail opens on pointer release.
  • Edge rule: an edge is highlighted only when both its source and target nodes are in the active chain.

Controls

Toolbar (bottom-right)

  • Zoom in (ZoomIn) — button + Ctrl+= / Cmd+=
  • Zoom out (ZoomOut) — button + Ctrl+- / Cmd+-
  • Zoom percent label — live readout (for example 100%, 75%, 250%)
  • Fit to graph (Maximize) — button + Ctrl+Shift+F / Cmd+Shift+F
  • Reset view (RotateCcw) — button + Ctrl+0 / Cmd+0

Additional keyboard behavior

  • Escape resets to default view (zoom=1, pan=0,0)
  • Shortcuts are suppressed when focus is inside input, textarea, select, or contentEditable elements

All controls are rendered as floating .btn-icon actions in the bottom-right corner, with mobile-friendly 44px touch targets in the @media (max-width: 768px) override.