Commit Graph

18 Commits

Author SHA1 Message Date
bvandeusen 5924e565b1 fix: floating mini-chat overlay style + weather precip fallback
- KnowledgeView mini-chat: replace harsh border-top with upward box-shadow
  and rounded top corners (16px); remove padding-bottom from content area
  so widget truly overlays cards without pushing layout; add collapse
  toggle (chevron) that hides messages without closing the conversation
- WeatherCard: show precip_mm as fallback when precipitation_probability_max
  is null but actual rainfall is expected (Open-Meteo omits probability for
  some forecast days even when rain is shown)
- Pass precip_mm through weather service → frontend type definitions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:04:47 -04:00
bvandeusen 882ea176b2 fix: briefing discuss prompt — suppress research tool to prevent full research note
Explicitly instruct the LLM to respond conversationally and not use any
research/search tools when summarizing a shared article excerpt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 16:54:18 -04:00
bvandeusen baeb0b14e5 feat: listen mode + volume knob in chat; briefing discuss auto-send; fix LLM proactive note search
- ChatView: listen mode toggle (auto-reads new responses via TTS), volume popup
  with range slider persisted per-device in localStorage via GainNode
- useVoiceAudio: shared module-level _volume ref with localStorage persistence,
  GainNode for volume control, exported setVoiceVolume()
- tts.py: pre-warm all Kokoro voices at pipeline load to eliminate HuggingFace
  HEAD requests at synthesis time (reduces TTS latency)
- BriefingView: discuss article button now auto-sends instead of just filling input;
  prompt capped to 15 sentences; send() accepts optional overrideText
- llm.py: instruct LLM not to proactively search notes or comment on note absence

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 16:52:11 -04:00
bvandeusen ea23f16bd7 feat: weather card — precip probability %, condition text, unit-aware wind
- Fetch precipitation_probability_max from Open-Meteo (replaces precip_sum
  in the card display — probability is more useful at a glance than mm)
- Show WMO condition description text on each forecast day
- Convert wind speed to mph when temp unit is F; pass wind_unit in response
- Display 💧 X% chance of rain; 💨 X mph/km/h wind per day

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 13:02:36 -04:00
bvandeusen c1fcb1e287 feat: discuss article in briefing chat via 💬 button on news cards
Clicking 💬 on a news card in the briefing panel pre-fills the briefing
chat input with the article title, snippet, and source so the user can
ask the briefing LLM to summarize or discuss it directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 12:57:25 -04:00
bvandeusen 71ca0ecb5c fix: voice status global store, per-view mic reactivity, single-voice preview
- Move voice status into settings store (voiceSttReady, voiceTtsReady),
  checked once at login and refreshed after admin model reload
- ChatView, BriefingView, DashboardChatInput now use computed refs from
  the store — mic buttons appear reactively without needing a page reload
- BriefingView: separate STT-only guard for mic PTT vs TTS-only guard for
  listen mode / speak buttons
- Add ▶ Preview button to Voice & Speed section in Settings for single-
  voice testing without enabling blend mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:53:06 -04:00
bvandeusen b4b4b0d9d6 feat: weather precip/wind, dashboard mic, remove global voice overlay
- WeatherCard: show precipitation (mm) and max wind speed per forecast day
- DashboardChatInput: add PTT mic button (transcribe-to-input, voice-gated)
- Remove global VoiceOverlay floating button and Space PTT shortcut from
  App.vue — inline mic buttons in chat/briefing/dashboard are the right UX;
  global overlay had focus/latency/context issues

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:00:06 -04:00
bvandeusen 76c3dbc4b7 fix: stop TTS playback when PTT is activated
Pressing push-to-talk now immediately stops any ongoing TTS audio before
opening the microphone, preventing the assistant from hearing itself.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 19:43:15 -04:00
bvandeusen dd304bb556 feat: unify voice PTT across briefing and chat views
- ChatView: add PTT mic button in input bar (hold to speak → transcribe → send)
- BriefingView: restyle input bar to match ChatView (floating pill, circle send)
- BriefingView: move listen/mic controls into input bar, remove from header
- BriefingView: consistent icon-button style for speaker/mic matching ChatView

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 21:58:20 -04:00
bvandeusen 6f84d90dff feat: voice S2S — faster-whisper STT, Kokoro TTS, PTT overlay
Implements full speech-to-speech pipeline (all 4 phases):

Backend (Phase 1):
- services/stt.py: lazy WhisperModel singleton, run_in_executor transcription
- services/tts.py: lazy KPipeline singleton, WAV synthesis at 24kHz/16-bit
- routes/voice.py: /api/voice/status, /voices, /transcribe, /synthesise
- config.py: VOICE_ENABLED, STT_BACKEND, STT_MODEL, TTS_BACKEND env vars
- app.py: load STT/TTS models at startup when VOICE_ENABLED=true
- llm.py: voice_mode + voice_speech_style params inject speak-naturally prefix
- generation_task.py: voice_mode passed through from chat route
- chat.py: "voice" conversation type allowed + excluded from retention cleanup
- pyproject.toml + Dockerfile: faster-whisper, kokoro, soundfile dependencies

Frontend (Phases 2–4):
- composables/useVoiceRecorder.ts: MediaRecorder PTT wrapper
- composables/useVoiceAudio.ts: AudioContext WAV playback wrapper
- BriefingView.vue: Listen button (TTS read-aloud), auto-TTS mode, mic PTT
- VoiceOverlay.vue: global floating PTT button; creates/reuses voice conv;
  full record→transcribe→stream→TTS flow; Space bar hold-to-talk via App.vue
- SettingsView.vue: Voice tab (status badge, speech style, voice/speed)
- App.vue: mounts VoiceOverlay; Space keydown/keyup fires voice:ptt-toggle
- api/client.ts: getVoiceStatus, getVoiceList, transcribeAudio, synthesiseSpeech

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 20:03:38 -04:00
bvandeusen 697d99cc4d fix: pass temp_unit from API to WeatherCard so F/C label matches converted values 2026-03-28 13:21:30 -04:00
bvandeusen f81c38df84 feat: redesign BriefingView into 3-column layout (weather · chat · news)
- Left column: weather loaded independently via /api/briefing/weather
- Center column: chat messages with input bar (unchanged behavior)
- Right column: news panel loaded from /api/briefing/news (last 2 days)
- Auto-scroll to bottom on mount and after streaming
- Background refresh also refreshes news panel
- Responsive: stacks to single column on narrow screens
- Fix TaskCard.vue to include 'cancelled' in status records

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 00:38:04 -04:00
bvandeusen 2a1644e571 feat: news story cards in briefing — backend embeds structured RSS items in metadata
Previously metadata only stored rss_item_ids (integers); the full item data
was discarded after LLM synthesis. Now rss_items (id, title, url, source,
snippet, published_at) is also stored so clients can render per-story cards
without additional API calls.

Web BriefingView: replaces bare reaction-row buttons with news cards showing
source, headline (linked), 2-line snippet, and 👍/👎 per card.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 00:08:21 -04:00
bvandeusen 699e525cb9 refactor: DRY pass on frontend — shared palette, composable, and tab loader
- Extract milestoneColor to utils/palette.ts; remove duplicate in HomeView + ProjectListView
- Create useBackgroundRefresh composable; wire into HomeView + BriefingView (removes manual setInterval/clearInterval boilerplate)
- Extract _loadTabContent() in SettingsView so watch and onMounted share one tab→loader mapping
- Move raw fetch() api-key calls to typed helpers in api/client.ts (listApiKeys, createApiKey, revokeApiKey)
- Drop unused onUnmounted import from BriefingView

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 22:43:59 -04:00
bvandeusen 916cfa50df fix: add missing onUnmounted import in BriefingView 2026-03-26 18:50:25 -04:00
bvandeusen 08d738ddfb feat: silent background polling for dashboard and briefing views
HomeView: setInterval every 90s refreshes events, orphan tasks/notes,
and hero next-up task. Never touches loading ref, so the page content
stays stable — only the data silently swaps when the fetch completes.
Skips when document.hidden or initial load is still in progress.

BriefingView: setInterval every 60s refetches today's messages, but
only when viewing today's conversation and not currently streaming.
Compares message count and last message content before updating to
avoid unnecessary re-renders.

Both timers are cleared on unmount.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 17:43:21 -04:00
bvandeusen 06cb7cc86d feat(briefing): render WeatherCard and RSS reaction buttons from message metadata
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 10:45:16 -04:00
bvandeusen 1686289af4 feat: daily briefing frontend — view, setup wizard, settings tab, nav
- BriefingView: soft chat UI with conversation history dropdown, SSE
  streaming via chat store, manual refresh trigger, today/past conv toggle
- BriefingSetupWizard: 4-step first-time setup overlay (welcome →
  locations → office days → RSS feeds)
- SettingsView: Briefing tab with enable toggle, location geocoding,
  office day picker, slot toggles, RSS feed management, notifications
- AppHeader + router: /briefing route and nav link (desktop + mobile)
- client.ts: briefing types and API functions (config, feeds, convs,
  geocode, trigger)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 20:30:45 -04:00