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 | /** | |
| * App β page composition. Hero, workbench, chapters, numbers, footer. | |
| * The 3D paper's state is lifted here so the hero can react to extraction. | |
| */ | |
| import { useCallback, useRef, useState } from "react"; | |
| import { CustomCursor } from "./components/CustomCursor"; | |
| import { ExtractSection } from "./components/ExtractSection"; | |
| import { Footer } from "./components/Footer"; | |
| import { Hero } from "./components/Hero"; | |
| import { HowItWorks } from "./components/HowItWorks"; | |
| import { Numbers } from "./components/Numbers"; | |
| import { TopNav } from "./components/TopNav"; | |
| import type { PaperState } from "./components/PaperScene"; | |
| export default function App() { | |
| const [paperState, setPaperState] = useState<PaperState>("idle"); | |
| const heroCTA = useRef<() => void>(() => {}); | |
| const bindExtract = useCallback((fn: () => void) => { | |
| heroCTA.current = fn; | |
| }, []); | |
| return ( | |
| <div className="cursor-ink"> | |
| <CustomCursor /> | |
| <TopNav /> | |
| <main> | |
| <Hero | |
| paperState={paperState} | |
| onCTAClick={() => heroCTA.current()} | |
| /> | |
| <ExtractSection | |
| onStateChange={setPaperState} | |
| bindExtract={bindExtract} | |
| /> | |
| <HowItWorks /> | |
| <Numbers /> | |
| </main> | |
| <Footer /> | |
| </div> | |
| ); | |
| } | |