Fixtures/screenshots tweaks

This commit is contained in:
Kalle
2026-09-20 13:38:10 +03:00
parent f574f2b948
commit 84db5e3c02
2 changed files with 51 additions and 38 deletions

View File

@@ -1,5 +1,7 @@
import clsx from "clsx";
import { ExternalLink } from "lucide-react";
import { type ReactNode, useEffect, useState } from "react";
import { SendouButton } from "~/components/elements/Button";
import { useSearchParam } from "~/modules/search-params/hooks";
import { mainWeaponImageUrl, SCANNER_PAGE } from "~/utils/urls";
import type { Roi } from "../core/canonical";
@@ -12,6 +14,7 @@ import { newInspectKey, putInspectFrame } from "../store/inspect";
import styles from "./FixturesPage.module.css";
import { drawNormalizedCanvas } from "./normalized-canvas";
import { formatTimer, RoiCrop } from "./ScreenshotPage";
import { SessionHeader } from "./SessionHeader";
const FIXTURES_ENDPOINT = "/scanner/fixtures";
/** Filtering down to this many cases opens every card, for one-glance review */
@@ -76,25 +79,27 @@ export function FixturesPage() {
return (
<div className={styles.page}>
<div className={styles.controls}>
<input
type="search"
className={styles.filter}
value={q}
onChange={(e) => setQ(e.target.value)}
placeholder="Narrow by name substring — comma separates alternatives"
/>
<span className={styles.count}>
{fixtures
? `${filtered.length} / ${fixtures.length} fixtures`
: "loading…"}
</span>
</div>
<p className={styles.hint}>
Ground-truth review: check each expected label against its frame. The
filter lives in the URL, so a narrowed selection can be shared as a
link.
</p>
<SessionHeader>
<div className={styles.controls}>
<input
type="search"
className={styles.filter}
value={q}
onChange={(e) => setQ(e.target.value)}
placeholder="Narrow by name substring — comma separates alternatives"
/>
<span className={styles.count}>
{fixtures
? `${filtered.length} / ${fixtures.length} fixtures`
: "loading…"}
</span>
</div>
<p className={styles.hint}>
Ground-truth review: check each expected label against its frame. The
filter lives in the URL, so a narrowed selection can be shared as a
link.
</p>
</SessionHeader>
{error ? <p className="text-error">{error}</p> : null}
{[...groups.entries()].map(([detector, group]) => (
<section key={detector} className={styles.group}>
@@ -150,9 +155,14 @@ function FixtureCard(props: { fixture: FixtureListItem; autoExpand: boolean }) {
{fixture.name}
</button>
<span className={styles.cardEvent}>{fixture.expected.event}</span>
<button type="button" onClick={() => inspectFixture(url)}>
<SendouButton
size="small"
variant="outlined"
icon={<ExternalLink />}
onClick={() => inspectFixture(url)}
>
Inspect
</button>
</SendouButton>
</header>
{open ? (
<div className={styles.cardBody}>

View File

@@ -50,6 +50,7 @@ import { downloadCsv } from "./download";
import { type CardData, downloadExpectedJson } from "./fixture-export";
import { drawNormalizedCanvas } from "./normalized-canvas";
import styles from "./ScreenshotPage.module.css";
import { SessionHeader } from "./SessionHeader";
type Result = Extract<WorkerResponse, { kind: "result" }>;
@@ -528,23 +529,25 @@ export function ScreenshotPage() {
return (
<div>
<Dropzone onFile={(file: File) => void analyze(file)}>
Drop a frame (PNG/JPEG) here, or{" "}
<label>
pick a file
<input
type="file"
accept="image/png,image/jpeg"
style={{ display: "none" }}
onChange={(e) => {
const file = e.target.files?.[0];
e.target.value = ""; // allow re-picking the same file
if (file) void analyze(file);
}}
/>
</label>
{busy ? " — analyzing…" : null}
</Dropzone>
<SessionHeader>
<Dropzone onFile={(file: File) => void analyze(file)}>
Drop a frame (PNG/JPEG) here, or{" "}
<label>
pick a file
<input
type="file"
accept="image/png,image/jpeg"
style={{ display: "none" }}
onChange={(e) => {
const file = e.target.files?.[0];
e.target.value = ""; // allow re-picking the same file
if (file) void analyze(file);
}}
/>
</label>
{busy ? " — analyzing…" : null}
</Dropzone>
</SessionHeader>
{error ? <p className="text-error">{error}</p> : null}
<div