feat: improvements

This commit is contained in:
2026-04-05 22:39:31 +02:00
parent 3c5856aecb
commit a1ad332e01
4 changed files with 49 additions and 5 deletions

View File

@@ -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">
↻
</button>
<div className="flex-1 flex items-center h-5 px-2 bg-background/60 border border-border rounded text-[10px] font-mono text-foreground/80 truncate">
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<div className="flex-1 flex items-center h-5 px-2 bg-background/60 border border-border rounded text-[10px] font-mono text-foreground/80 truncate cursor-text"
onClick={(e) => { if (e.target === e.currentTarget) { const sel = window.getSelection(); if (sel) { const range = document.createRange(); range.selectNodeContents(e.currentTarget); sel.removeAllRanges(); sel.addRange(range); } } }}>
<span className="text-muted-foreground/60 truncate">{d.node.hash.slice(0, 12)}\u2026/</span>{d.currentPath}
</div>
<span className="flex items-center gap-1 text-[9px] uppercase tracking-wider shrink-0">

View File

@@ -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]);

View File

@@ -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 {

View File

@@ -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 (
<div className="relative overflow-hidden" style={{ height: "100%" }}>
<div ref={containerRef} className="absolute inset-0" />
<div ref={containerRef} className="absolute inset-0 graph-canvas" />
{/* Hover label */}
{hoveredLabel && (