@@ -44,8 +44,10 @@ const arrowMarker = `<defs><marker id="arrow" markerWidth="9" markerHeight="9" r
4444// pointing at the target, trimmed back by each endpoint's node radius so the
4545// line sits between the rims and the arrowhead lands just outside the target
4646// node instead of hiding under it. fromID/toID tag the edge with its endpoint
47- // node ids so a node-hover handler can light up every incident edge.
48- func directedEdge (b * strings.Builder , x1 , y1 , r1 , x2 , y2 , r2 int , fromID , toID string ) {
47+ // node ids so a node-hover handler can light up every incident edge. A
48+ // non-empty rel (a typed relation's predicate) draws dashed with the
49+ // predicate as its hover tooltip; "" is a plain reference.
50+ func directedEdge (b * strings.Builder , x1 , y1 , r1 , x2 , y2 , r2 int , fromID , toID , rel string ) {
4951 dx , dy := float64 (x2 - x1 ), float64 (y2 - y1 )
5052 d := math .Hypot (dx , dy )
5153 if d == 0 {
@@ -55,8 +57,13 @@ func directedEdge(b *strings.Builder, x1, y1, r1, x2, y2, r2 int, fromID, toID s
5557 const gap = 3.0 // breathing room between arrow tip and target rim
5658 sx , sy := x1 + int (ux * float64 (r1 )), y1 + int (uy * float64 (r1 ))
5759 ex , ey := x2 - int (ux * (float64 (r2 )+ gap )), y2 - int (uy * (float64 (r2 )+ gap ))
58- fmt .Fprintf (b , `<line class="graph-edge" x1="%d" y1="%d" x2="%d" y2="%d" data-from="%s" data-to="%s" marker-end="url(#arrow)"/>` ,
59- sx , sy , ex , ey , html .EscapeString (fromID ), html .EscapeString (toID ))
60+ if rel == "" {
61+ fmt .Fprintf (b , `<line class="graph-edge" x1="%d" y1="%d" x2="%d" y2="%d" data-from="%s" data-to="%s" marker-end="url(#arrow)"/>` ,
62+ sx , sy , ex , ey , html .EscapeString (fromID ), html .EscapeString (toID ))
63+ return
64+ }
65+ fmt .Fprintf (b , `<line class="graph-edge edge-rel" x1="%d" y1="%d" x2="%d" y2="%d" data-from="%s" data-to="%s" marker-end="url(#arrow)"><title>%s</title></line>` ,
66+ sx , sy , ex , ey , html .EscapeString (fromID ), html .EscapeString (toID ), html .EscapeString (rel ))
6067}
6168
6269// GraphPage renders the graph neighborhood as a standalone permalink —
@@ -149,9 +156,9 @@ func graphSVG(n domain.Neighborhood, urlFor func(domain.Ref) string, onTrail map
149156 // outbound link points center→neighbor, a backlink points neighbor→center.
150157 for _ , pn := range placed {
151158 if pn .inbound {
152- directedEdge (& b , pn .x , pn .y , graphNodeR , cx , cy , graphCenterR , pn .ref .Path , n .Center .Path )
159+ directedEdge (& b , pn .x , pn .y , graphNodeR , cx , cy , graphCenterR , pn .ref .Path , n .Center .Path , "" )
153160 } else {
154- directedEdge (& b , cx , cy , graphCenterR , pn .x , pn .y , graphNodeR , n .Center .Path , pn .ref .Path )
161+ directedEdge (& b , cx , cy , graphCenterR , pn .x , pn .y , graphNodeR , n .Center .Path , pn .ref .Path , "" )
155162 }
156163 }
157164 // Center node (data-node so hovering it lights up all its edges).
0 commit comments