Skip to content

feat(qr): decode QR codes from an uploaded image#85

Merged
lyfuci merged 1 commit into
mainfrom
feat/qr-decode
May 31, 2026
Merged

feat(qr): decode QR codes from an uploaded image#85
lyfuci merged 1 commit into
mainfrom
feat/qr-decode

Conversation

@lyfuci

@lyfuci lyfuci commented May 30, 2026

Copy link
Copy Markdown
Owner

What

The QR tool could only generate codes — this adds the missing reverse half: decode a QR from an image.

A Generate / Decode view toggle sits at the top of the page. The Decode view shows a drop-zone (reusing FileDrop) that accepts an image, rasterises it on an offscreen canvas, and runs jsQR entirely locally — nothing is uploaded. The decoded payload renders with a copy button, plus an "open link" affordance when it parses as an http(s) URL (the regex is anchored, so javascript: etc. can't slip through). The "no QR found" and non-image-file branches surface an inline error.

How

  • src/lib/qr-decode.ts
    • decodeQrFromImageData(data, w, h) — thin wrapper over jsQR on raw RGBA; no canvas/network, so it's unit-testable in Node.
    • decodeQrFromBlob(blob) — browser path: createImageBitmap -> canvas -> getImageData -> decode, with a downscale cap for large images.
  • src/pages/QrCode.tsx — view toggle + decode panel; generator UI unchanged, just wrapped in the generate branch.
  • New dep: jsqr (pure-JS, MIT).
  • i18n keys added in both en.json and zh-CN.json.

Tests / verification

  • New round-trip unit test: render a QR via the qrcode lib -> rasterise -> decodeQrFromImageData -> assert the original text (URL + CJK cases), plus a blank-image -> null case.
  • pnpm typecheck / pnpm lint / pnpm build clean; 211 tests green.
  • Headless-Chrome run against a fresh build: upload a QR PNG in the Decode view -> decoded text + working open-link appear, no raw i18n keys, zero page errors; the Generate view still renders its SVG.

🤖 Generated with Claude Code

The QR tool could only generate codes; add the missing reverse half.
A Generate/Decode view toggle reveals a drop-zone that rasterises any
image (PNG/JPEG/WebP/GIF) on an offscreen canvas and runs jsQR locally —
nothing is uploaded. Shows the decoded payload with copy, plus an "open
link" affordance when it parses as an http(s) URL.

- src/lib/qr-decode.ts: decodeQrFromImageData (ImageData->text, Node-testable)
  + decodeQrFromBlob (browser canvas path).
- Round-trip unit test renders a QR via the qrcode lib and decodes it back
  (URL + CJK + blank-image cases).
- i18n keys added in both locales.

Verified: typecheck/lint/build clean, 211 tests green, and a headless-Chrome
run confirmed upload->decode shows the payload + open-link with no page
errors and no raw i18n keys, and the generate view still renders its SVG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lyfuci lyfuci merged commit 1e6b1f3 into main May 31, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant