aditya0103's picture
Tasks 5-7: eval harness, FastAPI backend, Paper & Ink UI- src/eval/ — precision/recall/F1 harness with type-aware comparators,micro/macro F1, CSV + markdown reports, --model benchmark flag- src/api/ — FastAPI backend with /extract, /schemas, /health,request-ID middleware, typed error envelope, injectable extractor- ui/ — Vite + React + TS + Tailwind + Motion + React Three FiberPaper & Ink editorial UI with 3D paper hero, dark/light mode,confidence inkwell, wax-stamp metrics, kinetic typography- 95 passing tests (up from 54); UI is a separate npm workspace
557ab38
Raw
History Blame Contribute Delete
2.3 kB
/**
* ThemeToggle — a moon/sun rendered as ink strokes, not a Material icon.
* Deliberately hand-drawn feel: the moon has a slight crescent bite, the sun
* has four short rays. Both drawn with strokeLinecap="round".
*/
import { motion } from "motion/react";
import { useTheme } from "@/hooks/useTheme";
export function ThemeToggle() {
const { theme, toggle } = useTheme();
const dark = theme === "dark";
return (
<button
type="button"
onClick={toggle}
aria-label={`Switch to ${dark ? "light" : "dark"} mode`}
className="group relative h-9 w-9 rounded-full border border-[var(--rule)] bg-[var(--surface)] transition-colors duration-500 ease-editorial hover:border-[var(--ink)]"
>
<motion.span
key={theme}
initial={{ rotate: -30, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
className="pointer-events-none absolute inset-0 flex items-center justify-center"
>
{dark ? <Moon /> : <Sun />}
</motion.span>
</button>
);
}
function Sun() {
return (
<svg width="18" height="18" viewBox="0 0 20 20" fill="none" aria-hidden="true">
<circle cx="10" cy="10" r="3.4" stroke="currentColor" strokeWidth="1.2" />
{[0, 45, 90, 135, 180, 225, 270, 315].map((deg) => {
const rad = (deg * Math.PI) / 180;
const x1 = 10 + Math.cos(rad) * 6;
const y1 = 10 + Math.sin(rad) * 6;
const x2 = 10 + Math.cos(rad) * 7.8;
const y2 = 10 + Math.sin(rad) * 7.8;
return (
<line
key={deg}
x1={x1}
y1={y1}
x2={x2}
y2={y2}
stroke="currentColor"
strokeWidth="1.2"
strokeLinecap="round"
/>
);
})}
</svg>
);
}
function Moon() {
// A crescent drawn as one path — feels like a pen stroke, not an icon.
return (
<svg width="18" height="18" viewBox="0 0 20 20" fill="none" aria-hidden="true">
<path
d="M15 12.5A6 6 0 0 1 7.5 5c0-.5.06-.98.17-1.44A6 6 0 1 0 16.44 12.33c-.46.11-.94.17-1.44.17Z"
stroke="currentColor"
strokeWidth="1.2"
strokeLinejoin="round"
strokeLinecap="round"
/>
</svg>
);
}