diff --git a/src/lib/components/ProgressOverlay.svelte b/src/lib/components/ProgressOverlay.svelte
index 5adcc55..f55c488 100644
--- a/src/lib/components/ProgressOverlay.svelte
+++ b/src/lib/components/ProgressOverlay.svelte
@@ -4,9 +4,25 @@
percent?: number;
stage?: string;
message?: string;
+ onCancel?: () => void;
}
- let { visible = false, percent = 0, stage = '', message = '' }: Props = $props();
+ let { visible = false, percent = 0, stage = '', message = '', onCancel }: Props = $props();
+
+ let showConfirm = $state(false);
+
+ function handleCancelClick() {
+ showConfirm = true;
+ }
+
+ function confirmCancel() {
+ showConfirm = false;
+ onCancel?.();
+ }
+
+ function dismissCancel() {
+ showConfirm = false;
+ }
// Pipeline steps in order
const pipelineSteps = [
@@ -89,6 +105,20 @@
{message || 'Please wait...'}
This may take several minutes for large files
+
+ {#if onCancel && !showConfirm}
+
+ {/if}
+
+ {#if showConfirm}
+
+
Processing is incomplete. If you cancel now, the transcription will need to be started over.
+
+
+
+
+
+ {/if}
{/if}
@@ -174,4 +204,62 @@
font-size: 0.75rem;
color: #555;
}
+ .cancel-btn {
+ margin-top: 1.25rem;
+ width: 100%;
+ padding: 0.5rem;
+ background: none;
+ border: 1px solid #4a5568;
+ color: #999;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 0.85rem;
+ }
+ .cancel-btn:hover {
+ color: #e0e0e0;
+ border-color: #e94560;
+ }
+ .confirm-box {
+ margin-top: 1.25rem;
+ padding: 0.75rem;
+ background: rgba(233, 69, 96, 0.08);
+ border: 1px solid #e94560;
+ border-radius: 6px;
+ }
+ .confirm-text {
+ margin: 0 0 0.75rem;
+ font-size: 0.8rem;
+ color: #e0e0e0;
+ line-height: 1.4;
+ }
+ .confirm-actions {
+ display: flex;
+ gap: 0.5rem;
+ }
+ .confirm-keep {
+ flex: 1;
+ padding: 0.4rem;
+ background: #0f3460;
+ border: 1px solid #4a5568;
+ color: #e0e0e0;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 0.8rem;
+ }
+ .confirm-keep:hover {
+ background: #1a4a7a;
+ }
+ .confirm-cancel {
+ flex: 1;
+ padding: 0.4rem;
+ background: #e94560;
+ border: none;
+ color: white;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 0.8rem;
+ }
+ .confirm-cancel:hover {
+ background: #d63851;
+ }
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 67d16eb..8c5887a 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -119,11 +119,23 @@
};
});
let isTranscribing = $state(false);
+ let transcriptionCancelled = $state(false);
let transcriptionProgress = $state(0);
let transcriptionStage = $state('');
let transcriptionMessage = $state('');
let extractingAudio = $state(false);
+ function handleCancelProcessing() {
+ transcriptionCancelled = true;
+ isTranscribing = false;
+ transcriptionProgress = 0;
+ transcriptionStage = '';
+ transcriptionMessage = '';
+ // Clear any partial results
+ segments.set([]);
+ speakers.set([]);
+ }
+
// Speaker color palette for auto-assignment
const speakerColors = ['#e94560', '#4ecdc4', '#ffe66d', '#a8e6cf', '#ff8b94', '#c7ceea', '#ffd93d', '#6bcb77'];
@@ -310,6 +322,7 @@
// Start pipeline (transcription + diarization)
isTranscribing = true;
+ transcriptionCancelled = false;
transcriptionProgress = 0;
transcriptionStage = 'Starting...';
transcriptionMessage = 'Initializing pipeline...';
@@ -420,6 +433,9 @@
numSpeakers: $settings.num_speakers && $settings.num_speakers > 0 ? $settings.num_speakers : undefined,
});
+ // If cancelled while processing, discard results
+ if (transcriptionCancelled) return;
+
// Create speaker entries from pipeline result
const newSpeakers: Speaker[] = (result.speakers || []).map((label, idx) => ({
id: `speaker-${idx}`,
@@ -607,6 +623,7 @@
percent={transcriptionProgress}
stage={transcriptionStage}
message={transcriptionMessage}
+ onCancel={handleCancelProcessing}
/>
{#if extractingAudio}