diff --git a/frontend/src/components/browse/BrowseNodeWindow.tsx b/frontend/src/components/browse/BrowseNodeWindow.tsx
index 9ae36c7..3e69fad 100644
--- a/frontend/src/components/browse/BrowseNodeWindow.tsx
+++ b/frontend/src/components/browse/BrowseNodeWindow.tsx
@@ -47,7 +47,9 @@ export default function BrowseNodeWindow({
className="w-5 h-5 flex items-center justify-center rounded text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-30 disabled:cursor-default transition-colors" title="Reload">
↻
-
+ {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
+
{ if (e.target === e.currentTarget) { const sel = window.getSelection(); if (sel) { const range = document.createRange(); range.selectNodeContents(e.currentTarget); sel.removeAllRanges(); sel.addRange(range); } } }}>
{d.node.hash.slice(0, 12)}\u2026/{d.currentPath}
diff --git a/frontend/src/components/shared/FloatingWindow.tsx b/frontend/src/components/shared/FloatingWindow.tsx
index 7897363..517e257 100644
--- a/frontend/src/components/shared/FloatingWindow.tsx
+++ b/frontend/src/components/shared/FloatingWindow.tsx
@@ -44,17 +44,20 @@ export default function FloatingWindow({
const onDragStart = useCallback((e: React.MouseEvent) => {
if ((e.target as HTMLElement).closest("button")) return;
e.preventDefault(); onFocus(id);
+ ref.current?.focus();
dragRef.current = { startX: e.clientX, startY: e.clientY, origX: x, origY: y };
+ document.documentElement.classList.add("cursor-grabbing");
const onMove = (ev: MouseEvent) => { if (!dragRef.current) return; onUpdate(id, { x: dragRef.current.origX + (ev.clientX - dragRef.current.startX), y: Math.max(0, dragRef.current.origY + (ev.clientY - dragRef.current.startY)) }); };
- const onUp = () => { dragRef.current = null; document.removeEventListener("mousemove", onMove); document.removeEventListener("mouseup", onUp); };
+ const onUp = () => { dragRef.current = null; document.documentElement.classList.remove("cursor-grabbing"); document.removeEventListener("mousemove", onMove); document.removeEventListener("mouseup", onUp); };
document.addEventListener("mousemove", onMove); document.addEventListener("mouseup", onUp);
}, [id, x, y, onUpdate, onFocus]);
const onResizeStart = useCallback((e: React.MouseEvent) => {
e.preventDefault(); e.stopPropagation(); onFocus(id);
resizeRef.current = { startX: e.clientX, startY: e.clientY, origW: w, origH: h };
+ document.documentElement.classList.add("cursor-nwse-resize");
const onMove = (ev: MouseEvent) => { if (!resizeRef.current) return; onUpdate(id, { w: Math.max(minW, resizeRef.current.origW + (ev.clientX - resizeRef.current.startX)), h: Math.max(minH, resizeRef.current.origH + (ev.clientY - resizeRef.current.startY)) }); };
- const onUp = () => { resizeRef.current = null; document.removeEventListener("mousemove", onMove); document.removeEventListener("mouseup", onUp); };
+ const onUp = () => { resizeRef.current = null; document.documentElement.classList.remove("cursor-nwse-resize"); document.removeEventListener("mousemove", onMove); document.removeEventListener("mouseup", onUp); };
document.addEventListener("mousemove", onMove); document.addEventListener("mouseup", onUp);
}, [id, w, h, minW, minH, onUpdate, onFocus]);
diff --git a/frontend/src/index.css b/frontend/src/index.css
index c70c75b..a004a11 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -196,6 +196,11 @@
letter-spacing: -0.01em;
}
+ ::selection {
+ background: var(--primary);
+ color: var(--primary-foreground);
+ }
+
html {
@apply font-mono;
}
@@ -413,6 +418,25 @@
color: var(--foreground);
}
+ /* ── Global drag cursors ── */
+ .cursor-grabbing, .cursor-grabbing * {
+ cursor: grabbing !important;
+ }
+ .cursor-nwse-resize, .cursor-nwse-resize * {
+ cursor: nwse-resize !important;
+ }
+
+ /* ── Graph cursors ── */
+ .graph-canvas canvas {
+ cursor: grab !important;
+ }
+ .graph-canvas canvas:active {
+ cursor: grabbing !important;
+ }
+ .graph-canvas.graph-node-hovered canvas {
+ cursor: pointer !important;
+ }
+
/* ── Editor Pointer ── */
.editor-pointer {
diff --git a/frontend/src/routes/BrowseView.tsx b/frontend/src/routes/BrowseView.tsx
index f4d7c42..47207aa 100644
--- a/frontend/src/routes/BrowseView.tsx
+++ b/frontend/src/routes/BrowseView.tsx
@@ -10,6 +10,14 @@ import BrowseNodeWindow from "@/components/browse/BrowseNodeWindow";
import BrowseSearchBar from "@/components/browse/BrowseSearchBar";
import Loader from "@/components/shared/Loader";
+function cssVarToHex(varName: string): string {
+ const raw = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
+ if (!raw) return "#888888";
+ const ctx = document.createElement("canvas").getContext("2d")!;
+ ctx.fillStyle = raw;
+ return ctx.fillStyle;
+}
+
// ---------------------------------------------------------------------------
// Main component
// ---------------------------------------------------------------------------
@@ -186,7 +194,7 @@ export default function BrowseView() {
fitViewDelay: 1500,
fitViewPadding: 0.2,
renderHoveredPointRing: true,
- hoveredPointRingColor: "#ffffff",
+ hoveredPointRingColor: cssVarToHex("--primary"),
scalePointsOnZoom: true,
pointGreyoutOpacity: 0.1,
simulationGravity: 0.5,
@@ -204,10 +212,16 @@ export default function BrowseView() {
onMouseMove: (index, pointPosition) => {
if (index === undefined || !pointPosition || !graphRef.current) {
setHoveredLabel(null);
+ containerRef.current?.classList.remove("graph-node-hovered");
return;
}
const entry = entriesRef.current[index];
if (!entry) return;
+ if (entry.type !== "interface") {
+ containerRef.current?.classList.add("graph-node-hovered");
+ } else {
+ containerRef.current?.classList.remove("graph-node-hovered");
+ }
const screen = graphRef.current.spaceToScreenPosition(pointPosition);
setHoveredLabel({ text: entry.name, x: screen[0], y: screen[1] });
},
@@ -238,6 +252,7 @@ export default function BrowseView() {
simulationRepulsion: 0.5 + scale * 9.5,
simulationCluster: 1.0 - scale * 0.75,
simulationGravity: 0.25 + scale * 1.75,
+ hoveredPointRingColor: cssVarToHex("--primary"),
});
graph.setPointPositions(graphData.positions);
@@ -411,7 +426,7 @@ export default function BrowseView() {
return (
-
+
{/* Hover label */}
{hoveredLabel && (