fix(d3/ai): implement pill-node geometry, text truncation, and resolve SignalR scroll loop [issue #21]

This commit is contained in:
2026-05-09 08:20:42 +02:00
parent 9e77aee231
commit 8a2786333e
7 changed files with 38 additions and 18 deletions
@@ -1,5 +1,8 @@
import * as d3 from 'https://esm.sh/d3@7';
const getDisplayLabel = d => d.label.length > 20 ? d.label.substring(0, 17) + "..." : d.label;
const getPillWidth = d => getDisplayLabel(d).length * 8 + 30;
let simulation;
let zoomBehavior;
let svgElement;
@@ -73,7 +76,7 @@ export function mount(containerId, data, dotNetHelper) {
.force("link", d3.forceLink().id(d => d.id).distance(120))
.force("charge", d3.forceManyBody().strength(-400))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collide", d3.forceCollide().radius(50));
.force("collide", d3.forceCollide().radius(d => (getPillWidth(d) / 2) + 20));
simulation.on("tick", () => {
if (link) {
@@ -168,11 +171,11 @@ export function updateData(data) {
g.append("rect")
.attr("class", "node-pill")
.attr("x", d => -(d.label.length * 4 + 10))
.attr("y", -12)
.attr("width", d => d.label.length * 8 + 20)
.attr("height", 24)
.attr("rx", 12)
.attr("x", d => -getPillWidth(d) / 2)
.attr("y", -15)
.attr("width", d => getPillWidth(d))
.attr("height", 30)
.attr("rx", 15)
.attr("fill", "rgba(20, 20, 20, 0.9)")
.attr("stroke", d => {
if (d.type === 'Definition') return 'var(--nexus-accent)';
@@ -182,11 +185,14 @@ export function updateData(data) {
.attr("stroke-width", 1);
g.append("text")
.text(d => d.label)
.text(d => getDisplayLabel(d))
.attr("text-anchor", "middle")
.attr("y", 4)
.attr("y", 5)
.attr("fill", d => d.type === 'Definition' ? 'var(--nexus-accent)' : '#ccc')
.attr("font-size", "0.8rem");
g.append("title")
.text(d => d.label);
g.transition().duration(500).style("opacity", 1);