diff --git a/.convoys/ship-readiness.md b/.convoys/ship-readiness.md index d27240b..125c653 100644 --- a/.convoys/ship-readiness.md +++ b/.convoys/ship-readiness.md @@ -318,7 +318,7 @@ These MUST land before any anonymous traffic touches the production URL. | `pages/collections.js` | 989 | Similar structure to collection/[identifier]. Possibly share extracted pieces. | | `pages/card/[id].js` | 913 | `CardDetail` — split into header, owned-badge, add-to-collection-flow. | | `pages/deck-builder.js` | 823 | `DeckBuilder` — extract card-search, deck-list, mana-curve panels. | -| `components/CameraScanner.js` | 817 | Camera + AI-OCR + detection-loop — extract the detection loop into a hook. | +| `components/CameraScanner.js` | ~45 | **RESOLVED 2026-06-02** — god-component-split slice shipped PRs #67–#72 + view extract. Pre-split ~1,050 lines; now composes `useCameraScanner` + `useScannerIdentification` + `CameraScannerView`. Logic lives in `lib/scanner-card-detection.js`, `lib/scanner-card-identify.js`, `lib/scan-capture-upload.js`, `components/ScanDisambiguationDialog.js`. | | `pages/admin/card-editor.js` | 778 | Form heavy. Use a `useFormState` pattern + separate the search-results subview. | | `pages/scanner.js` | 776 | Mirror of CameraScanner concerns plus queue management. | | `pages/settings.js` | 669 | One screen per settings section is the usual fix. | @@ -360,7 +360,7 @@ The `getIcon` registry in `Layout.js` and `MobileNavigation.js` redefines the sa - **Error states** — error messages bubble to `console.error` and toast nothing. Add a global toast system (e.g. `sonner`) and wire every catch block. - **Empty states** — `/my-cards` and `/collections` when empty drop to "no cards yet". Replace with first-time CTA: "Scan your first card" or "Browse popular sets". - **Mobile drawer** — `MobileNavigation` is solid (recent commit `442e906`). One thing: the bottom-bar's active state contrast looks low in light mode; verify against AA. -- **Camera scanner UX** — 817 lines of detection loop. Add a one-line "scanning…" status under the viewfinder and a single "captured N cards" badge. The current toolbar is busy. +- **Camera scanner UX** — detection/identify logic split complete (PRs #67–#72); view markup in `CameraScannerView.js`. Remaining polish: theme-token cleanup for overlay hex colors, busier toolbar simplification. ### Role-design-system-auditor findings @@ -487,6 +487,7 @@ Six convoys authored from the scanner audit portfolio plan. Dependency order: - **`server-side-scan-pipeline`** — **RESOLVED 2026-05-27** — PR #35 (`e81dd49`). Server-owned identify, `card_submissions`, disambiguation. Convoy: `.convoys/server-side-scan-pipeline.md`. - **`add-real-ocr-layer`** — **RESOLVED 2026-05-27** — PR #38 (`d798e28`) + polish PR #39. Layer-1 Tesseract + `pg_trgm`. Convoy: `.convoys/add-real-ocr-layer.md`. - **`redesign-scanner-flow`** — **RESOLVED 2026-05-27** — PRs #42 (Brief 1), #43 (Brief 2), #44 (Brief 3). Post-PR audit `audit-redesign-scanner-flow-44` posted to PR #44; outcome comment-only. See `.convoys/redesign-scanner-flow/audit-redesign-scanner-flow-44.md`. Follow-ups: `scanner-redesign-a11y-fixes` **RESOLVED** PR #45; `scanner-user-cards-quantity-guard` **RESOLVED** PR #46; `test-scanner-redesign-surfaces` **RESOLVED** PR #47. +- **`god-component-split` / `CameraScanner.js` slice** — **RESOLVED 2026-06-02** — PRs #67–#72 (Briefs 1–5) + view extract (`CameraScannerView.js`). Pre-split ~1,050 lines → ~45-line composer + presentational view. Remaining `god-component-split` targets: `pages/cards.js`, `pages/scanner.js`, etc. (see § P2 #13 table). - **`rename-collections-vocabulary`** — **RESOLVED 2026-05-29** — PR #54 + follow-up PR #55. Convoy: `.convoys/rename-collections-vocabulary.md`. - **`scanner-correctness-polish`** — **RESOLVED 2026-05-27** — PR #41 (`55af7e3`). Convoy: `.convoys/scanner-correctness-polish.md`. - **`schema-cleanup-from-scanner-audit`** (priority: P2 schema; **deferred** — diff --git a/components/CameraScanner.js b/components/CameraScanner.js index 1f8a088..885018a 100644 --- a/components/CameraScanner.js +++ b/components/CameraScanner.js @@ -1,284 +1,44 @@ import { useRef } from 'react'; import { useCameraScanner } from '../lib/use-camera-scanner.js'; import { useScannerIdentification } from '../lib/use-scanner-identification.js'; -import ScanDisambiguationDialog from './ScanDisambiguationDialog.js'; +import CameraScannerView from './CameraScannerView.js'; export default function CameraScanner({ onCardScanned, onError }) { const verificationPausedRef = useRef(false); const onVerifyCardRef = useRef(() => {}); - const { - videoRef, - canvasRef, - detectionCanvasRef, - isStreaming, - isDetecting, - trackedCards, - videoMetrics, - startCamera, - stopCamera, - } = useCameraScanner({ + const camera = useCameraScanner({ onError, verificationPausedRef, onVerifyCard: (card) => onVerifyCardRef.current(card), }); - const { - disambiguation, - scanNotice, - submittingReview, - handleDisambiguationPick, - handleNotInCatalog, - cancelDisambiguation, - } = useScannerIdentification({ + const identification = useScannerIdentification({ onCardScanned, onError, - videoRef, - canvasRef, + videoRef: camera.videoRef, + canvasRef: camera.canvasRef, onVerifyCardRef, verificationPausedRef, }); - const foundCardCount = trackedCards.filter( - (card) => card.status === 'confirmed' || card.status === 'scanned' - ).length; - return ( -
- {scanNotice && ( -
- {scanNotice} -
- )} - {/* Camera Feed Container */} -
- {/* Video Element - Always rendered but visibility controlled */} -
- - {/* Hidden canvases for image processing */} - - - - {/* Detection Info Panel - Only show when streaming */} - {isStreaming && ( -
-
-
- 🎯 -
-

- Smart Detection Active -

-
-
-
- - Shape recognition -
-
- - Server identification -
-
- - Position tracking -
-
- - Database lookup -
-
-
- )} - - -
+ ); } diff --git a/components/CameraScannerView.js b/components/CameraScannerView.js new file mode 100644 index 0000000..576a6a7 --- /dev/null +++ b/components/CameraScannerView.js @@ -0,0 +1,252 @@ +import ScanDisambiguationDialog from './ScanDisambiguationDialog.js'; + +const CONFIRMED_COLOR = '#10B981'; +const SCANNED_COLOR = '#3B82F6'; + +function overlayBorderColor(status) { + if (status === 'scanned') return SCANNED_COLOR; + return CONFIRMED_COLOR; +} + +function overlayGlowColor(status) { + if (status === 'scanned') return `${SCANNED_COLOR}50`; + return `${CONFIRMED_COLOR}50`; +} + +export default function CameraScannerView({ + videoRef, + canvasRef, + detectionCanvasRef, + isStreaming, + isDetecting, + trackedCards, + videoMetrics, + startCamera, + stopCamera, + scanNotice, + disambiguation, + submittingReview, + onPick, + onNotInCatalog, + onCancelDisambiguation, +}) { + const foundCards = trackedCards.filter( + (card) => card.status === 'confirmed' || card.status === 'scanned' + ); + const foundCardCount = foundCards.length; + + return ( +
+ {scanNotice && ( +
+ {scanNotice} +
+ )} +
+
+ + + + + {isStreaming && ( +
+
+
+ 🎯 +
+

+ Smart Detection Active +

+
+
+
+ + Shape recognition +
+
+ + Server identification +
+
+ + Position tracking +
+
+ + Database lookup +
+
+
+ )} + + +
+ ); +}