diff --git a/src/lib/pages/DictationPage.svelte b/src/lib/pages/DictationPage.svelte index 41e325e..1497fc4 100644 --- a/src/lib/pages/DictationPage.svelte +++ b/src/lib/pages/DictationPage.svelte @@ -2,11 +2,13 @@ import { onMount, onDestroy } from "svelte"; import { invoke } from "@tauri-apps/api/core"; import { listen } from "@tauri-apps/api/event"; - import { page, settings, templates, addToHistory, addTask, tasks, saveTasks } from "$lib/stores/page.svelte.js"; + import { page, settings, templates, addToHistory, addTask, tasks } from "$lib/stores/page.svelte.js"; import Card from "$lib/components/Card.svelte"; import ModelDownloader from "$lib/components/ModelDownloader.svelte"; import { exportTranscript } from "$lib/utils/export.js"; import { extractTasks } from "$lib/utils/taskExtractor.js"; + import { pad } from "$lib/utils/time.js"; + import { MAX_PCM_SAMPLES, MIN_CHUNK_SAMPLES, CHUNK_INTERVAL_MS, FEEDBACK_TIMEOUT_MS } from "$lib/utils/constants.js"; let transcript = $state(""); let segments = $state([]); @@ -62,14 +64,14 @@ handleResult(result); }); - window.addEventListener("ramble:toggle-recording", hotkeyHandler); + window.addEventListener("kon:toggle-recording", hotkeyHandler); await checkModelState(); }); onDestroy(() => { if (unlisten) unlisten(); - window.removeEventListener("ramble:toggle-recording", hotkeyHandler); + window.removeEventListener("kon:toggle-recording", hotkeyHandler); clearInterval(timerInterval); clearInterval(chunkTimer); if (page.recording) { @@ -106,9 +108,9 @@ }); } else { // Append mode - if (transcript && result.chunk_id > 1) { - transcript += " " + text; - } else if (!transcript) { + // First chunk (id=1) starts the transcript; subsequent chunks append to it. + // When transcript is empty, assign directly; otherwise prepend a space separator. + if (!transcript) { transcript = text; } else { transcript += " " + text; @@ -247,7 +249,7 @@ page.status = "Recording..."; page.statusColor = "#e87171"; timerInterval = setInterval(updateTimer, 1000); - chunkTimer = setInterval(sendChunk, 3000); + chunkTimer = setInterval(sendChunk, CHUNK_INTERVAL_MS); } catch (err) { error = `Microphone access denied: ${err.message}`; page.status = "Error"; @@ -294,15 +296,16 @@ } async function sendChunk() { - if (pcmBuffer.length < 8000) return; + if (pcmBuffer.length < MIN_CHUNK_SAMPLES) return; if (transcribing) return; chunkId++; const currentChunkId = chunkId; const samples = [...pcmBuffer]; pcmBuffer = []; - if (samples.length > 4_800_000) { - samples.length = 4_800_000; + if (samples.length > MAX_PCM_SAMPLES) { + console.warn(`PCM buffer truncated from ${samples.length} to ${MAX_PCM_SAMPLES} samples (~5 min at 16 kHz)`); + samples.length = MAX_PCM_SAMPLES; } transcribing = true; @@ -404,9 +407,7 @@ function updateTimer() { const elapsed = Math.floor((Date.now() - startTime) / 1000); - const mins = Math.floor(elapsed / 60).toString().padStart(2, "0"); - const secs = (elapsed % 60).toString().padStart(2, "0"); - page.timerText = `${mins}:${secs}`; + page.timerText = `${pad(Math.floor(elapsed / 60))}:${pad(elapsed % 60)}`; } function copyAll() { @@ -427,12 +428,13 @@ if (!transcript) return; showExportMenu = false; const content = exportTranscript(transcript, segments, format); - const ext = format === "vtt" ? "vtt" : format === "srt" ? "srt" : format === "md" ? "md" : "txt"; + const extMap = { vtt: "vtt", srt: "srt", md: "md", csv: "csv", html: "html" }; + const ext = extMap[format] || "txt"; const blob = new Blob([content], { type: "text/plain" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; - a.download = `ramble-${new Date().toISOString().slice(0, 10)}.${ext}`; + a.download = `kon-${new Date().toISOString().slice(0, 10)}.${ext}`; a.click(); URL.revokeObjectURL(url); } @@ -484,14 +486,15 @@ language: settings.language, }); saved = true; - setTimeout(() => { saved = false; }, 3000); + setTimeout(() => { saved = false; }, FEEDBACK_TIMEOUT_MS); } let taskCount = $derived(tasks.filter((t) => !t.done).length); - let wordCount = $derived( - transcript.trim() ? transcript.trim().split(/\s+/).length : 0 - ); + let wordCount = $derived.by(() => { + const trimmed = transcript.trim(); + return trimmed ? trimmed.split(/\s+/).length : 0; + });