feat(ui B.1 #20): sound cues on start / stop / complete via Web Audio
Hands-off feedback for recording lifecycle: a short C5 pitch at record start, a falling G4 at record stop, and a staggered C5–E5–G5 major third when the finalise flow completes. Synthesised at runtime via the Web Audio API (OscillatorNode + linear-ramped GainNode envelope) rather than shipping WAV assets — keeps the binary size flat and lets us tweak timbre without touching bundled files. Off by default. Settings → Output exposes the toggle with a volume slider (0–100%, default 15%) and a "Test" button that plays the completion cue so the user can confirm loudness without recording. Hooked at three call sites in DictationPage: - playStartCue after page.recording = true in startRecording - playStopCue at the top of stopRecording - playCompleteCue just before `saved = true` at the end of finaliseTranscription's transcript-present branch All three no-op when settings.soundCues is false. The Web Audio context is lazily constructed on first cue (most browsers suspend it until a user gesture — Tauri's webview inherits that). If the AudioContext can't be built we silently drop the cue rather than throwing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { page, settings, templates, profiles, addToHistory, addTask, tasks } from "$lib/stores/page.svelte.js";
|
||||
import { markGenerating, markGenerationDone } from "$lib/stores/llmStatus.svelte.js";
|
||||
import { playStartCue, playStopCue, playCompleteCue } from "$lib/utils/sounds.js";
|
||||
import { profilesStore } from "$lib/stores/profiles.svelte.ts";
|
||||
import { toasts } from "$lib/stores/toasts.svelte.js";
|
||||
import Card from "$lib/components/Card.svelte";
|
||||
@@ -358,6 +359,9 @@
|
||||
page.statusColor = "#e87171";
|
||||
timerInterval = setInterval(updateTimer, 1000);
|
||||
|
||||
// Start cue (brief item #20). Off by default; volume user-set.
|
||||
playStartCue(settings.soundCueVolume, settings.soundCues);
|
||||
|
||||
// Preview overlay: if the user opted in, reset any leftover state
|
||||
// from a prior run and open the window when the main window is not
|
||||
// focused (i.e. the user is dictating into some other app).
|
||||
@@ -389,6 +393,9 @@
|
||||
page.status = "Finalising...";
|
||||
page.statusColor = "#e8c86e";
|
||||
|
||||
// Stop cue (brief item #20). "Recording over, finalising now."
|
||||
playStopCue(settings.soundCueVolume, settings.soundCues);
|
||||
|
||||
try {
|
||||
const activityBeforeStop = lastLiveActivityAt;
|
||||
drainingSessionId = sessionId;
|
||||
@@ -570,6 +577,11 @@
|
||||
page.taskSidebarOpen = true;
|
||||
}
|
||||
|
||||
// Complete cue (brief item #20) — major-third arpeggio. Plays
|
||||
// after the full finalise flow so the user hears "done"
|
||||
// aligned with the history entry + paste landing.
|
||||
playCompleteCue(settings.soundCueVolume, settings.soundCues);
|
||||
|
||||
saved = true;
|
||||
setTimeout(() => { saved = false; extractedCount = 0; }, 4000);
|
||||
} else if (settings.transcriptionPreview) {
|
||||
|
||||
Reference in New Issue
Block a user