ControlledGraph API

ControlledGraph declares your application the canonical owner of line topology. It renders nothing.

PropMeaning
onLineChangeRequestReceives one atomic LineChangeRequest per gesture and returns the line list that should now be canonical
onDiagnosticsChangedReceives the current ReconciliationError[] when it changes

There is no lines prop. The handler’s return value is the channel, so a typical graph is one line:

<script lang="ts">
  import { applyLineChange } from "@snap-engine/snapline";
  let lines = $state.raw<LineRecord[]>([]);
</script>

<ControlledGraph
  onLineChangeRequest={(request) => (lines = applyLineChange(lines, request))}
/>

The assignment updates your state and evaluates to the array the bridge needs. Reject by returning the list unchanged (return lines) — the staged line is then discarded. The return type is required, so forgetting to return is a type error rather than an accidental rejection. The handler must be synchronous.

A LineChangeRequest carries an intent ("connect" | "disconnect" | "replace" | "reconnect") plus add (ProposedLine[] with SnapLine-minted ids), remove (LineId[]), and update (endpoint retargets). Adopting a proposed id settles the staged line in place, with no flicker and the same mirror.

For changes no request asked for — loading a saved graph, undo/redo, a collaborator’s edit — take a component reference and push:

<ControlledGraph bind:this={graph} onLineChangeRequest={...} />

graph.setLines(records) replaces the canonical document; graph.flush() runs any pending reconciliation synchronously.

ReconciliationError.code is one of "duplicate-id" (a record id appears twice in the snapshot), "capacity-exceeded" (a record violates a connector’s maxIncoming), or "connection-rejected" (an endpoint’s isValidConnection vetoed the record). Errored records stay in your document and are retried when relevant state changes; records whose endpoints are not mounted stay silently latent.