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 && (