Skip to content

Workflow-Centric UI refactor#10

Merged
tsmarvin merged 13 commits intobugfix/workflow-expectationsfrom
feature/UI-Improvements
Mar 14, 2026
Merged

Workflow-Centric UI refactor#10
tsmarvin merged 13 commits intobugfix/workflow-expectationsfrom
feature/UI-Improvements

Conversation

@tsmarvin
Copy link
Copy Markdown
Contributor

Workflow UI & Interactive DAG Editor

This PR delivers a complete UI refactor: a TypeScript/Node.js build pipeline, real-time run monitoring via SignalR, a vis-timeline Gantt view, and a full AntV X6-based DAG renderer and interactive workflow editor - replacing the previous server-rendered SVG DagView.razor entirely.


Interactive Visual Workflow Editor

Why: The form-based step/dependency management was not intuitive or visually appealing. The best visual workflow editors share a common set of critical UX patterns: composable architecture (separating mapping, operations, layout, and traversal concerns), connection-drop-to-node-creator, undo/redo with grouped history, node dirtiness tracking, fixed connection points with visual feedback state machines (green/blue port indicators), and searchable, categorized shape palettes.

The editor uses a batch-save architecture: all mutations modify local X6 graph state and an in-memory changeset. An explicit "Save" syncs to the API. Undo/redo operates purely on local state - avoiding the complexity of reversing per-action API calls.

  • DagEditor.razor - Top-level editor page at /workflows/{Id}/dag-editor. Three-column layout: StepPalette | DagEditorCanvas | NodeConfigPanel. Manages draft restore, metadata editing, DAG validation. Authorized for Admin/Operator roles.
  • DagEditorCanvas.razor - Interactive X6 canvas with minimap
  • DagEditorJsInterop - Editor interop: InitEditorAsync, LoadGraphAsync, AddNodeAsync, UpdateNodeDataAsync, GetChangesetJsonAsync, UndoAsync/RedoAsync, SetLayoutDirectionAsync, ZoomToAsync/ZoomToFitAsync. JS→.NET callbacks: OnNodeSelected, OnNodeDropped, OnSaveRequested, OnCycleDetected, OnGraphDirtyChanged.
  • StepPalette.razor - Categorized, searchable sidebar with drag-to-canvas via X6 Dnd. Collapsible categories with filtered step creation for rapid discovery.
  • NodeConfigPanel.razor - Right-side property editor for task info, control flow, conditions, variable bindings, dependencies. Enables task-level I/O inspection - the Bump actions/download-artifact from 1 to 4.1.7 in /.github/workflows #1 debugging capability for workflow operators.
  • EditorToolbar.razor - Save, undo/redo, validate, zoom, export controls
  • Undo/redo via X6 History plugin - tracks all mutations with grouped operations (e.g., delete node + edges = one undo step)
  • Keyboard shortcuts via X6 Keyboard: Delete, Ctrl+A, Ctrl+C/V, Ctrl+Z/Y, Escape, arrow keys
  • Clipboard via X6 Clipboard: copy/paste subgraphs with preserved internal edges
  • changeset.ts - Tracks add/delete/move mutations locally. Debounced: rapid operations accumulate, Save sends a single batch.
  • cycle-detection.ts - Client-side DFS cycle detection O(V+E) with visual feedback on invalid connections. Most diagramming tools don't perform cycle detection; this is a Werkr addition required for DAG integrity.
  • draft-storage.ts - Auto-save drafts to localStorage. beforeunload warning for unsaved changes.
  • export-handler.ts - PNG and SVG export via graph.toSVG() / graph.toPNG()
  • dnd-handler.ts - Drag-and-drop from palette to canvas
  • annotation-node.ts - Free-form sticky note annotations on canvas for documenting workflow sections

AntV X6 DAG Renderer - Replacing Server-Rendered SVG

Why: The previous DagView.razor was a server-rendered SVG using Kahn's algorithm with fixed-size nodes (160x52) and click-to-select only. SVG + foreignObject rendering with Dagre layout is the proven approach for rich node content (full HTML/CSS) while maintaining SVG coordinates for pan, zoom, and edges. AntV X6 provides all visual interactions client-side at 0ms latency, with built-in undo/redo, clipboard, drag-from-toolbar, minimap, and snapline alignment.

  • DagCanvas.razor - Read-only X6 DAG component with toolbar (LR/TB layout switching)
  • DagJsInterop - Typed C# wrapper inheriting GraphJsInteropBase. Methods: CreateGraphAsync, LoadGraphAsync, ZoomToFitAsync, ApplyStatusOverlayAsync. No scattered IJSRuntime.InvokeAsync("eval", ...) calls.
  • TypeScript core: create-graph.ts, dag-readonly.ts, werkr-node.ts, werkr-edge.ts
  • Dagre hierarchical layout via layout-engine.ts - configurable TB/LR rank direction. The tight-tree ranker was chosen over network-simplex for dramatically better performance at scale (minutes → seconds for large graphs).
  • foreignObject HTML nodes with Bootstrap styling: cards, badges, action-type indicators - enabling full HTML/CSS inside graph nodes rather than flat SVG text
  • parallel-lanes.ts - Light background lanes behind same-level parallel nodes for visual concurrency indication
  • status-overlay.ts - Color-coded execution status per selected run via cell.setData(). Consistent color language: Green=Success, Red=Failed, Amber=Running, Purple=Pending, Gray=Skipped.
  • X6 plugins: Selection, Snapline, Scroller, Transform, MiniMap - all built into X6 3.x core
  • Type definitions: dag-types.ts (node/edge DTOs), dotnet-interop.d.ts (.NET callback interface)

Real-Time Run Monitoring & Execution Views

Why: The workflow debugging experience is the primary functionality for the product - building workflows happens once; debugging them happens daily.

  • Three-view tiered complexity - different users need different detail levels (Compact for quick glance, Timeline for duration analysis, Detail for debugging)
  • Steps × Runs grid view - the most effective at-a-glance cross-run pattern recognition, with color-coded cells making failures immediately visible
  • Gantt charts - essential for bottleneck identification and understanding parallelism
  • Sparkline bar charts - quick pattern recognition for duration and failure trends without drilling in
  • Event grouping - collapsing related events (Scheduled→Started→Completed) into one logical unit reduces cognitive load
  • Real-time liveness - all views update as events stream, rather than polling on a timer
  • Task-level I/O inspection - see exactly what went in/out at every task boundary
  • Saved filter views - operators managing many workflows need personalized, filtered views by status, agent, tag, and owner

Workflow event types in Werkr.Core/Communication/:

  • WorkflowEvent.cs - Six new event types: StepStartedEvent, StepCompletedEvent, StepFailedEvent, StepSkippedEvent, RunCompletedEvent, LogAppendedEvent
  • WorkflowEventBroadcaster - Singleton fan-out via System.Threading.Channels. Published from gRPC service handlers (JobReportingGrpcService, OutputStreamingService, VariableService) when agents report execution progress.
  • WorkflowEventSubscription - Consumer subscription pattern for SSE endpoints

Real-time data pipeline:

Agent → gRPC → API WorkflowEventBroadcaster → SSE stream → JobEventRelayService → SignalR WorkflowRunHub → Browser
  • WorkflowRunHub - SignalR hub at /hubs/workflow-run. Authorized clients join groups keyed by run ID to receive StepStatusChanged, RunCompleted, and LogAppended events.
  • JobEventRelayService - BackgroundService + IHealthCheck that opens a long-lived SSE connection to GET /api/events/workflow-runs via the "ApiServiceSse" named HttpClient, deserializes events, and relays them to the hub via IHubContext<WorkflowRunHub>. Auto-reconnects with exponential backoff (1s → 30s). Tracks active run subscriptions in a ConcurrentDictionary. Registered as health check ("sse-relay") for operational visibility into SSE connection status.
  • ConnectionStatus.razor - Visual indicator (Live / Reconnecting / Polling) in the UI
  • RunDetail.razor - Multi-view run detail page (Compact / Timeline / Log tabs)
  • GanttTab.razor + TimelineJsInterop - vis-timeline Gantt chart showing horizontal bars per task with duration and status. Live bars grow during active runs via SignalR push.
  • RunSparkline.razor - Server-side SVG mini bar charts (bar height = duration, bar color = status) inline on the workflow list
  • RunGridView.razor - Steps × Runs 2D status matrix for cross-run comparison
  • StepDetailPanel.razor - Step-level log preview and error messages
  • TimelineView.razor - Chronological step execution list with attempt tracking and event grouping
  • SavedFilterService + FilterBar.razor - Named saved filter views persisted via localStorage. Backed by a full filter model ecosystem: FilterField, FilterDefinition, FilterCriteria, FilterFieldType, SavedFilterView in Werkr.Common/Models/.
  • SignalR DTOs - StepStatusDto, RunStatusDto, LogLineDto in WorkflowRunHubDtos.cs - typed payloads for each hub broadcast method
  • Additional DTOs - RunSparklineDto, StepStatusSummaryDto, AnnotationDto, DagValidationResult in Werkr.Common/Models/
  • Dedicated SSE HttpClient ("ApiServiceSse") configured with infinite timeout and no resilience handlers in Program.cs

API Endpoints & Batch Operations

Batch save endpoint - The server-side contract for the editor's batch-save architecture:

  • POST /api/workflows/{workflowId}/steps/batch - Atomic batch operations for editor save. Accepts a WorkflowStepBatchRequest containing ordered step operations.
  • Sign convention: negative StepId = temp ID (new nodes created in the editor), positive = real ID (existing nodes). The response includes StepIdMapping[] for temp→real ID resolution after save.
  • WorkflowStepBatchModels.cs - WorkflowStepBatchRequest, StepBatchOperation, DependencyBatchItem, WorkflowStepBatchResponse, StepIdMapping

Run management:

  • POST /api/workflows/{id}/runs/{runId}/retry-from/{stepId} - Retry from a failed step with optional variable overrides via RetryFromFailedRequest

SSE event streams:

  • GET /api/events/workflow-runs - Dedicated SSE stream for workflow run events (consumed by JobEventRelayService)
  • GET /api/workflows/runs/{runId}/events - Per-run filtered event stream
  • GET /api/events/jobs - Existing job event stream (backward-compatible)

Saved filter persistence:

  • Full CRUD at /api/filters/{pageKey} (GET list, POST create, PUT update, DELETE) with page keys for runs, workflows, jobs, agents, schedules, tasks. Admin-only share toggle.

Consistent Color Standard & Theme System

Why: A consistent color language across all views - where the same semantic colors map to the same states throughout the entire UI - is essential for quick visual parsing.

  • CSS custom properties in theme.css defining a standardized palette with dark/light theme support:
    • Status: --werkr-success (#28a745), --werkr-failed (#dc3545), --werkr-running (#ffc107), --werkr-pending (#6f42c1), --werkr-skipped (#6c757d), --werkr-not-run
    • Node types: --werkr-node-default, --werkr-node-conditional, --werkr-node-fallback, --werkr-node-loop
    • Structural: --werkr-edge-color, --werkr-node-stroke, --werkr-parallel-group-bg, --werkr-snapline
  • Applied consistently across DAG nodes, sparklines, grid view cells, Gantt bars, and status badges

JS/TS Build Infrastructure

Why: The X6 graph engine, vis-timeline, and Dagre layout all require a proper JS/TS build pipeline. Keeping all drag, zoom, pan, and connection drawing in the browser while reserving SignalR round-trips for state commits.

  • TypeScript 5.9 project with strict mode under src/Werkr.Server/graph-ui/
  • esbuild bundler (~50ms incremental builds) with code-split output to wwwroot/js/dist/: dag-readonly.js, dag-editor.js, timeline-view.js, plus shared vendor chunks. Editor bundle lazy-loaded only when entering edit mode.
  • MSBuild integration in Werkr.Server.csproj: EnsureNodeNpmInstallBuildGraphUi targets fire automatically during dotnet build. No separate npm steps needed during development.
  • Vitest 3.0 test runner with 7 unit tests covering changeset operations, cycle detection, draft storage, clipboard handling, timeline item mapping, and status styling
  • Blazor<->JS interop hierarchy: GraphJsInteropBase<T> manages IJSObjectReference + DotNetObjectReference lifecycle with IAsyncDisposable. Three derived classes: DagJsInterop (read-only), DagEditorJsInterop (editor), TimelineJsInterop (Gantt).
  • .nvmrc pins Node.js 22. .gitignore excludes node_modules/ and wwwroot/js/dist/.

CI/CD & Dependency Updates

  • CI workflow (ci.yml): Added Node.js 22 setup → npm ci → Vitest → npm run build:prod → bundle size check via check-bundle-size.mjs (250 KB gzipped budget)
  • DocFX workflow (DocFX_gh-pages.yml): Simplified from multi-job Windows workflow to single Ubuntu-based job
  • NuGet dependency updates in Directory.Packages.props: Microsoft.Extensions, ASP.NET Core, EF Core, SignalR, and test packages updated to latest patch versions
  • Lock file updates across all projects to match new package versions
  • Cross-platform test fix in UrlValidatorTests.cs: RelativeUrl_Rejected test now handles Unix vs. Windows URI parsing differences
  • System.Text.Json transitive dependency update in installer project

New Dependencies

Package Version Purpose
@antv/x6 3.1.6 Graph visualization & editing engine (MIT)
dagre 0.8.5 Hierarchical DAG layout (MIT)
vis-timeline 7.7.3 Gantt timeline rendering (Apache-2.0 / MIT)
vis-data 7.1.9 Reactive data containers for vis-timeline (Apache-2.0 / MIT)
typescript 5.9.x TypeScript compiler
esbuild 0.27.x Bundler
vitest 3.0.x Test runner

No new NuGet packages - all required .NET packages already existed in Directory.Packages.props.

New Prerequisite

  • Node.js 22+ required on dev machines and CI agents

Copilot AI review requested due to automatic review settings March 14, 2026 22:39
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 126 out of 214 changed files in this pull request and generated no comments.

Files not reviewed (2)
  • src/Werkr.Data/Migrations/Postgres/20260314004316_InitialCreate.Designer.cs: Language not supported
  • src/Werkr.Data/Migrations/Sqlite/20260314004323_InitialCreate.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@tsmarvin tsmarvin merged commit 8ca38ae into bugfix/workflow-expectations Mar 14, 2026
@tsmarvin tsmarvin deleted the feature/UI-Improvements branch March 14, 2026 22:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants