diff --git a/frontend/src/routes/BrowseView.tsx b/frontend/src/routes/BrowseView.tsx index 47207aa..4feb841 100644 --- a/frontend/src/routes/BrowseView.tsx +++ b/frontend/src/routes/BrowseView.tsx @@ -117,14 +117,29 @@ export default function BrowseView() { } } + const rect = container.getBoundingClientRect(); + const pad = 8; + for (let i = 0; i < nClusters; i++) { const x = positions[i * 2]; const y = positions[i * 2 + 1]; if (x === undefined || y === undefined) continue; const screen = graph.spaceToScreenPosition([x, y]); const div = labelDivsRef.current[i]!; - div.style.left = `${screen[0]}px`; - div.style.top = `${screen[1]}px`; + + // Measure label so we can clamp within container + const lw = div.offsetWidth; + const lh = div.offsetHeight; + // Default transform is translate(-50%, -100%), so anchor is center-bottom + let left = screen[0] - lw / 2; + let top = screen[1] - lh; + // Clamp to container bounds + left = Math.max(pad, Math.min(left, rect.width - lw - pad)); + top = Math.max(pad, Math.min(top, rect.height - lh - pad)); + + div.style.transform = "none"; + div.style.left = `${left}px`; + div.style.top = `${top}px`; } }, []); @@ -431,7 +446,7 @@ export default function BrowseView() { {/* Hover label */} {hoveredLabel && (
+ style={{ left: Math.min(hoveredLabel.x + 12, (containerRef.current?.offsetWidth ?? 9999) - 160), top: Math.max(8, hoveredLabel.y - 10), boxShadow: "0 2px 8px rgba(0,0,0,0.3)" }}> {hoveredLabel.text}
)}