Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f347bea
video checkpoint
cwbooth5 Feb 2, 2026
7ac7bdf
fix(a11y): add accessibility labels to all camera controls
cwbooth5 May 26, 2026
3f2a12c
fix(a11y): correct camera switch and gallery accessibility labels per…
cwbooth5 May 26, 2026
72c9b52
fix(a11y): add accessibility labels to PIN entry screens
cwbooth5 May 26, 2026
573a054
fix(a11y): add accessibility labels to gallery cells and actions
cwbooth5 May 26, 2026
f32c82b
fix(a11y): hide decorative icons from VoiceOver in security overlays …
cwbooth5 May 26, 2026
43928e7
fix(a11y): replace hardcoded icon font sizes with .title3 in photo tools
cwbooth5 May 26, 2026
6c19871
fix(a11y): replace hardcoded font sizes with Dynamic Type styles in s…
cwbooth5 May 26, 2026
b60fef3
fix(a11y): replace hardcoded font sizes in PIN and onboarding screens
cwbooth5 May 26, 2026
62d6fe7
fix(a11y): replace remaining hardcoded font sizes with Dynamic Type s…
cwbooth5 May 26, 2026
387a4b5
fix(a11y): replace remaining camera and detail hardcoded font sizes
cwbooth5 May 26, 2026
ba71a60
fix(ux): add haptic feedback to shutter, recording, and PIN entry
cwbooth5 May 26, 2026
07a02d7
fix(swiftui): replace foregroundColor→foregroundStyle and cornerRadiu…
cwbooth5 May 26, 2026
0dd08dc
fix(swiftui): replace deprecated NavigationView with NavigationStack
cwbooth5 May 26, 2026
fe5ad6a
fix(swiftui): replace UIImpactFeedbackGenerator with .sensoryFeedback…
cwbooth5 May 26, 2026
c78bc00
fix(nav): remove nested NavigationStack from wizard, ImageInfo, and P…
cwbooth5 May 26, 2026
f574c6f
fix(nav): avoid double-dismiss black screen after decoy save
cwbooth5 May 30, 2026
9004965
fix(security): destroy non-decoy videos on poison pill activation
cwbooth5 May 30, 2026
a4992de
test: clean up orphaned test files; fold valid suites into the target
cwbooth5 May 30, 2026
9971b72
feat(security): support decoy videos so non-decoy videos are destroyed
cwbooth5 May 30, 2026
79fe8a5
feat(gallery): show progress spinner while saving decoy media
cwbooth5 May 30, 2026
9e3e1a7
feat(gallery): show real thumbnails for videos
cwbooth5 May 30, 2026
92b5ef5
refactor(gallery): remove dead SecureGalleryViewModel
cwbooth5 May 30, 2026
d21bc50
ci: guard that every test source file is a member of its test target
cwbooth5 May 30, 2026
fd97090
docs(readme): document fastlane build/test/release and the test-membe…
cwbooth5 May 30, 2026
dbc5c8f
fix(security): decoy videos were silently never created -> poison pil…
cwbooth5 May 30, 2026
b0fe93b
fix(video): decrypt over-read the final chunk -> fileIOError (broke s…
cwbooth5 May 30, 2026
dd72a7c
fix(security): keep decoy video thumbnails through the poison pill
cwbooth5 May 31, 2026
2159c54
feat(gallery): import videos from the photo library (encrypted)
cwbooth5 May 31, 2026
638b13f
style(camera): Liquid Glass control buttons
cwbooth5 May 31, 2026
62cb10b
feat(gallery): swipe through photos and videos together in detail view
cwbooth5 May 31, 2026
3c849ba
feat(video): glass inline player on the detail pager with reliable co…
cwbooth5 May 31, 2026
f8c09d0
fix(security): add file protection to wrapped DEK files
cwbooth5 May 31, 2026
dca6dfd
fix(security): propagate key-derivation errors instead of crashing
cwbooth5 Jun 1, 2026
3e5a823
fix(compiler): audioInput is nonisolated
cwbooth5 Jun 1, 2026
657ffda
fix(security): delete hardware keys and await eviction in security reset
cwbooth5 Jun 1, 2026
b849819
fix(security): use monotonic clock for session timeout and PIN backoff
cwbooth5 Jun 1, 2026
b6f2e25
fix(security): short-circuit poison-pill PIN verification
cwbooth5 Jun 1, 2026
eb9cd80
fix(security): delete dead stub-cipher-key plumbing
cwbooth5 Jun 1, 2026
b5f9ad3
fix(security): bind the PIN cryptographically to DEK unwrap (C1)
cwbooth5 Jun 1, 2026
0ac7655
fix(security): single-writer failed-attempt counter (M1)
cwbooth5 Jun 1, 2026
e4516c4
fix(security): complete file protection on the settings file (H2, par…
cwbooth5 Jun 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .claude/apple-design-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Apple Design Context

## Product
- **Name**: SnapSafe
- **Description**: Privacy-focused camera app that encrypts photos and videos locally using AES-256-GCM; no cloud, no leaks
- **Category**: Photography (public.app-category.photography)
- **Stage**: Active development (v1.3.0, shipping)

## Platforms
| Platform | Supported | Min OS | Notes |
|----------|-----------|--------|-------|
| iOS | Yes | 18.5 | Portrait-only (locked) |
| iPadOS | Yes | 18.5 | All orientations; just added in v1.3.x |
| macOS | No | — | Catalyst disabled |
| tvOS | No | — | |
| watchOS | No | — | |
| visionOS | No | — | |

## Technology
- **UI Framework**: SwiftUI (primary) + UIKit (UIViewRepresentable for AVFoundation camera preview)
- **Architecture**: Single-window, custom programmatic NavigationStack (AppNavigationState)
- **Apple Technologies**: AVFoundation, AVKit, CryptoKit, Security (Secure Enclave), CoreLocation, Vision (face detection), AppIntents (Action Button), Photos/PhotosUI

## Design System
- **Base**: Custom; no design system library
- **Accent Color**: #3DDC84 (brand green) — no dark mode variant defined in asset catalog
- **Typography**: Mix of `.font(.system(size: X))` hardcoded sizes (60+ instances) and semantic styles (`.body`, `.caption`, etc., 74 instances) — inconsistent
- **Dark Mode**: User-selectable (system/light/dark) via Settings; `preferredColorScheme` applied at root
- **Dynamic Type**: Not supported — hardcoded font sizes do not scale

## Accessibility
- **Target Level**: Baseline (aspirational)
- **Current State**: **None** — zero `.accessibilityLabel`, `.accessibilityHint`, or `.accessibilityValue` modifiers found in the entire app
- **Key Considerations**: VoiceOver unusable; camera controls, gallery cells, and PIN entry all unlabeled
- **Regulatory**: No known regulatory requirements stated

## Users
- **Primary Persona**: Privacy-conscious individuals who want to capture sensitive photos/videos without risk of cloud upload, screenshot capture, or unauthorized access
- **Key Use Cases**: Capture photo/video → stored encrypted locally → view in secure gallery → optionally share (decrypted) → security features (PIN, poison pill, privacy shield)
- **Known Challenges**: High security requirements create UX tension; PIN entry must be custom (no system keyboard for screenshots); camera access is the primary surface and must feel fast and trustworthy
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,9 @@ Configs/LocalOverrides.xcconfig
vendor/

# fastlane snapshot
screenshots/
screenshots/

SecureCameraAndroid/

# Local TODO scratch
TODO.md
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ GEM
faraday-em_synchrony (1.0.1)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.1.1)
faraday-multipart (1.2.0)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
faraday-retry (1.0.4)
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.4.0)
Expand Down
Loading
Loading