Install any skill in seconds. Free to start, no credit card required.
Get Started Free →QR code scanner using the camera.
.claude/skills/aiskillstore-extension-qr-code/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-12 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 28% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 64% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 53% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 6% | 0% |
QR code scanner extension for Caffeine AI.
This skill adds QR code scanning using the device camera. Built on top of the camera component with jsQR for decoding.
For QR code scanner support:
There is a prefabricated React hook imported from @caffeinelabs/qr-code that cannot be modified.
typescript filepath=@caffeinelabs/qr-code/src/hooks/useQRScanner.tsimport { RefObject } from 'react'; import { CameraConfig, CameraError } from '@caffeineai/camera'; export interface QRResult { // The decoded QR code data data: string; // Timestamp when the QR code was scanned timestamp: number; } export interface QRScannerConfig extends CameraConfig { // How often to scan for QR codes in milliseconds (default: 100) scanInterval?: number; // Maximum number of results to keep in history (default: 10) maxResults?: number; // URL to load jsQR library from (default: jsdelivr CDN) jsQRUrl?: string; } export interface UseQRScannerReturn { // Array of scanned QR codes (newest first) qrResults: QRResult[]; // Whether currently scanning for QR codes isScanning: boolean; // Whether jsQR library has been loaded jsQRLoaded: boolean; // Camera state (pass-through from useCamera) isActive: boolean; isSupported: boolean | null; error: CameraError | null; isLoading: boolean; currentFacingMode: 'user' | 'environment'; // Start camera and begin scanning - returns true on success startScanning: () => Promise<boolean>; // Stop scanning and camera stopScanning: () => Promise<void>; // Switch camera facing mode - returns true on success switchCamera: () => Promise<boolean>; // Clear all scan results clearResults: () => void; // Reset scanner state (stop scanning and clear results) reset: () => void; // Retry camera initialization after error - returns true on success retry: () => Promise<boolean>; // Ref to attach to video element for camera preview videoRef: RefObject<HTMLVideoElement>; // Ref to attach to canvas element used for QR processing (can be hidden) canvasRef: RefObject<HTMLCanvasElement>; // Computed state // Whether scanner is ready to use (jsQR loaded and camera supported) isReady: boolean; // Whether scanning can be started (ready + not loading) canStartScanning: boolean; } export declare function useQRScanner(config?: QRScannerConfig): UseQRScannerReturn;
Usage example:
typescript filepath=example.tsimport { useQRScanner } from '@caffeineai/qr-code'; function QRScannerComponent() { const { qrResults, isScanning, isActive, isSupported, error, isLoading, canStartScanning, startScanning, stopScanning, switchCamera, clearResults, videoRef, canvasRef } = useQRScanner({ facingMode: 'environment', scanInterval: 100, maxResults: 5 }); if (isSupported === false) { return <div>Camera not supported</div>; } return ( <div> <video ref={videoRef} style={{ width: '100%', height: 'auto' }} playsInline muted /> <canvas ref={canvasRef} style={{ display: 'none' }} /> {error && <div>Error: {error.message}</div>} <div> <button onClick={startScanning} disabled={!canStartScanning}> Start Scanning </button> <button onClick={stopScanning} disabled={isLoading || !isActive}> Stop Scanning </button> {/* Only show switch camera on mobile */} {/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) && ( <button onClick={switchCamera} disabled={isLoading || !isActive}> Switch Camera </button> )} </div> <div> <h3>Results {qrResults.length > 0 && <button onClick={clearResults}>Clear</button>}</h3> {qrResults.map(result => ( <div key={result.timestamp}> <small>{new Date(result.timestamp).toLocaleTimeString()}</small> <p>{result.data}</p> </div> ))} </div> </div> ); }
Properly display QR scanner error messages in the app.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-12 | fail→pass | 9,425 | 13,925 | +48% | 1 | 1 | 0% | 1,794 | 2,931 | +63% | 0 | 0 | — |
case-01 | fail→pass | 39,728 | 43,772 | +10% | 1 | 1 | 0% | 7,331 | 9,393 | +28% | 0 | 0 | — |
case-02 | fail→fail | 42,935 | 44,640 | +4% | 1 | 1 | 0% | 8,236 | 9,385 | +14% | 0 | 0 | — |
case-03 | fail→pass | 24,679 | 36,304 | +47% | 1 | 1 | 0% | 4,952 | 8,110 | +64% | 0 | 0 | — |
case-04 | fail→pass | 19,172 | 25,440 | +33% | 1 | 1 | 0% | 3,762 | 5,746 | +53% | 0 | 0 | — |
case-05 | pass→pass | 13,703 | 4,853 | -65% | 1 | 1 | 0% | 2,473 | 2,073 | -16% | 0 | 0 | — |
case-06 | fail→pass | 23,247 | 12,307 | -47% | 1 | 1 | 0% | 3,402 | 3,606 | +6% | 0 | 0 | — |
case-13 | pass→fail | 16,235 | 14,253 | -12% | 1 | 1 | 0% | 3,077 | 3,757 | +22% | 0 | 0 | — |
case-07 | pass→pass | 10,408 | 5,021 | -52% | 1 | 1 | 0% | 1,738 | 1,895 | +9% | 0 | 0 | — |
case-08 | fail→pass | 17,545 | 14,138 | -19% | 1 | 1 | 0% | 2,476 | 2,944 | +19% | 0 | 0 | — |
case-09 | pass→pass | 10,579 | 4,329 | -59% | 1 | 1 | 0% | 1,902 | 2,035 | +7% | 0 | 0 | — |
case-10 | fail→pass | 8,555 | 8,228 | -4% | 1 | 1 | 0% | 1,364 | 1,801 | +32% | 0 | 0 | — |
case-11 | fail→fail | 14,522 | 16,217 | +12% | 1 | 1 | 0% | 2,611 | 3,330 | +28% | 0 | 0 | — |
case-14 | fail→fail | 6,864 | 17,870 | +160% | 1 | 1 | 0% | 985 | 1,681 | +71% | 0 | 0 | — |
case-15 | fail→pass | 6,202 | 2,633 | -58% | 1 | 1 | 0% | 1,222 | 1,555 | +27% | 0 | 0 | — |
case-16 | pass→pass | 18,204 | 12,928 | -29% | 1 | 1 | 0% | 2,085 | 2,140 | +3% | 0 | 0 | — |
case-17 | fail→pass | 22,743 | 14,290 | -37% | 1 | 1 | 0% | 3,291 | 3,267 | -1% | 0 | 0 | — |
case-18 | fail→pass | 20,786 | 20,286 | -2% | 1 | 1 | 0% | 2,966 | 4,404 | +48% | 0 | 0 | — |
case-19 | fail→pass | 18,600 | 4,109 | -78% | 1 | 1 | 0% | 2,430 | 1,895 | -22% | 0 | 0 | — |
case-20 | pass→fail | 23,731 | 58,558 | +147% | 1 | 1 | 0% | 3,901 | 9,371 | +140% | 0 | 0 | — |
case-21 | pass→pass | 24,688 | 91,370 | +270% | 1 | 1 | 0% | 4,121 | 8,166 | +98% | 0 | 0 | — |
case-22 | pass→fail | 22,732 | 89,026 | +292% | 1 | 1 | 0% | 4,711 | 9,381 | +99% | 0 | 0 | — |
case-23 | pass→pass | 20,999 | 46,259 | +120% | 1 | 1 | 0% | 4,257 | 9,376 | +120% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +35 percentage points is the difference between those two pass rates over the 23 comparable cases. 3 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.