diff --git a/assets/css/tools.css b/assets/css/tools.css index 3b1f3a6..23b1e87 100644 --- a/assets/css/tools.css +++ b/assets/css/tools.css @@ -457,6 +457,12 @@ p { .trace-status.running { background: var(--amber); + animation: trace-pulse 1.4s ease-in-out infinite; +} + +@keyframes trace-pulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.4; transform: scale(0.7); } } .trace-status.warning { diff --git a/assets/js/tools.js b/assets/js/tools.js index 2adb355..50f08e2 100644 --- a/assets/js/tools.js +++ b/assets/js/tools.js @@ -564,7 +564,19 @@ async function runTranscribe() { return; } setBusy(true); - renderTrace([{ label: 'Sending to Whisper', detail: 'Uploading audio to cuttlefish GPU…', status: 'running' }]); + + const startTime = Date.now(); + let elapsed = 0; + updateTranscribeTrace(0); + els.status.textContent = 'Transcribing…'; + + const timer = setInterval(() => { + elapsed = Math.floor((Date.now() - startTime) / 1000); + const m = Math.floor(elapsed / 60); + const s = elapsed % 60; + els.status.textContent = m > 0 ? `Transcribing… ${m}:${pad2(s)}` : `Transcribing… ${s}s`; + updateTranscribeTrace(elapsed); + }, 1000); try { const formData = new FormData(); @@ -595,10 +607,29 @@ async function runTranscribe() { els.status.textContent = error.message; renderTrace([{ label: 'Transcription error', detail: error.message, status: 'warning' }]); } finally { + clearInterval(timer); setBusy(false); } } +function updateTranscribeTrace(elapsed) { + let label, detail; + if (elapsed < 10) { + label = 'Uploading to Whisper'; + detail = 'Sending audio to cuttlefish GPU…'; + } else if (elapsed < 60) { + label = 'Processing on GPU'; + detail = 'Whisper is transcribing. Large files take 1–3 minutes.'; + } else if (elapsed < 120) { + label = 'Still processing…'; + detail = `${Math.floor(elapsed / 60)} min elapsed — Whisper is working through the audio.`; + } else { + label = 'Still processing…'; + detail = `${Math.floor(elapsed / 60)} min ${pad2(elapsed % 60)}s — long recordings can take several minutes.`; + } + renderTrace([{ label, detail, status: 'running' }]); +} + function renderTranscriptResults(data) { const speakerRoles = data.speaker_roles || {}; const segments = data.segments || [];