Checkpoint frontend UI/UX overhaul and ingestion embed benchmark work
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTheme } from "./ThemeContext";
|
||||
|
||||
interface CitationBeamOverlayProps {
|
||||
activeCitationIndex: number | null;
|
||||
}
|
||||
|
||||
interface Coords {
|
||||
x1: number;
|
||||
y1: number;
|
||||
x2: number;
|
||||
y2: number;
|
||||
}
|
||||
|
||||
export function CitationBeamOverlay({ activeCitationIndex }: CitationBeamOverlayProps) {
|
||||
const { resolvedTheme } = useTheme();
|
||||
const [coords, setCoords] = useState<Coords | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeCitationIndex) {
|
||||
setCoords(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const updateCoords = () => {
|
||||
const markerEl = document.getElementById(`citation-marker-${activeCitationIndex}`);
|
||||
const cardEl = document.getElementById(`citation-card-${activeCitationIndex}`);
|
||||
|
||||
if (!markerEl || !cardEl) {
|
||||
setCoords(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const markerRect = markerEl.getBoundingClientRect();
|
||||
const cardRect = cardEl.getBoundingClientRect();
|
||||
|
||||
// Ensure both elements are visible on screen
|
||||
if (markerRect.width === 0 || cardRect.width === 0) {
|
||||
setCoords(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setCoords({
|
||||
x1: markerRect.left + markerRect.width / 2,
|
||||
y1: markerRect.top + markerRect.height / 2,
|
||||
x2: cardRect.left,
|
||||
y2: cardRect.top + cardRect.height / 2,
|
||||
});
|
||||
};
|
||||
|
||||
updateCoords();
|
||||
const handleScrollOrResize = () => updateCoords();
|
||||
|
||||
window.addEventListener("resize", handleScrollOrResize);
|
||||
window.addEventListener("scroll", handleScrollOrResize, true);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleScrollOrResize);
|
||||
window.removeEventListener("scroll", handleScrollOrResize, true);
|
||||
};
|
||||
}, [activeCitationIndex]);
|
||||
|
||||
if (!activeCitationIndex || !coords) return null;
|
||||
|
||||
// Compute smooth bezier curve control points
|
||||
const dx = Math.abs(coords.x2 - coords.x1);
|
||||
const cx1 = coords.x1 + dx * 0.4;
|
||||
const cy1 = coords.y1;
|
||||
const cx2 = coords.x2 - dx * 0.4;
|
||||
const cy2 = coords.y2;
|
||||
|
||||
const pathD = `M ${coords.x1} ${coords.y1} C ${cx1} ${cy1}, ${cx2} ${cy2}, ${coords.x2} ${coords.y2}`;
|
||||
|
||||
const isGlass = resolvedTheme === "glass";
|
||||
|
||||
return (
|
||||
<svg
|
||||
className="pointer-events-none fixed inset-0 z-50 h-full w-full overflow-visible"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<defs>
|
||||
{/* Luminous Specular Gradient */}
|
||||
<linearGradient id="beamGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" stopColor="var(--accent-primary)" stopOpacity="0.8" />
|
||||
<stop offset="50%" stopColor="#38BDF8" stopOpacity="1" />
|
||||
<stop offset="100%" stopColor="var(--accent-primary)" stopOpacity="0.8" />
|
||||
</linearGradient>
|
||||
|
||||
<filter id="beamGlow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feGaussianBlur stdDeviation={isGlass ? "6" : "3"} result="blur" />
|
||||
<feComposite in="SourceGraphic" in2="blur" operator="over" />
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
{/* Background Outer Glow Line */}
|
||||
<path
|
||||
d={pathD}
|
||||
fill="none"
|
||||
stroke="var(--accent-glow)"
|
||||
strokeWidth={isGlass ? "8" : "4"}
|
||||
strokeLinecap="round"
|
||||
filter="url(#beamGlow)"
|
||||
className="opacity-70"
|
||||
/>
|
||||
|
||||
{/* Main Connection Path Line */}
|
||||
<path
|
||||
d={pathD}
|
||||
fill="none"
|
||||
stroke="url(#beamGradient)"
|
||||
strokeWidth={isGlass ? "3" : "2"}
|
||||
strokeDasharray={isGlass ? "8 4" : "none"}
|
||||
strokeLinecap="round"
|
||||
className={isGlass ? "animate-pulse-beam" : "opacity-90"}
|
||||
/>
|
||||
|
||||
{/* Start Dot at Citation Marker */}
|
||||
<circle
|
||||
cx={coords.x1}
|
||||
cy={coords.y1}
|
||||
r={isGlass ? "6" : "4"}
|
||||
fill="var(--accent-primary)"
|
||||
className="animate-ping opacity-75"
|
||||
/>
|
||||
<circle
|
||||
cx={coords.x1}
|
||||
cy={coords.y1}
|
||||
r="4"
|
||||
fill="#FFFFFF"
|
||||
/>
|
||||
|
||||
{/* End Dot at Citation Card */}
|
||||
<circle
|
||||
cx={coords.x2}
|
||||
cy={coords.y2}
|
||||
r={isGlass ? "6" : "4"}
|
||||
fill="#38BDF8"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user