mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-07 13:02:07 +00:00
* feat(desktop): add curated completion sound bank for turn completion Replace the prior haptic-only completion cue with a curated Web Audio completion sound flow, defaulting to the minimal two-note comfort preset while keeping alternate presets available for quick iteration. Play the cue on every message completion event (including background sessions) so turn-end feedback is consistent across active and non-active chats. * refactor(desktop): drop done1 byte sample from completion bank Keep the curated Web Audio presets only; the embedded sample added bulk without shipping as the default cue. * feat(desktop): expand completion sounds and add Appearance picker Add fourteen synthesized turn-end presets with preview in settings, persisted variant selection, and softer default mixing for late-night use. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(desktop): dedupe completion-sound resolver, trim audio comments Make the store the single source of truth for the variant default + range validation and have the sound lib import it (one-way lib→store edge, no cycle), instead of two divergent copies. Extract the shared white-noise buffer used by the air/whoosh voices and cut the synth comments down to why-only notes. --------- Co-authored-by: Austin Pickett <pickett.austin@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
32 lines
1.1 KiB
TypeScript
32 lines
1.1 KiB
TypeScript
import { atom } from 'nanostores'
|
|
|
|
import { persistString, storedString } from '@/lib/storage'
|
|
|
|
const STORAGE_KEY = 'hermes.desktop.completionSoundVariantId'
|
|
|
|
export const DEFAULT_COMPLETION_SOUND_VARIANT_ID = 1
|
|
|
|
// Range mirrors COMPLETION_SOUND_VARIANTS in lib/completion-sound.ts. Validating
|
|
// by range (not membership) keeps this store free of a dependency on the lib,
|
|
// which imports the atom back — a membership check would close that cycle.
|
|
const VARIANT_COUNT = 14
|
|
|
|
export function resolveCompletionSoundVariantId(variantId: number): number {
|
|
return Number.isInteger(variantId) && variantId >= 1 && variantId <= VARIANT_COUNT
|
|
? variantId
|
|
: DEFAULT_COMPLETION_SOUND_VARIANT_ID
|
|
}
|
|
|
|
function load(): number {
|
|
const stored = storedString(STORAGE_KEY)
|
|
|
|
return stored ? resolveCompletionSoundVariantId(Number.parseInt(stored, 10)) : DEFAULT_COMPLETION_SOUND_VARIANT_ID
|
|
}
|
|
|
|
export const $completionSoundVariantId = atom(load())
|
|
|
|
$completionSoundVariantId.subscribe(id => persistString(STORAGE_KEY, String(id)))
|
|
|
|
export function setCompletionSoundVariantId(variantId: number) {
|
|
$completionSoundVariantId.set(resolveCompletionSoundVariantId(variantId))
|
|
}
|