From f574f2b948cdeefd7e04edda5de8ca28992690be Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sun, 20 Sep 2026 13:34:09 +0300 Subject: [PATCH] Screenshot page fixes --- app/features/scanner/README.md | 11 +-- .../scanner/components/LandingView.tsx | 24 +++++-- .../scanner/components/ScannerApp.tsx | 4 +- .../components/ScreenshotPage.module.css | 7 ++ .../scanner/components/ScreenshotPage.tsx | 67 +++++++++++-------- .../scanner/components/SettingsPopover.tsx | 26 +++---- 6 files changed, 81 insertions(+), 58 deletions(-) diff --git a/app/features/scanner/README.md b/app/features/scanner/README.md index 639585cba..ef2d9d81c 100644 --- a/app/features/scanner/README.md +++ b/app/features/scanner/README.md @@ -28,7 +28,9 @@ running capture (`LiveView`), a past session (`PastSessionView`) and a scanned VoD (`VodView`): header, clip strip, then match cards (`components/MatchCard.tsx`) newest first. Views are picked by the `view` search param (`scanner-search-params.ts`: `live`, `session&id=`, -`vod&name=`, `clips`, and the debug-gated `debug` / dev-only `fixtures`). +`vod&name=`, `clips`, `debug` and the dev-only `fixtures`). Nothing links to +the `debug` screenshot view: dropping an image on the landing's File card +opens it, for anyone, through the same handoff Inspect uses. - **Controllers are module singletons**, not view state: the capture (`components/live-session.ts`) and a running VoD scan @@ -61,10 +63,11 @@ search param (`scanner-search-params.ts`: `live`, `session&id=`, `Matches` (`core/csv/matches.ts`, one row per game, the rows the cards render) and `Raw detections` (`core/csv/events.ts`, one row per event). Column names stay English keys. -- **Debug gate** (`use-debug.ts`: DEV/ADMIN role or `?debug=true`): the - image/screenshot view (`ScreenshotPage.tsx`), the dev-only fixtures view, +- **Debug gate** (`use-debug.ts`: DEV/ADMIN role or `?debug=true`): `Save frame as fixture`, `?telemetry=true`, and the `Raw detections` - disclosure inside a match card (the per-event cards with Inspect). + disclosure inside a match card (the per-event cards with Inspect). The + screenshot view (`ScreenshotPage.tsx`) is not gated; the fixtures view is + dev-only. ## Clips diff --git a/app/features/scanner/components/LandingView.tsx b/app/features/scanner/components/LandingView.tsx index 292344a2e..a95f6cb57 100644 --- a/app/features/scanner/components/LandingView.tsx +++ b/app/features/scanner/components/LandingView.tsx @@ -1,7 +1,9 @@ /** * The landing: two entry cards (Live / File), the clip history strip and * the sessions list. Anyone can capture, scan files and get clips locally; - * only uploading needs a login. Dropping a file anywhere here starts a scan. + * only uploading needs a login. Dropping a file anywhere here starts a scan; + * an image opens the screenshot view instead, through the same handoff + * Inspect uses. */ import clsx from "clsx"; import { Play, Upload } from "lucide-react"; @@ -17,6 +19,7 @@ import { import { SCANNER_PAGE } from "~/utils/urls"; import { scannerSearchParams } from "../scanner-search-params"; import { MAX_HISTORY_CLIPS, type ScannerClip } from "../store/clips"; +import { newInspectKey, putInspectFrame } from "../store/inspect"; import { ClipDialog } from "./ClipDialog"; import { ClipStrip } from "./ClipStrip"; import { useClips } from "./clips-feed"; @@ -71,10 +74,20 @@ export function LandingView() { }; const scanFile = (file: File) => { + if (file.type.startsWith("image/")) { + void inspectScreenshot(file); + return; + } void startVodScan(file, { saveClips: saveClipsFromFile, telemetry }); setParams({ view: "vod", name: file.name }); }; + const inspectScreenshot = async (file: File) => { + const key = newInspectKey(); + await putInspectFrame(key, file); + setParams({ view: "debug", inspect: key }); + }; + return ( // biome-ignore lint/a11y/noStaticElementInteractions: drag-and-drop target for the whole landing; the file input is the accessible path
File -

Scan a recorded VoD.

+

+ Scan a recorded VoD, or a screenshot to see what the scanner reads + from it. +

{frame && !busy ? ( -

- {" "} - -

+ + ) : null} {Object.keys(results).length > 0 ? ( @@ -834,8 +837,14 @@ export function ScreenshotPage() { {status ? ( <> {status.data.layout} - - + + + + + ) : null} diff --git a/app/features/scanner/components/SettingsPopover.tsx b/app/features/scanner/components/SettingsPopover.tsx index 2b6e5f28a..a4dd4dda3 100644 --- a/app/features/scanner/components/SettingsPopover.tsx +++ b/app/features/scanner/components/SettingsPopover.tsx @@ -1,8 +1,8 @@ /** * The settings popover, opened from ⚙ on the landing and the live header: - * the upload and clip toggles, the retention notes, and the debug links for - * those who see them. The - * source lives on the landing's Live card, the one place it must be right. + * the upload and clip toggles, the retention notes, and in development the + * fixtures link. The source lives on the landing's Live card, the one place + * it must be right. */ import { Settings } from "lucide-react"; import { Link } from "react-router"; @@ -25,14 +25,12 @@ import { useScannerSettings, } from "./settings"; import { isLoggedIn } from "./upload"; -import { useDebug } from "./use-debug"; /** a step of one frame-ish: fine enough to tune by ear, coarse enough to reach a second in a few clicks */ const AUDIO_OFFSET_STEP_MS = 25; export function SettingsPopover() { const settings = useScannerSettings(); - const debug = useDebug(); const loggedIn = isLoggedIn(); return ( @@ -122,26 +120,18 @@ export function SettingsPopover() {
Sessions: last 30 days or {MAX_SESSIONS} sessions.

- {debug ? ( + {process.env.NODE_ENV === "development" ? (
Debug
- Image / screenshot view + Fixtures - {process.env.NODE_ENV === "development" ? ( - - Fixtures - - ) : null}
) : null}