From d55712c68a1417e2f77ef3e6a84fec4448944cf4 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Wed, 5 Aug 2026 11:15:32 +0300 Subject: [PATCH] Rename cv -> scanner --- .gitignore | 2 +- AGENTS.md | 8 +- app/features/cv/cv-search-params.ts | 11 -- app/features/ingest/core/Scoreboards.test.ts | 11 +- app/features/ingest/core/Scoreboards.ts | 4 +- app/features/ingest/core/VodMatches.test.ts | 6 +- app/features/ingest/core/VodMatches.ts | 2 +- app/features/ingest/ingest-schemas.ts | 30 ++-- app/features/ingest/ingest-vod-schemas.ts | 6 +- app/features/{cv => scanner}/README.md | 48 +++---- .../{cv => scanner}/capture/sampler.ts | 0 .../{cv => scanner}/capture/vod-frames.ts | 0 .../components/AbilityGrid.tsx | 10 +- .../{cv => scanner}/components/App.tsx | 18 ++- .../{cv => scanner}/components/DeathCard.tsx | 0 .../{cv => scanner}/components/EventCard.tsx | 4 +- .../{cv => scanner}/components/LivePage.tsx | 0 .../components/MapStartCard.tsx | 0 .../components/MinimapCard.tsx | 4 +- .../components/ScoreboardCard.tsx | 0 .../components/ScoreboardOwnCard.tsx | 0 .../components/ScreenshotPage.tsx | 0 .../{cv => scanner}/components/VodPage.tsx | 0 .../{cv => scanner}/components/events-csv.ts | 0 .../components/fixture-export.ts | 0 .../{cv => scanner}/components/flags.ts | 0 .../{cv => scanner}/components/format.ts | 0 .../{cv => scanner}/components/labels.ts | 10 +- .../components/screenshot-handoff.ts | 0 .../components/sendou-ingest.ts | 2 +- .../components/sendou-upload.ts | 0 .../{cv => scanner}/components/styles.css | 132 +++++++++--------- .../{cv => scanner}/components/thumbnail.ts | 0 .../{cv => scanner}/core/ability-harvest.ts | 4 +- app/features/{cv => scanner}/core/batches.ts | 0 .../{cv => scanner}/core/canonical.ts | 0 app/features/{cv => scanner}/core/cv.ts | 0 .../core/detectors/death/abilities.ts | 0 .../core/detectors/death/index.ts | 12 +- .../detectors/death/localized-messages.ts | 2 +- .../core/detectors/death/rois.ts | 0 .../core/detectors/death/weapon-names.ts | 0 .../core/detectors/map-start/index.ts | 0 .../core/detectors/map-start/rois.ts | 0 .../core/detectors/minimap/abilities.ts | 0 .../core/detectors/minimap/index.ts | 22 +-- .../core/detectors/minimap/rois.ts | 0 .../core/detectors/minimap/stage.ts | 0 .../core/detectors/registry.ts | 0 .../detectors/scoreboard-own/abilities.ts | 0 .../core/detectors/scoreboard-own/index.ts | 20 +-- .../core/detectors/scoreboard-own/rois.ts | 0 .../core/detectors/scoreboard-replay/code.ts | 0 .../detectors/scoreboard-replay/header.ts | 6 +- .../core/detectors/scoreboard-replay/index.ts | 0 .../core/detectors/scoreboard-replay/rois.ts | 0 .../core/detectors/scoreboard/digits.ts | 0 .../core/detectors/scoreboard/header.ts | 4 +- .../core/detectors/scoreboard/index.ts | 4 +- .../core/detectors/scoreboard/kits.ts | 0 .../core/detectors/scoreboard/names.ts | 0 .../core/detectors/scoreboard/pov.ts | 0 .../core/detectors/scoreboard/rois.ts | 0 .../core/detectors/scoreboard/row.ts | 2 +- .../core/detectors/scoreboard/specials.ts | 0 .../core/detectors/scoreboard/weapons.ts | 0 .../core/detectors/suppressor.ts | 0 .../{cv => scanner}/core/detectors/types.ts | 0 app/features/{cv => scanner}/core/glyphs.ts | 0 app/features/{cv => scanner}/core/image.ts | 0 .../{cv => scanner}/core/localized-entries.ts | 6 +- .../{cv => scanner}/core/localized.ts | 4 +- .../{cv => scanner}/core/replay-time.ts | 0 .../{cv => scanner}/core/resources.ts | 2 +- app/features/{cv => scanner}/core/text.ts | 0 .../{cv => scanner}/core/timeline/index.ts | 0 .../core/timeline/same-scoreboard.ts | 0 .../{cv => scanner}/core/vod-matches.ts | 0 .../{cv => scanner}/node/assets-dir.ts | 10 +- app/features/{cv => scanner}/node/fixtures.ts | 12 +- app/features/{cv => scanner}/node/image-io.ts | 0 .../{cv => scanner}/node/resources.ts | 6 +- .../cv.tsx => scanner/routes/scanner.tsx} | 12 +- .../scanner-schemas.ts} | 48 ++++--- .../scanner-search-params.test.ts} | 8 +- app/features/scanner/scanner-search-params.ts | 11 ++ .../cv-types.ts => scanner/scanner-types.ts} | 12 +- app/features/{cv => scanner}/store/db.ts | 0 app/features/{cv => scanner}/store/events.ts | 0 app/features/{cv => scanner}/store/vods.ts | 0 .../tests/ability-harvest.test.ts | 8 +- .../{cv => scanner}/tests/batches.test.ts | 8 +- .../{cv => scanner}/tests/death.1.test.ts | 0 .../{cv => scanner}/tests/death.2.test.ts | 0 .../{cv => scanner}/tests/death.3.test.ts | 0 .../death/classic-squiffer-jp/expected.json | 0 .../death/classic-squiffer-jp/frame.png | Bin .../death/colorful-splashtag/expected.json | 0 .../death/colorful-splashtag/frame.png | Bin .../death/e-liter-4k-jp-720p/expected.json | 0 .../death/e-liter-4k-jp-720p/frame.png | Bin .../death/fxg-supaatan-slosher/expected.json | 0 .../death/fxg-supaatan-slosher/frame.png | Bin .../death/german-booyah-bomb/expected.json | 0 .../death/german-booyah-bomb/frame.png | Bin .../fixtures/death/headphones/expected.json | 0 .../tests/fixtures/death/headphones/frame.png | Bin .../death/pink-text-special/expected.json | 0 .../death/pink-text-special/frame.png | Bin .../expected.json | 0 .../rapid-blaster-deco-heavy-kez/frame.jpg | Bin .../death/splattershot-tezuka/expected.json | 0 .../death/splattershot-tezuka/frame.jpg | Bin .../death/ultra-stamp-jp-720p/expected.json | 0 .../death/ultra-stamp-jp-720p/frame.png | Bin .../death/wipeout-52-gal/expected.json | 0 .../fixtures/death/wipeout-52-gal/frame.png | Bin .../death/z-f-splatterscope-vod/expected.json | 0 .../death/z-f-splatterscope-vod/frame.png | Bin .../expected.json | 0 .../german-splat-zones-robo-rom-en/frame.png | Bin .../map-start/splat-zones-mahi/expected.json | 0 .../map-start/splat-zones-mahi/frame.png | Bin .../expected.json | 0 .../splat-zones-undertow-spillway/frame.png | Bin .../minimap/snix-special-ready/expected.json | 0 .../minimap/snix-special-ready/frame.png | Bin .../spectator-area-cup-round1/expected.json | 0 .../spectator-area-cup-round1/frame.jpg | Bin .../expected.json | 0 .../spectator-area-cup-winners-qf/frame.png | Bin .../minimap/spectator-pxg/expected.json | 0 .../fixtures/minimap/spectator-pxg/frame.png | Bin .../stonks-cross-both-teams/expected.json | 0 .../minimap/stonks-cross-both-teams/frame.jpg | Bin .../order-slosher-replica/expected.json | 0 .../order-slosher-replica/frame.png | Bin .../expected.json | 0 .../frame.png | Bin .../expected.json | 0 .../frame.png | Bin .../scoreboard-replay/low-res-2/expected.json | 0 .../scoreboard-replay/low-res-2/frame.png | Bin .../scoreboard-replay/low-res/expected.json | 0 .../scoreboard-replay/low-res/frame.png | Bin .../expected.json | 0 .../private-battle-crableg-capital/frame.png | Bin .../expected.json | 0 .../frame.jpeg | Bin .../expected.json | 0 .../frame.png | Bin .../expected.json | 0 .../frame.png | Bin .../expected.json | 0 .../frame.png | Bin .../expected.json | 0 .../frame.png | Bin .../expected.json | 0 .../frame.png | Bin .../expected.json | 0 .../x-battle-rainmaker-urchin-1434/frame.png | Bin .../expected.json | 0 .../frame.png | Bin .../expected.json | 0 .../frame.png | Bin .../expected.json | 0 .../frame.png | Bin .../expected.json | 0 .../frame.png | Bin .../fixtures/scoreboard/robot/expected.json | 0 .../tests/fixtures/scoreboard/robot/frame.png | Bin .../scoreboard/special-symbols/expected.json | 0 .../scoreboard/special-symbols/frame.png | Bin .../scoreboard/splash-sploosh/expected.json | 0 .../scoreboard/splash-sploosh/frame.png | Bin .../expected.json | 0 .../xbattle-splat-zones-ko-capture/frame.png | Bin .../xbattle-splat-zones-ko/expected.json | 0 .../xbattle-splat-zones-ko/frame.jpg | Bin .../{cv => scanner}/tests/map-start.test.ts | 0 .../{cv => scanner}/tests/minimap.test.ts | 0 .../{cv => scanner}/tests/node-test-compat.ts | 0 .../tests/opencv-smoke.test.ts | 0 .../{cv => scanner}/tests/replay-time.test.ts | 0 .../tests/scoreboard-own.test.ts | 0 .../tests/scoreboard-replay.1.test.ts | 0 .../tests/scoreboard-replay.2.test.ts | 0 .../{cv => scanner}/tests/scoreboard.test.ts | 0 .../{cv => scanner}/tests/suites/death.ts | 0 .../tests/suites/scoreboard-replay.ts | 0 .../{cv => scanner}/tests/suppressor.test.ts | 0 .../{cv => scanner}/tests/timeline.test.ts | 0 .../{cv => scanner}/tests/vod-matches.test.ts | 0 .../{cv => scanner}/worker/analyzer.worker.ts | 0 app/features/{cv => scanner}/worker/client.ts | 0 app/features/{cv => scanner}/worker/pool.ts | 0 .../{cv => scanner}/worker/protocol.ts | 0 .../{cv => scanner}/worker/resources.ts | 12 +- app/features/vods/loaders/vods.new.server.ts | 2 +- app/routes.ts | 2 +- app/utils/urls.ts | 2 +- biome.json | 4 +- package.json | 14 +- .../v1/glyphs/death-tag-name.json | 0 .../v1/glyphs/death-tag-name.png | Bin .../v1/glyphs/death-weapon-ja.json | 0 .../v1/glyphs/death-weapon-ja.png | Bin .../v1/glyphs/death-weapon.json | 0 .../v1/glyphs/death-weapon.png | Bin .../v1/glyphs/map-start-mode.json | 0 .../v1/glyphs/map-start-mode.png | Bin .../v1/glyphs/map-start-stage.json | 0 .../v1/glyphs/map-start-stage.png | Bin .../v1/glyphs/scoreboard-header-line.json | 0 .../v1/glyphs/scoreboard-header-line.png | Bin .../v1/glyphs/scoreboard-header-lobby.json | 0 .../v1/glyphs/scoreboard-header-lobby.png | Bin .../v1/glyphs/scoreboard-names.json | 0 .../v1/glyphs/scoreboard-names.png | Bin .../v1/glyphs/scoreboard-paint-digits.json | 0 .../v1/glyphs/scoreboard-paint-digits.png | Bin .../v1/glyphs/scoreboard-replay-code.json | 0 .../v1/glyphs/scoreboard-replay-code.png | Bin .../v1/glyphs/scoreboard-replay-result.json | 0 .../v1/glyphs/scoreboard-replay-result.png | Bin .../v1/glyphs/scoreboard-stat-digits.json | 0 .../v1/glyphs/scoreboard-stat-digits.png | Bin .../v1/glyphs/scoreboard-team-digits.json | 0 .../v1/glyphs/scoreboard-team-digits.png | Bin .../{cv => scanner}/v1/planner/manifest.json | 0 .../{cv => scanner}/v1/planner/signatures.png | Bin .../bootstrap-atlas-from-fixture.ts | 26 ++-- scripts/{cv => scanner}/build-glyph-atlas.ts | 22 +-- .../build-localized-entries.ts | 28 ++-- .../build-planner-signatures.ts | 22 +-- scripts/{cv => scanner}/dump-crops.ts | 12 +- scripts/{cv => scanner}/otf-cmap.ts | 0 scripts/{cv => scanner}/overlay-rois.ts | 20 +-- scripts/{cv => scanner}/report.ts | 22 +-- scripts/{cv => scanner}/run-fixtures.ts | 10 +- scripts/{cv => scanner}/vite-node.config.ts | 2 +- vite.config.ts | 2 +- ...t.cv.config.ts => vitest.scanner.config.ts | 8 +- vitest.unit.config.ts | 4 +- 244 files changed, 383 insertions(+), 362 deletions(-) delete mode 100644 app/features/cv/cv-search-params.ts rename app/features/{cv => scanner}/README.md (79%) rename app/features/{cv => scanner}/capture/sampler.ts (100%) rename app/features/{cv => scanner}/capture/vod-frames.ts (100%) rename app/features/{cv => scanner}/components/AbilityGrid.tsx (86%) rename app/features/{cv => scanner}/components/App.tsx (71%) rename app/features/{cv => scanner}/components/DeathCard.tsx (100%) rename app/features/{cv => scanner}/components/EventCard.tsx (96%) rename app/features/{cv => scanner}/components/LivePage.tsx (100%) rename app/features/{cv => scanner}/components/MapStartCard.tsx (100%) rename app/features/{cv => scanner}/components/MinimapCard.tsx (95%) rename app/features/{cv => scanner}/components/ScoreboardCard.tsx (100%) rename app/features/{cv => scanner}/components/ScoreboardOwnCard.tsx (100%) rename app/features/{cv => scanner}/components/ScreenshotPage.tsx (100%) rename app/features/{cv => scanner}/components/VodPage.tsx (100%) rename app/features/{cv => scanner}/components/events-csv.ts (100%) rename app/features/{cv => scanner}/components/fixture-export.ts (100%) rename app/features/{cv => scanner}/components/flags.ts (100%) rename app/features/{cv => scanner}/components/format.ts (100%) rename app/features/{cv => scanner}/components/labels.ts (80%) rename app/features/{cv => scanner}/components/screenshot-handoff.ts (100%) rename app/features/{cv => scanner}/components/sendou-ingest.ts (98%) rename app/features/{cv => scanner}/components/sendou-upload.ts (100%) rename app/features/{cv => scanner}/components/styles.css (77%) rename app/features/{cv => scanner}/components/thumbnail.ts (100%) rename app/features/{cv => scanner}/core/ability-harvest.ts (96%) rename app/features/{cv => scanner}/core/batches.ts (100%) rename app/features/{cv => scanner}/core/canonical.ts (100%) rename app/features/{cv => scanner}/core/cv.ts (100%) rename app/features/{cv => scanner}/core/detectors/death/abilities.ts (100%) rename app/features/{cv => scanner}/core/detectors/death/index.ts (98%) rename app/features/{cv => scanner}/core/detectors/death/localized-messages.ts (99%) rename app/features/{cv => scanner}/core/detectors/death/rois.ts (100%) rename app/features/{cv => scanner}/core/detectors/death/weapon-names.ts (100%) rename app/features/{cv => scanner}/core/detectors/map-start/index.ts (100%) rename app/features/{cv => scanner}/core/detectors/map-start/rois.ts (100%) rename app/features/{cv => scanner}/core/detectors/minimap/abilities.ts (100%) rename app/features/{cv => scanner}/core/detectors/minimap/index.ts (97%) rename app/features/{cv => scanner}/core/detectors/minimap/rois.ts (100%) rename app/features/{cv => scanner}/core/detectors/minimap/stage.ts (100%) rename app/features/{cv => scanner}/core/detectors/registry.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard-own/abilities.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard-own/index.ts (95%) rename app/features/{cv => scanner}/core/detectors/scoreboard-own/rois.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard-replay/code.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard-replay/header.ts (97%) rename app/features/{cv => scanner}/core/detectors/scoreboard-replay/index.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard-replay/rois.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard/digits.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard/header.ts (98%) rename app/features/{cv => scanner}/core/detectors/scoreboard/index.ts (99%) rename app/features/{cv => scanner}/core/detectors/scoreboard/kits.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard/names.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard/pov.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard/rois.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard/row.ts (98%) rename app/features/{cv => scanner}/core/detectors/scoreboard/specials.ts (100%) rename app/features/{cv => scanner}/core/detectors/scoreboard/weapons.ts (100%) rename app/features/{cv => scanner}/core/detectors/suppressor.ts (100%) rename app/features/{cv => scanner}/core/detectors/types.ts (100%) rename app/features/{cv => scanner}/core/glyphs.ts (100%) rename app/features/{cv => scanner}/core/image.ts (100%) rename app/features/{cv => scanner}/core/localized-entries.ts (99%) rename app/features/{cv => scanner}/core/localized.ts (97%) rename app/features/{cv => scanner}/core/replay-time.ts (100%) rename app/features/{cv => scanner}/core/resources.ts (98%) rename app/features/{cv => scanner}/core/text.ts (100%) rename app/features/{cv => scanner}/core/timeline/index.ts (100%) rename app/features/{cv => scanner}/core/timeline/same-scoreboard.ts (100%) rename app/features/{cv => scanner}/core/vod-matches.ts (100%) rename app/features/{cv => scanner}/node/assets-dir.ts (71%) rename app/features/{cv => scanner}/node/fixtures.ts (93%) rename app/features/{cv => scanner}/node/image-io.ts (100%) rename app/features/{cv => scanner}/node/resources.ts (89%) rename app/features/{cv/routes/cv.tsx => scanner/routes/scanner.tsx} (76%) rename app/features/{cv/cv-schemas.ts => scanner/scanner-schemas.ts} (71%) rename app/features/{cv/cv-search-params.test.ts => scanner/scanner-search-params.test.ts} (56%) create mode 100644 app/features/scanner/scanner-search-params.ts rename app/features/{cv/cv-types.ts => scanner/scanner-types.ts} (72%) rename app/features/{cv => scanner}/store/db.ts (100%) rename app/features/{cv => scanner}/store/events.ts (100%) rename app/features/{cv => scanner}/store/vods.ts (100%) rename app/features/{cv => scanner}/tests/ability-harvest.test.ts (95%) rename app/features/{cv => scanner}/tests/batches.test.ts (96%) rename app/features/{cv => scanner}/tests/death.1.test.ts (100%) rename app/features/{cv => scanner}/tests/death.2.test.ts (100%) rename app/features/{cv => scanner}/tests/death.3.test.ts (100%) rename app/features/{cv => scanner}/tests/fixtures/death/classic-squiffer-jp/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/classic-squiffer-jp/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/death/colorful-splashtag/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/colorful-splashtag/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/death/e-liter-4k-jp-720p/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/e-liter-4k-jp-720p/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/death/fxg-supaatan-slosher/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/fxg-supaatan-slosher/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/death/german-booyah-bomb/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/german-booyah-bomb/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/death/headphones/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/headphones/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/death/pink-text-special/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/pink-text-special/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/death/rapid-blaster-deco-heavy-kez/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/rapid-blaster-deco-heavy-kez/frame.jpg (100%) rename app/features/{cv => scanner}/tests/fixtures/death/splattershot-tezuka/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/splattershot-tezuka/frame.jpg (100%) rename app/features/{cv => scanner}/tests/fixtures/death/ultra-stamp-jp-720p/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/ultra-stamp-jp-720p/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/death/wipeout-52-gal/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/wipeout-52-gal/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/death/z-f-splatterscope-vod/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/death/z-f-splatterscope-vod/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/map-start/german-splat-zones-robo-rom-en/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/map-start/german-splat-zones-robo-rom-en/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/map-start/splat-zones-mahi/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/map-start/splat-zones-mahi/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/map-start/splat-zones-undertow-spillway/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/map-start/splat-zones-undertow-spillway/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/minimap/snix-special-ready/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/minimap/snix-special-ready/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/minimap/spectator-area-cup-round1/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/minimap/spectator-area-cup-round1/frame.jpg (100%) rename app/features/{cv => scanner}/tests/fixtures/minimap/spectator-area-cup-winners-qf/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/minimap/spectator-area-cup-winners-qf/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/minimap/spectator-pxg/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/minimap/spectator-pxg/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/minimap/stonks-cross-both-teams/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/minimap/stonks-cross-both-teams/frame.jpg (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-own/order-slosher-replica/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-own/order-slosher-replica/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-own/private-battle-splat-zones-museum/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-own/private-battle-splat-zones-museum/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/anarchy-open-rainmaker-knockout-museum/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/anarchy-open-rainmaker-knockout-museum/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/low-res-2/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/low-res-2/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/low-res/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/low-res/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/private-battle-crableg-capital/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/private-battle-crableg-capital/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/private-battle-splat-zones-hagglefish/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/private-battle-splat-zones-hagglefish/frame.jpeg (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/private-battle-splat-zones-marlin/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/private-battle-splat-zones-marlin/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/x-battle-rainmaker-brinewater-1404/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/x-battle-rainmaker-brinewater-1404/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/x-battle-rainmaker-brinewater-1411/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/x-battle-rainmaker-brinewater-1411/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/x-battle-rainmaker-brinewater-1416/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/x-battle-rainmaker-brinewater-1416/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/x-battle-rainmaker-brinewater-1422/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/x-battle-rainmaker-brinewater-1422/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/x-battle-rainmaker-urchin-1434/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard-replay/x-battle-rainmaker-urchin-1434/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/german-private-battle-clam-blitz/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/german-private-battle-clam-blitz/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/private-battle-splat-zones-ko-kera-2/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/private-battle-splat-zones-ko-kera-2/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/private-battle-splat-zones-ko-kera/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/private-battle-splat-zones-ko-kera/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/private-battle-triton-cup-154-sendou/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/private-battle-triton-cup-154-sendou/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/robot/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/robot/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/special-symbols/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/special-symbols/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/splash-sploosh/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/splash-sploosh/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/xbattle-splat-zones-ko-capture/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/xbattle-splat-zones-ko-capture/frame.png (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/xbattle-splat-zones-ko/expected.json (100%) rename app/features/{cv => scanner}/tests/fixtures/scoreboard/xbattle-splat-zones-ko/frame.jpg (100%) rename app/features/{cv => scanner}/tests/map-start.test.ts (100%) rename app/features/{cv => scanner}/tests/minimap.test.ts (100%) rename app/features/{cv => scanner}/tests/node-test-compat.ts (100%) rename app/features/{cv => scanner}/tests/opencv-smoke.test.ts (100%) rename app/features/{cv => scanner}/tests/replay-time.test.ts (100%) rename app/features/{cv => scanner}/tests/scoreboard-own.test.ts (100%) rename app/features/{cv => scanner}/tests/scoreboard-replay.1.test.ts (100%) rename app/features/{cv => scanner}/tests/scoreboard-replay.2.test.ts (100%) rename app/features/{cv => scanner}/tests/scoreboard.test.ts (100%) rename app/features/{cv => scanner}/tests/suites/death.ts (100%) rename app/features/{cv => scanner}/tests/suites/scoreboard-replay.ts (100%) rename app/features/{cv => scanner}/tests/suppressor.test.ts (100%) rename app/features/{cv => scanner}/tests/timeline.test.ts (100%) rename app/features/{cv => scanner}/tests/vod-matches.test.ts (100%) rename app/features/{cv => scanner}/worker/analyzer.worker.ts (100%) rename app/features/{cv => scanner}/worker/client.ts (100%) rename app/features/{cv => scanner}/worker/pool.ts (100%) rename app/features/{cv => scanner}/worker/protocol.ts (100%) rename app/features/{cv => scanner}/worker/resources.ts (88%) rename public/{cv => scanner}/v1/glyphs/death-tag-name.json (100%) rename public/{cv => scanner}/v1/glyphs/death-tag-name.png (100%) rename public/{cv => scanner}/v1/glyphs/death-weapon-ja.json (100%) rename public/{cv => scanner}/v1/glyphs/death-weapon-ja.png (100%) rename public/{cv => scanner}/v1/glyphs/death-weapon.json (100%) rename public/{cv => scanner}/v1/glyphs/death-weapon.png (100%) rename public/{cv => scanner}/v1/glyphs/map-start-mode.json (100%) rename public/{cv => scanner}/v1/glyphs/map-start-mode.png (100%) rename public/{cv => scanner}/v1/glyphs/map-start-stage.json (100%) rename public/{cv => scanner}/v1/glyphs/map-start-stage.png (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-header-line.json (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-header-line.png (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-header-lobby.json (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-header-lobby.png (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-names.json (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-names.png (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-paint-digits.json (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-paint-digits.png (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-replay-code.json (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-replay-code.png (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-replay-result.json (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-replay-result.png (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-stat-digits.json (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-stat-digits.png (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-team-digits.json (100%) rename public/{cv => scanner}/v1/glyphs/scoreboard-team-digits.png (100%) rename public/{cv => scanner}/v1/planner/manifest.json (100%) rename public/{cv => scanner}/v1/planner/signatures.png (100%) rename scripts/{cv => scanner}/bootstrap-atlas-from-fixture.ts (96%) rename scripts/{cv => scanner}/build-glyph-atlas.ts (95%) rename scripts/{cv => scanner}/build-localized-entries.ts (93%) rename scripts/{cv => scanner}/build-planner-signatures.ts (80%) rename scripts/{cv => scanner}/dump-crops.ts (78%) rename scripts/{cv => scanner}/otf-cmap.ts (100%) rename scripts/{cv => scanner}/overlay-rois.ts (81%) rename scripts/{cv => scanner}/report.ts (95%) rename scripts/{cv => scanner}/run-fixtures.ts (82%) rename scripts/{cv => scanner}/vite-node.config.ts (85%) rename vitest.cv.config.ts => vitest.scanner.config.ts (60%) diff --git a/.gitignore b/.gitignore index d7215323d..473235b62 100644 --- a/.gitignore +++ b/.gitignore @@ -35,5 +35,5 @@ dump notepad.txt -# proprietary game fonts for the CV glyph-atlas builders (scripts/cv) +# proprietary game fonts for the scanner glyph-atlas builders (scripts/scanner) /assets/fonts/ diff --git a/AGENTS.md b/AGENTS.md index c9248738e..b248fb40c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -98,10 +98,10 @@ - use the template `/github/pull_request_template.md` - do not mention claude or claude code in the description -## CV feature (app/features/cv) +## Scanner feature (app/features/scanner) -- computer-vision match-event detection; full docs in `app/features/cv/README.md` — read it before touching detector/recognition code +- computer-vision match-event detection; full docs in `app/features/scanner/README.md` — read it before touching detector/recognition code - OpenCV ROI-view gotcha: `.data`/`.clone()` are broken on ROI views — always `view.copyTo(freshMat)` before pixel access -- fixture workflow: every live misread becomes a fixture under `app/features/cv/tests/fixtures/`; ground-truth labels are hand-corrected by the maintainer and definitive over any matcher output -- test with `pnpm test:cv`; accuracy report with `pnpm cv:report`; atlas regen commands and the assets-repo/CDN flow are in the README +- fixture workflow: every live misread becomes a fixture under `app/features/scanner/tests/fixtures/`; ground-truth labels are hand-corrected by the maintainer and definitive over any matcher output +- test with `pnpm test:scanner`; accuracy report with `pnpm scanner:report`; atlas regen commands and the assets-repo/CDN flow are in the README - events, snap tables, and fixtures speak sendou ids (`ModeShort`/`StageId`/weapon ids/`Ability`) — never reintroduce English game-name literals outside the generated localized snap tables diff --git a/app/features/cv/cv-search-params.ts b/app/features/cv/cv-search-params.ts deleted file mode 100644 index 7f3d2619c..000000000 --- a/app/features/cv/cv-search-params.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { z } from "zod"; -import * as SearchParams from "~/modules/search-params/search-params"; -import { SP } from "~/modules/search-params/search-params"; - -export const CV_TABS = ["live", "screenshot", "vod"] as const; - -export type CvTab = (typeof CV_TABS)[number]; - -export const cvSearchParams = SearchParams.define({ - tab: SP.param(z.enum(CV_TABS), { default: "live", loader: false }), -}); diff --git a/app/features/ingest/core/Scoreboards.test.ts b/app/features/ingest/core/Scoreboards.test.ts index 665dccc6b..41928d637 100644 --- a/app/features/ingest/core/Scoreboards.test.ts +++ b/app/features/ingest/core/Scoreboards.test.ts @@ -1,5 +1,8 @@ import { describe, expect, it } from "vitest"; -import type { CvAbility, CvLobby } from "~/features/cv/cv-types"; +import type { + ScannerAbility, + ScannerLobby, +} from "~/features/scanner/scanner-types"; import type { MainWeaponId, ModeShort, @@ -43,10 +46,10 @@ function testScoreboard({ t?: number; mode?: ModeShort | null; stage?: StageId | null; - lobby?: CvLobby | null; + lobby?: ScannerLobby | null; names?: string[]; weapons?: (MainWeaponId | null)[]; - abilities?: Record; + abilities?: Record; povIndex?: number | null; } = {}): IngestedEventInput { return { @@ -103,7 +106,7 @@ describe("matchedScoreboards", () => { }); it("carries ingested player abilities through to the stored scoreboard", () => { - const build: CvAbility[][] = [ + const build: ScannerAbility[][] = [ ["ISM", "ISS", "ISS", "ISS"], ["QR", "QSJ", "QSJ", "QSJ"], ["SSU", "RSU", "RSU", "RSU"], diff --git a/app/features/ingest/core/Scoreboards.ts b/app/features/ingest/core/Scoreboards.ts index 188e22dde..d2e36912e 100644 --- a/app/features/ingest/core/Scoreboards.ts +++ b/app/features/ingest/core/Scoreboards.ts @@ -1,4 +1,4 @@ -import type { CvAbility } from "~/features/cv/cv-types"; +import type { ScannerAbility } from "~/features/scanner/scanner-types"; import type { MainWeaponId, ModeShort, @@ -70,7 +70,7 @@ export interface IngestedScoreboardPlayer { s: number | null; paint: number | null; /** [head, clothes, shoes] ability rows gathered from the match's death screens */ - abilities?: CvAbility[][]; + abilities?: ScannerAbility[][]; /** set only via povIndex attribution */ userId?: number; } diff --git a/app/features/ingest/core/VodMatches.test.ts b/app/features/ingest/core/VodMatches.test.ts index 1fac3b66c..78b7d6a92 100644 --- a/app/features/ingest/core/VodMatches.test.ts +++ b/app/features/ingest/core/VodMatches.test.ts @@ -61,9 +61,9 @@ describe("prefillVodMatches", () => { expect(parsed.success).toBe(false); }); - it("accepts the `ingest` search param the CV VoD tab sends", () => { - // what the CV VoD tab's "Upload as VoD" button puts in the URL - // (~/features/cv/components/sendou-upload.ts): a { type?, matches } + it("accepts the `ingest` search param the scanner VoD tab sends", () => { + // what the scanner VoD tab's "Upload as VoD" button puts in the URL + // (~/features/scanner/components/sendou-upload.ts): a { type?, matches } // payload in the compressed `ingest` param const href = vodsNewSearchParams.href("/vods/new", { ingest: { type: "CAST", matches: [testMatch()] }, diff --git a/app/features/ingest/core/VodMatches.ts b/app/features/ingest/core/VodMatches.ts index ad760419e..eb1fac827 100644 --- a/app/features/ingest/core/VodMatches.ts +++ b/app/features/ingest/core/VodMatches.ts @@ -13,7 +13,7 @@ export interface PrefillVodMatch { } /** - * Turns the per-match rows a CV VoD scan sends into prefill data for the + * Turns the per-match rows a scanner VoD scan sends into prefill data for the * /vods/new form. The rows already carry sendou ids (validated by * ingestVodPrefillSchema); this only renames fields into the form's shape. */ diff --git a/app/features/ingest/ingest-schemas.ts b/app/features/ingest/ingest-schemas.ts index 3bb7c00f1..2877e7421 100644 --- a/app/features/ingest/ingest-schemas.ts +++ b/app/features/ingest/ingest-schemas.ts @@ -1,32 +1,32 @@ import { z } from "zod"; import { - cvAbilitySchema, - cvDeathDataSchema, - cvMapStartDataSchema, - cvScoreboardDataSchema, - cvScoreboardPlayerSchema, - cvScoreboardReplayDataSchema, -} from "~/features/cv/cv-schemas"; + scannerAbilitySchema, + scannerDeathDataSchema, + scannerMapStartDataSchema, + scannerScoreboardDataSchema, + scannerScoreboardPlayerSchema, + scannerScoreboardReplayDataSchema, +} from "~/features/scanner/scanner-schemas"; import { id } from "~/utils/zod"; const INGEST_MAX_EVENTS_PER_REQUEST = 1000; /** - * The event data shapes come from the producer (~/features/cv/cv-schemas — - * the single source of truth for the CV events domain); this module only + * The event data shapes come from the producer (~/features/scanner/scanner-schemas — + * the single source of truth for the scanner events domain); this module only * adds the ingest-specific envelope and enrichments. */ /** [head, clothes, shoes] ability rows gathered from the match's death screens */ -const scoreboardPlayerSchema = cvScoreboardPlayerSchema.extend({ - abilities: z.array(z.array(cvAbilitySchema)).optional(), +const scoreboardPlayerSchema = scannerScoreboardPlayerSchema.extend({ + abilities: z.array(z.array(scannerAbilitySchema)).optional(), }); -const scoreboardDataSchema = cvScoreboardDataSchema.extend({ +const scoreboardDataSchema = scannerScoreboardDataSchema.extend({ players: z.array(scoreboardPlayerSchema).length(8), }); -const scoreboardReplayDataSchema = cvScoreboardReplayDataSchema.extend({ +const scoreboardReplayDataSchema = scannerScoreboardReplayDataSchema.extend({ players: z.array(scoreboardPlayerSchema).length(8), }); @@ -54,11 +54,11 @@ const ingestedEventSchema = z.discriminatedUnion("type", [ }), eventBaseSchema.extend({ type: z.literal("Death"), - data: cvDeathDataSchema, + data: scannerDeathDataSchema, }), eventBaseSchema.extend({ type: z.literal("MapStart"), - data: cvMapStartDataSchema, + data: scannerMapStartDataSchema, }), ]); diff --git a/app/features/ingest/ingest-vod-schemas.ts b/app/features/ingest/ingest-vod-schemas.ts index 29605cf2d..d99fa147c 100644 --- a/app/features/ingest/ingest-vod-schemas.ts +++ b/app/features/ingest/ingest-vod-schemas.ts @@ -3,10 +3,10 @@ import { mainWeaponIdSchema, modeShortSchema, stageIdSchema, -} from "~/features/cv/cv-schemas"; +} from "~/features/scanner/scanner-schemas"; import { videoMatchTypes } from "~/features/vods/vods-constants"; -/** One detected match of a CV VoD scan (~/features/cv/core/vod-matches.ts). */ +/** One detected match of a scanner VoD scan (~/features/scanner/core/vod-matches.ts). */ const ingestVodMatchSchema = z.object({ /** whole seconds into the video the match starts at */ startsAt: z.number().int().min(0), @@ -25,7 +25,7 @@ const ingestVodMatchSchema = z.object({ }); /** - * The CV VoD tab's "Upload as VoD" button packs this into /vods/new's + * The scanner VoD tab's "Upload as VoD" button packs this into /vods/new's * `ingest` search param (an `SP.json` param, compressed by the search-params * module) to prefill the form: the detected match rows, minus the submission * fields (YouTube URL, title, date) the user fills in the form. `type` is diff --git a/app/features/cv/README.md b/app/features/scanner/README.md similarity index 79% rename from app/features/cv/README.md rename to app/features/scanner/README.md index 9e8f7be1d..220171dd2 100644 --- a/app/features/cv/README.md +++ b/app/features/scanner/README.md @@ -1,6 +1,6 @@ -# CV — Splatoon match-event detection +# Scanner — Splatoon match-event detection -Browser app (route `/cv`, dev-only until promoted) that watches OBS Virtual +Browser app (route `/scanner`, dev-only until promoted) that watches OBS Virtual Camera footage, VoD files, or screenshots, detects Splatoon 3 UI screens with OpenCV.js in a Web Worker, parses them into events speaking sendou.ink ids (`ModeShort`/`StageId`/weapon ids/`Ability`), records them to IndexedDB, and @@ -10,16 +10,16 @@ the emberz repo (kept read-only for archaeology); see `MIGRATION.md` there. ## Commands ```sh -pnpm test:cv # golden-file suite over tests/fixtures/ (Vitest, Node) -pnpm cv:report # accuracy table + name character error rate across fixtures -pnpm cv:fixtures [name-substring] # run detectors over matching fixtures, verbose -pnpm cv:bootstrap-atlas # harvest labeled fixture crops into the glyph atlases -pnpm cv:build-glyph-atlas # add the font-rendered charset (fonts required, see below) -pnpm cv:build-localized-entries # regen localized closed sets from ../splat3 -pnpm cv:build-planner-signatures # regen the minimap stage-ID atlas from the assets repo +pnpm test:scanner # golden-file suite over tests/fixtures/ (Vitest, Node) +pnpm scanner:report # accuracy table + name character error rate across fixtures +pnpm scanner:fixtures [name-substring] # run detectors over matching fixtures, verbose +pnpm scanner:bootstrap-atlas # harvest labeled fixture crops into the glyph atlases +pnpm scanner:build-glyph-atlas # add the font-rendered charset (fonts required, see below) +pnpm scanner:build-localized-entries # regen localized closed sets from ../splat3 +pnpm scanner:build-planner-signatures # regen the minimap stage-ID atlas from the assets repo ``` -The cv scripts run through `vite-node -c scripts/cv/vite-node.config.ts` — the +The scanner scripts run through `vite-node -c scripts/scanner/vite-node.config.ts` — the root vite config pre-bundles `@techstark/opencv-js` for the browser worker, and vite-node must not consume that prebundle (it crashes on `__dirname` in Node). The package itself is pnpm-patched (`patches/`): its CJS export is the @@ -39,13 +39,13 @@ video file → capture/vod-frames (WebCodecs decode, seek fallback) [VoD tab] ``` - `core/` is pure (mats in, events out) and must stay runnable in three - contexts: the worker, the `/cv` Screenshot tab, and Node tests. Keep + contexts: the worker, the `/scanner` Screenshot tab, and Node tests. Keep DOM/browser APIs out of it; Node-only helpers (image IO, fixture loading) live in `node/`. Importing pure data/type modules from `~/modules` and `~/features/build-analyzer/data` is fine — zod and the app config graph are - not (schemas live in `cv-schemas.ts`, consumed by `features/ingest`; + not (schemas live in `scanner-schemas.ts`, consumed by `features/ingest`; detectors only `import type` the shapes). -- The route (`routes/cv.tsx`) is SSR-guarded: everything below it assumes a +- The route (`routes/scanner.tsx`) is SSR-guarded: everything below it assumes a browser (worker, IndexedDB, WebCodecs, getUserMedia), so the client tree loads via `React.lazy` after `useHydrated`. Nothing from `core/worker/capture/store` may be imported at route-module top level. @@ -66,16 +66,16 @@ video file → capture/vod-frames (WebCodecs decode, seek fallback) [VoD tab] - New event types implement `Detector` (`core/detectors/types.ts`): a cheap `gate(mat)` at sample rate plus `parse(mat, t)` when the gate fires. Register in `core/detectors/registry.ts`. Event data shapes are pinned to - `cv-schemas.ts` by compile-time asserts — extend both together. + `scanner-schemas.ts` by compile-time asserts — extend both together. ## Assets (CDN) and fonts Weapon/ability/special/sub template sources are the site's shared game icons in the **sendou-ink/assets repo** under `assets/img/**` (`.avif`; ids -come from `~/modules/in-game-lists`, plus the CV-only `UNKNOWN` ability -badge — `toCvAbility` narrows template ids back to sendou ids). The -CV-specific sets — glyph atlases and the planner signature atlas — live in -this repo under `public/cv/v1/**` (override with `CV_ASSETS_DIR`; the +come from `~/modules/in-game-lists`, plus the scanner-only `UNKNOWN` ability +badge — `toScannerAbility` narrows template ids back to sendou ids). The +scanner-specific sets — glyph atlases and the planner signature atlas — live in +this repo under `public/scanner/v1/**` (override with `SCANNER_ASSETS_DIR`; the version segment bumps on breaking atlas-format changes). xxx: the atlases are in `public/` only while the feature is in development — move them to the assets repo (and the worker back to the CDN base) later: @@ -84,15 +84,15 @@ the assets repo (and the worker back to the CDN base) later: (the base URL rides the worker init message; the DO Space needs CORS — GET, sendou.ink + localhost origins — because the worker `fetch()`es cross-origin, plain `` consumers don't); atlases fetched same-origin - from `/cv/v1/**`. For local dev against fresh icon regens, serve the + from `/scanner/v1/**`. For local dev against fresh icon regens, serve the checkout with CORS — `npx serve /Users/kalle/Developer/assets/assets -l 9100 --cors` — and set `VITE_STATIC_ASSETS_URL=http://localhost:9100` in `.env`. -- Node (tests/scripts): atlases from `public/cv/v1`, icons from the +- Node (tests/scripts): atlases from `public/scanner/v1`, icons from the `../assets` checkout directly, never the CDN. AVIF icons decode through `sharp` (`node/image-io.ts`) — `@napi-rs/canvas` mis-decodes AVIF partial-alpha pixels. -- Atlas regens overwrite `public/cv/v1` in place and ship with the app +- Atlas regens overwrite `public/scanner/v1` in place and ship with the app build; breaking format changes bump `v1`. Fonts are proprietary and gitignored: `BlitzMain.otf`, `BlitzBold.otf`, @@ -101,10 +101,10 @@ root; from the splatoon3-fonts repo). Atlas builders fail loudly without them. Names and row digits use BlitzMain; team totals use BlitzBold; the replay code line and VICTORY/DEFEAT tags use FOT-RowdyStd-EB; the JP death message mixes condensed Kurokane and Rowdy (`death-weapon-ja`). Regeneration -order: `cv:bootstrap-atlas` (fixture crops win via tie-break) → -`cv:build-glyph-atlas`; localized sets via `cv:build-localized-entries` +order: `scanner:bootstrap-atlas` (fixture crops win via tie-break) → +`scanner:build-glyph-atlas`; localized sets via `scanner:build-localized-entries` (expects a splat3 checkout at `../splat3`) then the atlas rebuild; planner -atlas via `cv:build-planner-signatures` (reads the assets repo's +atlas via `scanner:build-planner-signatures` (reads the assets repo's `assets/planner-maps/`, MINI variant). ## Fixtures are the workflow diff --git a/app/features/cv/capture/sampler.ts b/app/features/scanner/capture/sampler.ts similarity index 100% rename from app/features/cv/capture/sampler.ts rename to app/features/scanner/capture/sampler.ts diff --git a/app/features/cv/capture/vod-frames.ts b/app/features/scanner/capture/vod-frames.ts similarity index 100% rename from app/features/cv/capture/vod-frames.ts rename to app/features/scanner/capture/vod-frames.ts diff --git a/app/features/cv/components/AbilityGrid.tsx b/app/features/scanner/components/AbilityGrid.tsx similarity index 86% rename from app/features/cv/components/AbilityGrid.tsx rename to app/features/scanner/components/AbilityGrid.tsx index b68f8e7b2..ba859efd9 100644 --- a/app/features/cv/components/AbilityGrid.tsx +++ b/app/features/scanner/components/AbilityGrid.tsx @@ -5,11 +5,11 @@ import { useState } from "react"; import { Ability } from "~/components/Ability"; -import type { CvAbility } from "../cv-types"; +import type { ScannerAbility } from "../scanner-types"; const ROW_LABELS = ["head", "clothes", "shoes"] as const; -export function AbilityGrid({ abilities }: { abilities: CvAbility[][] }) { +export function AbilityGrid({ abilities }: { abilities: ScannerAbility[][] }) { return ( @@ -32,7 +32,11 @@ export function AbilityGrid({ abilities }: { abilities: CvAbility[][] }) { * Click-to-toggle popover showing a player's ability grid; the trigger is * the head-main ability icon. Closes when the pointer leaves it. */ -export function AbilityPopover({ abilities }: { abilities: CvAbility[][] }) { +export function AbilityPopover({ + abilities, +}: { + abilities: ScannerAbility[][]; +}) { const [open, setOpen] = useState(false); const trigger = abilities[0]?.[0]; if (!trigger) return null; diff --git a/app/features/cv/components/App.tsx b/app/features/scanner/components/App.tsx similarity index 71% rename from app/features/cv/components/App.tsx rename to app/features/scanner/components/App.tsx index 9b4d03ff0..01126b1ae 100644 --- a/app/features/cv/components/App.tsx +++ b/app/features/scanner/components/App.tsx @@ -1,22 +1,26 @@ import { Link } from "react-router"; import { useUser } from "~/features/auth/core/user"; import { useSearchParam } from "~/modules/search-params/hooks"; -import { CV_PAGE } from "~/utils/urls"; -import { CV_TABS, type CvTab, cvSearchParams } from "../cv-search-params"; +import { SCANNER_PAGE } from "~/utils/urls"; +import { + SCANNER_TABS, + type ScannerTab, + scannerSearchParams, +} from "../scanner-search-params"; import { LivePage } from "./LivePage"; import { ScreenshotPage } from "./ScreenshotPage"; import type { SendouUser } from "./sendou-ingest"; import { VodPage } from "./VodPage"; import "./styles.css"; -const TAB_LABELS: Record = { +const TAB_LABELS: Record = { live: "Live", screenshot: "Screenshot", vod: "VoD", }; export function App() { - const [tab] = useSearchParam(cvSearchParams, "tab"); + const [tab] = useSearchParam(scannerSearchParams, "tab"); const rootUser = useUser(); const sendouUser: SendouUser | null = rootUser ? { id: rootUser.id, username: rootUser.username } @@ -32,13 +36,13 @@ export function App() { ); return ( -
+