{
  "version": 3,
  "sources": ["../../../src/rendering-util/rendering-elements/lineJump.ts"],
  "sourcesContent": ["/**\n * Line jumps (\"hops\") for edge crossings.\n *\n * Detects true segment crossings between edge polylines and rewrites the SVG\n * path of the later edge so the crossing renders as either a small arc\n * (`jumpStyle: 'arc'`) or a visible break (`jumpStyle: 'gap'`).\n *\n * The pure functions (`findEdgeIntersections`, `processEdgesWithJumps`) are\n * DOM-free. The DOM-side `applyLineJumpsToSvg` helper reads geometry from\n * layout data and leaves curved (non-`M`/`L`) rendered paths untouched.\n */\n\nimport type { D3Selection } from '../../types.js';\nimport { markerOffsets } from '../../utils/lineWithOffset.js';\n\n/** Radius used by edges.js' generateRoundedPath. Kept in sync so rewritten\n * paths look like the originals at bends. */\nconst ROUNDED_CORNER_RADIUS = 5;\n\n/** Skip the jump if its clamped radius falls below this \u2014 avoids invisible\n * zero-length arcs on very crowded paths. */\nconst CORNER_EPSILON = 1e-5;\n\n/**\n * Straight run kept between a hop and the bend next to it.\n *\n * Without it a hop may start exactly at the tangent point of a rounded corner,\n * so the path leaves the corner's quadratic and enters the arc with no straight\n * run between them. The two curves read as one malformed squiggle rather than\n * as a corner followed by a hop.\n */\nconst CORNER_JUMP_CLEARANCE = 2;\n\n/**\n * Smallest share of the requested radius a hop may shrink to before it is\n * dropped instead of drawn.\n *\n * A hop close to a bend has little room, and the clamps below will happily fit\n * one into whatever is left. That is the wrong trade: an arc at half radius no\n * longer clears the stroke it is meant to hop, so the lines still touch and the\n * result looks like a rendering fault rather than a crossing. An undrawn hop is\n * just an ordinary crossing, which is what every diagram looked like before\n * hops existed \u2014 a much better failure than a broken-looking one.\n *\n * This happens for real: ELK routes subgraph-internal edges into lanes 10px\n * apart, and a 10px offset cannot hold a 7.07px corner cut plus a 6px hop, so\n * every crossing in such a lane was being drawn at 2.9px hard against the bend.\n */\nconst MIN_USEFUL_RADIUS_RATIO = 0.6;\n\nexport interface Point {\n  x: number;\n  y: number;\n}\n\nexport interface EdgeGeom {\n  id: string;\n  points: Point[];\n  /**\n   * Optional curve hint matching `edge.curve` from the rendering layer.\n   * When set, line jumps are only applied for orthogonal-friendly curves\n   * (`'linear'`, `'rounded'`, `'step'`, `'stepBefore'`, `'stepAfter'`, or\n   * undefined). Other curves (basis, monotoneX, \u2026) are skipped to avoid\n   * corrupting smoothed geometry.\n   */\n  curve?: string;\n  /** Arrow type at the start (first point) \u2014 used to apply marker offset so\n   * the rewritten path's endpoint matches the original rendered geometry and\n   * the arrow marker orients correctly. */\n  arrowTypeStart?: string;\n  /** Arrow type at the end (last point). */\n  arrowTypeEnd?: string;\n}\n\nexport interface LineJumpConfig {\n  enabled: boolean;\n  jumpRadius: number;\n  jumpStyle: 'arc' | 'gap';\n}\n\nexport interface Crossing {\n  jumpEdgeId: string;\n  otherEdgeId: string;\n  /** Index of the segment within the jumping edge's polyline. */\n  segIndex: number;\n  /** Position of the crossing along the jumping edge's segment, 0..1. */\n  t: number;\n  point: Point;\n}\n\nconst ENDPOINT_EPSILON = 1e-6;\n\ninterface Segment {\n  a: Point;\n  b: Point;\n}\n\nfunction buildSegmentList(points: Point[]): Segment[] {\n  const segments: Segment[] = [];\n  for (let i = 0; i < points.length - 1; i++) {\n    segments.push({ a: points[i], b: points[i + 1] });\n  }\n  return segments;\n}\n\ninterface SegmentIntersection {\n  point: Point;\n  tA: number;\n  tB: number;\n}\n\n/**\n * Parametric segment-segment intersection. Returns null if the segments are\n * parallel, do not intersect, or only meet at one of their endpoints (within\n * `ENDPOINT_EPSILON`). Endpoint rejection prevents normal joins, T-junctions,\n * and shared-start edges from being treated as crossings.\n */\nfunction segmentIntersection(\n  a1: Point,\n  a2: Point,\n  b1: Point,\n  b2: Point\n): SegmentIntersection | null {\n  const dxA = a2.x - a1.x;\n  const dyA = a2.y - a1.y;\n  const dxB = b2.x - b1.x;\n  const dyB = b2.y - b1.y;\n\n  const denom = dxA * dyB - dyA * dxB;\n  if (denom === 0) {\n    return null;\n  }\n\n  const dx = b1.x - a1.x;\n  const dy = b1.y - a1.y;\n\n  const tA = (dx * dyB - dy * dxB) / denom;\n  const tB = (dx * dyA - dy * dxA) / denom;\n\n  if (\n    tA <= ENDPOINT_EPSILON ||\n    tA >= 1 - ENDPOINT_EPSILON ||\n    tB <= ENDPOINT_EPSILON ||\n    tB >= 1 - ENDPOINT_EPSILON\n  ) {\n    return null;\n  }\n\n  return {\n    point: { x: a1.x + tA * dxA, y: a1.y + tA * dyA },\n    tA,\n    tB,\n  };\n}\n\n/** True if the segment is horizontally dominant (abs(dx) is at least abs(dy)).\n * Ties go to horizontal to keep pure-diagonal edges grouped with the\n * horizontal bucket \u2014 they don't occur in orthogonal layouts anyway. */\nfunction isHorizontalSeg(seg: Segment): boolean {\n  return Math.abs(seg.b.x - seg.a.x) >= Math.abs(seg.b.y - seg.a.y);\n}\n\n/**\n * True if a crossing on `edge`'s segment `segIndex` at parameter `t` falls\n * inside the stretch where the drawn stroke has left the polyline to round a\n * bend.\n *\n * Crossings are found on polylines, but a `rounded` edge is not drawn as its\n * polyline: `generateRoundedPath` replaces each bend with a quadratic that\n * departs the line up to `cutLen` before the vertex and rejoins it `cutLen`\n * after. Inside that stretch the polyline says the stroke is somewhere it is\n * not, so a \"crossing\" computed there is at best mislocated and at worst\n * fictional \u2014 and a hop drawn for it arches over blank paper while the two\n * strokes still touch alongside it.\n *\n * Only `rounded` edges lie this way; every other supported curve is drawn as\n * the polyline it describes.\n */\nfunction crossingSitsInRoundedCorner(edge: EdgeGeom, segIndex: number, t: number): boolean {\n  if (edge.curve !== 'rounded') {\n    return false;\n  }\n  const pts = edge.points;\n  const a = pts[segIndex];\n  const b = pts[segIndex + 1];\n  if (!a || !b) {\n    return false;\n  }\n  const segLen = Math.hypot(b.x - a.x, b.y - a.y);\n  const d = t * segLen;\n\n  const entering =\n    segIndex > 0 ? computeRoundedCorner(pts[segIndex - 1], a, b, ROUNDED_CORNER_RADIUS) : null;\n  if (entering && d < entering.cutLen) {\n    return true;\n  }\n\n  const leaving =\n    segIndex + 2 < pts.length\n      ? computeRoundedCorner(a, b, pts[segIndex + 2], ROUNDED_CORNER_RADIUS)\n      : null;\n  return leaving !== null && segLen - d < leaving.cutLen;\n}\n\nexport function findEdgeIntersections(edges: EdgeGeom[]): Crossing[] {\n  const crossings: Crossing[] = [];\n\n  for (let i = 0; i < edges.length; i++) {\n    const edgeA = edges[i];\n    const segmentsA = buildSegmentList(edgeA.points);\n    for (let j = i + 1; j < edges.length; j++) {\n      const edgeB = edges[j];\n      const segmentsB = buildSegmentList(edgeB.points);\n\n      for (const [si, segA] of segmentsA.entries()) {\n        for (const [sj, segB] of segmentsB.entries()) {\n          const hit = segmentIntersection(segA.a, segA.b, segB.a, segB.b);\n          if (!hit) {\n            continue;\n          }\n\n          // Either edge rounding a bend here means the polyline is not where\n          // the stroke is, so there is nothing trustworthy to hop over.\n          if (\n            crossingSitsInRoundedCorner(edgeA, si, hit.tA) ||\n            crossingSitsInRoundedCorner(edgeB, sj, hit.tB)\n          ) {\n            continue;\n          }\n\n          // Orthogonal-orientation rule: when one segment is horizontal-\n          // dominant and the other vertical-dominant, the HORIZONTAL one\n          // gets the jump (classic line-hop convention \u2014 arcs arch upward\n          // over the vertical line beneath). Falls back to later-index-wins\n          // when both segments share an orientation.\n          const aHoriz = isHorizontalSeg(segA);\n          const bHoriz = isHorizontalSeg(segB);\n          const orthogonalPair = aHoriz !== bHoriz;\n          const jumpOnA = orthogonalPair ? aHoriz : false;\n\n          if (jumpOnA) {\n            crossings.push({\n              jumpEdgeId: edgeA.id,\n              otherEdgeId: edgeB.id,\n              segIndex: si,\n              t: hit.tA,\n              point: hit.point,\n            });\n          } else {\n            crossings.push({\n              jumpEdgeId: edgeB.id,\n              otherEdgeId: edgeA.id,\n              segIndex: sj,\n              t: hit.tB,\n              point: hit.point,\n            });\n          }\n        }\n      }\n    }\n  }\n\n  return crossings;\n}\n\nfunction fmt(n: number): string {\n  // Strip trailing zeros so \"5.00\" \u2192 \"5\"; keep up to 3 decimals otherwise.\n  const rounded = Math.round(n * 1000) / 1000;\n  return Number.isInteger(rounded) ? `${rounded}` : `${rounded}`;\n}\n\nfunction pointToString(p: Point): string {\n  return `${fmt(p.x)},${fmt(p.y)}`;\n}\n\n/**\n * Determines the SVG arc sweep flag so the jump bumps in the conventional\n * direction: horizontal segments bump up (smaller y in SVG), vertical segments\n * bump right (larger x).\n */\nfunction getArcSweepFlag(seg: Segment): 0 | 1 {\n  const dx = seg.b.x - seg.a.x;\n  const dy = seg.b.y - seg.a.y;\n  if (Math.abs(dx) >= Math.abs(dy)) {\n    // Horizontal-dominant: bump up (smaller y in SVG's y-down frame).\n    // Going +x \u2192 sweep=1 sweeps through increasing angle 180\u00B0\u2192270\u00B0\u21920\u00B0,\n    //   which passes through (mid, y-r) = up.\n    // Going -x \u2192 sweep=0 (reverse direction) also lands the bump above.\n    return dx >= 0 ? 1 : 0;\n  }\n  // Vertical-dominant: bump right (positive x).\n  // Going +y \u2192 sweep=1; going -y \u2192 sweep=0.\n  return dy >= 0 ? 1 : 0;\n}\n\ninterface JumpOnSegment {\n  t: number;\n  point: Point;\n  /** Distance from segment start along the segment direction. */\n  d: number;\n  /** Effective radius after boundary + adjacency clamping. */\n  r: number;\n}\n\n/**\n * Shifts the first/last point inward along the edge direction by the amount\n * required for their arrow markers, matching `applyMarkerOffsetsToPoints` in\n * edges.js so the rewritten path ends exactly where the original did.\n */\nfunction applyMarkerOffsets(points: Point[], edge: EdgeGeom): Point[] {\n  if (points.length < 2) {\n    return points.map((p) => ({ ...p }));\n  }\n  const out = points.map((p) => ({ ...p }));\n  const startOff =\n    edge.arrowTypeStart && markerOffsets[edge.arrowTypeStart as keyof typeof markerOffsets];\n  if (startOff) {\n    const a = points[0];\n    const b = points[1];\n    const ang = Math.atan2(b.y - a.y, b.x - a.x);\n    out[0].x = a.x + startOff * Math.cos(ang);\n    out[0].y = a.y + startOff * Math.sin(ang);\n  }\n  const endOff =\n    edge.arrowTypeEnd && markerOffsets[edge.arrowTypeEnd as keyof typeof markerOffsets];\n  if (endOff) {\n    const n = points.length;\n    const a = points[n - 2];\n    const b = points[n - 1];\n    const ang = Math.atan2(b.y - a.y, b.x - a.x);\n    out[n - 1].x = b.x - endOff * Math.cos(ang);\n    out[n - 1].y = b.y - endOff * Math.sin(ang);\n  }\n  return out;\n}\n\n/**\n * Emits the arc or gap command for a crossing, in the segment's direction.\n * Returns the part strings; caller inserts them in order.\n */\nfunction emitJump(\n  jump: JumpOnSegment,\n  ux: number,\n  uy: number,\n  sweep: 0 | 1,\n  style: 'arc' | 'gap'\n): string[] {\n  const cx = jump.point.x;\n  const cy = jump.point.y;\n  const pre = { x: cx - ux * jump.r, y: cy - uy * jump.r };\n  const post = { x: cx + ux * jump.r, y: cy + uy * jump.r };\n  const out = [`L${pointToString(pre)}`];\n  if (style === 'arc') {\n    out.push(`A${fmt(jump.r)},${fmt(jump.r)} 0 0 ${sweep} ${pointToString(post)}`);\n  } else {\n    out.push(`M${pointToString(post)}`);\n  }\n  return out;\n}\n\n/**\n * Mirrors the corner-rounding logic of `generateRoundedPath` in edges.js:\n * given a bend at `curr` between segments `prev\u2192curr` and `curr\u2192next`,\n * computes (startX, startY) just before curr on the incoming segment and\n * (endX, endY) just after curr on the outgoing segment, plus the Q control\n * point (which is curr itself). Returns `null` if the angle is degenerate\n * and the caller should just emit a straight `L curr`.\n */\ninterface RoundedCorner {\n  startX: number;\n  startY: number;\n  endX: number;\n  endY: number;\n  ctrlX: number;\n  ctrlY: number;\n  /** How much the start of the rounded corner eats into the incoming segment. */\n  cutLen: number;\n}\nfunction computeRoundedCorner(\n  prev: Point,\n  curr: Point,\n  next: Point,\n  radius: number\n): RoundedCorner | null {\n  const dx1 = curr.x - prev.x;\n  const dy1 = curr.y - prev.y;\n  const dx2 = next.x - curr.x;\n  const dy2 = next.y - curr.y;\n  const len1 = Math.hypot(dx1, dy1);\n  const len2 = Math.hypot(dx2, dy2);\n  if (len1 < CORNER_EPSILON || len2 < CORNER_EPSILON) {\n    return null;\n  }\n  const nx1 = dx1 / len1;\n  const ny1 = dy1 / len1;\n  const nx2 = dx2 / len2;\n  const ny2 = dy2 / len2;\n  const dot = nx1 * nx2 + ny1 * ny2;\n  const clamped = Math.max(-1, Math.min(1, dot));\n  const angle = Math.acos(clamped);\n  if (angle < CORNER_EPSILON || Math.abs(Math.PI - angle) < CORNER_EPSILON) {\n    return null;\n  }\n  const cutLen = Math.min(radius / Math.sin(angle / 2), len1 / 2, len2 / 2);\n  return {\n    startX: curr.x - nx1 * cutLen,\n    startY: curr.y - ny1 * cutLen,\n    endX: curr.x + nx2 * cutLen,\n    endY: curr.y + ny2 * cutLen,\n    ctrlX: curr.x,\n    ctrlY: curr.y,\n    cutLen,\n  };\n}\n\nfunction rewriteEdgePath(edge: EdgeGeom, jumps: Crossing[], config: LineJumpConfig): string {\n  const rawPoints = edge.points;\n  if (rawPoints.length < 2) {\n    return '';\n  }\n\n  // Match edges.js: shift the first/last point inward so arrow markers line up.\n  const points = applyMarkerOffsets(rawPoints, edge);\n  const rounded = edge.curve === 'rounded';\n\n  // Jumps are indexed into the ORIGINAL (un-offset) segment list. For mid-\n  // segments (i > 0 and i < n-2) the offsets don't change anything, and for\n  // the first/last segment the shift is tiny compared to jump radius so\n  // reusing the same (segIndex, t) is fine.\n  const segments = buildSegmentList(points);\n  const bySeg = new Map<number, JumpOnSegment[]>();\n  for (const j of jumps) {\n    const seg = segments[j.segIndex];\n    if (!seg) {\n      continue;\n    }\n    const segLen = Math.hypot(seg.b.x - seg.a.x, seg.b.y - seg.a.y);\n    const list = bySeg.get(j.segIndex) ?? [];\n    list.push({\n      t: j.t,\n      point: j.point,\n      d: j.t * segLen,\n      r: config.jumpRadius,\n    });\n    bySeg.set(j.segIndex, list);\n  }\n\n  const parts: string[] = [`M${pointToString(points[0])}`];\n  // Running cursor along the current segment measured from seg.a.\n  // Consumed at the front by the previous corner's cutLen (for rounded) and\n  // after that by mid-segment jumps.\n  for (let i = 0; i < segments.length; i++) {\n    const seg = segments[i];\n    const segLen = Math.hypot(seg.b.x - seg.a.x, seg.b.y - seg.a.y);\n    const ux = segLen === 0 ? 0 : (seg.b.x - seg.a.x) / segLen;\n    const uy = segLen === 0 ? 0 : (seg.b.y - seg.a.y) / segLen;\n    const sweep = getArcSweepFlag(seg);\n\n    // How much of the front of this segment was consumed by the previous\n    // corner's Q end-point (endX,endY). Default 0.\n    let segStartConsumed = 0;\n    if (rounded && i > 0) {\n      const corner = computeRoundedCorner(\n        points[i - 1],\n        points[i],\n        points[i + 1] ?? points[i],\n        ROUNDED_CORNER_RADIUS\n      );\n      if (corner) {\n        segStartConsumed = corner.cutLen;\n      }\n    }\n\n    // Rounded: if there's a next corner ahead, we stop short of it by cutLen.\n    let segEndStop = segLen;\n    let upcomingCorner: RoundedCorner | null = null;\n    if (rounded && i < segments.length - 1) {\n      upcomingCorner = computeRoundedCorner(\n        points[i],\n        points[i + 1],\n        points[i + 2] ?? points[i + 1],\n        ROUNDED_CORNER_RADIUS\n      );\n      if (upcomingCorner) {\n        segEndStop = segLen - upcomingCorner.cutLen;\n      }\n    }\n\n    // Clamp each jump to the room between the bends at either end of the\n    // segment, then drop the ones with too little room to be worth drawing.\n    // Dropping happens BEFORE the adjacency pass below so that a hop being\n    // squeezed out by a corner does not also shrink its neighbours.\n    const minUsefulRadius = config.jumpRadius * MIN_USEFUL_RADIUS_RATIO;\n    const segJumps = [...(bySeg.get(i) ?? [])]\n      .sort((a, b) => a.t - b.t)\n      .filter((j) => {\n        const room = Math.min(j.d - segStartConsumed, segEndStop - j.d) - CORNER_JUMP_CLEARANCE;\n        j.r = Math.min(j.r, room);\n        return j.r >= minUsefulRadius;\n      });\n    for (let k = 0; k < segJumps.length - 1; k++) {\n      const gap = segJumps[k + 1].d - segJumps[k].d;\n      if (segJumps[k].r + segJumps[k + 1].r > gap) {\n        const half = gap / 2;\n        segJumps[k].r = Math.min(segJumps[k].r, half);\n        segJumps[k + 1].r = Math.min(segJumps[k + 1].r, half);\n      }\n    }\n\n    for (const j of segJumps) {\n      // Checked AGAIN after the adjacency pass, not only before it. That pass\n      // can halve a radius to keep two hops off each other, and a hop shrunk\n      // that way is just as unreadable as one squeezed by a bend \u2014 same rule,\n      // both times. Two crossings too close to carry a hop each carry none.\n      if (j.r < minUsefulRadius) {\n        continue;\n      }\n      parts.push(...emitJump(j, ux, uy, sweep, config.jumpStyle));\n    }\n\n    // End of segment: either a straight L to seg.b (last segment or linear),\n    // or a Q-corner into seg.b's neighborhood (rounded, middle).\n    if (rounded && upcomingCorner) {\n      parts.push(`L${fmt(upcomingCorner.startX)},${fmt(upcomingCorner.startY)}`);\n      parts.push(\n        `Q${fmt(upcomingCorner.ctrlX)},${fmt(upcomingCorner.ctrlY)} ${fmt(upcomingCorner.endX)},${fmt(upcomingCorner.endY)}`\n      );\n    } else {\n      parts.push(`L${pointToString(seg.b)}`);\n    }\n  }\n\n  return parts.join(' ');\n}\n\nfunction plainPath(points: Point[]): string {\n  if (points.length === 0) {\n    return '';\n  }\n  const parts = [`M${pointToString(points[0])}`];\n  for (let i = 1; i < points.length; i++) {\n    parts.push(`L${pointToString(points[i])}`);\n  }\n  return parts.join(' ');\n}\n\nexport function processEdgesWithJumps(\n  edges: EdgeGeom[],\n  config: LineJumpConfig\n): Map<string, string> {\n  const result = new Map<string, string>();\n\n  if (!config.enabled) {\n    for (const edge of edges) {\n      result.set(edge.id, plainPath(edge.points));\n    }\n    return result;\n  }\n\n  const crossings = findEdgeIntersections(edges);\n  const jumpsByEdge = new Map<string, Crossing[]>();\n  for (const c of crossings) {\n    const list = jumpsByEdge.get(c.jumpEdgeId) ?? [];\n    list.push(c);\n    jumpsByEdge.set(c.jumpEdgeId, list);\n  }\n\n  for (const edge of edges) {\n    const jumps = jumpsByEdge.get(edge.id);\n    if (!jumps || jumps.length === 0) {\n      result.set(edge.id, plainPath(edge.points));\n    } else {\n      result.set(edge.id, rewriteEdgePath(edge, jumps, config));\n    }\n  }\n\n  return result;\n}\n\n/**\n * Returns true iff the SVG path `d` is a straight-line path \u2014 only `M`/`L`/`m`/`l`\n * move/line commands plus their numeric coordinates (digits, sign, decimal point,\n * scientific-notation `e`, and `,`/space separators). Curved paths are skipped by\n * the caller.\n */\nexport function isStraightPath(d: string): boolean {\n  return /^[\\d\\s+,.LMelm-]*$/.test(d);\n}\n\n/**\n * Returns true iff the named curve produces orthogonal-friendly segments that\n * can be safely re-emitted with line jumps. Includes `'rounded'` even though\n * its rendered `d` contains `Q` corner-rounding commands \u2014 when an edge with\n * a jump is rewritten the corner rounding is dropped in exchange for visible\n * arc hops at crossings, which is the desired trade-off.\n */\nexport function curveSupportsLineHops(curve: string | undefined): boolean {\n  if (!curve) {\n    return true;\n  }\n  return (\n    curve === 'linear' ||\n    curve === 'rounded' ||\n    curve === 'step' ||\n    curve === 'stepBefore' ||\n    curve === 'stepAfter'\n  );\n}\n\n/**\n * Decodes the `data-points` attribute set by edges.js at render time. This\n * gives us the exact point list edges.js used to emit the rendered path \u2014\n * i.e. after node-boundary `intersect()` clipping and any orthogonalization,\n * but BEFORE `applyMarkerOffsetsToPoints`. Using these points guarantees the\n * rewrite's endpoints match the original rendered endpoints.\n */\nfunction decodeDataPoints(raw: string | null): Point[] | null {\n  if (!raw) {\n    return null;\n  }\n  try {\n    const json = typeof atob === 'function' ? atob(raw) : Buffer.from(raw, 'base64').toString();\n    const parsed = JSON.parse(json);\n    if (!Array.isArray(parsed)) {\n      return null;\n    }\n    const pts: Point[] = [];\n    for (const p of parsed) {\n      if (p && typeof p.x === 'number' && typeof p.y === 'number') {\n        pts.push({ x: p.x, y: p.y });\n      }\n    }\n    return pts.length >= 2 ? pts : null;\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Patches the rendered SVG paths in `edgePathsGroup` for any edges that\n * cross. The true geometry is read from each path's `data-points` attribute\n * (written by edges.js at render time) so the rewrite's endpoints match\n * exactly what was originally rendered. Edges whose curve is a true\n * smoothing curve (`basis`, `monotoneX`, \u2026) are skipped.\n */\nexport function applyLineJumpsToSvg(\n  edgePathsGroup: D3Selection<SVGGElement>,\n  edges: EdgeGeom[],\n  config: LineJumpConfig\n): void {\n  if (!config.enabled) {\n    return;\n  }\n\n  const groupNode = edgePathsGroup.node();\n  if (!groupNode) {\n    return;\n  }\n\n  // Build a metadata lookup so per-edge properties (curve, arrow types)\n  // survive the DOM round-trip.\n  const edgeMeta = new Map<string, EdgeGeom>();\n  for (const e of edges) {\n    edgeMeta.set(e.id, e);\n  }\n\n  // Collect geometry from each path's data-points, preferring that over the\n  // incoming `edges[].points` which came from pre-render layout state.\n  // Index the paths by their own `data-id` instead of building one selector per\n  // edge. An id is author-controlled, so interpolating it into a selector needs\n  // `CSS.escape`, which is not guaranteed outside a browser \u2014 and the fallback\n  // of using the id raw turns a trailing backslash into a `SyntaxError` that\n  // aborts the whole render. Reading the attribute avoids the selector entirely.\n  const pathByDataId = new Map<string, Element>();\n  for (const el of groupNode.querySelectorAll('path[data-id]')) {\n    const id = el.getAttribute('data-id');\n    if (id !== null && !pathByDataId.has(id)) {\n      pathByDataId.set(id, el);\n    }\n  }\n\n  const renderedEdges: EdgeGeom[] = [];\n  for (const e of edges) {\n    const pathEl = pathByDataId.get(e.id);\n    if (!pathEl) {\n      continue;\n    }\n    const decoded = decodeDataPoints(pathEl.getAttribute('data-points'));\n    const points = decoded ?? e.points;\n    renderedEdges.push({ ...e, points });\n  }\n\n  const crossings = findEdgeIntersections(renderedEdges);\n  if (crossings.length === 0) {\n    return;\n  }\n\n  const jumpsByEdge = new Map<string, Crossing[]>();\n  for (const c of crossings) {\n    const list = jumpsByEdge.get(c.jumpEdgeId) ?? [];\n    list.push(c);\n    jumpsByEdge.set(c.jumpEdgeId, list);\n  }\n\n  for (const renderedEdge of renderedEdges) {\n    const jumps = jumpsByEdge.get(renderedEdge.id);\n    if (!jumps || jumps.length === 0) {\n      continue;\n    }\n    const meta = edgeMeta.get(renderedEdge.id);\n    const curveHint = meta?.curve;\n    if (curveHint !== undefined && !curveSupportsLineHops(curveHint)) {\n      continue;\n    }\n\n    const pathEl = pathByDataId.get(renderedEdge.id);\n    if (!pathEl) {\n      continue;\n    }\n\n    if (curveHint === undefined) {\n      const currentD = pathEl.getAttribute('d') ?? '';\n      if (!isStraightPath(currentD)) {\n        continue;\n      }\n    }\n\n    // Read the ORIGINAL stroke-dasharray before rewriting so we can\n    // recompute it against the new total length. The `neo` look emits:\n    //   stroke-dasharray: 0 <oValueS> <len - oValueS - oValueE> <oValueE>;\n    // which hides the first oValueS and last oValueE pixels of the stroke\n    // \u2014 this is what actually prevents the stroke from poking into the arrow\n    // marker body. Our rewritten path has a different length, so without\n    // updating the \"on\" portion the hidden tail ends up in the wrong place.\n    const originalStyle = pathEl.getAttribute('style') ?? '';\n    const dasharrayMatch = /stroke-dasharray\\s*:\\s*0\\s+([\\d.]+)\\s+[\\d.]+\\s+([\\d.]+)/.exec(\n      originalStyle\n    );\n    const preservedOValueS = dasharrayMatch ? Number.parseFloat(dasharrayMatch[1]) : null;\n    const preservedOValueE = dasharrayMatch ? Number.parseFloat(dasharrayMatch[2]) : null;\n\n    const newD = rewriteEdgePath(renderedEdge, jumps, config);\n    pathEl.setAttribute('d', newD);\n\n    if (\n      preservedOValueS !== null &&\n      preservedOValueE !== null &&\n      typeof (pathEl as SVGPathElement).getTotalLength === 'function'\n    ) {\n      const newLen = (pathEl as SVGPathElement).getTotalLength();\n      const onLen = Math.max(0, newLen - preservedOValueS - preservedOValueE);\n      const newDasharray = `0 ${preservedOValueS} ${onLen} ${preservedOValueE}`;\n      const cleaned = originalStyle\n        .replace(/stroke-dasharray\\s*:[^;]*;?/g, `stroke-dasharray: ${newDasharray};`)\n        .replace(/;\\s*;+/g, ';');\n      pathEl.setAttribute('style', cleaned);\n    }\n  }\n}\n"],
  "mappings": "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",
  "names": []
}
