Import these from @grafloria/renderer.
Classes
ArrowRenderer
ArrowRenderer generates SVG VNodes for different arrow types.
Supports:
- Basic arrows (arrow, circle, square, diamond)
- ERD arrows (crow-foot, one, zero-or-one, zero-or-many, one-or-many)
- UML arrows (hollow-diamond, filled-diamond, generalization, open-arrow, double-arrow)
- Additional arrows (cross, bar, dot, oval)
- Half-arrowheads (Mermaid 11.13) —
half-arrow-left/half-arrow-right - AUTHOR-DEFINED markers: a raw SVG
path, or anything registered withregisterMarker— the catalogue is no longer a closed enum.
tsclass ArrowRenderer
Methods
getTipOffset(style: ArrowStyle): number— Distance from the marker's local origin to its forward-most point (its visual tip), in the +x direction the marker is rotated toward.renderArrow( style: ArrowStyle, transform: string, backgroundColor: string = 'white', end: 'source' | 'target' = 'target' ): VNode | null— Render an arrow based on the provided style and transform
Example
typescriptconst renderer = new ArrowRenderer();
const arrowVNode = renderer.renderArrow({
type: 'crow-foot',
size: 10,
filled: false,
color: '#000'
}, 'translate(100, 50) rotate(45)');
Example
A custom marker
typescriptregisterMarker('feather', {
tipOffset: style => style.size,
render: ctx => ({ type: 'path', props: { d: `M0,0 L${ctx.size},0`, stroke: ctx.color, transform: ctx.transform } }),
});
link.updateStyle({ arrowHead: { type: 'feather', size: 12, filled: true } });
EdgeOptimizer
tsclass EdgeOptimizer
Methods
constructor(options: EdgeOptimizerOptions = {})get stats(): Readonly<OptimizerStats>update(frame: OptimizerFrame): void— Run the pass for a frame. Cheap when little changed: a frame whose links and nodes all carry the same signatures as last time performs zero segment tests and zero label searches.getJumps(linkId: string): Intersection[]— The crossings on this link, for the jump-point path builder. Empty when the link draws no jumps.getLabelOffset(linkId: string, labelId: string, fallback: Point): Point— The offset this label should actually be drawn at. For a label that did not opt intoautoOffsetthis is exactly the offset the author set.reset(): void— Drop all state (renderer disposal, or a wholesale diagram swap).
JumpPointDetector
JumpPointDetector detects line-line intersections for jump point rendering.
Features:
- Line segment intersection detection
- Angle calculation between intersecting lines
- Multiple detection modes (all, perpendicular, threshold)
- Performance optimized for many links
Algorithm: Uses parametric line intersection algorithm with bounds checking.
tsclass JumpPointDetector
Methods
findIntersection(line1: LineSegment, line2: LineSegment): Intersection | null— Find intersection between two line segmentsdetectIntersections( targetLink: LinkWithPoints, otherLinks: LinkWithPoints[], mode: DetectionMode = 'all', threshold: number = 45 ): Intersection[]— Detect all intersections for a link with other links
JumpPointRenderer
JumpPointRenderer modifies link paths to show jump points at intersections.
Supports three visual styles:
- arc: Small arc over intersection
- gap: Break in line
- bridge: Bridge shape over intersection
Algorithm:
- Parse path to extract points
- Sort intersections by position
- Split path at intersections
- Insert jump point geometry
- Reconstruct path
tsclass JumpPointRenderer
Methods
renderWithJumpPoints( pathData: string, intersections: Intersection[], config: JumpPointConfig, originalProps?: Record<string, any> ): VNode— Render path with jump points at intersections
LabelRenderer
LabelRenderer generates SVG (or HTML) VNodes for link labels.
Features:
- Position labels at any point along path (0-1), or in one of three slots
- Auto-rotation with path or fixed angle
- Text wrapping with maxWidth
- Multiple labels per link
- Rich styling support
- HTML content and author templates
Architecture: Uses LinkModel utilities (getPointAtPosition, getAngleAt) for positioning. Returns VNode tree compatible with framework-agnostic rendering.
tsclass LabelRenderer
Methods
renderLabel(label: LinkLabel, link: LinkModel, context: LabelRenderContext = {}): VNode | null— Render a label on a linklabelBox(label: LinkLabel): { width: number; height: number }— Estimated on-screen box of a label, in LOCAL units (before the path anchor and offset are applied). The edge optimizer needs a box to test collisions against; HTML/template labels declare their own, and text labels are measured with the same estimator the background rect already uses.
RouteMemo
tsclass RouteMemo
Methods
get stats(): Readonly<RouteMemoStats>get size(): numberbeginFrame(rects: Map<string, Rect>, obstacleEpoch: string): Rect[]— Start a frame. Diffs this frame's node rectangles against last frame's and returns the world regions that changed — each moved node contributes BOTH where it was and where it now is, because a link routed around its old position is just as stale as one routed through its new one.lookup(linkId: string, key: string): RoutedPath | undefined— The route for this link if its inputs are unchanged since we cached it.store(linkId: string, key: string, routed: RoutedPath): voiddrop(linkId: string): void— A route computed but NOT cacheable (e.g. no route found) must not keep a stale entry.invalidate(linkIds: Iterable<string>): voidclear(): void
RouteSolverBridge
tsclass RouteSolverBridge
Methods
constructor(options: RouteSolverBridgeOptions = {})get stats(): Readonly<RouteSolverStats>hasRoutesFor(version: number): boolean— True once the solver has an answer for THIS world — i.e. one safe to paint.routeFor(linkId: string, version: number): RoutedPath | undefinedsubmit(version: number, edges: SolverEdge[], obstacles: Obstacle[]): void— Ask for this world to be solved. Idempotent per version: submitting the same world twice does nothing, so calling this every frame is free.dispose(): void
Was this page helpful?