Files
cameleer-server/docs/superpowers/specs/2026-03-27-interactive-process-diagram-design.md
hsiegeln ac32396a57
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m0s
CI / docker (push) Successful in 56s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 38s
feat: add interactive ProcessDiagram SVG component (sub-project 1/3)
New interactive route diagram component with SVG rendering using
server-computed ELK layout coordinates. TIBCO BW5-inspired top-bar
card node style with zoom/pan, hover toolbars, config badges, and
error handler sections below the main flow.

Backend: add direction query parameter (LR/TB) to diagram render
endpoints, defaulting to left-to-right layout.

Frontend: 14-file ProcessDiagram component in ui/src/components/
with DiagramNode, CompoundNode, DiagramEdge, ConfigBadge, NodeToolbar,
ErrorSection, ZoomControls, and supporting hooks. Dev test page at
/dev/diagram for validation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:55:29 +01:00

15 KiB
Raw Blame History

Interactive Process Diagram — Design Spec

Sub-project: 1 of 3 (Component → Execution Overlay → Page Integration) Scope: Interactive SVG diagram component with zoom/pan, node interactions, config badges, and a configurable layout direction. Does NOT include execution overlay or page replacement — those are sub-projects 2 and 3.


Problem

The current RouteFlow component renders Camel routes as a flat vertical list of nodes. It cannot show compound structures (choice branches, split fan-out, try-catch nesting), does not support zoom/pan, and has no interactive controls beyond click-to-select. Routes with 10+ processors become hard to follow, and the relationship between processors is not visually clear.

Goal

Build an interactive process diagram component styled after MuleSoft / TIBCO BusinessWorks 5, rendering Camel routes as left-to-right flow diagrams using server-computed ELK layout coordinates. The component supports zoom/pan, node hover toolbars for tracing/tap configuration, config badge indicators, and a collapsible detail side-panel.


Decisions

Decision Choice Rationale
Rendering SVG + custom React Full control over styling, no heavy deps. Server owns layout.
Node style Top-Bar Cards TIBCO BW5-inspired white cards with colored top accent bar. Professional, clean.
Flow direction Left-to-right (default) Matches MuleSoft/BW5 conventions. Query param for flexibility.
Component location ui/src/components/ProcessDiagram/ Tightly coupled to Cameleer data model, no design-system abstraction needed.
Interactions Hover floating toolbar + click-to-select Discoverable, no right-click dependency.
Error handlers Below main flow Clear visual separation, labeled divider.
Selection behavior Side panel with config info; execution data only with overlay Keeps base diagram focused on topology.

1. Backend: Layout Direction Parameter

Change

Add optional direction query parameter to diagram render endpoints.

Files

  • cameleer3-server-app/.../diagram/ElkDiagramRenderer.java — accept direction param, map to ELK Direction.RIGHT (LR) or Direction.DOWN (TB)
  • cameleer3-server-core/.../diagram/DiagramRenderer.java — update interface to accept direction
  • cameleer3-server-app/.../controller/DiagramRenderController.java — add @RequestParam(defaultValue = "LR") String direction to render endpoints
  • ui/src/api/queries/diagrams.ts — pass direction query param to API calls; also update DiagramLayout edge type to match backend PositionedEdge serialization: { sourceId, targetId, label?, points: number[][] } (currently defines { from?, to? } which is missing points and label)

Behavior

  • GET /diagrams/{contentHash}/render?direction=LR → left-to-right layout (default)
  • GET /diagrams/{contentHash}/render?direction=TB → top-to-bottom layout
  • GET /diagrams?application=X&routeId=Y&direction=LR → same for by-route endpoint

Compound Node Direction

The direction parameter applies to the root layout only. Compound nodes (CHOICE, SPLIT, TRY_CATCH, etc.) keep their internal layout direction as top-to-bottom regardless of the root direction. This matches how MuleSoft/BW5 render branching patterns: the main flow goes left-to-right, but branches within a choice or split fan out vertically inside their container.


2. Frontend: ProcessDiagram Component

File Structure

ui/src/components/ProcessDiagram/
├── ProcessDiagram.tsx          # Root: SVG container, zoom/pan, section layout
├── ProcessDiagram.module.css   # Styles using design system tokens
├── DiagramNode.tsx             # Individual node: top-bar card rendering
├── DiagramEdge.tsx             # Edge: cubic Bezier path with arrowhead
├── CompoundNode.tsx            # Container for compound types (choice, split)
├── NodeToolbar.tsx             # Floating action toolbar on hover
├── ConfigBadge.tsx             # Indicator badges (TRACE, TAP) on nodes
├── ErrorSection.tsx            # Visual separator + error handler flow section
├── ZoomControls.tsx            # HTML overlay: zoom in/out/fit buttons
├── useZoomPan.ts               # Hook: viewBox transform, wheel zoom, drag pan
├── useDiagramData.ts           # Hook: fetch + separate layout into sections
├── node-colors.ts              # NodeType → design system color token mapping
├── types.ts                    # Shared TypeScript interfaces
└── index.ts                    # Public exports

Props API

interface ProcessDiagramProps {
  application: string;
  routeId: string;
  direction?: 'LR' | 'TB';                        // default 'LR'
  selectedNodeId?: string;                          // controlled selection
  onNodeSelect?: (nodeId: string) => void;
  onNodeAction?: (nodeId: string, action: NodeAction) => void;
  nodeConfigs?: Map<string, NodeConfig>;            // active taps/tracing per processor
  className?: string;
}

type NodeAction = 'inspect' | 'toggle-trace' | 'configure-tap' | 'copy-id';

interface NodeConfig {
  traceEnabled?: boolean;
  tapExpression?: string;
}

// ExecutionOverlay types will be added in sub-project 2 when needed.
// No forward-declared types here to avoid drift.

SVG Structure

<div class="process-diagram">
  <svg viewBox="...">                    // zoom = viewBox transform
    <defs>                               // arrowhead markers, filters
      <marker id="arrow">...</marker>
    </defs>
    <g class="diagram-content">          // pan offset transform

      <!-- Main Route section -->
      <g class="section section--main">
        <g class="edges">               // rendered first (behind nodes)
          <path d="M ... C ..." />       // cubic bezier from ELK waypoints
        </g>
        <g class="nodes">
          <g transform="translate(x,y)"> // ELK-computed position
            <!-- DiagramNode: top-bar card -->
            <!-- ConfigBadge: top-right corner pills -->
            <!-- NodeToolbar: foreignObject on hover -->
          </g>
          <g class="compound">           // CompoundNode: dashed border container
            <g transform="translate(...)"> <!-- children inside -->
          </g>
        </g>
      </g>

      <!-- Error Handler section(s) -->
      <g class="section section--error"
         transform="translate(0, mainHeight + gap)">
        <text>onException: java.lang.Exception</text>
        <line ... />                      // divider
        <g class="edges">...</g>
        <g class="nodes">...</g>
      </g>

    </g>
  </svg>
  <div class="zoom-controls">...</div>   // HTML overlay, bottom-right
</div>

3. Node Visual States

Base States

State Visual
Normal White card, --border (#E4DFD8), colored top bar per type
Hovered Warm tint background (--bg-hover / #F5F0EA), stronger border, floating toolbar appears above
Selected Amber selection ring (2.5px solid --amber), side panel opens

Config Badges

Small colored pill badges positioned at the top-right corner of the node card, always visible:

  • TRACE — teal (--running) pill, shown when tracing is enabled
  • TAP — purple (--purple) pill, shown when a tap expression is configured

Execution Overlay States (sub-project 2 — node must support these props)

State Visual
Executed (OK) Green left border or subtle green tint
Failed (caused error handler) Red border (2px --error), red marker icon
Not executed Dimmed (reduced opacity)
Has trace data Small "data available" indicator icon
No trace data No indicator (or grayed-out data icon)

Node Type Colors

Category Token Hex Types
Endpoints --running #1A7F8E teal ENDPOINT
Processors --amber #C6820E PROCESSOR, BEAN, LOG, SET_HEADER, SET_BODY, TRANSFORM, MARSHAL, UNMARSHAL
Targets --success #3D7C47 green TO, TO_DYNAMIC, DIRECT, SEDA
EIP Patterns --purple #7C3AED EIP_CHOICE, EIP_WHEN, EIP_OTHERWISE, EIP_SPLIT, EIP_MULTICAST, EIP_LOOP, EIP_AGGREGATE, EIP_FILTER, etc.
Error Handling --error #C0392B red ERROR_HANDLER, ON_EXCEPTION, TRY_CATCH, DO_TRY, DO_CATCH, DO_FINALLY
Cross-Route (hardcoded) #06B6D4 cyan EIP_WIRE_TAP, EIP_ENRICH, EIP_POLL_ENRICH

Note: This frontend color mapping intentionally differs from the backend ElkDiagramRenderer SVG colors (which use blue for endpoints, green for processors). The frontend uses design system tokens for consistency with the rest of the UI. The backend SVG renderer is not changed.

Compound Node Rendering

Compound types (CHOICE, SPLIT, TRY_CATCH, LOOP, etc.) render as:

  • Full-width colored header bar with white text label (type name)
  • White body area with subtle border matching the type color
  • Children rendered inside at their ELK-relative positions
  • Children have their own hover/select/badge behavior

4. Interactions

Hover Floating Toolbar

On mouse enter over a node, a dark floating toolbar appears above the node (centered). Uses <foreignObject> for HTML accessibility.

Icon Action Callback
Search Inspect onNodeAction(id, 'inspect') — selects node, opens side panel
T Toggle Trace onNodeAction(id, 'toggle-trace') — enables/disables tracing
Pencil Configure Tap onNodeAction(id, 'configure-tap') — opens tap config
... More onNodeAction(id, 'copy-id') — copies processor ID

Toolbar hides on mouse leave after a short delay (150ms) to prevent flicker when moving between node and toolbar.

Click-to-Select

Click on a node → calls onNodeSelect(nodeId). Parent controls selectedNodeId prop. Selected node shows amber ring.

Zoom & Pan

useZoomPan hook manages:

  • Mouse wheel → zoom centered on cursor
  • Click+drag on background → pan
  • Pinch gesture → zoom (trackpad/touch)
  • State: { scale, translateX, translateY }
  • Applied to SVG viewBox attribute

ZoomControls component:

  • Three buttons: + (zoom in), (zoom out), fit-to-view icon
  • Positioned as HTML overlay at bottom-right of diagram container
  • Fit-to-view calculates viewBox to show entire diagram with 40px padding

Zoom limits: 25% to 400%.

Keyboard Navigation

Required:

Key Action
Escape Deselect / close panel
+/- Zoom in/out
0 Fit to view

Stretch (implement if time permits):

Key Action
Arrow keys Move selection between connected nodes
Tab Cycle through nodes in flow order
Enter Open detail panel for selected node

5. Error Handler Sections

Error handler compounds (ON_EXCEPTION, ERROR_HANDLER) render as separate sections below the main flow:

  1. Divider: Horizontal line with label text (e.g., "onException: java.lang.Exception")
  2. Gap: 40px vertical gap between main section and error section
  3. Layout: Error section gets its own ELK-computed layout (compound node children already have relative coordinates)
  4. Styling: Same node rendering as main section, but the section background has a subtle red tint
  5. Multiple handlers: Each ON_EXCEPTION becomes its own section, stacked vertically

The useDiagramData hook separates top-level compound error nodes from regular nodes, computing the Y offset for each error section based on accumulated heights.


6. Data Flow

useDiagramByRoute(app, routeId)
  → contentHash
  → useDiagramLayout(contentHash, direction)
    → DiagramLayout { nodes[], edges[], width, height }

useDiagramData hook:
  1. Separate nodes into mainNodes[] and errorSections[]
     (reuses logic from buildFlowSegments: error-handler compounds with children → error sections)
  2. Filter edges: mainEdges (between main nodes), errorEdges (within each error section)
  3. Compute total SVG dimensions: max(mainWidth, errorWidths) × (mainHeight + gap + errorHeights)
  4. Return { mainNodes, mainEdges, errorSections, totalWidth, totalHeight }

The existing diagram-mapping.ts buildFlowSegments function handles the separation logic. The new useDiagramData hook adapts this for SVG coordinate-based rendering instead of RouteFlow's FlowSegment format.


7. Side Panel (Detail Panel)

When a node is selected, a collapsible side panel slides in from the right of the diagram container.

Base mode (no execution overlay):

  • Processor ID
  • Processor type
  • Endpoint URI (if applicable)
  • Active configuration: tracing status, tap expression
  • Node metadata from the diagram

With execution overlay (sub-project 2):

  • Execution status + duration
  • Input/output body (if trace data captured)
  • Input/output headers
  • Error message + stack trace (if failed)
  • Loop iteration selector (if inside a loop)

For sub-project 1, the side panel shows config info only. The component accepts an onNodeSelect callback — the parent page controls what appears in the panel.

The side panel is NOT part of the ProcessDiagram component itself. It is rendered by the parent page and controlled via the selectedNodeId / onNodeSelect props. This keeps the diagram component focused on visualization.

Dev test page (/dev/diagram): In sub-project 1, the test page renders the ProcessDiagram with a simple stub side panel that shows the selected node's ID, type, label, and any nodeConfigs entry. This validates the selection interaction without needing full page integration.


8. Non-Goals (Sub-project 2 & 3)

These are explicitly out of scope for sub-project 1:

  • Execution overlay rendering — animated flow, per-node status/duration, dimming non-executed nodes
  • Loop/split iteration stepping — "debugger" UI with iteration tabs
  • Page integration — replacing RouteFlow on RouteDetail, ExchangeDetail, Dashboard
  • Minimap — small overview for large diagrams (stretch goal, not v1)
  • Drag to rearrange — nodes are server-positioned, not user-movable

Verification

  1. Backend: mvn clean verify -DskipITs passes after direction param addition
  2. Frontend types: npx tsc -p tsconfig.app.json --noEmit passes
  3. Manual test: Create a temporary test page or Storybook-like route (/dev/diagram) that renders the ProcessDiagram component with a known route
  4. Zoom/pan: Mouse wheel zooms, drag pans, fit-to-view works
  5. Node interaction: Hover shows toolbar, click selects with amber ring
  6. Config badges: Pass mock nodeConfigs and verify TRACE/TAP pills render
  7. Error sections: Route with ON_EXCEPTION renders error handler below main flow
  8. Compound nodes: Route with CHOICE renders children inside dashed container
  9. Keyboard (required): Escape deselects, +/- zooms, 0 fits to view
  10. Direction: ?direction=TB renders top-to-bottom layout