Flow Editor
Flow Editor is a controlled Angular canvas for AI agents and automation workflows. Your application owns graph state and persistence; the editor renders that state and emits typed user intents.
Install
Install the optional flow package together with its TailNG and Foblex peer dependencies.
Install Flow Editor
bash
pnpm add @tailng-ui/flow @tailng-ui/components @tailng-ui/icons \
@foblex/flow @foblex/2d @foblex/mediator @foblex/platform @foblex/utilsGlobal styles
Import the flow theme once in the consuming application's global stylesheet.
styles.css
css
@import '@tailng-ui/flow/styles.css';Controlled agent workflow
Drag a palette item onto the canvas, activate it to place at center, move nodes, or use the viewport controls.
Controlled workflow with Plain CSS
1 validation warning
1 graph validation issue(s)
Controlled workflow with Tailwind CSS
1 validation warning
1 graph validation issue(s)
State ownership
Inputs are immutable graph snapshots. Handle outputs, produce new arrays or objects, and then persist those values in your application or agent backend.
agent-flow.component.ts
ts
import { Component, signal } from '@angular/core';
import {
TngFlowEditorComponent,
type TngFlowConnection,
type TngFlowConnectionCreateRequest,
type TngFlowNode,
type TngFlowNodesMovedEvent,
type TngFlowViewport,
} from '@tailng-ui/flow';
@Component({
imports: [TngFlowEditorComponent],
template: `
<tng-flow-editor
[nodes]="nodes()"
[connections]="connections()"
[presentation]="presentation"
[validation]="validation"
[showMinimap]="true"
[viewport]="viewport()"
(nodesMoved)="moveNodes($event)"
(connectionCreateRequested)="createConnection($event)"
(viewportChange)="viewport.set($event)"
/>
`,
})
export class AgentFlowComponent {
readonly nodes = signal<readonly TngFlowNode[]>(initialNodes);
readonly connections = signal<readonly TngFlowConnection[]>(initialConnections);
readonly viewport = signal<TngFlowViewport | null>(null);
readonly presentation = { nodes: { model: { status: 'running' as const, progress: 68 } } };
readonly validation = { issues: [] };
moveNodes(event: TngFlowNodesMovedEvent): void {
const positions = new Map(event.nodes.map((node) => [node.id, node.position]));
this.nodes.update((nodes) =>
nodes.map((node) => ({ ...node, position: positions.get(node.id) ?? node.position })),
);
}
createConnection(event: TngFlowConnectionCreateRequest): void {
this.connections.update((connections) => [
...connections,
{
id: crypto.randomUUID(),
source: event.source,
target: event.target,
type: 'bezier',
},
]);
}
}- Node, port, and connection ids must be stable.
- Port ids are unique within their node and connection endpoints include both ids.
presentationkeeps execution state separate from persisted graph structure, whilevalidationremains a controlled application input.
Accessibility baseline
- The editor installs Foblex keyboard accessibility with
withA11y()for spatial, topology, selection, grab/move/drop, cancellation, and controlled deletion behavior. - Press C on one selected node to choose an eligible source and validator-compatible target port with arrows, then confirm with Enter or Space. Port and connection status is announced throughout the operation.
- Locked and disabled graph items are filtered, focus survives controlled updates, and focus indicators remain visible across the supported zoom range.
- Native form controls and focusable custom-template controls keep their native keyboard behavior; graph shortcuts do not intercept their events.
- Every connector receives a node-, direction-, and port-aware accessible label.
- Viewport controls have text alternatives and visible keyboard focus.
- Reduced-motion preferences suppress nonessential animation.