Forward-looking work tracked outside of the open issues. Each item has a rough effort estimate and a pointer into the codebase so a contributor can pick it up. Items move off this list when they're shipped β check git log for the implementation details.
The internal renderer lives at src/lib/qr-code/. See CONTRIBUTING.md β Internal QR library for the architecture and the build-vs-vendor split.
Publish the library to npm β #242
Effort: ~1 day.
So consumers can npm install @lyqht/mini-qr-lib and pull in the renderer without the Vue app. Needs:
- A
vite.lib.config.tswithbuild.libmode targetingsrc/lib/qr-code/index.ts, dual ESM + CJS output package.json#exports+typespointing at the bundled.d.ts- A dedicated
READMEaimed at npm consumers (separate from the app README), with thecreateQRCodeAPI surface and one minimal usage example - A
prepublishOnlyscript that runs vitest, type-check, andvite build - CI release workflow (optional β can ship manually first)
The library is already DOM-agnostic except for createQRCode (which calls DOMParser) and render/canvas.ts (which uses Image + <canvas>). Pure SVG-string output (buildSvgExportString, renderQrFragment) is already SSR-safe.
Custom QR shapes β circle / triangle / heart / arbitrary path β #257
Effort: 3β4 days for the polished version.
QR codes are mandatorily square at the spec level (three finder patterns at the corners), so this is a visual shape mask, not a re-shaped matrix.
- Add a
shapeMaskfield toQRCodeConfig:'circle' | 'rounded-square' | 'heart' | 'triangle' | 'star' | { svgPath: string } - Built-in shape SVG path definitions live under
render/shapes.ts(new file) - Plumb the mask through
render/svg.tseither as a<clipPath>wrapping the QR fragment (simplest, half-clipped edges) or β preferred β by extending the existinghideCell(r, c)predicate inrender/dots.tsso cells whose centre falls outside the shape are skipped entirely (clean edges, smaller paths) - Scannability guard: hiding cells eats into error-correction budget. A circle inscribed in the square hides ~21 % of cells; at EC level H (~30 % tolerance) it scans, at L it doesn't. Auto-bump EC to H when a shape mask is set, or compute the fraction of dark modules hidden and refuse / warn if it exceeds the level's capacity (reuse the
EC_FACTORtable fromrender/image.ts) - Storybook stories under
src/lib/qr-code/stories/shape-mask.stories.ts, one per built-in shape plus a custom-path example - Playwright e2e: scan-back round-trip per shape to lock in the scannability floor
Out of scope: generating multiple candidate matrices with different masks and picking the one that looks best under the shape (what the very fanciest commercial QR vendors do β at least a week of work on its own).
Effort: 1β2 days.
The Vue QRCodeFrame.vue component still renders the in-app preview via Tailwind/flexbox. The library's frame primitive (src/lib/qr-code/frame.ts) is already used for SVG / PNG / JPG exports. Replacing the Vue component's internals with the lib primitive would:
- Eliminate a per-frame visual delta between preview and export
- Reduce the surface that has to be kept in sync when frame fields change
- Make the Vue component a thin shim, the same way
StyledQRCode.vueis
Risks: the in-app preview is currently text-laid-out by the browser (real font metrics), while the lib primitive uses a chars Γ 0.6 Γ fontSize width heuristic. Replacing one with the other will shift visible padding slightly; expect to re-baseline the preview Playwright snapshots.
Open an issue at lyqht/mini-qr/issues and tag it enhancement. If it's a roadmap-shaped item (not a small bug or single-PR feature), maintainers will move it onto this file after triage.