# Functions

Import these from `@grafloria/renderer`.

## Functions

### `activeRegistryScope`

The active scope, or null. Hosts should not need this; the registries do.

```ts
function activeRegistryScope(): RegistryScope | null
```

### `assertEngineCompatible`

Reject a plugin built against an incompatible host API.

```ts
function assertEngineCompatible(
  manifest: ExtensionManifest<CapabilityName>,
  apiVersion: string
): void
```

### `clearConnectionValidators`

```ts
function clearConnectionValidators(): void
```

### `clearLinkPipeline`

Drop every registration (tests, host teardown). Built-ins are re-seeded.

```ts
function clearLinkPipeline(): void
```

### `clearTools`

```ts
function clearTools(): void
```

### `connectionValidatorCount`

How many validators are live (tests assert this returns to 0 after dispose).

```ts
function connectionValidatorCount(): number
```

### `createCounterScaledPortal`

A world-space portal that must also stay a FIXED SCREEN SIZE (a resize handle,
a badge that should not grow when you zoom in). It lives in world space but
counter-scales by 1/zoom on every camera change.

```ts
function createCounterScaledPortal(
  htmlLayer: HTMLElement,
  viewport: ViewportController,
  options: { x?: number; y?: number; className?: string; style?: string } = {}
): ViewportPortal
```

### `createDiagramApi`

```ts
function createDiagramApi(instance: DiagramInstance): DiagramApi
```

### `createExtensionHost`

Convenience: a host bound to a live `createDiagram()` instance.

```ts
function createExtensionHost(options: ExtensionHostOptions): ExtensionHost
```

### `createPortal`

Mount a SCREEN-SPACE portal — a floating panel pinned to the viewport.

```ts
const panel = createPortal(diagram.container, { placement: 'top-right' });
panel.element.appendChild(myToolbar);
// later
panel.dispose();
```

```ts
function createPortal(root: HTMLElement, options: PortalOptions = {}): Portal
```

### `createViewportPortal`

Mount a WORLD-SPACE portal — content that pans and zooms WITH the canvas.

`x`/`y` are WORLD coordinates. The element is placed inside the camera-
transformed HTML layer, so it needs no per-frame updates: the single transform
on the layer moves it. That is why this takes the layer, not the viewport, and
why there is no `onChange` subscription to leak.

```ts
const note = createViewportPortal(htmlLayer, { x: 320, y: 180 });
note.element.textContent = 'sticky note';
```

```ts
function createViewportPortal(
  htmlLayer: HTMLElement,
  options: { x?: number; y?: number; className?: string; style?: string } = {}
): ViewportPortal
```

### `defineNodeComponent`

```ts
function defineNodeComponent<D = Record<string, unknown>>(
  component: NodeComponent<D>
): NodeComponent<D>
```

### `ensureScreenLayer`

The screen-space layer, created lazily on the diagram root. Idempotent: many
portals share one layer.

```ts
function ensureScreenLayer(root: HTMLElement): HTMLElement
```

### `getAnchor`

```ts
function getAnchor(name: string): AnchorFn | undefined
```

### `getConnectionPoint`

```ts
function getConnectionPoint(name: string): ConnectionPointFn | undefined
```

### `getConnector`

```ts
function getConnector(name: string): ConnectorFn | undefined
```

### `getLinkPipelineVersion`

```ts
function getLinkPipelineVersion(): number
```

### `getTool`

```ts
function getTool(id: string): CanvasTool | undefined
```

### `hasAnchor`

```ts
function hasAnchor(name: string): boolean
```

### `hasConnectionPoint`

```ts
function hasConnectionPoint(name: string): boolean
```

### `hasConnector`

```ts
function hasConnector(name: string): boolean
```

### `hasTool`

```ts
function hasTool(id: string): boolean
```

### `isValidConnection`

Evaluate every registered validator. This is the public `isValidConnection`
hook. With no validators registered it is `{ valid: true }` — i.e. free.

```ts
function isValidConnection(candidate: ConnectionCandidate): ConnectionValidity
```

### `listAnchors`

```ts
function listAnchors(): string[]
```

### `listConnectionPoints`

```ts
function listConnectionPoints(): string[]
```

### `listConnectors`

```ts
function listConnectors(): string[]
```

### `listTools`

```ts
function listTools(): string[]
```

### `loadCanvasPlugins`

```ts
function loadCanvasPlugins(): Promise<typeof import('./components/attach')>
```

### `mountNodeComponents`

Wire a component registry into a live diagram.

Returns a disposer that unmounts every component and disconnects every
observer — the leak rule; a stranded ResizeObserver keeps its target's whole
subtree alive.

```ts
function mountNodeComponents(
  instance: DiagramInstance,
  registry: NodeComponentRegistry
): Disposer
```

### `nodeComponentOptions`

The options you hand to `createDiagram()` to use a component registry from the
very first paint (preferred over `mountNodeComponents` on a running instance,
because the first render then already has the components).

```ts
const diagram = createDiagram(el, {
  nodes, edges,
  ...nodeComponentOptions(registry, () => diagram),
});
```

```ts
function nodeComponentOptions(
  registry: NodeComponentRegistry,
  getInstance: () => DiagramInstance
): {
  renderCustomNode: (node: NodeModel, element: HTMLElement) => void;
  removeCustomNode: (id: string, element: HTMLElement) => void;
}
```

### `notifyLinkPipelineChanged`

Internal: let a PER-DIAGRAM registry participate in the version/notify
protocol. A scoped registration must invalidate cached VNodes for exactly the
reason a global one must — the definition is baked into the cache. Notifying
every renderer (not just the contributing one) is deliberate: over-invalidation
costs a repaint, under-invalidation shows a stale picture.

```ts
function notifyLinkPipelineChanged(): void
```

### `once`

Wrap a function so it can only ever run once.

```ts
function once(fn: Disposer): Disposer
```

### `onLinkPipelineChange`

```ts
function onLinkPipelineChange(listener: () => void): Disposer
```

### `registerAnchor`

Register a named anchor. Address it per link via
`link.metadata.sourceAnchor` / `link.metadata.targetAnchor`.

```ts
function registerAnchor(name: string, fn: AnchorFn): Disposer
```

**Returns** a disposer that RESTORES whatever was registered under this name
before (so overriding a built-in is reversible).

### `registerConnectionPoint`

Register a named connection-point strategy. Address it per link via
`link.metadata.connectionPoint`, or set it as the diagram-wide default with
the renderer's `connectionPoint` config.

The built-in `'smart'` strategy is the draw.io-style floating attachment that
used to be reachable ONLY through the boolean `smartConnectionPoints` config
flag. That flag still works (it now selects this strategy by name), so nothing
that relied on it changes behaviour.

```ts
function registerConnectionPoint(name: string, fn: ConnectionPointFn): Disposer
```

### `registerConnectionValidator`

Register a connection validator. ALL registered validators must pass for a
connection to be offered — veto power, not voting power, because a rule that
can be outvoted is not a rule.

```ts
function registerConnectionValidator(validator: ConnectionValidator): Disposer
```

### `registerConnector`

Register a named connector. Address it per link via `link.connector`
.

The four built-in names — `straight` / `rounded` / `smooth` / `bezier` — are
NOT in this map: they are the renderer's own internal branches and stay
exactly as they were. This registry is consulted only for names the renderer
does not recognise, which is precisely the case that used to be dropped.

```ts
function registerConnector(name: string, fn: ConnectorFn): Disposer
```

### `registerTool`

Register (or replace) a canvas tool. Returns a disposer that RESTORES the
previous tool of the same id — so overriding `'node-drag'` and then unloading
the extension gives the original back rather than leaving a hole.

On every pointerdown, {@link resolveTool} asks EVERY registered tool
`hitTest(event, hit)` and the gesture goes to the claiming tool with the
HIGHEST `priority` (default 1; the built-in ladder is effectively 0). Ties
resolve by registration order — first registered wins — and that is a
fallback, NOT a contract: tools composed from different waves and extensions
register in an order nobody designed. A tool that can ever be active at the
same time as another MUST state an explicit priority.

Choosing one: a POINT-SPECIFIC claim (hitTest inspects the point — "is there
ink under the pointer?") should outrank a POINT-AGNOSTIC mode claim (hitTest
is just "am I active?"), because the specific tool only fires where it means
to and would otherwise be starved by the broad one everywhere it matters. The whiteboard tools are the worked example: draw/rectangle/eraser sit at
`WHITEBOARD_MODE_TOOL_PRIORITY` (1), the ink-hit-only StrokeEditTool at
`WHITEBOARD_INK_TOOL_PRIORITY` (2) — see whiteboard-tools.ts.

```ts
function registerTool(tool: CanvasTool): Disposer
```

### `resolveTool`

The tool (if any) that claims this gesture, highest priority first (ties:
first registered — see the arbitration contract on {@link registerTool}).

The DomEventBinder calls this FIRST on pointerdown. `undefined` means "no
registered tool wants it" — and then the built-in ladder runs untouched, which
is why adding this seam changed no existing behaviour.

```ts
function resolveTool(
  event: ToolPointerEvent,
  hit: ToolHitContext
): CanvasTool | undefined
```

### `runInRegistryScope`

Run `fn` with `scope` active, restoring whatever was active before.

An EMPTY scope is treated as no scope at all. That is not merely an
optimisation: it keeps the hot read path for a diagram that contributed
nothing — which is every existing embedder and all 104 demos — at exactly its
previous cost, one null check with no Map lookup behind it.

```ts
function runInRegistryScope<T>(scope: RegistryScope | null | undefined, fn: () => T): T
```

### `satisfies`

A deliberately small semver range matcher. Supports:
  *  / x            any
  1.2.3             exact
  ^1.2.3            >=1.2.3 <2.0.0   (and ^0.2.3 → >=0.2.3 <0.3.0)
  ~1.2.3            >=1.2.3 <1.3.0
  >=1.2.3, >1.2.3, <=1.2.3, <1.2.3
  1.x / 1.2.x
  " || "            union of any of the above

```ts
function satisfies(version: string, range: string): boolean
```

### `scopedTable`

The active scope's table for `name`, or undefined when there is no scope or it
has no such table. THE READ HELPER every registry uses — deliberately one
function so all of them shadow identically.

```ts
function scopedTable<V>(name: string): Map<string, V> | undefined
```

### `sideTowards`

The side of `rect` you would leave from to head towards `towards` — the same
dominant-axis rule the built-in smart strategy uses.

```ts
function sideTowards(rect: ExtRect, towards: ExtPoint): ExtSide
```

### `snapshotRestore`

Build a disposer that puts a registry key back the way it was.

```ts
function snapshotRestore<T>(
  previous: T | undefined,
  restore: (value: T) => void,
  remove: () => void
): Disposer
```

**Parameters**

- `previous`: the value read out of the registry BEFORE the write (or `undefined` when the key did not exist)
- `restore`: re-register the previous value
- `remove`: delete the key (used when there was no previous value)

### `validateManifest`

Throw with a precise reason. A malformed manifest must never half-load.

```ts
function validateManifest(manifest: ExtensionManifest<CapabilityName>): void
```
