diff --git a/CHANGELOG.md b/CHANGELOG.md index 288458bd1..b6177b0a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Docs: https://docs.clawd.bot - Markdown: add per-channel table conversion (bullets for Signal/WhatsApp, code blocks elsewhere). (#1495) Thanks @odysseus0. ### Fixes +- macOS: sync voice wake trigger words on submit/blur in settings. (#1506) Thanks @shiv19. - TUI: forward unknown slash commands (for example, `/context`) to the Gateway. - TUI: include Gateway slash commands in autocomplete and `/help`. - CLI: skip usage lines in `clawdbot models status` when provider usage is unavailable. diff --git a/apps/macos/Sources/Clawdbot/VoiceWakeSettings.swift b/apps/macos/Sources/Clawdbot/VoiceWakeSettings.swift index a41e8bb1f..c28cb41b8 100644 --- a/apps/macos/Sources/Clawdbot/VoiceWakeSettings.swift +++ b/apps/macos/Sources/Clawdbot/VoiceWakeSettings.swift @@ -22,6 +22,7 @@ struct VoiceWakeSettings: View { @State private var micRefreshTask: Task? @State private var availableLocales: [Locale] = [] @State private var triggerEntries: [TriggerEntry] = [] + @FocusState private var focusedTriggerEntryID: UUID? private let fieldLabelWidth: CGFloat = 140 private let controlWidth: CGFloat = 240 private let isPreview = ProcessInfo.processInfo.isPreview @@ -142,6 +143,11 @@ struct VoiceWakeSettings: View { Task { await self.meter.stop() } self.syncTriggerEntriesToState() } + .onChange(of: self.state.swabbleTriggerWords) { _, _ in + guard !self.isPreview else { return } + guard self.focusedTriggerEntryID == nil else { return } + self.loadTriggerEntries() + } } private func loadTriggerEntries() { @@ -177,6 +183,7 @@ struct VoiceWakeSettings: View { HStack(spacing: 8) { TextField("Wake word", text: $entry.value) .textFieldStyle(.roundedBorder) + .focused(self.$focusedTriggerEntryID, equals: entry.id) .onSubmit { self.syncTriggerEntriesToState() } @@ -197,6 +204,10 @@ struct VoiceWakeSettings: View { } } } + .onChange(of: self.focusedTriggerEntryID) { oldValue, newValue in + guard oldValue != nil, oldValue != newValue else { return } + self.syncTriggerEntriesToState() + } .frame(maxWidth: .infinity, minHeight: 180, alignment: .topLeading) .background(Color(nsColor: .textBackgroundColor)) .clipShape(RoundedRectangle(cornerRadius: 6)) diff --git a/src/config/sessions/store.ts b/src/config/sessions/store.ts index c41181623..8a0468bb3 100644 --- a/src/config/sessions/store.ts +++ b/src/config/sessions/store.ts @@ -267,9 +267,10 @@ async function withSessionStoreLock( fn: () => Promise, opts: SessionStoreLockOptions = {}, ): Promise { - const timeoutMs = opts.timeoutMs ?? 10_000; + const isFastTest = process.env.CLAWDBOT_TEST_FAST === "1"; + const timeoutMs = opts.timeoutMs ?? (isFastTest ? 30_000 : 10_000); const pollIntervalMs = opts.pollIntervalMs ?? 25; - const staleMs = opts.staleMs ?? 30_000; + const staleMs = opts.staleMs ?? (isFastTest ? 5_000 : 30_000); const lockPath = `${storePath}.lock`; const startedAt = Date.now();