# Services

Import these from `@grafloria/angular`.

## On their own pages

- [`AngularAnimationService`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-angular-services-angularanimationservice): Angular-injectable Animation Service
- [`BreakpointManagerService`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-angular-services-breakpointmanagerservice): BreakpointManager Service
- [`ComponentRendererService`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-angular-services-componentrendererservice): Service for rendering Angular components inside SVG foreignObject elements. Manages component lifecycle, inputs/outputs, and container management.
- [`ExecutionTrackerService`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-angular-services-executiontrackerservice): ExecutionTracker Service
- [`ModeManagerService`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-angular-services-modemanagerservice): Angular service wrapper for engine ModeManager. Provides reactive API using RxJS observables.
- [`PropertyPanelService`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-angular-services-propertypanelservice): Core service for managing property schemas and property values. Acts as bridge between property panel UI and diagram engine.

## Classes

### `HandleRegistryService`

Service to track HTML handles and query their DOM positions
Similar to React Flow's handle detection system

Hybrid HTML+SVG Rendering

This service:
- Tracks all HTML handles registered via GrafloriaHandleDirective
- Queries DOM positions using getBoundingClientRect() like React Flow
- Provides handle bounds for connection drawing
- Enables hit testing for connection drag operations

SHAPE AWARENESS:
This service queries actual DOM positions, so it automatically supports all shape types
(rect, circle, ellipse, diamond, hexagon) as long as the handles are positioned correctly
in the template. Handle positioning is done by diagram-canvas.component.ts using
getPortPositionForShape() which provides shape-aware positioning.

```ts
@Injectable({ providedIn: 'root' })
export class HandleRegistryService
```

**Methods**

- `registerHandle(nodeId: string, handle: HTMLHandle): void` — Register a handle for a node
Called by GrafloriaHandleDirective on init
- `unregisterHandle(nodeId: string, handleId: string): void` — Unregister a handle
Called by GrafloriaHandleDirective on destroy
- `getHandles(nodeId: string): HTMLHandle[]` — Get all handles for a node
- `getHandleBounds( nodeId: string, handleId: string, zoom: number = 1 ): HandleBounds | null` — Get bounds for a specific handle (React Flow style)
Queries DOM using getBoundingClientRect()
- `getAllHandleBounds(zoom: number = 1): Map<string, HandleBounds[]>` — Get bounds for all handles in the diagram
Used for connection drag operations
- `getHandleAtPoint( screenX: number, screenY: number, zoom: number = 1 ): { nodeId: string; handleId: string; handle: HTMLHandle } | null` — Find handle at screen coordinates (for hit testing)
- `getStats(): { nodeCount: number; handleCount: number }` — Get count of registered nodes and handles
Useful for debugging
- `clear(): void` — Clear all registered handles
Useful for cleanup/reset

### `InteractionHandlerService`

Also has every member of `InteractionController`, listed on its own entry.

InteractionHandlerService

Angular binding for {@link InteractionController} — and nothing else.

ALL of the interaction logic (port hover, connection dragging, link
reconnection, inline label repositioning, waypoint and control-point editing)
now lives in the framework-agnostic {@link InteractionController} in
`@grafloria/renderer`, so React / Vue / web-component wrappers get it for free. This class exists purely to make that controller injectable; it adds no
behaviour and overrides no methods.

## The separation this encodes

- **WHAT changed** → the controller. Its handlers are pure w.r.t. Angular and
  return a boolean meaning "a re-render is warranted".
- **TELL THE FRAMEWORK TO RENDER** → the caller. `DiagramCanvasComponent`
  turns those booleans into `cdr.markForCheck()` / `scheduleRender()`.

The controller therefore never imports Angular, never holds a
`ChangeDetectorRef`, and is instantiated with a plain `new` in the e2e
harness. Do not add Angular-aware behaviour here: put logic in the controller
and change detection in the component.

```ts
@Injectable({
  providedIn: 'root',
})
export class InteractionHandlerService extends InteractionController
```

**Methods**

- `constructor()`

See also: InteractionController — the real implementation (and its full API).

### `PropertyEditorRegistryService`

Registry service for property editor components.

This service manages all property editor components (both built-in and custom). It provides a centralized way to register and retrieve editor components by type.

Built-in editors are automatically registered on service initialization:
- string, number, boolean, select, multiselect, color, slider,
  textarea, date, datetime, file, json

Custom editors can be registered using the `registerEditor` method.

```ts
@Injectable({
  providedIn: 'root',
})
export class PropertyEditorRegistryService
```

**Methods**

- `constructor()`
- `registerEditor(type: string, component: Type<any>): void` — Register a custom editor component.

This method allows you to register custom property editor components
or override built-in editors. If an editor with the same type already
exists, it will be overwritten.
- `getEditor(type: string): Type<any> | null` — Get the editor component class for a given type.
- `hasEditor(type: string): boolean` — Check if an editor is registered for a given type.
- `getEditorTypes(): string[]` — Get all registered editor types.

**Example**

```typescript
// Register a custom editor

### `SimulationEngineService`

SimulationEngine Service

High-performance animation engine for diagram simulations. Runs at 60 FPS using requestAnimationFrame.

Features:
- 60 FPS animation loop
- Frame rate monitoring
- Delta time tracking
- Multiple animation callbacks
- Pause/resume support
- Performance stats

```ts
@Injectable({ providedIn: 'root' })
export class SimulationEngineService implements OnDestroy
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `state$` |  |  | Observable of simulation state. |
| `stats$` |  |  | Observable of animation stats (updated every second). |

**Methods**

- `constructor(private ngZone: NgZone)`
- `ngOnDestroy(): void`
- `configure(config: AnimationConfig): void` — Configure simulation engine.
- `start(): void` — Start simulation.
- `stop(): void` — Stop simulation.
- `pause(): void` — Pause simulation.
- `resume(): void` — Resume simulation.
- `isRunning(): boolean` — Check if simulation is running.
- `isPaused(): boolean` — Check if simulation is paused.
- `reset(): void` — Reset simulation timing.
- `registerAnimation(callback: AnimationCallback): () => void` — Register animation callback. Callback is called every frame with delta time.
- `clearAnimations(): void` — Clear all animation callbacks.
- `getAnimationCount(): number` — Get number of registered animations.
- `getStats(): AnimationStats` — Get current animation stats.

**Example**

```typescript
constructor(
  private simulationEngine: SimulationEngineService
) {}

ngOnInit() {
  // Register animation callback
  const unsubscribe = this.simulationEngine.registerAnimation((deltaTime, elapsedTime) => {
    // Update node positions, animations, etc.
    this.updateNodePositions(deltaTime);
  });

  // Start simulation
  this.simulationEngine.start();
}

ngOnDestroy() {
  this.simulationEngine.stop();
}
```

### `VNodeRendererService`

VNodeRendererService

Angular-facing wrapper around the framework-agnostic VNode → DOM patcher
(`VNodePatcher` in `@grafloria/renderer`). This service holds NO rendering logic
of its own: the diff/patch rules live in one place so the Angular canvas, the
e2e harness and any headless consumer all materialize VNodes identically.

`render()` used to wipe the container (`innerHTML = ''`) and rebuild the whole
DOM on every frame, which destroyed focus, text selection, running CSS
animations and anything mounted inside a `<foreignObject>`. It now reconciles:
existing DOM elements are reused, reordered by key, and only genuinely-changed
attributes are written.

```ts
@Injectable({
  providedIn: 'root',
})
export class VNodeRendererService
```

**Methods**

- `render(vnode: VNode, container: HTMLElement): void` — Render (or re-render) a VNode tree into a container, reusing the DOM that is
already there. This is the hot path — it runs once per frame.
- `reconcile(container: HTMLElement, vnode: VNode): Element` — Reconcile a VNode tree into a container and hand back the root element. Same as {@link render}, for callers that want the element.
- `renderVNode(vnode: VNode): Element` — Build a fresh, detached DOM element for a VNode (deep). Used for one-shot materialization; `render()` is what you want for a canvas.
- `updateVNode(element: Element, oldVNode: VNode, newVNode: VNode): void` — Diff two VNodes onto an existing element: props AND children. (The old implementation only diffed props and left a "TODO: handle children"
behind — children are now reconciled by key.)
- `unmount(container: HTMLElement): void` — Remove the tree this service mounted in `container` and forget it.
- `getLastPatchStats(): Readonly<PatchStats>` — Work done by the last `render()` — created / reused / moved / removed /
skipped node counts. A steady-state frame should create ~nothing.

## Interfaces

### `AnimationConfig`

Animation configuration

```ts
interface AnimationConfig
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `fps?` | `number` |  | Target FPS (default: 60) |
| `maxDeltaTime?` | `number` |  | Max delta time cap in ms (prevents spiral of death) |
| `autoStart?` | `boolean` |  | Whether to auto-start (default: false) |

### `AnimationStats`

Animation frame stats

```ts
interface AnimationStats
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `fps` | `number` |  | Current FPS |
| `avgFps` | `number` |  | Average FPS |
| `frameCount` | `number` |  | Frame count |
| `elapsedTime` | `number` |  | Total elapsed time in ms |
| `deltaTime` | `number` |  | Last frame delta time in ms |

### `Breakpoint`

Breakpoint definition

```ts
interface Breakpoint
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  | Unique breakpoint ID |
| `nodeId` | `string` |  | Node ID where breakpoint is set |
| `type` | `BreakpointType` |  | Breakpoint type |
| `enabled` | `boolean` |  | Whether breakpoint is enabled |
| `condition?` | `BreakpointCondition` |  | Condition for conditional breakpoints |
| `conditionExpression?` | `string` |  | Condition expression (for display) |
| `hitCount` | `number` |  | Hit count (how many times breakpoint was hit) |
| `createdAt` | `number` |  | Creation timestamp |
| `metadata?` | `Record<string, any>` |  | Metadata |

### `BreakpointHitEvent`

Breakpoint hit event

```ts
interface BreakpointHitEvent
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `breakpoint` | `Breakpoint` |  | Breakpoint that was hit |
| `nodeId` | `string` |  | Node ID |
| `context` | `any` |  | Execution context at breakpoint |
| `timestamp` | `number` |  | Timestamp |

### `ComponentBounds`

Component bounds for foreignObject.

```ts
interface ComponentBounds
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `x` | `number` |  |  |
| `y` | `number` |  |  |
| `width` | `number` |  |  |
| `height` | `number` |  |  |

### `ComponentUpdate`

Component update for batch operations.

```ts
interface ComponentUpdate
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `nodeId` | `string` |  |  |
| `inputs` | `Record<string, any>` |  |  |

### `DiagramNode`

Mock DiagramNode interface (temporary - should come from

```ts
interface DiagramNode
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  |  |
| `type` | `string` |  |  |
| `getMetadata?` | `() => Record<string, any>` |  |  |

### `ExecutionSession`

Execution session record

```ts
interface ExecutionSession
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  | Session ID |
| `startTime` | `number` |  | Session start timestamp |
| `endTime` | `number \| null` |  | Session end timestamp (null if still running) |
| `duration` | `number \| null` |  | Session duration in ms (null if still running) |
| `state` | `ExecutionState` |  | Session state |
| `steps` | `ExecutionStep[]` |  | Execution steps |
| `currentStepIndex` | `number` |  | Current step index |
| `metadata?` | `Record<string, any>` |  | Session metadata |

### `ExecutionStep`

Execution step record

```ts
interface ExecutionStep
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  | Step ID |
| `nodeId` | `string` |  | Node ID being executed |
| `nodeType` | `string` |  | Node type |
| `startTime` | `number` |  | Step start timestamp |
| `endTime` | `number \| null` |  | Step end timestamp (null if still running) |
| `duration` | `number \| null` |  | Step duration in ms (null if still running) |
| `status` | `'pending' \| 'running' \| 'completed' \| 'error' \| 'skipped'` |  | Step status |
| `input?` | `any` |  | Input data |
| `output?` | `any` |  | Output data |
| `error?` | `{ message: string; code?: string; stack?: string; }` |  | Error details (if status is error) |
| `metadata?` | `Record<string, any>` |  | Step metadata |

### `HandleBounds`

Handle bounds (calculated from DOM)
Used for connection drawing and hit testing

```ts
interface HandleBounds
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  |  |
| `type` | `'source' \| 'target'` |  |  |
| `nodeId` | `string` |  |  |
| `position` | `'top' \| 'right' \| 'bottom' \| 'left'` |  |  |
| `x` | `number` |  |  |
| `y` | `number` |  |  |
| `width` | `number` |  |  |
| `height` | `number` |  |  |
| `absoluteX?` | `number` |  |  |
| `absoluteY?` | `number` |  |  |

### `HTMLHandle`

HTML Handle metadata (stored in registry)

```ts
interface HTMLHandle
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  |  |
| `type` | `'source' \| 'target'` |  |  |
| `position` | `'top' \| 'right' \| 'bottom' \| 'left'` |  |  |
| `element` | `HTMLElement` |  |  |

### `ModeAnalytics`

Mode analytics data

```ts
interface ModeAnalytics
```

### `ModeChangeEvent`

Mode change event

```ts
interface ModeChangeEvent
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `previousMode` | `DiagramMode` |  |  |
| `currentMode` | `DiagramMode` |  |  |

### `ModeGuardResult`

Mode guard function result

```ts
interface ModeGuardResult
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `allowed` | `boolean` |  |  |
| `reason?` | `string` |  |  |

### `ModeHistoryEntry`

Mode history entry

```ts
interface ModeHistoryEntry
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | `DiagramMode` |  |  |
| `timestamp` | `number` |  |  |
| `duration` | `number \| null` |  |  |

### `ModeViewportSettings`

Viewport settings for a specific mode

```ts
interface ModeViewportSettings
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `allowZoom?` | `boolean` |  |  |
| `allowPan?` | `boolean` |  |  |
| `minZoom?` | `number` |  |  |
| `maxZoom?` | `number` |  |  |
| `centerOnLoad?` | `boolean` |  |  |
| `fitToScreen?` | `boolean` |  |  |
| `followNode?` | `string` |  |  |
| `autoCenter?` | `boolean` |  |  |
| `resetOnEnter?` | `boolean` |  |  |

### `PropertyChangeEvent`

Property change event

```ts
interface PropertyChangeEvent
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `nodeId?` | `string` |  | Node ID (single node change) |
| `nodeIds?` | `string[]` |  | Node IDs (bulk change) |
| `propertyKey` | `string` |  | Property key that changed |
| `oldValue?` | `any` |  | Old value (single node change) |
| `newValue` | `any` |  | New value |
| `timestamp` | `number` |  | Timestamp of change |

### `PropertyDiagramNode`

Diagram node interface for property management
Simplified interface to work with any node type that has data storage

```ts
interface PropertyDiagramNode
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  |  |
| `type` | `string` |  |  |
| `label?` | `string` |  | Optional human-readable label shown in the panel header (falls back to id) |
| `data` | `Record<string, any>` |  |  |

### `RenderComponentOptions`

Options for rendering a component.

```ts
interface RenderComponentOptions
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `inputs?` | `Record<string, any>` |  | Initial input values |
| `outputHandlers?` | `Record<string, (event: any) => void>` |  | Output event handlers |

## Types

### `AnimationCallback`

Animation callback function

```ts
type AnimationCallback = (deltaTime: number, elapsedTime: number) => void;
```

### `BreakpointCondition`

Breakpoint condition function

```ts
type BreakpointCondition = (context: any) => boolean;
```

### `ModeChangeHook`

Mode change hook function

```ts
type ModeChangeHook = (
  previousMode: DiagramMode,
  nextMode: DiagramMode,
  context?: any
) => void | false;
```

### `ModeGuardFunction`

Mode transition guard function

```ts
type ModeGuardFunction = (
  previousMode: DiagramMode,
  nextMode: DiagramMode
) => ModeGuardResult;
```

## Enums

### `BreakpointType`

Breakpoint type

```ts
enum BreakpointType
```

**Members**

- `BEFORE = 'before'` — Break before node execution
- `AFTER = 'after'` — Break after node execution
- `CONDITIONAL = 'conditional'` — Break on condition

### `ExecutionState`

Execution state for workflow/process execution

```ts
enum ExecutionState
```

**Members**

- `IDLE = 'idle'`
- `RUNNING = 'running'`
- `PAUSED = 'paused'`
- `COMPLETED = 'completed'`
- `ERROR = 'error'`

### `SimulationState`

Simulation state

```ts
enum SimulationState
```

**Members**

- `IDLE = 'idle'`
- `RUNNING = 'running'`
- `PAUSED = 'paused'`
