# Interfaces I–L

Import these from `@grafloria/engine`.

## Interfaces

### `ILayoutAlgorithm`

```ts
interface ILayoutAlgorithm
```

**Members**

- `getName(): string` — Get the name of the layout algorithm
- `getType(): 'grid' | 'force-directed' | 'hierarchical' | 'hybrid'` — Get the type of the layout algorithm
- `calculatePlacement(options: PlacementOptions): PlacementResult` — Calculate position for a single new node

This is called when a node is added to the diagram. The algorithm should return a position that:
- Doesn't overlap with existing nodes
- Follows the layout strategy
- Fits within the viewport (or is close to existing content)
- `reLayout(diagram: DiagramModel, config?: LayoutConfiguration): Map<string, Point>` — Re-layout all nodes in the diagram

This is called when the user explicitly requests a re-layout (e.g., clicks "Re-Arrange" button). The algorithm should calculate new positions for ALL nodes.
- `configure(config: LayoutConfiguration): void` — Configure the layout algorithm
- `getConfiguration(): LayoutConfiguration` — Get current configuration
- `canApply(diagram: DiagramModel): { valid: boolean; reason?: string }` — Validate if this algorithm can be applied to the given diagram

For example:
- Hierarchical layout requires a DAG (no cycles)
- Force-directed works better with connected nodes
- `onActivate?(): void` — Called when the algorithm is activated
Use this to initialize any state or caches
- `onDeactivate?(): void` — Called when the algorithm is deactivated
Use this to clean up state or caches

### `IncrementalLayoutOptions`

Options for incremental layout

```ts
interface IncrementalLayoutOptions
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `strategy?` | `IncrementalLayoutStrategy` | `'pin-existing'` | Strategy to use for incremental layout |
| `newNodeIds?` | `string[]` |  | Nodes that are new (to be laid out) If not provided, nodes without valid positions are considered new |
| `anchorNodeIds?` | `string[]` |  | Anchor nodes that should never move (in addition to strategy constraints) These have highest priority |
| `maxShift?` | `number` |  | Maximum distance a non-anchor node can move (pixels) Only applies to 'minimal-shift' strategy |
| `proximityRadius?` | `number` | `200` | Radius around new nodes where existing nodes can be adjusted (pixels) Only applies to 'proximity-aware' strategy |
| `allowMinorAdjustments?` | `boolean` | `false` | Whether to allow slight adjustments to improve layout quality |
| `customConstraints?` | `LayoutConstraints` |  | Custom constraints to apply in addition to incremental constraints |

### `IncrementalLayoutResult`

Result of incremental layout operation

```ts
interface IncrementalLayoutResult
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `movedNodeIds` | `string[]` |  | IDs of nodes that were moved during layout |
| `pinnedNodeIds` | `string[]` |  | IDs of nodes that were pinned/fixed |
| `newlyLaidOutNodeIds` | `string[]` |  | IDs of nodes that were newly laid out |
| `maxMovement` | `number` |  | Maximum distance any node moved (pixels) |
| `avgMovement` | `number` |  | Average distance nodes moved (pixels) |
| `strategy` | `IncrementalLayoutStrategy` |  | Strategy that was used |
| `autoConstraintCount` | `number` |  | Number of constraints that were auto-generated |

### `LabelBox`

The box an edge label needs. Layout's job is to reserve it; not to place it.

```ts
interface LabelBox
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  | The label this box belongs to. |
| `linkId` | `string` |  | The link the label rides on. |
| `text` | `string` |  |  |
| `width` | `number` |  |  |
| `height` | `number` |  |  |

### `LabelClearanceResult`

```ts
interface LabelClearanceResult
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `overlaps` | `number` |  | Label boxes that land on top of a node. |
| `judged` | `number` |  | Labelled links that could be judged. |
| `score` | `number` |  | 100 = no label box collides with any node. |
| `collidingLinks` | `string[]` |  | Which links' labels collided, for the report. |

### `LayeringEstimate`

Longest-path layering estimate for a DAG — the two numbers that predict which
hierarchical engine will choke (measured, not guessed; see the perf spec):

• dagre's pathology is DEPTH: a 2,000-rank chain never returns, while a
    2,000-node, ~1,000-wide tree takes ~700ms.
  • our layered (Sugiyama) engine's pathology is WIDTH: crossing minimisation
    over a ~1,000-node rank runs for tens of seconds, while a 45-wide,
    90-deep mesh takes ~480ms and a width-1, 2,000-deep chain ~130ms.

O(n + m) via Kahn's algorithm. Only meaningful when the graph is acyclic;
nodes left unranked by a cycle default to rank 0.

```ts
interface LayeringEstimate
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `depth` | `number` |  | Number of layers a longest-path layering would produce. |
| `maxWidth` | `number` |  | Node count of the widest layer. |

### `LayoutAdapter`

Interface that all layout adapters must implement

```ts
interface LayoutAdapter
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` |  | Name of the layout adapter (e.g., 'dagre', 'elk') |

**Members**

- `apply( nodes: NodeModel[], links: LinkModel[], options?: Partial<LayoutOptions> ): Promise<LayoutResult>` — Apply layout to nodes and links
- `applyIncremental( nodes: NodeModel[], links: LinkModel[], incrementalOptions: IncrementalLayoutOptions, layoutOptions?: Partial<LayoutOptions> ): Promise<LayoutResult & { incremental: IncrementalLayoutResult }>` — Apply incremental layout - layout new nodes while preserving existing positions
- `validateOptions(options: Partial<LayoutOptions>): boolean` — Validate that options are valid for this adapter

### `LayoutCandidate`

```ts
interface LayoutCandidate
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` |  | Registered layout name to run. |
| `options` | `UnifiedLayoutOptions` |  | Options to run it with. |
| `id` | `string` |  | A stable id for this candidate — name + tuning. Ties break on this. |
| `portAware` | `boolean` |  | Is this engine able to honour port sides? |

### `LayoutConfiguration`

Configuration for layout algorithms

```ts
interface LayoutConfiguration
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `type?` | `LayoutAlgorithmType` |  | Algorithm type (optional when passed to reLayout, as it uses current algorithm) |
| `options?` | `GridLayoutOptions \| ForceDirectedOptions \| HierarchicalOptions \| HybridOptions` |  | Algorithm-specific options |
| `animate?` | `boolean` |  | Whether to animate layout changes |
| `animationDuration?` | `number` |  | Animation duration in ms |
| `viewport?` | `Rectangle` |  | Viewport for viewport-aware layout |
| `margins?` | `number` |  | Margins around content |
| `direction?` | `'TB' \| 'BT' \| 'LR' \| 'RL'` |  | Shorthand for hierarchical direction |

### `LayoutConstraints`

Collection of layout constraints to apply

```ts
interface LayoutConstraints
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `constraints` | `NodeConstraint[]` |  | Array of node constraints |
| `conflictResolution?` | `'priority' \| 'first' \| 'last'` |  | Strategy for handling conflicting constraints - 'priority': Use constraint priority to resolve conflicts - 'first': First constraint wins - 'last': Last constraint wins |

### `LayoutErrorMessage`

```ts
interface LayoutErrorMessage
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `seq` | `number` |  |  |
| `kind` | `'error'` |  |  |
| `message` | `string` |  |  |

### `LayoutGraph`

A whole graph, structured-clone-safe: no functions, no class instances.

```ts
interface LayoutGraph
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `nodes` | `LayoutGraphNode[]` |  |  |
| `links` | `LayoutGraphLink[]` |  |  |

### `LayoutGraphLink`

A link, as it crosses the boundary.

```ts
interface LayoutGraphLink
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  |  |
| `sourceNodeId?` | `string` |  |  |
| `targetNodeId?` | `string` |  |  |
| `sourcePortId?` | `string` |  |  |
| `targetPortId?` | `string` |  |  |

### `LayoutGraphNode`

A node, as it crosses the boundary: geometry + topology only.

```ts
interface LayoutGraphNode
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  |  |
| `type` | `string` |  |  |
| `position` | `{ x: number; y: number }` |  |  |
| `size` | `{ width: number; height: number }` |  |  |
| `parentId?` | `string` |  | Container membership — the nested-layout card needs it; harmless otherwise. |
| `positionMode?` | `'absolute' \| 'relative' \| 'layout'` |  | How `position` relates to the parent. Absent on pre-v3 payloads, which meant summation — i.e. 'relative'; the consumer below applies exactly that default. |
| `ports?` | `LayoutGraphPort[]` |  |  |

### `LayoutGraphPort`

A port, as it crosses the boundary. Ids are preserved — see above.

```ts
interface LayoutGraphPort
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  |  |
| `type` | `'input' \| 'output' \| 'bi'` |  |  |
| `side?` | `'left' \| 'right' \| 'top' \| 'bottom'` |  |  |
| `index?` | `number` |  | Order within a side — carried so multi-port sides revive in the same order. |
| `position?` | `{ x: number; y: number }` |  |  |

### `LayoutHistoryOptions`

Options for layout history management

```ts
interface LayoutHistoryOptions
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `maxHistorySize?` | `number` |  | Maximum number of history entries to keep |
| `autoSnapshot?` | `boolean` |  | Whether to automatically create snapshots |
| `minSnapshotInterval?` | `number` |  | Minimum time between auto-snapshots (ms) |

### `LayoutPort`

The message-port surface the host needs — a real Worker satisfies it.

```ts
interface LayoutPort
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `onmessage` | `((ev: { data: LayoutResponse }) => void) \| null` |  |  |

**Members**

- `postMessage(msg: LayoutRequest): void`

### `LayoutPreset`

Layout preset configuration

```ts
interface LayoutPreset
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` |  | Unique identifier for the preset |
| `name` | `string` |  | Human-readable name |
| `description` | `string` |  | Description of when to use this preset |
| `adapter` | `'dagre' \| 'elk'` |  | Which adapter to use |
| `options` | `Partial<DagreLayoutOptions> \| Partial<ELKLayoutOptions>` |  | Layout options for the adapter |
| `constraints?` | `LayoutConstraints` |  | Optional pre-configured constraints |
| `incrementalOptions?` | `Partial<IncrementalLayoutOptions>` |  | Optional incremental layout settings |
| `tags?` | `string[]` |  | Tags for categorization |

### `LayoutPresetCategory`

Category of layout presets

```ts
interface LayoutPresetCategory
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` |  | Category name |
| `description` | `string` |  | Category description |
| `presets` | `LayoutPreset[]` |  | Presets in this category |

### `LayoutProgress`

```ts
interface LayoutProgress
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `progress` | `number` |  | 0..1. |
| `phase` | `string` |  |  |
| `iteration` | `number` |  |  |
| `totalIterations` | `number` |  |  |

### `LayoutProgressMessage`

```ts
interface LayoutProgressMessage
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `seq` | `number` |  |  |
| `kind` | `'progress'` |  |  |
| `progress` | `number` |  | 0..1. Monotonic. |
| `phase` | `string` |  |  |
| `iteration` | `number` |  |  |
| `totalIterations` | `number` |  |  |

### `LayoutQualityResult`

Overall layout quality assessment

```ts
interface LayoutQualityResult
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `overallScore` | `number` |  | Overall quality score (0-100) |
| `grade` | `'A' \| 'B' \| 'C' \| 'D' \| 'F'` |  | Quality grade (A, B, C, D, F) |
| `metrics` | `{ edgeCrossings: QualityMetric; nodeOverlap: QualityMetric; edgeLength: QualityMetric; nodeDistribution: QualityMetric; symmetry: QualityMetric; aspectRatio: QualityMetric; }` |  | Individual metrics |
| `topSuggestions` | `string[]` |  | Top suggestions for improvement |
| `timestamp` | `number` |  | Timestamp of assessment |

### `LayoutRequestCancel`

```ts
interface LayoutRequestCancel
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `seq` | `number` |  |  |
| `kind` | `'cancel'` |  |  |
| `target` | `number` |  | The seq of the run to cancel. |

### `LayoutRequestRun`

```ts
interface LayoutRequestRun
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `seq` | `number` |  |  |
| `kind` | `'run'` |  |  |
| `algorithm` | `string` |  |  |
| `graph` | `LayoutGraph` |  |  |
| `options` | `LayoutWireOptions` |  |  |
| `timeBudgetMs?` | `number` |  | Stop and return the best-so-far once this many ms have elapsed. |
| `sliceMs?` | `number` |  | How long to compute before surrendering the thread so `cancel` can land. |
| `stopAfterIteration?` | `number` |  | Stop after exactly N iterations — a DETERMINISTIC pre-emption. |

### `LayoutResult`

Result of applying a layout algorithm

```ts
interface LayoutResult
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `nodePositions` | `Map<string, { x: number; y: number }>` |  | Map of node IDs to their new positions |
| `bounds` | `{ x: number; y: number; width: number; height: number; }` |  | Bounding box of the laid-out graph |
| `metadata?` | `{ algorithm: string; executionTime: number; [key: string]: any; }` |  | Additional metadata about the layout execution |
| `quality?` | `LayoutQualityResult` |  | Quality assessment of the layout (if calculateQuality was true) |
| `portAware?` | `PortAwareLayoutResult` |  | Port-aware layout result (if portAware was enabled) |
| `subgraph?` | `SubgraphLayoutResult` |  | Subgraph layout result (if subgraph was enabled) |
| `edgeBundling?` | `EdgeBundlingResult` |  | Edge bundling result (if edgeBundling was enabled) |
| `routing?` | `LayoutRoutingHints` |  | The port positions and edge routes the layout engine computed. Present when the engine produces them (ELK does); previously computed and discarded. |

### `LayoutResultMessage`

```ts
interface LayoutResultMessage
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `seq` | `number` |  |  |
| `kind` | `'result'` |  |  |
| `algorithm` | `string` |  |  |
| `positions` | `Array<[string, { x: number; y: number }]>` |  |  |
| `bounds` | `{ x: number; y: number; width: number; height: number }` |  |  |
| `partial` | `boolean` |  | True when the run stopped early — the answer is the best-so-far, not the end. |
| `reason?` | `LayoutStopReason` |  |  |
| `iteration` | `number` |  |  |
| `totalIterations` | `number` |  |  |
| `metadata?` | `LayoutResult['metadata']` |  | Everything else the adapter reported, carried verbatim. |
| `quality?` | `LayoutResult['quality']` |  |  |
| `portAware?` | `LayoutResult['portAware']` |  |  |
| `subgraph?` | `LayoutResult['subgraph']` |  |  |
| `edgeBundling?` | `LayoutResult['edgeBundling']` |  |  |

### `LayoutRng`

A seeded, deterministic source of randomness.

```ts
interface LayoutRng
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `seed` | `number` |  | The seed this generator was created with (so a result can report it). |

**Members**

- `next(): number` — Uniform in [0, 1).
- `between(min: number, max: number): number` — Uniform in [min, max).

### `LayoutRoutingHints`

What the layout engine worked out about EDGES and PORTS,
which until now was computed and then thrown in the bin.

ELK does genuine port-aware layered layout with orthogonal edge routing. The
old adapter read back `child.x` / `child.y` and NOTHING else — every port
position and every edge section ELK produced was discarded. These are those
results.

They are HINTS, deliberately. Layout's job
is to place nodes so a good route EXISTS and to say where it thinks that route
runs — not to draw it.

```ts
interface LayoutRoutingHints
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `portPositions` | `Map<string, { x: number; y: number; side: PortSide }>` |  | Absolute position of each declared port, as the layout engine placed it. |
| `edgeRoutes` | `Map<string, { start: Point; end: Point; bends: Point[] }>` |  | The route the layout engine found for each link: endpoints + bend points. |
| `labelSpace` | `Map<string, { width: number; height: number }>` |  | The box reserved for each labelled link (keyed by link id). |
| `orthogonal` | `boolean` |  | Whether the engine routed orthogonally. |
