feat(tts): Phase 4 — Read Page Aloud with OS-native voices
Platform-dispatched TTS (spd-say + espeak-ng fallback on Linux, say on macOS, PowerShell System.Speech on Windows) with a shared SpeakerButton component. Tap to speak, tap again to stop; only one button speaks at a time so two surfaces don't talk over each other. Text always travels via argv (or a PowerShell here-string delivered through -EncodedCommand on Windows) so user content never enters a shell string. Mount points: DictationPage transcript footer, transcript viewer header, per-step in MicroSteps. Settings gains a "Read aloud" accordion with voice picker (lazy-loaded from the OS synth), rate slider 0.5-2.0x, and a British-English test utterance. Rust tests cover rate mapping, NaN handling, and Windows here-string terminator safety. No pause/resume, no SSML, no cloud voices — that stays out of scope per the Layer-1 roadmap.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { convertFileSrc, invoke } from "@tauri-apps/api/core";
|
||||
import type { TranscriptEntry, ViewerSegment } from "$lib/types/app";
|
||||
import SpeakerButton from "$lib/components/SpeakerButton.svelte";
|
||||
import { formatTime, formatDuration } from "$lib/utils/time.js";
|
||||
import { PLAYBACK_SPEEDS } from "$lib/utils/constants.js";
|
||||
import { errorMessage } from "$lib/utils/errors.js";
|
||||
@@ -374,13 +375,18 @@
|
||||
|
||||
{#if item}
|
||||
<!-- Item info -->
|
||||
<div class="px-5 pt-3 pb-2">
|
||||
<p class="text-[13px] text-text font-medium">
|
||||
{item.title || "Transcript"}
|
||||
</p>
|
||||
<p class="text-[10px] text-text-tertiary mt-0.5">
|
||||
{item.date}{#if item.duration} · {formatDuration(item.duration)}{/if} · {item.source}
|
||||
</p>
|
||||
<div class="px-5 pt-3 pb-2 flex items-start gap-2">
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-[13px] text-text font-medium">
|
||||
{item.title || "Transcript"}
|
||||
</p>
|
||||
<p class="text-[10px] text-text-tertiary mt-0.5">
|
||||
{item.date}{#if item.duration} · {formatDuration(item.duration)}{/if} · {item.source}
|
||||
</p>
|
||||
</div>
|
||||
{#if item.text?.trim()}
|
||||
<SpeakerButton text={item.text} label="Read transcript aloud" size={14} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Media controls -->
|
||||
|
||||
Reference in New Issue
Block a user