# Plan: Tape Identifier Dropzone on /zxdb ## Context We have 32,960 rows in `software_hashes` with MD5, CRC32, size, and inner_path for tape-image contents. This feature exposes that data to users: drop a tape file, get it identified against the ZXDB database. Uses RSC (server actions) rather than an API endpoint to make bulk scripted identification harder. ## Architecture **Client-side:** Compute MD5 + file size in the browser, then call a server action with just those two values (file never leaves the client). **Server-side:** A Next.js Server Action looks up `software_hashes` by MD5 (and optionally size_bytes for disambiguation), joins to `downloads` and `entries` to return the entry title, download details, and a link. **Client-side MD5:** Web Crypto doesn't support MD5. Include a small pure-JS MD5 utility (~80 lines, well-known algorithm). No new npm dependencies. ## Files to Create/Modify ### 1. `src/utils/md5.ts` — Pure-JS MD5 for browser use - Exports `async function computeMd5(file: File): Promise` - Reads file as ArrayBuffer, computes MD5, returns hex string - Standard MD5 algorithm implementation, typed for TypeScript ### 2. `src/app/zxdb/actions.ts` — Server Action - `'use server'` directive - `identifyTape(md5: string, sizeBytes: number)` - Queries `software_hashes` JOIN `downloads` JOIN `entries` by MD5 - If multiple matches and size_bytes narrows it, filter further - Returns array of `{ downloadId, entryId, entryTitle, innerPath, md5, crc32, sizeBytes }` ### 3. `src/app/zxdb/TapeIdentifier.tsx` — Client Component - `'use client'` - States: `idle` → `hashing` → `identifying` → `results` / `not-found` - Dropzone UI: - Dashed border card, large tape icon, "Drop a tape file to identify it" - Lists supported formats: `.tap .tzx .pzx .csw .p .o` - Also has a hidden `` with a "or choose file" link - Drag-over highlight state - On file drop/select: - Validate extension against supported list - Show spinner + "Computing hash..." - Compute MD5 + size client-side - Call server action `identifyTape(md5, size)` - Show spinner + "Searching ZXDB..." - Results view (replaces dropzone): - Match found: entry title as link to `/zxdb/entries/{id}`, inner filename, MD5, file size - Multiple matches: list all - No match: "No matching tape found in ZXDB" - "Identify another tape" button to reset ### 4. `src/app/zxdb/page.tsx` — Add TapeIdentifier section - Insert `` as a new section between the hero and "Start exploring" grid - Wrap in a card with distinct styling to make it visually prominent ### 5. `src/server/repo/zxdb.ts` — Add lookup function - `lookupByMd5(md5: string)` — joins `software_hashes` → `downloads` → `entries` - Returns download_id, entry_id, entry title, inner_path, hash details ## Verification - Visit http://localhost:4000/zxdb - Dropzone should be visible and prominent between hero and navigation grid - Drop a known .tap/.tzx file → should show the identified entry with a link - Drop an unknown file → should show "No matching tape found" - Click "Identify another tape" → resets to dropzone - Check file never leaves browser (Network tab: only the server action call with md5 + size) - Verify non-supported extensions are rejected with helpful message