import type { ElkEdgeSection, ElkNode, ElkPoint, LayoutOptions } from 'elkjs/lib/elk.bundled.js'; import type { CollectionReturnValue } from 'cytoscape'; import type { LayoutDirection } from '../layout-direction.js'; import { type LayoutMode } from '../layout-mode.js'; import { edgeLabelText, type EdgeLabelData } from '../edge-label.js'; export { edgeLabelText, type EdgeLabelData }; export declare const CONTAINER_PADDING = 30; export declare const CONTAINER_LABEL_GAP = 22; export declare const EDGE_LABEL_MAX_TEXT_WIDTH = 110; export declare const EDGE_LABEL_FONT_SIZE = 10; export declare const COMPONENT_ASPECT_RATIO = 2.5; export declare const ELK_DIRECTION: Readonly>; /** * The spacing a mode asks for. A routed mode draws two labelled edges side by * side, and their labels lay over each other unless the lanes are wider than * a reading. Measured on the reference Landscape under served-by, labels * on another label: 20px 14, 30px 3, 60px 0, at a width cost of 5% for the * last step (12,346 to 12,943px); `elk.spacing.labelLabel`, `edgeLabel`, and * placing labels beside the edge instead of on it all left the 3 standing. * `edgeEdgeBetweenLayers` stays at 20 on purpose: it spaces the horizontal * channels between layers, and widening it measured as canvas height (8,020 * to 11,624px) for no gain against the same overlap. */ export declare const spacingFor: (mode: LayoutMode) => LayoutOptions; /** * The root graph's options for a mode and a direction. * * `layered` is the bag that shipped before 1.24, under the qualified * `elk.aspectRatio` key now that nothing injects a bare one. The routed modes * add hierarchy handling - without `INCLUDE_CHILDREN` ELK lays each container * out as a separate graph and cannot route an edge that crosses a container's * border - orthogonal routing, inline labels with room reserved, and model * order, which keeps siblings in the order they were authored. `bands` * activates partitioning; the partitions themselves are set per node. * * Never `elk.layered.compaction.postCompaction.strategy`: measured with * `EDGE_LENGTH` on the reference model, ELK throws "Invalid hitboxes for * scanline constraint calculation" on the nested graph. */ export declare const rootLayoutOptions: (direction: LayoutDirection, mode: LayoutMode) => LayoutOptions; /** * How much room ELK reserves for a label: an estimate of what cytoscape will * draw at 10px, wrapped at `EDGE_LABEL_MAX_TEXT_WIDTH`. An estimate is enough - * the point is that a label has a box of roughly the right size in the layout, * where before it had none. */ export declare const edgeLabelSize: (text: string) => { readonly width: number; readonly height: number; }; export interface ElkBuildOptions { readonly direction: LayoutDirection; readonly mode: LayoutMode; readonly showKindLabels: boolean; } export interface ElkBuild { readonly graph: ElkNode; /** Edges whose ELK source and target were swapped for layering. */ readonly reversed: ReadonlySet; } /** * The ELK graph for a cytoscape collection. * * Leaves carry their drawn size; containers carry their children and the * shared spacing, and no size, so ELK sizes them around what they hold. Every * edge sits on the root graph - ELK accepts an edge anywhere at or above its * endpoints' common ancestor - with its ends swapped where the mode layers the * target above the source, and with a label sized for its reading in the * routed modes. `layered` sends no labels, which is what it always sent. */ export declare function buildElkGraph(collection: CollectionReturnValue, options: ElkBuildOptions): ElkBuild; export interface ElkRoute { /** The route in absolute coordinates, source end first. */ readonly points: readonly ElkPoint[]; /** How far along the route, in px from the source end, the label's centre sits; null for no label. */ readonly labelAt: number | null; } export interface ElkPlacement { /** Leaf node centres, absolute, keyed by id. */ readonly nodes: ReadonlyMap; readonly edges: ReadonlyMap; } /** A section's polyline, moved from its container's frame into the root's. */ export declare const absoluteSection: (section: ElkEdgeSection, offset: ElkPoint) => ElkPoint[]; export declare const polylineLength: (points: readonly ElkPoint[]) => number; /** The arc length from the polyline's start to the point on it nearest `p`. */ export declare const arcPosition: (points: readonly ElkPoint[], p: ElkPoint) => number; /** * What ELK placed, in one frame. * * ELK writes every child's position relative to its parent and every edge's * route relative to the edge's container - the graph it was declared in, or * the one ELK moved it to (`container`). Both are resolved here to absolute * coordinates, and an edge whose ends were swapped for layering is turned * back so its route runs source to target the way the arrow does. */ export declare function readElkLayout(laid: ElkNode, reversed: ReadonlySet): ElkPlacement; /** * What lays a graph out. The bundled engine runs ELK on the calling thread * behind a promise, in the browser and under vitest alike, and it is what * every page starts with. A page that can serve a worker file installs an * engine over a Web Worker instead (#490), and the canvas never knows which * it is talking to: `layoutWithElk` is the one door. */ export interface LayoutEngine { readonly layout: (graph: ElkNode) => Promise; /** Lets go of whatever the engine holds; the bundled one holds nothing. */ readonly terminate?: () => void; } /** * The worker a page constructs for `workerLayoutEngine`: anything that can be * posted to. elk-api installs `onmessage` on it and speaks its own protocol * to `elkjs/lib/elk-worker.min.js` at the other end, so a host hands over a * `new Worker(url)` of that file and nothing else. */ export interface LayoutWorker { postMessage(message: unknown): void; } /** * An engine that runs ELK in the worker `workerFactory` constructs. The * served page installs one over the worker file vite emits beside its other * assets (#490); a host mounting the library passes its own factory when its * policy lets it serve that file, and leaves the bundled engine otherwise. */ export declare function workerLayoutEngine(workerFactory: () => LayoutWorker): LayoutEngine; /** * Makes `next` the engine every layout goes through; `undefined` restores the * bundled one. Installing never terminates the engine being replaced: the * page that made it owns it. */ export declare function installLayoutEngine(next: LayoutEngine | undefined): void; export declare const layoutWithElk: (graph: ElkNode) => Promise;