A local-first, offline-capable library manager for Flutter that scans book spines using device cameras for seamless book identification and organization.
- Camera-Based Scanning: Instant camera initialization (<1s cold start) with AI-powered book spine recognition
- Offline-First Design: Fully functional library management without internet connectivity
- Failed Scan Retry System: Persistent queue with automatic and manual retry for network failures
- SQLite Storage with Drift: Robust local database with FTS5 full-text search across titles, authors, and ISBNs
- SSE Job Processing: Real-time integration with Talaria backend via Server-Sent Events for live scan updates
- Swiss Utility Design: OLED black backgrounds, 1px borders, zero elevation, high-contrast interfaces
- ProMotion 120Hz Support: Butter-smooth animations optimized for iPhone Pro displays
- Native iOS Gestures: Swipe-back navigation, context menus, and iOS-native haptic feedback
- Advanced Camera Controls: Night Mode, depth sensing, focus/exposure lock for challenging lighting
- iOS Home Screen Widget: At-a-glance library stats and recent scans on your home screen
- Haptic Feedback Strategy: Light/medium/heavy haptics for shutter, success, and errors
- Collections & Tags: Organize books into custom collections (To Read, Favorites, etc.)
- Multi-Sort & Filters: Sort by date, title, author, confidence; filter by format, review status, date range
- Statistics Dashboard: Track total books, scan streaks, top authors, scanning sessions
- Manual Metadata Editing: Correct misidentified titles, authors, ISBNs with inline editing
- Session Gamification: Real-time scan counter with milestone celebrations
- Sentry Crash Reporting: Production monitoring with privacy-respecting error tracking
- Performance Monitoring: Cold start, shutter latency, and processing time metrics
- Memory Optimized: Leak prevention, cache management, iOS memory pressure handling
- TestFlight Ready: Complete beta testing setup with tester onboarding materials
- Flutter: Cross-platform UI framework
- Dart: Programming language (SDK 3.10.7+)
- Drift 2.0: SQLite ORM for database operations
- Riverpod 3.0: State management
- Camera Plugin: For device camera access
- HTTP/SSE: Backend communication with Talaria via HTTP and Server-Sent Events
- Google Fonts: Inter for UI text, JetBrains Mono for code/ISBNs
- Image Processing: Background isolate compression and resizing
- Flutter SDK (version 3.0 or later)
- Dart SDK 3.10.7+ (included with Flutter)
- For iOS: macOS with Xcode 14+ and an iOS device or simulator
- For Android: Android SDK and an emulator or device
-
Clone the repository:
git clone <repository-url> cd wingtip
-
Install Flutter dependencies:
flutter pub get
-
Run Drift code generation:
flutter pub run build_runner build --delete-conflicting-outputs
-
Verify your setup:
flutter doctor
iOS (Primary Target):
flutter run -d iPhoneAndroid:
flutter runDevelopment Mode:
flutter run --verboseThe project follows a modular structure under the lib/ directory:
core/: Core utilities, network clients, theme definitions, device ID managementdata/: Data layer including Drift database schemas, models, and DAOsfeatures/: Feature-specific modules (camera, library, book management, search)services/: External services integration (Talaria backend, SSE streams, image processing)widgets/: Reusable UI components following Swiss Utility design principles
Run the app:
flutter runRun tests:
flutter testRun tests with coverage:
flutter test --coverageGenerate Drift database code:
flutter pub run build_runner build --delete-conflicting-outputsWatch for changes (auto-regenerate):
flutter pub run build_runner watchStatic analysis:
flutter analyzeBuild for release:
# iOS (primary target)
flutter build ios --release
# Android
flutter build apk --releaseGenerate app icons:
flutter pub run flutter_launcher_iconsGenerate splash screens:
flutter pub run flutter_native_splash:createFollow Dart's effective Dart guidelines. Use dart format for formatting and flutter analyze for linting.
Wingtip employs a layered architecture focused on local-first design:
Riverpod 3.0 for dependency injection and reactive state handling:
- UI components use
ConsumerWidgetorConsumerto access providers - Business logic lives in Notifier classes (e.g.,
JobStateNotifier) - Database streams are exposed via
StreamProvider - Manual provider definitions (generator packages not yet added)
Drift ORM interacts with SQLite for local storage:
- Full-text search with FTS5 virtual tables
- Watch streams for reactive UI updates
- Schema migrations for version management
- Books table with ISBN as primary key
HTTP and SSE streams connect to the Talaria backend:
- Multipart image upload to
/v3/jobs/scans - SSE stream for real-time job updates (progress, result, complete, error)
- Device-ID based authentication via
X-Device-IDheader - Exponential backoff retry logic for connection failures
Swiss Utility design principles:
- OLED black backgrounds (#000000)
- International Orange accent (#FF3B30)
- 1px solid borders instead of shadows
- Zero elevation on all components
- Haptic feedback at every interaction stage (iOS-focused)
- Capture - User taps shutter → Image saved to temp cache → Background isolate compresses/resizes
- Upload - Multipart POST to
/v3/jobs/scanswithX-Device-IDheader - Listen - Connect to SSE stream at returned
streamUrl - Ingest - SSE events (
progress,result,complete,error) trigger UI updates and database upserts - Display - Library auto-refreshes via Drift's
.watch()streams
- Library is 100% functional offline (viewing, searching, deleting, editing)
- Only camera scanning requires network connection
- Network status shown in top-right corner when offline
- Failed Scan Queue: Failed uploads are automatically saved with persistent retry queue
- Manual retry for individual scans
- Batch retry for all failed scans
- Automatic retry prompt when network reconnects
- Configurable retention period (3/7/14/30 days or never)
- Failed scans preserved with original images for later retry
- Detailed analytics showing failure reasons (network, quality, no books found, etc.)
Wingtip has a comprehensive test suite with a simple test runner script for pre-commit validation.
Use the test runner script for quick validation before committing:
# Quick smoke test (~30 seconds) - Integration tests only
./scripts/test.sh quick
# Full safety net (~2-3 minutes) - Integration + widget tests [DEFAULT]
./scripts/test.sh
# Everything (~3-5 minutes) - Static analysis + all tests
./scripts/test.sh allThe scripts/test.sh script provides colored output (green for pass, red for fail) and multiple modes:
quick- Integration tests only (~30 sec) - Best for quick pre-commit checksfull- Integration + widget tests (~2-3 min) - Recommended safety net [DEFAULT]all- Analyze + all tests (~3-5 min) - Complete validation before pushunit- Unit tests only (core/data/services) - For backend logic changeswidget- Widget tests only (features) - For UI changes
Examples:
./scripts/test.sh quick # Quick pre-commit check
./scripts/test.sh # Full safety net (default)
./scripts/test.sh all # Complete validation
./scripts/test.sh unit # Unit tests only
./scripts/test.sh widget # Widget tests onlyRun tests manually with Flutter commands:
# Run all tests
flutter test
# Run specific test file
flutter test test/path/to/test_file.dart
# Run integration tests only
flutter test test/integration/
# Run widget tests only
flutter test test/features/
# Run with coverage
flutter test --coverageThe test suite is organized by type:
test/integration/- Critical flow tests (scan → save → display) - ~30 secondstest/features/- Widget tests for UI components - ~1-2 minutestest/core/- Unit tests for network, SSE, device ID - ~10 secondstest/data/- Database and repository tests - ~10 secondstest/services/- Background service tests - ~10 seconds
See test/README.md for detailed testing patterns, mocking strategies, and examples.
- Database queries and FTS5 full-text search
- SSE event parsing and job state management
- Failed scan retry logic and cleanup
- Collections and metadata editing
- Camera screen and library UI components
- Mock providers using Riverpod's testing utilities
- Cold start: < 800ms to live camera (target met in Epic 3)
- Shutter latency: < 30ms tap-to-haptic feedback (target met in Epic 3)
- Image processing: < 500ms per image (background isolate)
- ProMotion: 120fps on iPhone Pro devices with zero dropped frames
- Memory footprint: < 200MB for typical usage (100+ books, active scanning)
- SSE timeout: 5 minutes maximum job duration
- Image specs: Resized to max 1920px, compressed to JPEG quality 85
- CLAUDE.md: Comprehensive development guide for AI assistants and developers
- TODO.md: Current status, Epic 3 completion summary, launch readiness checklist
- docs/INDEX.md: Documentation index and quick links
- tasks/prd-epic3.json: Complete product requirements (72 user stories)
- CRASH_REPORTING.md: Sentry integration and crash monitoring setup
- TESTFLIGHT_SETUP.md: Beta testing configuration guide
- BETA_TESTER_EMAIL.md: Welcome email template for beta testers
- PRIVACY.md: Privacy policy for App Store submission
- SUPPORT.md: User support documentation
- WIDGET_SETUP.md: iOS Home Screen widget setup and Xcode configuration
- docs/PROMOTION_120HZ.md: ProMotion 120Hz optimization guide and debugging
- assets/app-store/: Screenshots, app preview video script, metadata, submission checklist
- Fork the repository and create a feature branch
- Write tests for new features
- Ensure code passes
flutter analyzeandflutter test - Follow commit conventions (e.g., "feat: add camera scanning")
- Submit a pull request with a detailed description
Priorities:
- iOS compatibility is mandatory
- Maintain Swiss Utility design principles
- All features must work offline-first
See LICENSE file for details.
Platform Priority: This app is developed iOS-first. All development decisions, performance targets, and UX patterns prioritize iOS. Android and web are secondary targets.