Commit graph

91 commits

Author SHA1 Message Date
Daniel
059fbefa7e Add automatic ICD-10 and CPT billing code suggestions
New feature: after generating any clinical note, the app automatically
suggests relevant billing codes displayed as clickable chips below the output.

Backend (src/routes/billing.js):
- POST /api/suggest-codes endpoint analyzes note text
- Extracts diagnoses from Assessment section via regex
- Looks up ICD-10 codes: local common pediatric map (40+ conditions)
  first, then NLM Clinical Tables API for unknown terms
- Suggests CPT E/M codes based on note type, visit complexity,
  ROS/PE system counts, and MDM level estimation
- Supports: outpatient (new/established), well visit (age-based),
  ED, inpatient (admit/subsequent/discharge)

Frontend (public/js/app.js):
- suggestBillingCodes() renders collapsible card with ICD-10 and CPT chips
- Click any chip to copy the code to clipboard
- Shows E/M level assessment (diagnosis count, ROS, PE, MDM complexity)
- Disclaimer: "Suggestions only. Always verify codes."

Integration: called after note generation in all 6 tabs
(encounter, SOAP, sick visit, well visit, hospital course, chart review)
2026-04-11 01:50:17 +02:00
Daniel
5a394219db Add pediatric calculators: BP, BMI, growth, bilirubin, vitals, BSA, dosing
Calculators tab with 7 tools:
- BP Percentile (AAP 2017) with age/sex/height classification
- BMI Percentile (CDC 2000) with extended obesity classification
  (Class 1/2/3 using % of 95th percentile per CDC 2022)
- Growth Charts: weight-for-age, length-for-age, head circumference,
  weight-for-length (WHO/CDC LMS), Fenton preterm (22-50 weeks)
- Bilirubin: AAP 2022 phototherapy threshold + Bhutani nomogram
  with Nelson Table 137.1 risk factors for severe hyperbilirubinemia
- Vital Signs by Age (Harriet Lane) with quick reference formulas
  (estimated weight, min SBP, ETT size, maintenance fluids 4-2-1)
- Body Surface Area (Mosteller formula)
- Weight-Based Dosing with max cap and volume calculation

Fix growth chart sub-tab navigation (pills scoped separately from
top-level nav to prevent panel disappearing)
2026-04-09 17:56:30 +02:00
Daniel
5ec1ddec6b Replace all browser dialogs with modern modal, add OIDC admin UI
- Add reusable showConfirm() modal component (supports plain confirm,
  input prompt, danger styling, Enter key)
- Replace ALL 18 confirm() and prompt() calls across 8 JS files with
  showConfirm() modal: admin user actions, session revoke, document
  delete, template delete, milestone management, transcription settings
- Fix broken admin reset-password (btn was undefined in scope)
- Add OIDC/SSO configuration UI to Admin Panel (issuer, client ID/secret,
  button label, disable local auth toggle, callback URL display)
2026-04-09 02:43:23 +02:00
Daniel
c3f8dc49ef Fix session revocation bug that could log out current device
- Fix: DELETE all other sessions query used empty string fallback when
  req.sessionId was undefined, causing id != '' to match ALL rows
  (including current session). Now skips deletion if sessionId unknown.
- Fix: Revoke All endpoint returns error if current session not identified
- Fix: var confirm shadowing window.confirm in password change handler
2026-04-09 02:33:00 +02:00
Daniel
30638b968a Remove prompt() dialogs, breach warnings, cost display; fix 2FA disable UI
- Replace browser prompt() with inline UI for: 2FA disable (password field),
  admin password reset (inline input), admin test email (inline input)
- Remove all password breach warning UI (login, register, settings)
  Backend HIBP check endpoint remains but is no longer called from frontend
- Remove model cost display from dropdown and header badge
- Hide empty cost-badge element in header
- Fix model dropdown to flat list (no category grouping)
2026-04-09 02:27:55 +02:00
Daniel
0e4e4f5f17 FAQ page, dep security patches, model dropdown and UI fixes
- Add FAQ tab with accordion sections: Getting Started, AI & Models,
  Voice & Transcription, Saving & Export, Privacy & Security,
  Well Visit & Sick Visit, Learning Hub, Troubleshooting
- Documents how AI learns from physician edits (correction tracker)
- Fix FAQ accordion (CSP was blocking inline script, moved to app.js)
- Patch all 5 npm vulnerabilities: nodemailer 8.0.5, xmldom, basic-ftp,
  path-to-regexp (npm audit now reports 0 vulnerabilities)
- Remove model category grouping from dropdown (flat list, no optgroups)
- Fix model dropdown dark background on options (white bg, dark text)
- Update FAQ model guidance to reflect admin-managed model selection
2026-04-09 01:56:11 +02:00
Daniel
d12d4f4e63 v6.2: Session management, password change, audit logging, refine context, UI fixes
Security:
- Add session management: users can view/revoke active sessions in Settings
- Add password change in Settings (requires current password, HIBP check)
- Force logout all sessions on password reset
- Fix logout to destroy server-side session (was only clearing cookie)
- Add trust proxy for correct client IP in rate limiting and audit logs
- Add CORS support for multiple domains (CORS_ORIGINS env var)
- Add HIBP breach check endpoint and inline warnings on password fields

Audit logging:
- Add audit logging to all 24 PHI-handling endpoints across 13 route files
- Covers: generation, transcription, TTS, refine, encounters, documents, Nextcloud
- All fire-and-forget (no response delay)

AI improvements:
- Refine now includes original source material (transcript, notes, labs)
  so AI can reference the full input when modifying output
- Add correction tracking (trackAIOutput) to sick visit and well visit tabs
- Fix sickvisit missing from encounter save noteIdMap

UI fixes:
- Non-blocking busy bar for transcription and AI generation (replaces full-screen overlay)
- Fix encounter recording: hide record button during recording (was showing two stop buttons)
- Fix ROS/PE "All WNL" stacking duplicate event handlers; add Clear buttons
- Enlarge AI instructions textarea in Learning Hub CMS

Domain:
- Primary domain now app.pedshub.com, with scribe.pedshub.com and peds.danvics.com as CORS origins
2026-04-08 20:27:45 +02:00
Daniel
b97c5f146b v6.1: Turnstile bot protection, LiteLLM provider, PPTX tables, audio backup fixes, docs
- Add Cloudflare Turnstile to login, register, and password reset forms
- Switch AI provider to LiteLLM, transcription to OpenAI Whisper
- Change domain to scribe.pedshub.com
- Fix PPTX export: add tables, bold/italic, numbered lists, code blocks, blockquotes
- Fix announcement banner close button (CSP was blocking inline onclick)
- Fix auth middleware: empty Bearer token now falls through to cookie auth
- Fix audio backups: only save on transcription failure, stop auto-deleting on success
- Soften AI correction injection to prevent model hallucination from correction history
- Fix LiteLLM TTS model name handling (no incorrect openai/ prefix)
- Expand AI instructions textarea in Learning Hub CMS
- Update README for v6 with all features and providers
- Add comprehensive docs/: architecture, API reference, database schema,
  authentication, AI providers, speech, learning hub, configuration, deployment
2026-04-04 22:56:24 +02:00
ifedan-ed
9c1d86d4b6 feat: Add model search, testing, and TTS/STT/embedding management to admin
- Fix model search for all providers: Bedrock now falls back to built-in
  list (with live ListFoundationModels attempt), Azure returns built-in list
- Add Test button on every model row (built-in, discovered, custom) that
  sends a live prompt and shows response + latency in a toast
- Add TTS management section: search voices from provider API (Google TTS
  voices.list, LiteLLM /v1/models, ElevenLabs /v1/voices), Set as Default
  writes tts.voice/tts.model to DB, runtime respects DB override
- Add STT management section: search models from provider (Gemini, Whisper,
  LiteLLM, OpenAI, local), Set as Default writes stt.model to DB, runtime
  respects DB override in transcribe.js
- Add Embedding models section: search from provider (LiteLLM, Vertex,
  OpenAI), Set as Default writes embeddings.model+dimensions to DB,
  embeddings.js respects DB override
- Add record-and-transcribe STT test (browser MediaRecorder)
- Add TTS synthesize-and-play test (returns base64 audio)
- Add embedding generate test (shows dims + vector sample)
- Expand PUT /config/:key(*) whitelist to include tts., stt., embeddings.
- Add @aws-sdk/client-bedrock as optional dependency for live Bedrock discovery
2026-04-03 19:55:11 +00:00
ifedan-ed
78bb30e42f v2.2: Remove milestone admin UI, add CMS content refresh button
REMOVED:
- Milestone editing UI from Admin Panel (per user request)
- Milestones will be managed via hardcoded static data only
- Kept backend routes and database support for future use

ADDED:
- Refresh button in Learning Hub CMS content list
- Manual refresh for AI-generated content updates
- Better discoverability of content refresh functionality

FIXES:
- AI learning content now has visible refresh button
- Users can manually refresh content list after AI generation
- Cleaner admin panel without milestone management clutter

NOTE:
- Developmental milestones still work via static fallback
- Edit milestones by modifying public/js/milestonesData.js
- Backend API still supports milestone management if needed later
2026-04-01 18:16:00 +00:00
ifedan-ed
2bccd4e374 v2.1: Add visible bulk import UI for developmental milestones
NEW FEATURES:
- Bulk Import button in Admin Panel → Developmental Milestones section
- "Import Default Milestones Data" button appears when database is empty
- "Re-import All" button to clear and re-import all static data
- Visible notice when no milestones exist with one-click import

IMPROVEMENTS:
- Auto-shows empty state notice when database has no milestones
- Backend bulk-import endpoint now supports clearExisting parameter
- Imports ALL age groups from static data (birth to 11 years)
- Better UX - admin doesn't need CLI to populate milestone data

FIXES:
- Makes milestone admin editing feature discoverable and usable
- No need to manually run import script anymore
2026-04-01 18:06:42 +00:00
ifedan-ed
75a3a47598 v4: Fix milestones display + add OpenID auth + 100MB PDF support
FIXES:
- Milestones now show correctly on encounter page (use static fallback if DB empty)
- Static data preserved as MILESTONES_DATA_STATIC for compatibility
- Database-driven milestones still work (admin can edit via CMS)

NEW FEATURES:
- OpenID Connect (OIDC) authentication support (PocketID, Keycloak, Azure AD, etc.)
- Comprehensive setup guide: OPENID_SETUP.md
- Auto-linking existing users by email on SSO login
- Multiple PDF upload support in Learning Hub (up to 10 files)
- 100 MB per file limit (was 20 MB)
- Full PDF content used for AI generation
- Embeddings use first ~8K chars for semantic search

IMPROVEMENTS:
- Updated UI to show multiple file selection with list
- Drag-and-drop supports multiple files
- Better file upload validation and error handling
- Added clarifying comments about embedding truncation
2026-04-01 17:59:51 +00:00
ifedan-ed
8ac519a190 FIX: Browser Whisper - 100% self-hosted, zero CDN dependencies
Some checks failed
Build TWA APK / build-apk (push) Failing after 1s
Build & Push Docker Image / build (push) Failing after 7s
FINAL WORKING SOLUTION:

Previous attempts failed because:
- transformers.js v2.17.2 is ES module-only
- Module workers require complex CSP and external imports
- importScripts() doesn't work with ES modules

Solution:
- Use transformers.js v2.6.2 (has worker-compatible UMD build)
- Bundle library + models, serve entirely from our server
- Classic worker with importScripts() - no CSP issues

What's self-hosted:
-  transformers.min.js (760KB) - at /models/transformers.min.js
-  Whisper models (42MB) - at /models/Xenova/whisper-tiny.en/

Worker loads:
1. importScripts('/models/transformers.min.js') - OUR SERVER
2. Loads models from /models/ - OUR SERVER
3. ZERO external network calls
4. Works in any network (firewalled, air-gapped, etc.)

This is the production-ready, truly offline solution.
2026-03-31 23:12:21 +00:00
ifedan-ed
0cb1cac7ae Fix Browser Whisper: Use ES module worker with CDN library
Issue: transformers.js is an ES module package and cannot be loaded
with importScripts() in classic workers.

Solution:
- Changed to module worker (type: 'module')
- Import transformers.js from CDN as ES module
- Models (42MB) still served from local server at /models/

Trade-off:
- Library (900KB): Loads from cdn.jsdelivr.net once, cached
- Models (42MB): Self-hosted, served from /models/ (no CDN)

This is necessary because:
1. @xenova/transformers is ES module-only (package.json: "type": "module")
2. ES modules cannot use importScripts()
3. Module workers require HTTPS for imports
4. CDN is HTTPS and cacheable

If CDN is blocked:
- Use Web Speech API (with privacy warnings)
- OR use Server Transcription (Vertex AI/AWS)

Models remain self-hosted as they're 40MB+ and contain the AI.
2026-03-31 22:55:28 +00:00
ifedan-ed
e29de42816 Add Web Speech Recognition option for real-time streaming
Provides two transcription options:

1. Browser Whisper (Offline, Batch) - RECOMMENDED
   - 100% offline, zero network calls
   - HIPAA-compliant, audio never leaves device
   - Highest accuracy (Whisper)
   - Processes after recording (batch mode)
   - Models self-hosted, bundled in v2

2. Web Speech API (Real-time, Streaming) - EXPERIMENTAL
   - Real-time transcription (see words as you speak)
   - Uses browser's built-in speech recognition
   - ⚠️ Sends audio to cloud (Chrome/Edge → Google)
   - ⚠️ NOT HIPAA-compliant
   - Requires user consent with clear warnings

Features:
- Settings UI for both options
- Clear privacy warnings for Web Speech
- Mutual exclusion (only one active at a time)
- Browser detection shows which provider is used
- Confirmation dialog before enabling Web Speech

Use Cases:
- Clinical/HIPAA: Use Browser Whisper only
- Personal/Non-clinical: Can use Web Speech for real-time feedback
- Maximum privacy: Browser Whisper (offline)
- Maximum speed: Web Speech (if privacy not required)

Implementation:
- speechRecognition.js: Web Speech API wrapper
- transcriptionSettings.js: Settings UI handler
- Privacy info displayed per browser

User can choose based on their privacy vs. speed preference.
2026-03-31 21:57:24 +00:00
ifedan-ed
ce26594592 Add admin dashboard for developmental milestones management
Features:
- Admin can add, edit, and delete developmental milestones via dashboard
- Milestones stored in PostgreSQL (developmental_milestones table)
- Client-side loads milestones from API instead of static file
- Import script to migrate existing static data to database
- Organized by age group and domain
- Supports sorting and filtering

Admin UI:
- New section in Admin panel for milestone management
- Filter by age group
- Add/Edit modal with validation
- Delete with confirmation
- Auto-complete for age groups and domains

API Endpoints:
- GET /api/milestones-data - Public endpoint for authenticated users
- GET /api/admin/milestones - List all milestones (admin only)
- GET /api/admin/milestones/meta - Get age groups and domains
- POST /api/admin/milestones - Create milestone
- PUT /api/admin/milestones/:id - Update milestone
- DELETE /api/admin/milestones/:id - Delete milestone
- POST /api/admin/milestones/bulk-import - Bulk import

Usage:
1. Run import script: node scripts/import-milestones.js
2. Access Admin dashboard → Developmental Milestones section
3. Add/Edit/Delete milestones as needed
2026-03-31 20:55:41 +00:00
ifedan-ed
f8c75145af v18: Self-hosted Browser Whisper (zero CDN dependencies)
BREAKING FIX: Browser Whisper now fully self-contained

Previous issue:
- Loaded transformers.js from cdn.jsdelivr.net
- Downloaded models from cdn-lfs.huggingface.co
- Failed in corporate/clinical networks with firewall
- Stuck at "Initializing..." with no progress

Solution:
- Bundle transformers.js library (~876KB)
- Bundle Whisper tiny.en model (~42MB)
- Serve everything from local server
- Works in ANY network environment

Changes:
- whisperWorker.js: Load transformers from /models/ instead of CDN
- Dockerfile: Download models during Docker build
- Add download script for local dev
- Add comprehensive setup documentation

Docker image size: +~42MB (one-time cost, runtime benefit)

Tested: Works on unrestricted and firewalled networks
2026-03-31 20:02:11 +00:00
ifedan-ed
332b4dc20e v16: Make Browser Whisper CDN failure graceful with clear warnings
Some checks failed
Build TWA APK / build-apk (push) Failing after 1s
Build & Push Docker Image / build (push) Failing after 7s
REALITY CHECK: Browser Whisper CDN loading cannot work in all environments
- Corporate firewalls block cdn.jsdelivr.net
- Network proxies filter JavaScript CDN
- Workers + importScripts + cross-origin = blocked by CSP/CORS

SOLUTION: Graceful degradation
- Clear user-friendly error messages
- Automatic fallback to server transcription
- Warning banner in Settings if CDN blocked
- Comprehensive troubleshooting documentation

Changes:
- browserWhisper.js: Show toast on worker error, fallback gracefully
- app.js: Display CSP warning banner on preload failure
- settings.html: Add warning about network/firewall requirements
- BROWSER_WHISPER_TROUBLESHOOTING.md: Complete guide for users

Key Message:
Browser Whisper is OPTIONAL. Server transcription (Google/AWS/OpenAI)
is the primary method and works everywhere. Browser Whisper is a
privacy-focused bonus feature that requires CDN access.

User Experience:
- If CDN works: Great! Browser Whisper available
- If CDN blocked: No problem! Server transcription works perfectly
- Clear messaging: User knows what to expect
2026-03-31 16:18:46 +00:00
ifedan-ed
6edcb8c215 Fix TTS preview for 'Server default' voice option
- Allow empty voice value to preview server default
- Display 'server default' in preview text
- Clears user preference (sets to null) when testing default
2026-03-31 16:15:41 +00:00
ifedan-ed
9466065393 v15.1: CRITICAL FIX - TTS Preview + Browser Whisper actually working now
Some checks failed
Build TWA APK / build-apk (push) Failing after 1s
Build & Push Docker Image / build (push) Failing after 7s
ROOT CAUSES FOUND AND FIXED:
1. TTS Preview not working: voicePreferences.js listening for wrong event
   - Was: 'tab-loaded' (never dispatched)
   - Now: 'tabChanged' (correct event name used by app.js)
   - Added immediate init if page already loaded
   - Added 500ms delay for DOM readiness

2. Browser Whisper CDN blocked: CSP too restrictive
   - Added 'unsafe-eval' to scriptSrc (required by transformers.js)
   - Added cdn.jsdelivr.net to connectSrc (worker importScripts)
   - Added childSrc directive for worker script loading
   - Better error messages in worker

3. Worker loading errors: Now logged with specific reasons
   - importScripts wrapped in try-catch
   - Posts error message to main thread
   - Verifies transformers object exists after load

Testing:
- TTS Preview should now work when clicking Settings tab
- Browser Whisper should load from CDN (or show specific error)
- Console logs will show exact init sequence
2026-03-31 16:00:10 +00:00
ifedan-ed
72bc016a38 v15: Fix TTS preview + Browser Whisper preload with extensive debugging
BREAKING FIXES:
- TTS Preview: Added event.preventDefault(), console logging, proper init check
- Browser Whisper: Complete console logging pipeline, error handling, progress tracking
- Voice Preferences: DOMContentLoaded fallback, explicit button click handlers
- Whisper Worker: Console logs at every step, better error messages

Debugging Features:
- Console logs show: button clicks, init events, progress updates, errors
- Progress tracking: [WhisperWorker] Progress: model.bin 47%
- Error messages: Specific failure reasons (not generic failures)
- Timeout warnings: 30s check for stuck downloads

Audio Backup Confirmed:
- Deletes immediately on successful transcription (line 621-624 app.js)
- NOT after 24 hours - 24h is server retention limit for failed transcriptions
- User was correct - this is working as designed

How to Debug:
1. Open DevTools → Console (F12)
2. Click button
3. Watch for [VoicePrefs] or [BrowserWhisper] logs
4. Check Network tab for actual downloads
5. Report what you see in console
2026-03-31 15:28:38 +00:00
ifedan-ed
b820255aa9 Fix TTS preview + Browser Whisper preload, add comprehensive docs
Some checks failed
Build TWA APK / build-apk (push) Failing after 1s
Build & Push Docker Image / build (push) Failing after 7s
Fixes:
- TTS preview: Better error handling, console logging, empty value check
- Browser Whisper: Add progress logging, 30s timeout warning, better UX
- Voice preferences: Clearer error messages

New Documentation:
- FEATURES_EXPLAINED.md: Complete guide to all v14 features
  - Audio backups explained (works every recording, not just on failure)
  - S3 integration setup guide (AWS, B2, MinIO)
  - Learning Hub default path explained (AI file picker starting folder)
  - Browser Whisper troubleshooting (download progress tracking)
  - TTS preview debugging steps
  - Comprehensive troubleshooting guide
2026-03-31 15:13:53 +00:00
ifedan-ed
fb53aa709f Add per-user voice preferences (STT model + TTS voice selection)
- NEW: User preferences for STT model and TTS voice
- Database: stt_model and tts_voice columns in users table
- UI: Voice Preferences section in Settings with dropdowns
- API: /api/user/preferences (GET/POST) + /preferences/options
- Transcribe: Respects user's STT model (Google, LiteLLM)
- TTS: Respects user's TTS voice (Google, LiteLLM, OpenAI, ElevenLabs)
- Preview: Test TTS voice before saving
- Available models/voices auto-detected from provider config
2026-03-31 14:47:00 +00:00
Daniel Onyejesi
59bf0a5cd8 Add browser-side Whisper transcription (local, zero network, HIPAA-safe)
- whisperWorker.js: Web Worker running @xenova/transformers Whisper in WASM
- browserWhisper.js: main-thread manager — audio→Float32 conversion, worker lifecycle
- transcribeAudio() checks BrowserWhisper.isEnabled() first, falls back to server
- Settings UI: enable/disable, model picker (tiny/base/small), pre-download button
- CSP: add wasm-unsafe-eval, cdn.jsdelivr.net, HuggingFace CDN domains
- Default: whisper-tiny.en (~39MB, ~2-3s per clip)
2026-03-31 07:30:17 -04:00
Daniel Onyejesi
1e8aaf75f8 Fix Read aloud stop button: findReadButton now finds data-action=speak buttons
The button was always returning null because it searched for onclick=speakText
but all output cards use data-action="speak" data-target="id". Now checks
data-action first so the button correctly toggles to Stop during playback.
2026-03-30 21:37:00 -04:00
ifedan-ed
136a3402ab v13: Increase JSON limit to 10MB, client-side size check for chart review
Some checks failed
Build TWA APK / build-apk (push) Failing after 2s
Build & Push Docker Image / build (push) Failing after 8s
- Raise express.json limit from 1MB to 10MB — handles large chart reviews
  with many notes (50 full clinic notes ≈ 600KB, well within new limit)
- Client-side: warn user if payload >8MB, friendly toast if >30 notes
- Bump to v13.0.0
2026-03-30 22:41:17 +00:00
ifedan-ed
b64c967765 Show TTS provider in toast, support full LiteLLM model paths
- TTS response now includes X-TTS-Provider header (google-tts, litellm/model, elevenlabs)
- Frontend reads header and shows actual provider in toast instead of hardcoded "Adam/ElevenLabs"
- CORS exposes X-TTS-Provider header so frontend can access it
- Updated .env.example: clarify that LITELLM_TTS_MODEL and LITELLM_STT_MODEL
  can be either the model_name alias OR the full provider/model path depending
  on your LiteLLM config (important for BAA compliance routing)
2026-03-30 13:37:20 +00:00
Daniel Onyejesi
8b94a49272 Fix TTS axios/Vertex, generic toast, add stop button to encounter
- TTS: switch from OpenAI SDK to axios (same fix as STT), drop voice
  param since it's configured inside LiteLLM per model
- Fix 'ElevenLabs unavailable' toast shown even when provider is LiteLLM
- Add dedicated red Stop button to encounter recording UI
2026-03-29 22:09:56 -04:00
Daniel Onyejesi
0ca4ba1c04 Fix model management: empty LiteLLM list, always reload panel, clear-all
- LITELLM_MODELS = [] — no hardcoded models, global selector now only
  shows what admin has actually added via Search API
- getAvailableModelsWithOverrides: for LiteLLM returns only custom list
- Remove toggle safety check — admin can disable any/all models freely
- Admin panel always reloads on tab open (was cached, showing stale data)
- Add 'Clear all models' button for LiteLLM to wipe and start fresh
- Add POST /config/models/clear-all endpoint
2026-03-29 19:32:09 -04:00
Daniel Onyejesi
3cd7268e5d Fix admin model management: route ordering, LiteLLM built-ins, auto-select
- Root cause: PUT /config/:key(*) wildcard was registered before
  /config/models/toggle and /config/models/default, intercepting them
  and returning "value is required" (body had modelId not value)
- Fix: move all model-specific PUT routes before the wildcard
- LiteLLM: return empty built-in list with discovery hint (hardcoded
  models don't match user's proxy — must use Search API)
- After adding a discovered model: auto-select it in the default dropdown
- GET /config/models now returns defaultModel so dropdown pre-selects it
2026-03-29 18:42:09 -04:00
Daniel Onyejesi
f8dd4d9f86 v9.1: Add Google Vertex AI + LiteLLM support, admin model management panel
- Add Vertex AI provider (Gemini models via @google-cloud/vertexai SDK)
- Add LiteLLM proxy support (OpenAI-compatible, routes to any provider)
- Admin panel: model search/discover from provider API, enable/disable, custom models, set default
- New endpoints: /config/models/discover, /config/models/add-discovered, /config/models/default
- Updated models.js with VERTEX_MODELS and LITELLM_MODELS lists
- Updated health endpoint with vertex + litellm status
2026-03-29 10:32:45 -04:00
ifedan-ed
49b5a49999 Server-side audio backups with compression, viewable AI corrections
Audio Backups:
- New audio_backups table in PostgreSQL (bytea, gzip compressed)
- POST /api/audio-backups — upload with gzip compression (level 6)
- GET /api/audio-backups — list user's backups
- GET /api/audio-backups/:id/audio — download decompressed audio
- DELETE /api/audio-backups/:id — delete backup
- Auto-cleanup every hour (24h expiry)
- Frontend saves to server first, falls back to IndexedDB
- Settings shows source badge (server/local) per backup

AI Corrections:
- Corrections list is now expandable — click to view original vs corrected
- Shows red "Original" and green "Corrected to" sections
- Click arrow to expand/collapse each correction
- Date shown on each correction
2026-03-29 10:56:31 +00:00
ifedan-ed
3d503ba309 Security: remove auth debug logging that exposed emails and responses
Removed console.log statements in auth.js that logged email addresses
and auth API responses to browser console. Final cleanup for v9.
2026-03-29 10:41:35 +00:00
ifedan-ed
0e350b8cad Remove admin model management panel — models use global selector instead
The admin model management (enable/disable, custom models, default override)
had persistent rendering issues. Removed the UI panel — models are managed
via the global model selector in the header, which works reliably. Backend
API endpoints for model config are retained for future use.
2026-03-29 02:30:29 +00:00
ifedan-ed
bf89cad954 Zero-config speech: skip upload when no transcription API, use browser speech directly
- Add GET /api/transcribe/status endpoint — returns whether any server
  transcription provider (Whisper/AWS/Local) is configured
- Frontend checks status on login via checkTranscribeStatus()
- When no provider configured: recording stops instantly, keeps live
  Web Speech API text, shows friendly toast — no error, no upload wait
- Works in encounter, dictation, and SOAP tabs
- App now works fully out-of-the-box with just an AI provider key
2026-03-29 02:09:53 +00:00
ifedan-ed
51fc818e4a Fix: admin models loading, clear refine/instructions on New, bigger HPI areas, unique labels
- Admin models: reset modelsLoaded flag on error so retry works
- Admin default model: fix redundant fetch race condition
- clearTab: now clears refine inputs, instructions, and demographic fields
- Encounter/dictation clear buttons: also clear refine input
- SOAP: instructions textarea already cleared by clearTab (soap-instructions)
- Encounter HPI: bigger transcript (400px) and output (600px) text areas
- Unique label enforcement: 409 error if saving with duplicate label
2026-03-29 01:35:45 +00:00
ifedan-ed
d52c0fba38 Optimize transcription speed: remove artificial delays, add timing
- AWS Transcribe: remove 10ms delay between chunks (was adding ~1.25s/MB),
  increase chunk size from 8KB to 32KB (AWS max per frame)
- Add detailed timing logs (ffmpeg, streaming, total) for diagnostics
- OpenAI Whisper: use response_format='text' for faster response parsing
- Frontend: show transcription time in toast, request 16kHz sample rate,
  increase bitrate to 32kbps Opus (better quality, still small files)
- Return duration in API response for all providers
2026-03-29 00:59:30 +00:00
Daniel Onyejesi
6f671b3f47 Add admin model management dashboard — enable/disable, custom models, default override
- Full model management UI in admin panel: toggle models on/off, add custom
  model IDs (any OpenRouter/Bedrock ID), set admin-configured default model
- /api/models now returns admin-set default model, frontend respects it
- Toggle switch CSS for clean enable/disable UX
- Backend already had the API endpoints, this adds the missing UI
2026-03-28 22:07:16 +00:00
Daniel Onyejesi
5f066bb760 v9: Major feature update — audio backup, SOAP save, Dragon memory, S3 docs, CI/CD, APK
Phase 1 — Critical Fixes:
- Fix SOAP instructions not clearing on Clear button
- Show transcription provider (AWS/OpenAI) in UI toast
- Fix silent transcription failures in dictation and SOAP modules
- Add IndexedDB audio backup system (24hr retention, retry from Settings)
- Prevent duplicate encounter saves with idempotency keys
- Add Save/Load/New bar to SOAP note generator

Phase 2 — Features:
- Dragon-like AI memory: auto-track user corrections, inject into prompts
- Per-section template categories (SOAP, HPI, well visit, sick visit)
- Bigger textarea for SOAP instructions
- S3 document upload/management (AWS S3, Backblaze B2, MinIO compatible)
- Faster transcription via lower bitrate recording (16kbps opus)

Phase 3 — APK & CI/CD:
- GitHub Actions: Docker build+push on version tags
- GitHub Actions: TWA APK build for Obtainium auto-updates
- Android TWA project with foreground service for background recording
- Enhanced PWA manifest with shortcuts and maskable icons
2026-03-28 21:08:32 +00:00
Daniel Onyejesi
15494b4675 Security fixes: remove SSO token from URL, add prompt boundaries
- OIDC callback now passes only ?sso=ok flag, token stays in
  httpOnly cookie (prevents token leaking to logs/referrer/history)
- Frontend auth.js uses cookie-based auth for SSO flow
- Add [PHYSICIAN TEMPLATES] boundary markers around physicianMemories
  in all 5 generation routes to mitigate prompt injection
- Consistent boundary format across wellVisit, sickVisit, hpi, soap,
  hospitalCourse
2026-03-25 19:25:49 -04:00
Daniel Onyejesi
4194715e5c Remove Firefox speech notice, fix auth.js SSO flow race condition
- Remove Firefox speech recognition notice (not needed)
- Fix missing closing brace that made speech recognition unreachable
- Fix auth.js SSO token handling to prevent brief auth screen flash
2026-03-25 19:04:20 -04:00
Daniel Onyejesi
e776487cb1 Add OpenID Connect SSO + Firefox speech notice
OIDC/SSO:
- New /api/auth/oidc route with PKCE for secure authorization
- Supports Azure AD, Okta, Keycloak, PocketID, Google, any OIDC provider
- Admin configurable: issuer, client ID/secret, button label
- Option to disable local auth (force SSO only)
- Auto-creates users on first SSO login, links existing by email
- SSO button on login page, hidden until admin enables OIDC

Firefox:
- Show info toast on first recording that live preview requires
  Chrome/Edge; server-side transcription still works in all browsers
2026-03-25 18:54:44 -04:00
Daniel Onyejesi
79f9fefb52 Wire physician memories/templates into all AI generation routes
Previously only well-visit and sick-visit used saved physician
templates. Now HPI encounter, HPI dictation, SOAP, and hospital
course all fetch getUserMemoryContext() and pass physicianMemories
to the backend so the AI learns from saved templates/preferences.
2026-03-25 17:35:30 -04:00
Daniel Onyejesi
70d2f34770 v8.0.0: Fix speech recognition repeating text, enable AWS Transcribe Medical
- Add deduplication logic to prevent Chrome Speech API from repeating
  sentences during long recording sessions (all 4 recording modules)
- Enable AWS Transcribe Medical with PRIMARYCARE specialty in .env
- Bump version to 8.0.0
2026-03-25 17:24:28 -04:00
Daniel Onyejesi
f397ca7725 v7: fix speech recognition repetition, HTML injection, long-session guard
- Fix word repetition: use sessionFinals pattern so each browser SR
  session starts fresh; no overlap when recognition auto-restarts
- Fix HTML injection / '>' parse error: escape < > & in live transcript
  innerHTML before inserting speech recognition text
- Add 24 MB blob guard: fall back to live SR transcript if audio file
  is too large for Whisper API (long sessions)
- Bump version to 7.0.0, update docker-compose image tag to v7
2026-03-25 20:07:10 +00:00
Daniel Onyejesi
9437da2c99 Add resend verification link on login + rate limit (3/15min) 2026-03-24 14:14:42 -04:00
Daniel Onyejesi
114bc4e7cd Fix CMS button clickability, replace AI topic input with context box
- Add type="button" to all CMS action buttons to prevent default submit
- Change overflow:hidden to overflow:visible on .cms-main to prevent clipping
- Add z-index to .cms-editor-actions for reliable click targeting
- Replace AI "Topic" text input with a descriptive textarea context box
  so users can tell AI what to generate (AI creates its own title)
- Rename AI tab from "By Topic" to "Describe Content"
2026-03-24 11:52:02 -04:00
Daniel Onyejesi
11e4302e97 Fix Learning Hub search and content viewer isolation
Search (was completely broken):
- wireSearch() registered inside tabChanged for 'learning' tab — runs
  after component HTML is in the DOM (lh-search existed but listener
  was never attached because element was null at IIFE load time)
- Proper debounced search with 350ms delay, Escape key to clear
- Search results show count, 'X results for "query"', Clear Search button
- Empty state with helpful message; loading state while fetching

Content viewer showing feed items beneath it (CSS cascade bug):
- .lh-feed{display:flex} and .lh-category-bar{display:flex} defined
  at CSS lines 513/507 — after .hidden{display:none} at line 242
  So classList.add('hidden') was silently overridden
- showFeed() and showViewer() now use element.style.display with
  explicit values ('flex'/'block'/'none') which always win over class rules
- viewer started hidden via class="hidden"; now shown with style.display='block'
2026-03-24 03:13:22 -04:00
Daniel Onyejesi
e3da7c5bb0 Render presentations as slides in Learning Hub viewer
- New GET /api/learning/content/:slug/slides (authMiddleware only)
  Renders Marp markdown via marp-core, returns {css, slides[]} for
  any authenticated user — no moderator access required

- showViewer: when content_type === 'presentation', hides the body card
  and shows a presentation card with title, slide count, and View Slides button
  Auto-opens the slide modal immediately on load; button allows re-opening

- openSlidesFromSlug(): fetches slides from user-accessible endpoint
  then opens the existing slide preview modal (arrow keys, swipe, dots)

- Viewer already correctly hides feed/categories/search when viewing content;
  "Back to Feed" button returns to the main list
2026-03-24 03:04:14 -04:00
Daniel Onyejesi
bc588fc320 Fix login flash, quiz in presentations, revert viewer body, feed labels
Login flash:
- Auth screen now hidden by CSS default (#auth-screen{display:none !important})
- auth.js shows it only when there is no valid token or token is expired
- No more flash of login page on refresh for logged-in users

Presentation quiz option (AI Generate panel):
- Presentation type now shows optional quiz question toggle (same as article)
- Backend: when questionCount>0, returns JSON {marpMarkdown, questions[]}
  instead of plain Marp markdown
- applyAiContent fills both the Marp textarea and question blocks for presentations

Viewer revert:
- Removed the viewer presentation card change (user wants markdown as-is)
- Removed dangling viewer button wiring

Feed/CMS list:
- Presentation type shows correct icon (fa-presentation-screen) and
  label "Slides" in the CMS content list
2026-03-24 02:50:31 -04:00