From 30981a31218aa01a2346c7b91adf864d1a68a962 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 1 Apr 2026 11:53:03 -0400 Subject: [PATCH] feat: persist listen mode across views via localStorage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract listen mode into a shared useListenMode() composable backed by localStorage ('fa_listen_mode'). ChatView and BriefingView both use it, so toggling auto-read on in one view keeps it on after navigation or page refresh — no need to re-enable it each visit. Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/composables/useListenMode.ts | 14 ++++++++++++++ frontend/src/views/BriefingView.vue | 3 ++- frontend/src/views/ChatView.vue | 3 ++- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 frontend/src/composables/useListenMode.ts diff --git a/frontend/src/composables/useListenMode.ts b/frontend/src/composables/useListenMode.ts new file mode 100644 index 0000000..bfcabd2 --- /dev/null +++ b/frontend/src/composables/useListenMode.ts @@ -0,0 +1,14 @@ +import { ref, watch } from 'vue' + +const LISTEN_MODE_KEY = 'fa_listen_mode' + +// Shared across all views — persisted to localStorage +const _listenMode = ref(localStorage.getItem(LISTEN_MODE_KEY) === 'true') + +watch(_listenMode, (v) => { + localStorage.setItem(LISTEN_MODE_KEY, String(v)) +}) + +export function useListenMode() { + return _listenMode +} diff --git a/frontend/src/views/BriefingView.vue b/frontend/src/views/BriefingView.vue index 0de15f3..a956c10 100644 --- a/frontend/src/views/BriefingView.vue +++ b/frontend/src/views/BriefingView.vue @@ -3,6 +3,7 @@ import { ref, computed, onMounted, watch, nextTick } from 'vue' import { useBackgroundRefresh } from '@/composables/useBackgroundRefresh' import { useVoiceRecorder } from '@/composables/useVoiceRecorder' import { useVoiceAudio } from '@/composables/useVoiceAudio' +import { useListenMode } from '@/composables/useListenMode' import { useChatStore } from '@/stores/chat' import { useSettingsStore } from '@/stores/settings' import ChatMessage from '@/components/ChatMessage.vue' @@ -258,7 +259,7 @@ function convLabel(c: BriefingConversation): string { // Mic PTT needs STT; listen/speak mode also needs TTS const voiceEnabled = computed(() => settingsStore.voiceSttReady) const voiceTtsEnabled = computed(() => settingsStore.voiceTtsReady) -const listenMode = ref(false) +const listenMode = useListenMode() const transcribing = ref(false) const synthesising = ref(false) diff --git a/frontend/src/views/ChatView.vue b/frontend/src/views/ChatView.vue index e3c1c08..33d92b2 100644 --- a/frontend/src/views/ChatView.vue +++ b/frontend/src/views/ChatView.vue @@ -7,6 +7,7 @@ import { apiGet, transcribeAudio, synthesiseSpeech } from "@/api/client"; import { renderMarkdown } from "@/utils/markdown"; import { useVoiceRecorder } from "@/composables/useVoiceRecorder"; import { useVoiceAudio, setVoiceVolume } from "@/composables/useVoiceAudio"; +import { useListenMode } from "@/composables/useListenMode"; import ChatMessage from "@/components/ChatMessage.vue"; import ToolCallCard from "@/components/ToolCallCard.vue"; import ToolConfirmCard from "@/components/ToolConfirmCard.vue"; @@ -30,7 +31,7 @@ const recorder = useVoiceRecorder(); const audio = useVoiceAudio(); const voiceEnabled = computed(() => settingsStore.voiceSttReady); const voiceTtsEnabled = computed(() => settingsStore.voiceTtsReady); -const listenMode = ref(false); +const listenMode = useListenMode(); const synthesising = ref(false); const showVolumeSlider = ref(false);