From b4b4b0d9d614de74652cd7b773fca402ec8a4230 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 30 Mar 2026 20:00:06 -0400 Subject: [PATCH] feat: weather precip/wind, dashboard mic, remove global voice overlay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- frontend/src/App.vue | 25 ------- .../src/components/DashboardChatInput.vue | 71 ++++++++++++++++++- frontend/src/components/WeatherCard.vue | 16 +++++ frontend/src/views/BriefingView.vue | 2 +- src/fabledassistant/services/weather.py | 2 + 5 files changed, 88 insertions(+), 28 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 7220313..6d89270 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -3,7 +3,6 @@ import { onMounted, onUnmounted, ref, watch } from "vue"; import { useRouter } from "vue-router"; import AppHeader from "@/components/AppHeader.vue"; import ToastNotification from "@/components/ToastNotification.vue"; -import VoiceOverlay from "@/components/VoiceOverlay.vue"; import { useTheme } from "@/composables/useTheme"; import { useShortcuts } from "@/composables/useShortcuts"; import { useAuthStore } from "@/stores/auth"; @@ -48,21 +47,9 @@ function clearPrefix() { prefixTimeout = null; } -let spaceHeld = false; - function onGlobalKeydown(e: KeyboardEvent) { if (!authStore.isAuthenticated) return; - // Space — PTT start (only when not typing, no modifiers, no repeat) - if (e.key === " " && !isInputActive() && !e.ctrlKey && !e.metaKey && !e.altKey && !e.repeat) { - e.preventDefault(); - if (!spaceHeld) { - spaceHeld = true; - document.dispatchEvent(new CustomEvent("voice:ptt-toggle")); - } - return; - } - // ? — toggle shortcuts overlay (only when not typing) if (e.key === "?" && !isInputActive() && !e.ctrlKey && !e.metaKey) { toggleShortcuts(); @@ -135,16 +122,8 @@ function onGlobalKeydown(e: KeyboardEvent) { } } -function onGlobalKeyup(e: KeyboardEvent) { - if (e.key === " " && spaceHeld) { - spaceHeld = false; - document.dispatchEvent(new CustomEvent("voice:ptt-toggle")); - } -} - onMounted(async () => { document.addEventListener("keydown", onGlobalKeydown); - document.addEventListener("keyup", onGlobalKeyup); await authStore.checkAuth(); if (authStore.isAuthenticated) { startAppServices(); @@ -170,7 +149,6 @@ watch( onUnmounted(() => { document.removeEventListener("keydown", onGlobalKeydown); - document.removeEventListener("keyup", onGlobalKeyup); stopAppServices(); }); @@ -186,9 +164,6 @@ onUnmounted(() => {
v{{ appVersion }}
- - -
diff --git a/frontend/src/components/DashboardChatInput.vue b/frontend/src/components/DashboardChatInput.vue index a761a74..17120c6 100644 --- a/frontend/src/components/DashboardChatInput.vue +++ b/frontend/src/components/DashboardChatInput.vue @@ -1,6 +1,7 @@ @@ -188,6 +222,27 @@ defineExpose({ focus }); rows="1" > + +
@@ -185,6 +190,17 @@ const fetchedAtLabel = computed(() => { white-space: nowrap; } +.forecast-precip, +.forecast-wind { + font-size: 0.72rem; + white-space: nowrap; + color: var(--color-text-muted); +} + +.forecast-precip--dry { + opacity: 0.35; +} + .weather-unavailable { color: var(--color-text-muted); font-style: italic; diff --git a/frontend/src/views/BriefingView.vue b/frontend/src/views/BriefingView.vue index 9dd06da..ca69a74 100644 --- a/frontend/src/views/BriefingView.vue +++ b/frontend/src/views/BriefingView.vue @@ -35,7 +35,7 @@ interface WeatherData { today_low: number | null yesterday_high: number | null yesterday_low: number | null - forecast: { day: string; condition: string; high: number; low: number }[] + forecast: { day: string; condition: string; high: number; low: number; precip_mm: number; windspeed_max: number }[] } const chatStore = useChatStore() diff --git a/src/fabledassistant/services/weather.py b/src/fabledassistant/services/weather.py index 6004b8b..9b08d49 100644 --- a/src/fabledassistant/services/weather.py +++ b/src/fabledassistant/services/weather.py @@ -146,6 +146,8 @@ def parse_weather_card_data( "condition": d["description"], "high": to_temp(d["temp_max"]), "low": to_temp(d["temp_min"]), + "precip_mm": round(d["precip_mm"], 1), + "windspeed_max": round(d["windspeed_max"]), } for d in future_days ],