Skip to content

feat: v1.3.0 — desktop app, on-device AI, attachment browser, specialty cards#69

Open
beniamin-szilagyi wants to merge 119 commits into
release/1.3.0from
dev/desktop/v1.3.0
Open

feat: v1.3.0 — desktop app, on-device AI, attachment browser, specialty cards#69
beniamin-szilagyi wants to merge 119 commits into
release/1.3.0from
dev/desktop/v1.3.0

Conversation

@beniamin-szilagyi

@beniamin-szilagyi beniamin-szilagyi commented May 5, 2026

Copy link
Copy Markdown
Collaborator

What's new in v1.3.0

AI document processing on Mac

HealthWallet can now process your health documents entirely on your Mac — no internet connection required. When you import a scan or PDF, the on-device AI model (Qwen3-VL) reads the document, extracts structured health data, and maps it to FHIR records automatically. The Mac's hardware is used directly, bypassing the need to offload to a mobile device. You can also trigger processing from your iPhone and have your Mac do the heavy lifting via the Handover flow — useful when your phone doesn't have enough memory for inference.

Sync with your iPhone via QR code

Pair your iPhone and Mac once by scanning a QR code shown on the desktop app. After pairing, the two devices stay in sync automatically over your local network — no iCloud, no third-party servers. Any health records you add, edit, or delete on one device propagate to the other using a Last-Write-Wins sync protocol with conflict resolution. The connection uses AES-256-GCM encryption over TCP, with MultipeerConnectivity as a fallback when a VPN or firewall blocks direct LAN access.

New way to browse your health documents

The records browser now offers two modes: Attachments view shows your documents as a scrollable visual timeline with thumbnail previews, a year/month scrubber, and a full detail panel — ideal for quickly locating a specific scan or lab report by date. Records list view shows the structured FHIR data extracted from your documents, grouped by type. You can switch between modes freely and share records directly from either view.

Organize by specialty

Your health dashboard now groups records by medical specialty — Cardiology, Oncology, Neurology, Orthopedics, and more. Each specialty card on the home screen shows a count of relevant records and lets you jump straight to a filtered view. Cards are fully reorderable so you can surface the specialties most relevant to you. The classifier runs locally against your existing records and updates automatically as new documents are processed.


Test plan

  • Import a PDF on Mac — AI processes it and creates FHIR records without internet
  • Handover: trigger processing from iPhone, confirm Mac runs the pipeline and syncs results back
  • Pair iPhone and Mac via QR code; edit a record on one device and confirm it appears on the other
  • MPC fallback: connect while VPN is active and confirm sync still works
  • Attachments view: timeline scrubber navigates by year/month; thumbnails render correctly
  • Records list view: records grouped by type, share flow launches correctly from list
  • Ephemeral viewer: thumbnails/timeline scrubber sit above SessionBottomBar on tablet
  • Specialty cards appear on home dashboard; reordering persists across restarts
  • Specialty classifier assigns correct specialty to Cardiology, Oncology, Neurology records
  • System tray icon visible on macOS/Windows; minimize-to-tray and restore work
  • Incoming Health Records dialog and SelectionBottomBar are constrained width on tablet
  • l10n strings display correctly in DE/ES/RO

…m-aware navigation

- Add AppPlatform enum with desktop/mobile detection
- Add main_desktop.dart entry point with desktop DI registration
- Enable macOS and Linux platform runners
- Desktop dashboard shows 4 tabs: Home, Records, Import, Backup
- Skip P2P discovery and receive mode on desktop
- Add BackupRoute to app router
- Add BMAD config, memory, and core templates
- Add project context, PRD, epics, and sprint status
- Add implementation artifact stubs for desktop stories
…m-aware navigation

- Add AppPlatform enum with desktop/mobile detection
- Add main_desktop.dart entry point with desktop DI registration
- Enable macOS and Linux platform runners
- Desktop dashboard shows 4 tabs: Home, Records, Import, Backup
- Skip P2P discovery and receive mode on desktop
- Add BackupRoute to app router
…encrypted TCP

- Add BackupBloc with pairing state management
- Implement QR-based pairing with DevicePairing model
- TCP service with AES-256-GCM encryption and keepalive
- Device discovery via mDNS (_healthwallet._tcp) and SSDP (UPnP)
- Desktop: shows QR code, runs TCP server, advertises via mDNS/SSDP
- Mobile: scans QR, discovers desktop, connects as TCP client
- PairingStorageService for persistent pairing via SharedPreferences
…firewall, encryption

- Fix BackupBloc not registered in GetIt on both mobile and desktop entry points
- Fix permission_handler MissingPluginException on macOS by guarding for desktop platforms
- Fix TCP server using isolate to bypass Flutter macOS engine socket issue
  where ServerSocket in main isolate silently drops external connections
- Fix discovery probe (Socket.connect + destroy) poisoning server state
  by removing TCP probe — discovery now returns saved IP directly
- Fix BackupBloc.close() destroying singleton TcpService connections
  when factory-created BLoC instances were garbage collected
- Fix GCM encryption buffer overflow — use actual bytes written (len + finalLen)
  instead of pre-allocated getOutputSize() buffer which included trailing zeros
- Fix nonce generation using proper Random.secure() instead of weak Fortuna seed
- Fix server port mismatch — startServer now returns actual port when fallback used
- Add QR scanner to Sync page for backup pairing (detects pairing_key in QR JSON)
- Add backup pairing flow via direct service calls instead of throwaway BackupBloc
- Update BackupPage with desktop two-column layout showing connection details,
  QR pairing card, and HM-2 epic phase progress tracker
- Update BackupBloc to dispatch BackupConnected on TcpService state changes
- Clean up debug logging, remove verbose hex dumps
… device_id

Tables modified:
- FhirResource: +updated_at, +deleted_at, +device_id
- Sources: +deleted_at (already has updatedAt)
- ProcessingSessions: +updated_at, +deleted_at, +device_id
- RecordNotes: +updated_at, +deleted_at

Schema bumped from v9 to v10 with ALTER TABLE migration.
Prerequisite for LWW sync and multi-device tracking.
Desktop tab renamed from "Backup" to "Sync" with sections:
- Connection card (QR pairing, device info, transport type)
- Backup card (placeholder: backup now, restore, history)
- LWW Sync card (placeholder: status, pending changes)
- Processing History card (origin badges: phone/desktop/import)

Added ConnectionTransport enum to BackupState:
- tcp (WiFi Router)
- multipeerConnectivity (Direct, no router)
- unknown

Transport type shown in connection chip and info rows
so user always knows HOW devices are connected.

Mobile layout updated: "Desktop Backup" → "Desktop Sync"
with transport label shown when connected.
New transport architecture:
  CommunicationService (abstract interface)
  ├── TcpTransport (wraps existing TcpService, for WiFi/cross-platform)
  ├── MpcTransport (MultipeerConnectivity, for iPhone ↔ macOS direct)
  └── TransportSelector (picks primary transport based on platform)

Apple platforms (iOS/macOS): MPC primary, TCP fallback
Other platforms (Windows/Linux/Android): TCP only

MpcTransport uses MethodChannel 'dev.lifevalue.healthwallet/mpc'
with methods: startAdvertising, startBrowsing, sendData, disconnect
and callbacks: onPeerConnected, onPeerDisconnected, onDataReceived

Native Swift/macOS implementation needed in macos/Runner/ (next step).
Airdrop package has iOS MPC only — macOS requires new platform code.
…S support

Transport abstraction layer (Dart):
- CommunicationService abstract interface
- TcpTransport wraps existing TcpService
- MpcTransport for MultipeerConnectivity via airdrop package
- TransportSelector auto-picks MPC on Apple, TCP elsewhere

macOS entitlements: added network.incoming for MPC

Airdrop package (packages/airdrop/) updated separately:
- Added macos/ platform with AirdropPlugin.swift + MacOSMultipeerManager.swift
- macOS platform registered in pubspec.yaml
- Same channel (dev.lifevalue.airdrop) for iOS and macOS
- Leaner than iOS: focused on data transport for desktop sync
- build.yaml: exclude transport/ from drift_dev analysis
- pubspec.yaml: airdrop → local path (../packages/airdrop) for dev
- Podfile: macOS deployment target 10.15 → 12.0 (MPC requires 12.0)
- Xcode project: MACOSX_DEPLOYMENT_TARGET → 12.0
…ktop/communication

Structure:
  sync/presentation/     — Universal Sync page, QR scanner
  sync/data/             — Shared: FHIR local data, remote data source, DTOs, JWT, mappers
  sync/domain/           — Shared: SyncRepository interface, Source entity, services, use cases
  sync/ehrs/fasten/      — Fasten-specific only (3 files: sources API, repository impl, QR format)
  sync/desktop/communication/ — Desktop sync (moved from backup/)

Generic code kept in shared layer (reusable for future EHR providers):
  - SyncRepository interface
  - SyncRemoteDataSource (generic FHIR fetch)
  - JwtService
  - Source entity, DTOs, mappers

Fasten-specific code isolated (only what cannot be reused):
  - source_remote_data_source (Fasten sources API)
  - sync_repository_impl (Fasten sync logic)
  - sync_qr_data (Fasten QR format)

No functional changes. All imports updated.
Connection order on mobile (iPhone/Android):
  Step 1: MPC (Apple only, 10s timeout, no network needed)
  Step 2: TCP saved IP (fast, works if network unchanged)
  Step 3: mDNS + SSDP network discovery (finds desktop on new IP)
  Step 4: Fail with "scan QR again" message

Auto-reconnect: on disconnect, waits 3s then retries the full chain.
Handles network changes (new WiFi, USB tethering, etc).

Added DiscoveryService.discoverViaNetwork() — skips saved IP,
goes directly to mDNS+SSDP for when saved IP is stale.
…-level features

Major restructure following SOLID principles:

features/processing/  ← Shared AI pipeline (platform-agnostic)
  - data/: llamadart engine, OCR, prompts, repositories, session storage
  - domain/: MappingResources, ProcessingSession, AI services
  - presentation/: ProcessingBloc, shared widgets, model management

features/capture/     ← Input methods (HOW documents enter the pipeline)
  - scan/: Camera capture (mobile only), focus mode, preview, reorder
  - import/: File picker (both platforms)

features/desktop/     ← Desktop-specific features
  - communication/: TCP, MPC, pairing, discovery (CommunicationBloc)
  - backup/: Snapshot transfer, restore (placeholder)
  - handover/: Processing handover protocol (placeholder)
  - lww_sync/: Delta sync (placeholder)

features/sync/        ← Data connections (Fasten + future EHRs)
  - presentation/: Universal Sync page, QR scanner
  - ehrs/fasten/: Fasten-specific (3 files)
  - data/: Shared FHIR data layer, DTOs, JWT
  - domain/: SyncRepository interface, Source entity

Class renames:
  BackupBloc → CommunicationBloc
  BackupState → DesktopSyncState
  BackupEvent → CommunicationEvent
  BackupPage → SyncDesktopPage
  BackupConnectionStatus → ConnectionStatus

All imports updated. No functional changes.
Features restructured:

features/processing/     — Shared AI pipeline (ProcessingBloc, no capture code)
features/capture/
  scan/                  — Camera capture (ScanBloc, mobile only)
  import/                — File picker (ImportBloc, both platforms)
  desktop_import/        — Drag & drop (DesktopBloc, desktop only)
features/desktop/
  communication/         — TCP, MPC, pairing (CommunicationBloc)
  presentation/          — Desktop hub page + widget cards
  backup/                — Placeholder
  handover/              — Placeholder
  lww_sync/              — Placeholder
features/sync/
  presentation/          — Universal Sync page
  ehrs/fasten/           — Fasten-specific (3 files)
  data/                  — Shared FHIR data layer
  domain/                — Generic interfaces + services

Class renames:
  ScanBloc (processing) → ProcessingBloc
  ScanState → ProcessingState
  ScanEvent → ProcessingEvent
  ScanStatus → PipelineStatus
  ScanMode → CaptureMode
  BackupBloc → CommunicationBloc
  BackupState → DesktopSyncState
  BackupPage → DesktopPage

Capture blocs extracted from ProcessingBloc:
  ScanBloc — camera scanning business logic
  ImportBloc — file picker + image picker logic
  DesktopBloc — drag & drop validation logic

Desktop page split into composable widgets:
  ConnectionCard, BackupCard, ProcessingCard,
  SyncStatusCard, ConnectionChip, DesktopCard, InfoRow

Connection improvements:
  Fallback chain: MPC → saved IP → mDNS+SSDP
  Auto-reconnect on disconnect (3s delay)
  discoverViaNetwork() bypasses stale saved IP

All imports updated. No functional changes to mobile flows.
…p PDF viewing

LWW delta sync engine syncing fhir_resource, sources, record_notes, and
processing_sessions between mobile and desktop over encrypted TCP.

Sync protocol: table status exchange → full delta for differing tables →
bidirectional verify → file request for missing attachments. Attachments
embedded as base64 in DocumentReference deltas, restored with unique names
and proper extensions. Broken file URLs from backup restore auto-fixed on
startup and after each delta apply. LWW override prefers incoming rows
when local has unresolvable file paths.

Schema v10 adds updated_at, deleted_at, device_id columns with SQLite
triggers. TCP write lock serializes concurrent socket writes. Offline
queue persists unsent deltas across app restarts.

Desktop PDF viewing uses pdfx (supports macOS/Windows/Linux) with
flutter_pdfview fallback on mobile. File existence checks prevent crashes
on missing attachments.
…r, history UI

BackupService creates SQLite snapshots via VACUUM INTO with SHA-256
checksums. BackupBloc manages backup/restore lifecycle with progress
tracking. Desktop streams backup to mobile in 64KB chunks over encrypted
TCP (backup_start → backup_chunk → backup_complete protocol).

MobileBackupHandler receives and reassembles chunks on mobile side.
DesktopBackupReceiver handles incoming backups on desktop. Both verify
checksums before applying. ChunkTransferService provides reusable chunked
binary transfer over TCP. MessageRouter dispatches incoming TCP messages
to appropriate handlers.

BackupCard UI shows backup history with timestamps, sizes, record counts,
progress indicators during backup/restore, location picker, and
delete confirmation dialogs.
HandoverService manages desktop-side session lifecycle: receives file
chunks from mobile, stores in temporary session directory, triggers AI
processing via ProcessingBloc, and returns results over TCP.

HandoverBloc orchestrates the handover flow with session tracking and
status updates. HandoverSession entity models session state including
file paths, processing status, and results.

Scaffolding for full integration — desktop processes documents with
on-device AI models and streams results back to mobile.
Add macOS/desktop handling in device capability service and scan network
data source. Update sync page layout for desktop. Minor processing bloc
adjustments for cross-platform compatibility.
macOS vm.page_free_count reports only truly free pages (not
inactive/purgeable cache), giving misleadingly low values like 218MB on
machines with 16GB+ RAM. Desktop platforms skip the available memory
gate since they have sufficient resources for AI model loading.
Google ML Kit Text Recognizer is not available on macOS/Windows/Linux.
Desktop now skips OCR entirely and uses the vision model directly for
both basic info extraction and remaining resources. Mobile flow unchanged
— still uses OCR fast path with vision fallback.
Saved SharedPreferences AI settings (context=512, gpuLayers=0) from
mobile caused vision model crash on macOS. Desktop now passes null for
gpuLayers, threads, and contextSize so auto-config kicks in based on
actual RAM (e.g., 64GB → ctx=4096, gpuLayers=99, threads=8).
The processing handler ran Google ML Kit OCR before AI extraction. On
desktop (macOS), ML Kit is unavailable so OCR returned empty text,
causing the session to be set to draft without ever running the AI model.

Desktop now skips the OCR pre-match phase entirely and goes directly to
mapBasicInfo which uses the vision model. Mobile flow unchanged.
…60px)

Mobile resizes images to 560px max to fit in constrained memory. Desktop
with abundant RAM uses 1120px for better text recognition in medical
documents. The vision model can read smaller text and extract more
accurate data from the higher resolution input.
Small vision models sometimes generate conversational text instead of
JSON. Adding '\n\nJSON:\n' suffix forces the model to start generating
JSON immediately, matching the text prompt behavior.
…me scan→processing

P0: GBNF grammar-constrained JSON output (all platforms)
- GbnfGrammars.jsonArray constrains model output to valid JSON arrays
- Eliminates conversational text and malformed JSON from all inference
- Applied to both vision and text inference via GenerationParams.grammar

P1: macOS OCR via Apple Vision framework
- AppDelegate.swift registers MethodChannel (dev.lifevalue.healthwallet/ocr)
- VNRecognizeTextRequest with accurate recognition + language correction
- MacOsOcrChannel Dart wrapper with graceful error handling

P2: Abstract TextRecognitionService with platform DI
- TextRecognitionService now abstract interface
- MobileTextRecognitionService (Google ML Kit) for iOS/Android
- DesktopTextRecognitionService (Apple Vision) for macOS
- PdfConversionMixin shares cross-platform PDF rasterization
- Platform-conditional registration in RegisterModule

P3: Unified pipeline — removed all isDesktop bypass
- Desktop uses same flow as mobile: OCR → text fast path → vision fallback
- Full quality stack enabled: post-processing, OCR validation, patient pre-match
- Handler passes null for AI settings on desktop → auto-config

P4: Image preprocessing for desktop OCR/vision
- ImagePreprocessor applies contrast (1.3x) + sharpen kernel before OCR
- No-op on mobile, 95% JPEG quality on desktop (vs 85% mobile)
- 1120px max image dimension on desktop (vs 560px mobile)

P5: Desktop vision tokens increased to 1024 (vs 512 mobile)

Qwen 8B desktop model variant:
- AiModelVariant.qwen7b (Qwen3-VL-8B-Instruct Q4_K_M, ~5.3GB)
- Default on desktop, available in model management dialog
- 4x parameters vs 2B for better text recognition accuracy

LoadModel state refactored to dynamic maps:
- Map<AiModelVariant, bool> for downloaded/downloading status
- Map<AiModelVariant, double> for per-variant progress
- Model management dialog iterates AiModelConfig.availableVariants

Rename scan→processing throughout processing feature:
- ScanRepository → ProcessingRepository
- ScanNetworkDataSource → AiInferenceDataSource
- ScanInferenceHandler → AiInferenceHandler
- ScanProcessingRepository → AiExtractionRepository
- ScanLocalDataSource → ProcessingLocalDataSource
- ScanLogBuffer → ProcessingLogBuffer
- ScanPathHelper → ProcessingPathHelper
MpcSyncPlugin registers a FlutterMethodChannel (dev.lifevalue.healthwallet/mpc_sync)
for iPhone↔macOS direct connection via Apple MultipeerConnectivity framework.

Supports: startAdvertising, startBrowsing, sendData, disconnect.
Auto-accepts peer invitations, relays connection state and received data
back to Dart via method channel callbacks.
- Create EphemeralAttachmentBrowseService for in-memory records
- Add view mode toggle (Attachments/Records) to ephemeral viewer
- Add styled header with shadow on scroll matching Records page
- Add search, filter, and filter chips to ephemeral viewer
- Pass sourceRecords through bloc for full record context
- Restrict filter bottom sheet to available FHIR types from sender
…shed AppButton variant

- Show warning style (amber) instead of error when AI model not found
- Add "Attach without processing" primary button for no-model case
- Hide Retry button when model is missing
- Add dashed variant to AppButton (replaces DottedBorder dependency)
- Add hasError support to AppDropdownField
- Fix specialty field validation with red border on error
- Align error messages from start in create encounter dialog
- Constrain attach-to-encounter dialog to 500px max width for desktop
- Add ConnectionDialog.openQRScanner for direct full-screen QR scanning
- Auto-detect QR type: Desktop pairing vs Fasten Health sync
- Fix Fasten sync by removing clearDemoData call that threw exception
- Make SyncBloc a lazySingleton to ensure single instance across app
- Add app-level BlocListener for sync success dialog
- Add debug logging throughout sync flow
- Bold HealthWallet.me and Fasten Health in scanner description
- Rename section label to "Data Sync & Backup"
…eferences

- Add GitHub-style type-to-confirm for source deletion
- Allow deletion of HealthWallet (wallet) sources
- Strip "Wallet - " prefix from wallet source display names
- Show EHR sync status in preferences sync section with last sync time
- Open QR scanner directly from preferences without dismissing modal
- Use wallet icon for all source types in source list dialog
- Add l10n keys for new features
…assifier

- Add specialty section with reorderable cards on home dashboard
- Add specialty icons (18 SVG assets) mapped to MedicalSpecialty enum
- Add SpecialtyClassifier for auto-classifying records by specialty
- Add specialty picker with icons in grid layout
- Match icon sizes (22/26) for both specialties and resources sections
- Add home overview toggle, section headers, and visibility preferences
…ITMS-90291)

Simplify framework fix into a reusable private lane that runs after
archive and before export. Fixes Resources symlink pointing to
Versions/A/Resources instead of Versions/Current/Resources.
… msix Store config

- Update FHIR entity interfaces and implementations across all resource types
- Refine attachment browse bloc, repository service, and ephemeral service
- Update records page, detail page, and attachment detail panel
- Add specialty classifier interface and implementation
- Update home data handler and dashboard sections
- Fix app_database schema alignment
- Update macos Fastfile app bundle name to HealthWallet.me
- Configure msix_config with Microsoft Store identity and publisher values
… panel fixes, multi-file attach

- Expand SpecialtyClassifier to classify all main record types (not just Encounter/DiagnosticReport)
- Add encounter-inheritance: solo records inherit parent encounter's specialty
- Add specialtyOverride column (DB v11) for manual overrides without mutating EHR data
- Remove hardcoded [Encounter, DiagnosticReport] defaults — specialty filter loads all types
- Fix specialty filter flow: clear type filters when specialty is active
- Desktop details panel: fix negative Row spacing crash (ITMS-90291), resizable to 800px
- Desktop nav arrows with pill buttons (Figma design) for attachment preview
- Responsive preview padding (240px desktop, 16px mobile)
- Multi-file attachment: accept List<File> in single event, process all files
- File counter shows current file index "(1 of 2 files)" below filename
- Transparent preview background in dark mode
- Larger preview area (55% screen height)
Storage:
- Add "Reset App Data" section in Preferences (macOS only)
- Type-to-confirm dialog (user must type "RESET")
- Clears database, SharedPreferences, cache, and documents
- Exits app after reset for clean restart

Tray performance fixes:
- Add .distinct() filters on CommunicationBloc/LwwSyncBloc stream listeners
- Capture and cancel messages stream subscription in CommunicationBloc
- Debounce tray menu rebuilds (100ms) to prevent rapid redraws
- Skip redundant state emissions in TrayBloc event handlers
Backup dialog:
- Fixed header and footer layout (scrollable list only)
- Location and History labels
- Inline backup description (date, records, size)
- Hide create form when viewing backup details
- Buttons right-aligned, checkbox left-aligned and smaller
- Divider color matched to app theme

Storage:
- Reset App Data section in preferences (macOS only)
- Type-to-confirm dialog (type RESET)
- Clears database, preferences, cache

Tray fixes:
- Debounced menu rebuilds (100ms)
- Captured message stream subscription
- Distinct filters on bloc listeners
- Skip redundant state emissions

UI polish:
- Mini status cards match Figma colors (light/dark mode)
- Nav pill buttons match Figma (rounded, proper opacity)
- Collapse pill button with overlapping chevrons
- Removed minimize-to-tray from preferences
- Reverted pubspec airdrop to git SSH ref
- Ephemeral viewer: 3-row split header (shared-from + info icon, toggle/search/filter, chips), AppBar removed in tablet landscape split mode, right detail panel full height
- SessionBottomBar centered and width-constrained (wideDialogWidth) in ephemeral viewer using Stack overlay; thumbnails/timeline pushed above bar via bottomNavHeight: 140
- AttachmentBrowseView: didUpdateWidget triggers rebuild when bottomNavHeight changes
- EphemeralDesktopDetailsPanel: resizable right panel for attachment browse in split mode
- Incoming Health Records dialog constrained to dialogWidth on tablet (receive_mode_manager)
- PeerInvitationView constrained to dialogWidth in PeerDiscoveryView on tablet
- SelectionBottomBar constrained to wideDialogWidth on tablet (record_selection_view)
- CustomArrowTooltip alignment changed to center in tablet landscape split mode
- Move demo_data.json to assets/demo_data/, add pdf_preview_demo_data.pdf
- Remove deprecated ephemeral_attachment_view and attachment_detail_panel_ephemeral widgets
- Update attachment_detail_panel, attachment_thumbnail_strip, records_repository_impl, pdf_thumbnail_utils
- Update pubspec.yaml dependency, CLAUDE.md, untranslated_messages
@beniamin-szilagyi beniamin-szilagyi changed the title feat(desktop): v1.3.0 — ephemeral viewer, specialty cards, system tray, Windows support feat: v1.3.0 — desktop app, on-device AI, attachment browser, specialty cards May 5, 2026
…ight

- Fix attachment count on Encounters showing files from child resources.
  Encounters now only count directly owned DocumentReferences (no
  context.related or related pointing to the Encounter itself).
- Non-Encounter records only show their own directly linked attachments,
  not all files sharing the same encounter.
- Encounter attachment dialog shows all related files with grey subtitle
  indicating source record (title · type), direct files labeled
  "Current record" and sorted to top.
- Fix attachment browse not refreshing after adding a file — capture bloc
  references before async FilePicker to avoid unmounted context.
- Preserve selected record and thumbnail scroll position after reload.
- Reduce mobile preview card height (0.45 clamp 280–420) without
  affecting tablet/desktop sizes.
- Add l10n key "currentRecord" (EN/DE/ES/RO).
Remove reverse-page logic that started from the last page when
navigating to a previous record. Tapping or swiping to any record
now consistently opens at page 1.
background_downloader fails silently on macOS when handling
HuggingFace 302 redirect chains. Desktop platforms now use Dio
for model downloads with proper redirect support. Mobile keeps
background_downloader for foreground service notifications.
…option

Store DocumentReference files in Documents/HealthWallet/Documents on
Windows instead of cluttering the root Documents folder. Enable reset
option on Windows. Fix import page browse button behind nav bar.
…s on desktop

- Use getApplicationSupportDirectory()/ocr_temp/ instead of getTemporaryDirectory()
  so preprocessed images are not purged by macOS during long AI inference
- Delete preprocessed files immediately after OCR to avoid stale file accumulation
- Guard handover sender against missing source files before readAsBytes()
- Wrap preprocessForOcr in try-catch so any failure gracefully falls back to original path
- Add disk-validation of cached image paths in onSessionActivated
…ve scroll and UX

- Fix specialty field rendering gender options instead of medical specialties
- Add icon + label display for each specialty item in both processing form and create-encounter dialog
- Wrap specialty in FormField to enforce mandatory selection with validation error
- Add openAbove option to AppDropdownField; specialty opens upward to avoid clipping
- Fix processing page scroll: move Column to Scaffold.body directly so Expanded gets tight
  height constraints; move width constraint inside SingleChildScrollView child
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants