From ada389004f73a65250dd27147372c356d09becf7 Mon Sep 17 00:00:00 2001 From: "hermes-seaeye[bot]" <307254004+hermes-seaeye[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 01:49:23 +0000 Subject: [PATCH] fmt(js): `npm run fix` on merge (#73770) Co-authored-by: github-actions[bot] --- apps/desktop/src/app/chat/composer/contrib.ts | 5 +---- apps/desktop/src/app/chat/composer/controls.tsx | 15 ++++++++++++++- .../app/chat/composer/hooks/use-composer-voice.ts | 2 ++ apps/desktop/src/app/contrib/wiring.tsx | 14 ++++++++++---- .../app/right-sidebar/terminal/clipboard.test.ts | 4 +++- .../terminal/use-terminal-session.ts | 1 - apps/desktop/src/lib/session-source.test.ts | 6 +----- apps/desktop/src/lib/voice-playback.ts | 2 ++ apps/desktop/src/lib/wake-sound.test.ts | 1 + apps/desktop/src/store/wake-word.test.ts | 8 ++++---- ui-tui/src/gatewayTypes.ts | 6 +++++- 11 files changed, 43 insertions(+), 21 deletions(-) diff --git a/apps/desktop/src/app/chat/composer/contrib.ts b/apps/desktop/src/app/chat/composer/contrib.ts index b7cba9d89e6f..94d19457a58f 100644 --- a/apps/desktop/src/app/chat/composer/contrib.ts +++ b/apps/desktop/src/app/chat/composer/contrib.ts @@ -133,10 +133,7 @@ export function useComposerMicroActionProviders(): ComposerMicroActionProvider[] const contributions = useContributions(COMPOSER_AREAS.microActions) return useMemo( - () => - contributions - .map(c => c.data as ComposerMicroActionProvider) - .filter(p => typeof p?.resolve === 'function'), + () => contributions.map(c => c.data as ComposerMicroActionProvider).filter(p => typeof p?.resolve === 'function'), [contributions] ) } diff --git a/apps/desktop/src/app/chat/composer/controls.tsx b/apps/desktop/src/app/chat/composer/controls.tsx index 7c1a90df4de2..06a3869894fe 100644 --- a/apps/desktop/src/app/chat/composer/controls.tsx +++ b/apps/desktop/src/app/chat/composer/controls.tsx @@ -5,7 +5,18 @@ import { Codicon } from '@/components/ui/codicon' import { Tip, TipKeybindLabel } from '@/components/ui/tooltip' import { useI18n } from '@/i18n' import { triggerHaptic } from '@/lib/haptics' -import { AudioLines, Ear, EarOff, iconSize, Layers3, Loader2, Square, SteeringWheel, Volume2, VolumeX } from '@/lib/icons' +import { + AudioLines, + Ear, + EarOff, + iconSize, + Layers3, + Loader2, + Square, + SteeringWheel, + Volume2, + VolumeX +} from '@/lib/icons' import { cn } from '@/lib/utils' import { $wakeWord, toggleWakeWord } from '@/store/wake-word' @@ -316,11 +327,13 @@ function WakeWordButton({ disabled, pausedForVoice = false }: { disabled: boolea const wake = useStore($wakeWord) const phrase = wake.phrase || 'hey hermes' + const label = pausedForVoice ? c.wakeWordPausedVoice(phrase) : wake.listening ? c.wakeWordListening(phrase) : c.wakeWordOff(phrase) + const tooltip = !pausedForVoice && wake.notice ? `${label} — ${wake.notice}` : label return ( diff --git a/apps/desktop/src/app/chat/composer/hooks/use-composer-voice.ts b/apps/desktop/src/app/chat/composer/hooks/use-composer-voice.ts index e25a6bfc8ed3..5184232f87b9 100644 --- a/apps/desktop/src/app/chat/composer/hooks/use-composer-voice.ts +++ b/apps/desktop/src/app/chat/composer/hooks/use-composer-voice.ts @@ -186,6 +186,7 @@ export function useComposerVoice({ // it guards resumeWakeIfPaused from resuming a detector another surface owns. const pauseWakeForVoice = useCallback(() => { wakePausedRef.current = true + const barrier = (async () => { try { await $gateway.get()?.request('wake.pause', {}) @@ -193,6 +194,7 @@ export function useComposerVoice({ // No wake listener / older backend — nothing held the mic. } })() + wakePauseBarrierRef.current = barrier return barrier diff --git a/apps/desktop/src/app/contrib/wiring.tsx b/apps/desktop/src/app/contrib/wiring.tsx index 0676c2da77d3..190e4b4acb5a 100644 --- a/apps/desktop/src/app/contrib/wiring.tsx +++ b/apps/desktop/src/app/contrib/wiring.tsx @@ -35,7 +35,15 @@ import { requestVoiceConversationStart } from '@/store/composer' import { setCronFocusJobId } from '@/store/cron' import { $pinnedSessionIds, pinSession, restoreWorktree, unpinSession } from '@/store/layout' import { $previewTarget } from '@/store/preview' -import { $activeGatewayProfile, $freshSessionRequest, $profileScope, ensureGatewayProfile, newSessionInProfile, normalizeProfileKey, refreshActiveProfile } from '@/store/profile' +import { + $activeGatewayProfile, + $freshSessionRequest, + $profileScope, + ensureGatewayProfile, + newSessionInProfile, + normalizeProfileKey, + refreshActiveProfile +} from '@/store/profile' import { $startWorkSessionRequest, followActiveSessionCwd } from '@/store/projects' import { $activeSessionId, @@ -667,9 +675,7 @@ export function ContribWiring({ children }: { children: ReactNode }) { emitGatewayEvent(event) if (event.type === 'wake.detected') { - const payload = event.payload as - | { profile?: null | string; start_new_session?: boolean } - | undefined + const payload = event.payload as { profile?: null | string; start_new_session?: boolean } | undefined // Audible confirmation that the wake registered, before voice capture // starts. Gated by the shared sound-mute toggle. diff --git a/apps/desktop/src/app/right-sidebar/terminal/clipboard.test.ts b/apps/desktop/src/app/right-sidebar/terminal/clipboard.test.ts index b39e3f9e4500..51ce6cb2d058 100644 --- a/apps/desktop/src/app/right-sidebar/terminal/clipboard.test.ts +++ b/apps/desktop/src/app/right-sidebar/terminal/clipboard.test.ts @@ -29,7 +29,9 @@ describe('terminalClipboardIntent', () => { }) it('claims paste regardless of selection, since paste has nothing to do with one', () => { - expect(terminalClipboardIntent(key({ key: 'v', metaKey: true }), { hasSelection: false, isMac: true })).toBe('paste') + expect(terminalClipboardIntent(key({ key: 'v', metaKey: true }), { hasSelection: false, isMac: true })).toBe( + 'paste' + ) expect( terminalClipboardIntent(key({ ctrlKey: true, key: 'v', shiftKey: true }), { hasSelection: false, isMac: false }) ).toBe('paste') diff --git a/apps/desktop/src/app/right-sidebar/terminal/use-terminal-session.ts b/apps/desktop/src/app/right-sidebar/terminal/use-terminal-session.ts index 170823ad4a9a..09cd815413a6 100644 --- a/apps/desktop/src/app/right-sidebar/terminal/use-terminal-session.ts +++ b/apps/desktop/src/app/right-sidebar/terminal/use-terminal-session.ts @@ -832,7 +832,6 @@ export function useTerminalSession({ return false } - void (async () => { const text = (await window.hermesDesktop?.readClipboard?.()) ?? '' diff --git a/apps/desktop/src/lib/session-source.test.ts b/apps/desktop/src/lib/session-source.test.ts index 2a5de830f905..46dcef941df1 100644 --- a/apps/desktop/src/lib/session-source.test.ts +++ b/apps/desktop/src/lib/session-source.test.ts @@ -1,10 +1,6 @@ import { describe, expect, it } from 'vitest' -import { - isMessagingSource, - MESSAGING_SESSION_SOURCE_IDS, - sessionSourceSearchTerms -} from './session-source' +import { isMessagingSource, MESSAGING_SESSION_SOURCE_IDS, sessionSourceSearchTerms } from './session-source' // Regression guard for #46761 / PR #47395: Photon (iMessage) must keep its own // sidebar section. refreshMessagingSessions() filters rows through diff --git a/apps/desktop/src/lib/voice-playback.ts b/apps/desktop/src/lib/voice-playback.ts index 2a15cd3d26ba..48a51d370817 100644 --- a/apps/desktop/src/lib/voice-playback.ts +++ b/apps/desktop/src/lib/voice-playback.ts @@ -273,6 +273,7 @@ function openSpeechStream(wsUrl: string, options: VoicePlaybackOptions): SpeechS if (frame.type === 'start') { streamRate = frame.sample_rate || 24_000 context = new AudioContext() + // Autoplay policy can hand back a suspended context when playback wasn't // started by a user gesture (e.g. a wake-word-started voice turn). Resume // it so the first reply is audible instead of silently buffering. Electron @@ -281,6 +282,7 @@ function openSpeechStream(wsUrl: string, options: VoicePlaybackOptions): SpeechS if (context.state === 'suspended') { void context.resume().catch(() => undefined) } + nextStartAt = 0 } else if (frame.type === 'end') { finishWhenDrained() diff --git a/apps/desktop/src/lib/wake-sound.test.ts b/apps/desktop/src/lib/wake-sound.test.ts index 354eff8e44fe..0da3f3e30492 100644 --- a/apps/desktop/src/lib/wake-sound.test.ts +++ b/apps/desktop/src/lib/wake-sound.test.ts @@ -63,6 +63,7 @@ describe('playWakeSound', () => { expect(oscillators).toHaveLength(2) expect(oscillators[0].frequency.setValueAtTime).toHaveBeenCalledWith(783.99, expect.any(Number)) expect(oscillators[1].frequency.setValueAtTime).toHaveBeenCalledWith(1046.5, expect.any(Number)) + for (const osc of oscillators) { expect(osc.start).toHaveBeenCalled() expect(osc.stop).toHaveBeenCalled() diff --git a/apps/desktop/src/store/wake-word.test.ts b/apps/desktop/src/store/wake-word.test.ts index f69321e3f5b3..1d09a92eef2a 100644 --- a/apps/desktop/src/store/wake-word.test.ts +++ b/apps/desktop/src/store/wake-word.test.ts @@ -13,7 +13,9 @@ import { } from './wake-word' const requester = (impl: (method: string, params?: Record) => unknown) => - vi.fn(async (method: string, params: Record = {}) => impl(method, params)) as unknown as WakeRequester + vi.fn(async (method: string, params: Record = {}) => + impl(method, params) + ) as unknown as WakeRequester beforeEach(() => { resetWakeWordState() @@ -108,9 +110,7 @@ describe('toggleWakeWord', () => { it('marks the feature unavailable when start refuses with reason unavailable', async () => { applyWakeStatus({ available: true, listening: false, phrase: 'hey hermes' }) - await toggleWakeWord( - requester(() => ({ hint: 'Set PORCUPINE_ACCESS_KEY', reason: 'unavailable', started: false })) - ) + await toggleWakeWord(requester(() => ({ hint: 'Set PORCUPINE_ACCESS_KEY', reason: 'unavailable', started: false }))) const state = $wakeWord.get() expect(state.available).toBe(false) diff --git a/ui-tui/src/gatewayTypes.ts b/ui-tui/src/gatewayTypes.ts index 954190d028a3..4a8184dc3cda 100644 --- a/ui-tui/src/gatewayTypes.ts +++ b/ui-tui/src/gatewayTypes.ts @@ -621,7 +621,11 @@ export type GatewayEvent = } | { payload?: { state?: 'idle' | 'listening' | 'transcribing' }; session_id?: string; type: 'voice.status' } | { payload?: { no_speech_limit?: boolean; text?: string }; session_id?: string; type: 'voice.transcript' } - | { payload?: { phrase?: string; profile?: null | string; start_new_session?: boolean }; session_id?: string; type: 'wake.detected' } + | { + payload?: { phrase?: string; profile?: null | string; start_new_session?: boolean } + session_id?: string + type: 'wake.detected' + } | { payload?: { reason?: string }; session_id?: string; type: 'dashboard.new_session_requested' } | { payload: { line: string }; session_id?: string; type: 'gateway.stderr' } | {