fix(kon): rename ramble refs, fix export extensions, use shared constants in DictationPage
- Rename custom event from ramble:toggle-recording to kon:toggle-recording - Change download filename prefix from ramble- to kon- - Fix export extension mapping: csv and html formats now produce correct file extensions instead of falling through to .txt - Add console.warn when PCM buffer exceeds 5-minute cap and gets truncated - Replace magic numbers with shared constants (MAX_PCM_SAMPLES, MIN_CHUNK_SAMPLES, CHUNK_INTERVAL_MS, FEEDBACK_TIMEOUT_MS) - Use shared pad() utility from time.js instead of inline reimplementation - Remove unused saveTasks import - Simplify redundant 3-branch append logic to 2 branches with clarifying comment - Use $derived.by to avoid double transcript.trim() in wordCount Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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;
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full animate-fade-in">
|
||||
|
||||
Reference in New Issue
Block a user