Skip to content

Latest commit

Β 

History

History
497 lines (386 loc) Β· 24.9 KB

File metadata and controls

497 lines (386 loc) Β· 24.9 KB

Meow Capture β€” A secure offline QR capture companion app for air-gapped file transfer

License React Native Security

Optical air-gap file transfer companion for meow-decoder. Scans animated QR code GIFs from any screen using your phone camera and exports the captured frame data as a structured JSON file for desktop decryption.

No network. No cloud. No traces.


πŸ“₯ Download & Install

Android β€” Available Now

Version Download Notes
v3.2.2 (latest) ⬇ Download APK Bug fixes: capture init + camera guard
v3.2.0 Download APK β€”

Sideload instructions:

  1. On your Android device: Settings β†’ Apps β†’ Special app access β†’ Install unknown apps β†’ allow your browser.
  2. Tap the download link above on your phone.
  3. Open the downloaded .apk and tap Install.
  4. Launch Meow Capture and grant camera permission when prompted.
  5. On your desktop, run meow-encode (or open the web demo) and display the QR code on screen.
  6. In the app, tap Scan Request QR or Import Capture Request (JSON) to begin.

Google Play Store listing coming soon β€” sideloading is the only install method for now.

iOS β€” Coming Soon

The iOS version is in active development. Apple App Store listing coming soon. iOS users can scan QR codes today via the web demo in Safari.

App Store Roadmap

Platform Status
Android sideload βœ… Available β€” see download links above
Google Play Store πŸ”œ Coming soon
iOS (App Store) πŸ”œ Coming soon

v3.2 (2026) β€” Capture Quality Coach, Calibration Wizard (5-step preflight: camera, QR visibility, light, brightness, thermal), Settings screen (Strict / Convenience security mode), Diagnostics Panel (long-press version badge, safe-to-share diagnostics export), one-tap sanitized debug bundle export on Export screen, Export SHA-256 + filename copy, Request QR scanner, enriched session resume banner, decode-rate / duplicate-rate live metrics, VoiceOver milestone announcements, and meow_decoder.merge multi-device capture merge CLI. Video import hook is present but feature-flagged OFF (hidden from release UI). 274/274 tests, strict TypeScript, zero network permissions.

v3.1 (2026) β€” Full accessibility + polish pass. Respects Reduce Motion system preference (SplashScreen, FrameOverlay, CatToast). VoiceOver/TalkBack announces toasts (accessibilityLiveRegion). KeyboardAvoidingView on Home; error banners announced; haptics on file load; stale errors cleared on re-focus. OnboardingScreen shows "Open Settings" recovery when camera permission is denied. Android hardware back button prompts confirmation before discarding an active capture session. 274/274 tests, strict TypeScript.

v3 (2026) β€” Major UX hardening. SVG arc progress ring with fountain-threshold indicator. Adaptive frame-rate scanning (60 Hz β†’ 10 Hz back-off). Stall detector toasts when no new frames arrive for 4 s. Pause / resume capture mid-session. Panic wipe via 3-second long-press cancel. Clipboard auto-wipe after export. Universal-link / deep-link support (meow://capture?…). VoiceOver improvements on StabilityIndicator.

v2 (2026) β€” Production-hardened release. Native VisionCamera v4 scanner, biometric export gate, FLAG_SECURE screenshot blocking, pinch-to-zoom + exposure control, haptic feedback, dynamic type, and full strict TypeScript coverage.


Prerequisites

Tool Version
Node.js β‰₯ 20
React Native CLI 0.73.4
Xcode β‰₯ 15 (iOS)
Android Studio β‰₯ Iguana (Android)
Ruby β‰₯ 3.0 (iOS CocoaPods)
Java JDK 17+ (Android)

System libraries

macOS (iOS):

brew install cocoapods

libzbar (not required) β€” QR decoding is handled on-device by the OS (MLKit on Android, AVFoundation on iOS) via VisionCamera v4's native useCodeScanner. No additional system libraries needed.


Quick Start

# Install JS dependencies
cd mobile
npm install

# iOS
cd ios && pod install && cd ..
npx react-native run-ios

# Android
npx react-native run-android

Usage Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Desktop (web demo or CLI encoder)                      β”‚
β”‚  Opens wasm_browser_example.html β€” choose a mode:      β”‚
β”‚    Standard / FS / SchrΓΆdinger / Hybrid-PQ / Duress     β”‚
β”‚  β†’ Displays QR code (static or animated GIF) on screen  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚ Camera (optical, air-gapped)
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  MeowCapture (this app)                                 β”‚
β”‚  1. Home  β€” load capture request JSON (session params)  β”‚
β”‚  2. Camera β€” aim at screen, app scans QR frames         β”‚
β”‚     β€’ Single QR: immediately captured & complete        β”‚
β”‚     β€’ Fountain GIF: scan until progress bar fills       β”‚
β”‚  3. Export β€” save captured_frames.json to Downloads     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚ USB/ADB transfer or manual copy
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Desktop (meow-decoder or web demo decrypt tab)         β”‚
β”‚  $ meow-decode-gif -i captured_frames.json -p "pass"   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Step-by-step

  1. Open the web demo on the desktop (examples/wasm_browser_example.html or web_demo/wasm_browser_example_FULL.html). Choose any encryption mode: Standard, Forward Secrecy, SchrΓΆdinger, Post-Quantum, or Duress.

  2. Encrypt a file or message in the demo. For multi-frame (animated QR), the payload is too large for one code and will be fountain-coded automatically. For single-frame, a static QR appears.

  3. Generate a capture request (or enter manually in the app):

    • Multi-frame: set expected_frames to the droplet count shown in the demo log.
    • Single-frame: set expected_frames: 1.
    # CLI alternative
    meow-encode --print-request -i file.pdf
  4. Load the request in the app β€” tap "Load JSON File" on the Home screen, or enter the session UUID and frame count manually.

  5. Point your camera at the QR on screen. The app shows a Calibration Wizard first β€” a quick 5-step preflight that verifies camera permission, QR readability, light conditions, screen brightness, and device temperature. You can skip it if conditions are clearly good.

    • Single-frame modes: app captures the QR and immediately completes.
    • Fountain animated GIF: hold steady until the progress bar reaches 100%.
  6. Confirm & Export β€” tap Confirm & Export on the Export screen. If Face ID / fingerprint is enrolled, biometric confirmation is required before any data is written to disk. Transfer meow_capture_<session_id>.json back to the desktop via USB.

  7. Debug bundle (optional) β€” from the Export screen, tap Export Debug Bundle to generate a sanitized diagnostics file. This contains only metadata (app version, device info, capture stats, error history) β€” no payloads, passwords, or sensitive content. Safe to share for troubleshooting.

  8. Decrypt β€” paste the captured JSON into the web demo's decrypt tab, or use the CLI:

    meow-decode-gif -i meow_capture_<session_id>.json -p "password"
  9. Multi-device merge (optional) β€” if multiple phones captured the same transfer:

    # Merge two captures for maximum frame coverage before decoding
    python -m meow_decoder.merge \
        --input capture-phone-a.json capture-phone-b.json \
        --output merged.json
    
    # Then decode the merged file
    meow-decode-gif -i merged.json -p "password"

    The merge tool deduplicates frame indices and recalculates the coverage ratio. All inputs must share the same session_id; mismatched sessions are rejected.


Supported QR Modes

The app recognises every QR format produced by the web demo (wasm_browser_example.html, wasm_browser_example_FULL.html) and the Python CLI encoder.

Multi-frame (fountain coded β€” animated GIF)

Format Prefix Source Notes
Fountain FOUNTAIN:<k>:<block_size>:<length>:<b64> Web demo animated QR / CLI Each frame is a fountain droplet. Capture any ~67% of frames to decode. App auto-completes at ceil(expected_frames Γ— 1.5).

Single-frame (static QR β€” scan once)

Format Prefix Web Demo Mode Notes
Standard MEOW:<b64> Standard / Normal AES-256-GCM password encryption.
Forward Secrecy FS:<b64> Forward Secrecy X25519 ephemeral key exchange (MEOW3).
SchrΓΆdinger QUANTUM:<b64> SchrΓΆdinger Dual-secret plausible deniability.
Post-Quantum Hybrid HYBRID-PQ:<b64> Post-Quantum ML-KEM-768/1024 + X25519 (MEOW5/MEOW4).
Duress DURESS:<b64> Duress Panic-password aware; reveals decoy on wrong key.

Legacy chunked (web demo older animated format)

Format Prefix Notes
MEOW chunks MEOW-N/total:<b64> Split MEOW: payload across N QR frames. Index = N-1.

CLI bridge (JSON envelope)

Format Shape Notes
JSON {"index": N, "data": "<b64>", "session_id"?: "..."} Used by meow-encode --mobile-bridge.

Behaviour by format type:

  • Single-frame (MEOW:, FS:, QUANTUM:, HYBRID-PQ:, DURESS:): capture session auto-starts and auto-completes on the first valid scan. Set expected_frames: 1 in the capture request.
  • Fountain / multi-frame: camera stays active until fountain threshold is met (captured >= ceil(expected_frames Γ— 1.5)).
  • Unknown prefixes (e.g. arbitrary QR codes in the environment) are silently ignored β€” only meow-format strings trigger the capture state machine.

Capture Request JSON Format

The app validates all incoming requests with Zod strict schema. Extra fields are rejected.

{
  "action": "capture",
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "expected_frames": 45,
  "timeout_seconds": 120
}
Field Type Required Constraints
action "capture" βœ“ Must be exactly "capture"
session_id UUID v4 βœ“ Validated as UUID regex
expected_frames integer βœ“ 1 for single-frame modes; fountain frame count for multi-frame
timeout_seconds integer β€” 1–600, defaults to 60

Output JSON Format

{
  "schema_version": "1",
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "captured_at": "2024-01-15T10:30:00.000Z",
  "elapsed_ms": 45230,
  "total_frames": 47,
  "frames": [
    { "index": 0, "data": "FOUNTAIN:10:800:4523:AAB...", "timestamp_ms": 1705312200123 },
    { "index": 1, "data": "FOUNTAIN:10:800:4523:CCF...", "timestamp_ms": 1705312200223 }
  ]
}

The data field contains the raw QR string including its format prefix:

Capture mode Example data value
Fountain multi-frame FOUNTAIN:10:800:4523:AABg...
Standard MEOW:AABg...
Forward Secrecy FS:AABg...
SchrΓΆdinger QUANTUM:AABg...
Post-Quantum HYBRID-PQ:AABg...
Duress DURESS:AABg...
Legacy chunked MEOW-1/5:AABg...
CLI bridge {"index":0,"data":"..."} (stored as-is)

The desktop decoder identifies the encryption mode from the prefix and dispatches accordingly.


Permissions

Permission Platform Why
CAMERA Android + iOS Scan QR codes from screen β€” no images ever stored or transmitted
WRITE_EXTERNAL_STORAGE Android ≀ 9 Write export JSON to Downloads folder
VIBRATE Android Haptic feedback: progress ticks, milestone pops, export outcome
USE_BIOMETRIC + USE_FINGERPRINT Android Biometric export gate (falls back gracefully if not enrolled)

Explicitly not requested: INTERNET, RECORD_AUDIO, ACCESS_FINE_LOCATION, READ_CONTACTS, READ_EXTERNAL_STORAGE, BLUETOOTH, or any permission not in the table above.

The INTERNET permission is deliberately absent from AndroidManifest.xml β€” the OS enforces zero network access at the platform level, not just in application code.


Security Model

Control Implementation
Zero network No INTERNET permission β€” OS-enforced, not application-level
Screenshot blocking FLAG_SECURE in MainActivity.onCreate β€” blocks screenshots, screen recording, and task-switcher thumbnail on Android
iOS task-switcher defense isBackgrounding renders a solid privacy overlay when applicationWillResignActive fires, before the OS captures its snapshot
Biometric export gate react-native-biometrics prompts Face ID / fingerprint / PIN before writing any data to disk
Memory wipe on background AppState listener dispatches RESET (clears all frames from React state) on background or inactive
Foreground recovery On returning from background the app navigates to Home β€” the wiped session cannot be resumed
Panic wipe 3-second long-press on Cancel triggers immediate RESET + navigation to Home β€” no confirmation needed
Android back guard Hardware back during CAPTURING / AWAITING_GIF / PAUSED triggers a confirmation alert before discarding frames
Clipboard wipe Export JSON string is removed from the clipboard 45 s after sharing
Explicit export only ExportScreen shows a confirmation card; no auto-export on mount
Input validation Every capture request validated with Zod .strict() schema; extra fields and malformed UUIDs rejected
No decryption on device Frame data stored as opaque base64 strings; all crypto operations happen on the desktop
No image retention VisionCamera useCodeScanner passes only decoded string values to JS β€” camera frames never reach app memory
audio={false} Microphone disabled on the <Camera> component

Camera Controls

The capture screen exposes live controls for real-world scanning conditions:

Control How to use Purpose
Pinch to zoom Standard two-finger pinch Move further from screen; range 1Γ— – 6Γ— (capped to preserve decode quality)
Exposure βˆ’ / + Tap β˜€οΈβˆ’ or β˜€οΈ+ buttons βˆ’2 … +2 in 0.5 steps; reduce glare from bright screens or boost dim displays
Torch Tap πŸ’‘ button Illuminates surroundings in low light (hardware torch required)
Pause / Resume Tap ⏸ / ▢️ button during CAPTURING Freeze scanning without losing captured frames; resumes from same state
Stop Tap ⏹ button Finalises early β€” exports whatever frames have been collected
Panic wipe Long-press Cancel for 3 s Instantly wipes all captured frames and navigates Home β€” for high-pressure situations
Stability indicator Automatic Accelerometer warns when device motion may cause motion blur
Stall detector Automatic Toasts after 4 s with no new frames β€” prompts you to adjust camera position

Development

# Install JS dependencies
npm install

# Run Jest unit tests (274 tests)
npm test

# TypeScript strict type check (zero errors)
npm run type-check

# Lint (zero warnings)
npm run lint

# Auto-format
npm run format

# Bump version across all sources (package.json, config.ts, Info.plist)
npm run bump-version 3.3.0

# iOS β€” install CocoaPods then launch
npm run pod-install
npx react-native run-ios

# Android
npx react-native run-android

Project structure

mobile/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ types/
β”‚   β”‚   β”œβ”€β”€ capture.ts          # CaptureRequest, CaptureResponse, CapturedFrame, ExportResult
β”‚   β”‚   β”œβ”€β”€ navigation.ts       # Typed screen props for React Navigation
β”‚   β”‚   └── declarations.d.ts   # Ambient module types (react-native-biometrics)
β”‚   β”œβ”€β”€ constants/
β”‚   β”‚   β”œβ”€β”€ config.ts           # FOUNTAIN_OVERHEAD, milestone thresholds, FPS, dedup timing
β”‚   β”‚   └── theme.ts            # Palette, PixelRatio-scaled typography, spacing, shadows
β”‚   β”œβ”€β”€ utils/                  # base64 validation, formatters (pure, fully tested)
β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”œβ”€β”€ requestValidator.ts # Zod .strict() schema + safeValidateRequest
β”‚   β”‚   β”œβ”€β”€ qrDecoder.ts        # Prefix-based format detection, payload parsing
β”‚   β”‚   β”œβ”€β”€ frameCollector.ts   # Dedup, fountain threshold tracking
β”‚   β”‚   β”œβ”€β”€ jsonExporter.ts     # RNFS write, chunked export, QR fallback chunks
β”‚   β”‚   └── debugBundleExporter.ts # Sanitized debug bundle (no payloads/passwords)
β”‚   β”œβ”€β”€ hooks/
β”‚   β”‚   β”œβ”€β”€ useCapture.ts           # useReducer state machine (IDLEβ†’AWAITING_GIFβ†’CAPTURINGβ†’PAUSEDβ†’COMPLETE)
β”‚   β”‚   β”œβ”€β”€ useQRScanner.ts         # VisionCamera v4 useCodeScanner (MLKit / AVFoundation); adaptive FPS
β”‚   β”‚   β”œβ”€β”€ useStabilityMonitor.ts  # Accelerometer-based shake detection
β”‚   β”‚   β”œβ”€β”€ useStallDetector.ts     # Detects 4 s+ periods with no new frames; fires toast callback
β”‚   β”‚   β”œβ”€β”€ useSessionManager.ts    # Orchestrates capture + scanner + stability
β”‚   β”‚   └── useSecureScreen.ts      # isBackgrounding flag for iOS privacy overlay
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ CameraPreview.tsx       # AnimatedCamera, pinch zoom, exposure bias, torch, privacy overlay
β”‚   β”‚   β”œβ”€β”€ ProgressHUD.tsx         # SVG arc ring with fountain-threshold indicator
β”‚   β”‚   β”œβ”€β”€ FrameOverlay.tsx        # Scan corners, status badges (AWAITING/CAPTURING/PAUSED/COMPLETE), reduce-motion-aware scan line
β”‚   β”‚   β”œβ”€β”€ CatWhiskerHUD.tsx        # Whisker-style motion feedback
β”‚   β”‚   β”œβ”€β”€ CaptureCoachPanel.tsx    # Live coaching hints (distance, brightness, shake)
β”‚   β”‚   β”œβ”€β”€ CalibrationWizard.tsx    # 5-step preflight (camera, QR, light, brightness, thermal)
β”‚   β”‚   β”œβ”€β”€ DiagnosticsPanel.tsx     # Hidden dev diagnostics (long-press version badge)
β”‚   β”‚   β”œβ”€β”€ StabilityIndicator.tsx   # Shake magnitude bar
β”‚   β”‚   └── CatToast.tsx            # Queued slide-up toasts with accessibilityLiveRegion
β”‚   β”œβ”€β”€ screens/
β”‚   β”‚   β”œβ”€β”€ SplashScreen.tsx        # Cat-eye animation; respects Reduce Motion system preference
β”‚   β”‚   β”œβ”€β”€ OnboardingScreen.tsx    # First-run camera permission with Settings recovery on denial
β”‚   β”‚   β”œβ”€β”€ HomeScreen.tsx          # Load capture request (file picker or manual entry); KeyboardAvoidingView
β”‚   β”‚   β”œβ”€β”€ CaptureScreen.tsx       # Live camera, haptics, pause/resume, panic wipe, Android back guard
β”‚   β”‚   └── ExportScreen.tsx        # Biometric-gated confirm β†’ JSON export or QR fallback
β”‚   β”œβ”€β”€ navigation/
β”‚   β”‚   └── AppNavigator.tsx        # NativeStack; gesture disabled on CaptureScreen
β”‚   └── App.tsx                     # GestureHandlerRootView, MeowDarkTheme, CatToastProvider
β”œβ”€β”€ __tests__/              # Jest unit tests β€” pure logic, no render tests
β”œβ”€β”€ __mocks__/              # Native module mocks (VisionCamera, biometrics, RNFS, MMKV, sensors)
β”œβ”€β”€ android/
β”‚   └── app/src/main/
β”‚       β”œβ”€β”€ AndroidManifest.xml       # CAMERA + VIBRATE + USE_BIOMETRIC; NO INTERNET
β”‚       └── java/…/MainActivity.kt   # FLAG_SECURE in onCreate
└── ios/                    # NSCameraUsageDescription only in Info.plist

Troubleshooting

Symptom Likely cause Fix
Blank camera preview Permission denied Settings β†’ Apps β†’ MeowCapture β†’ Permissions β†’ Camera
"Open Settings" button shown on Onboarding Camera permission denied at OS level Tap it β€” leads directly to the app's permission page
Frames not incrementing Camera too far Move 20–40 cm from screen
Low frame count warning Motion blur Use stability indicator; hold phone still
"No new frames" toast Stall detected Shift camera position slightly or adjust exposure
App completes instantly (0 frames shown) expected_frames=0 in request JSON Set expected_frames to the frame count from the demo log
Single-frame mode never auto-completes expected_frames > 1 Set expected_frames: 1 for static MEOW:/FS:/etc. QR codes
Export silently fails Downloads folder full Free storage and retry
Timeout before completion GIF too fast / poor lighting Increase timeout_seconds; use β˜€οΈβˆ’ / β˜€οΈ+ to compensate for glare
"Invalid request" error Extra fields in request JSON Remove unrecognised fields; see schema above
Non-meow QR silently ignored Unknown prefix in environment App only collects meow-prefixed codes; others are dropped
Biometric prompt never shows No biometric enrolled on device Falls back to unguarded export button automatically
Export button not visible Still on confirm card Tap Confirm & Export (or Export to Downloads if no biometrics)
Glare making QR unreadable Bright laptop screen Tap β˜€οΈβˆ’ to reduce exposure bias; tilt phone slightly off-axis
Android back button dismisses capture Expected β€” guarded A confirmation dialog appears before frames are discarded

ADB extract (Android)

If the picker doesn't show the file, pull it directly:

adb pull /sdcard/Download/meow_capture_<session_id>.json ./

Fountain Code Tolerance

Applies to multi-frame fountain coded QRs (FOUNTAIN: prefix) only. Single-frame modes (MEOW:, FS:, QUANTUM:, HYBRID-PQ:, DURESS:) complete in one scan.

For fountain-coded animated GIFs, the app uses a 1.5Γ— redundancy factor. Capture completes automatically when:

captured_frames β‰₯ ceil(expected_frames Γ— 1.5)

This means you can expect successful decryption even with ~33% frame loss due to:

  • Camera motion blur during scanning
  • Screen refresh timing mismatches
  • QR scan failures on low-contrast frames


Key Dependencies

Package Version Role
react-native-vision-camera ^4.0.0 Native QR scanning via useCodeScanner (MLKit / AVFoundation)
react-native-biometrics ^3.0.1 Biometric export gate (Face ID, fingerprint, device PIN fallback)
react-native-reanimated ^3.6.2 UI-thread pinch zoom via useAnimatedProps
react-native-gesture-handler ^2.14.1 Gesture.Pinch() for zoom, swipe-back guard on CaptureScreen
react-native-haptic-feedback ^2.2.0 Progress ticks, milestone pops, export success/error
react-native-sensors ^7.3.6 Accelerometer for stability monitor
react-native-mmkv ^2.12.2 Synchronous first-launch flag (settings only β€” never frame data)
react-native-fs ^2.20.0 Write export JSON to Downloads folder
zod ^3.22.4 Strict capture-request schema validation

Removed in v2: vision-camera-code-scanner (abandoned, used a fragile worklet require() hack, broken on Android 14+ / iOS 17+) and react-native-worklets-core (no longer needed for QR scanning).

Added in v3: @react-navigation/native deep-link support (universal links meow://capture?…). No new runtime dependencies β€” v3 features are built from existing packages (react-native-reanimated SVG arc, react-native-haptic-feedback, built-in BackHandler/Linking APIs).


License

CC BY-NC-SA 4.0 β€” see ../LICENSE