Give /scanner a md+ camera, live match inspector, and history strip (with device picker, batch scan, and tips) without regressing the mobile immersive checkout. Co-authored-by: Cursor <cursoragent@cursor.com>
3.4 KiB
3.4 KiB
| convoy | brief_number | depends_on | recommended_model | model_tier | files | cross_brief_commitments | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| scanner-desktop-layout | 3 | composer-2.5-fast | fast |
|
|
Brief 3: Live match inspector rail
Goal (1 sentence)
Build the right-rail ScannerResultPanel that inspects the focused cart entry and commits one card via VOCAB.ADD_TO_MY_COLLECTION.
Files in scope (do not edit anything else)
components/scanner/ScannerResultPanel.jstest/components/ScannerResultPanel.test.js
Conventions to follow
- Import
VOCABfromlib/collection-vocabulary.js— never ship forbidden strings (forbidden-stale-stringsCI). - Primitives:
GlassSurface,Buttonfromcomponents/ui. - Duplicate minimally from
ReviewCardItem.js:CONDITION_OPTIONS,confidencePercent,confidenceRingColor(do not editReviewCardItemin this brief). - Confidence captions (Design direction): ≥90% Excellent match. · ≥70% Good match. · <70% Low confidence — verify before adding.
- Inspector thumbnail + tiles: solid
--bg-secondary— nobackdrop-filteron per-card elements. - Empty state: centered muted scan/camera SVG illustration + single line: Point your camera at a card or upload an image to see a match. — no Upload/Batch CTAs in rail.
- Populated: thumbnail, name (
text-lg font-semibold), set · rarity · collector #, condition<select>(aria-labelincludes card name), foilrole="switch", confidence % + horizontal bar (300ms width transition;@media (prefers-reduced-motion: reduce)→ instant). - Actions: primary
VOCAB.ADD_TO_MY_COLLECTION· ghost Rescan · ghostVOCAB.ADD_TO_LIST. - Primary
ButtonusesloadingwhenisAdding(fromqueue.addingCardIds.has(card.id)). - Commit errors:
role="alert"div withcolor-mix(in srgb, var(--color-error) …)pattern frompages/scanner.js. isIdentifyingstate: show Identifying… placeholder when true and no focused card yet.
Props shape (for Brief 6 wiring)
export default function ScannerResultPanel({
focusedCard = null,
queue,
isIdentifying = false,
commitError = null,
onAddToOwned,
onAddToList,
onRescan,
}) {
onUpdateMetadata via queue.updateCardMetadata(focusedCard.id, patch) inline.
Acceptance criteria
- Empty state: illustration + one line only (no duplicate Upload/Batch)
- Populated state shows metadata, condition select, foil switch, confidence bar
- Primary button label is
VOCAB.ADD_TO_MY_COLLECTION - Secondary row: Rescan +
VOCAB.ADD_TO_LIST(no wishlist) prefers-reduced-motiondisables confidence bar width animation- tests: empty state, populated card, add button disabled while adding
- no scope expansion (do not edit files outside
files:above)
Rationale (≤3 sentences)
The inspector replaces the 360px cart side panel as the primary right-hand surface on desktop. Keeping it a dumb view lets Brief 6 own focus/advance/toast orchestration. Confidence and condition UX matches checkout patterns users already know from ReviewCardItem.