Phase 1 foundation: Tauri shell, Python sidecar, SQLite database
Tauri v2 + Svelte + TypeScript frontend:
- App shell with workspace layout (waveform, transcript, speakers, AI chat)
- Placeholder components for all major UI areas
- Typed stores (project, transcript, playback, AI)
- TypeScript interfaces matching the database schema
- Tauri bridge service with typed invoke wrappers
- svelte-check passes with 0 errors
Rust backend:
- Tauri v2 app entry point with command registration
- SQLite database layer (rusqlite with bundled SQLite)
- Full schema: projects, media_files, speakers, segments, words,
ai_outputs, annotations (with indexes)
- Model structs with serde serialization
- CRUD queries for projects, speakers, segments, words
- Segment text editing preserves original text
- Schema versioning for future migrations
- 6 tests passing
- Command stubs for project, transcribe, export, AI, settings, system
- App state management
Python sidecar:
- JSON-line IPC protocol (stdin/stdout)
- Message types: IPCMessage, progress, error, ready
- Handler registry with routing and error handling
- Ping/pong handler for connectivity testing
- Service stubs: transcribe, diarize, pipeline, AI, export
- Provider stubs: local (llama-server), OpenAI, Anthropic, LiteLLM
- Hardware detection stubs
- 14 tests passing, ruff clean
Also adds:
- Testing strategy document (docs/TESTING.md)
- Validation script (scripts/validate.sh)
- Updated .gitignore for Svelte, Rust, Python artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:06 -08:00
|
|
|
<script lang="ts">
|
2026-02-26 16:50:14 -08:00
|
|
|
import { invoke, convertFileSrc } from '@tauri-apps/api/core';
|
2026-02-26 17:14:25 -08:00
|
|
|
import { listen } from '@tauri-apps/api/event';
|
Phase 4: Export to SRT, WebVTT, ASS, plain text, and Markdown
- Implement ExportService using pysubs2 for caption formats (SRT, VTT, ASS)
and custom formatters for plain text and Markdown
- SRT exports with [Speaker]: prefix, WebVTT with <v Speaker> voice tags,
ASS with color-coded speaker styles
- Plain text groups by speaker with labels, Markdown adds timestamps
- Add export.start IPC handler and export_transcript Tauri command
- Add export dropdown menu in header (appears after transcription)
- Uses native save dialog for output file selection
- Add pysubs2 dependency
- Tests: 30 Python (6 export tests), 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:18:54 -08:00
|
|
|
import { open, save } from '@tauri-apps/plugin-dialog';
|
Phase 1 foundation: Tauri shell, Python sidecar, SQLite database
Tauri v2 + Svelte + TypeScript frontend:
- App shell with workspace layout (waveform, transcript, speakers, AI chat)
- Placeholder components for all major UI areas
- Typed stores (project, transcript, playback, AI)
- TypeScript interfaces matching the database schema
- Tauri bridge service with typed invoke wrappers
- svelte-check passes with 0 errors
Rust backend:
- Tauri v2 app entry point with command registration
- SQLite database layer (rusqlite with bundled SQLite)
- Full schema: projects, media_files, speakers, segments, words,
ai_outputs, annotations (with indexes)
- Model structs with serde serialization
- CRUD queries for projects, speakers, segments, words
- Segment text editing preserves original text
- Schema versioning for future migrations
- 6 tests passing
- Command stubs for project, transcribe, export, AI, settings, system
- App state management
Python sidecar:
- JSON-line IPC protocol (stdin/stdout)
- Message types: IPCMessage, progress, error, ready
- Handler registry with routing and error handling
- Ping/pong handler for connectivity testing
- Service stubs: transcribe, diarize, pipeline, AI, export
- Provider stubs: local (llama-server), OpenAI, Anthropic, LiteLLM
- Hardware detection stubs
- 14 tests passing, ruff clean
Also adds:
- Testing strategy document (docs/TESTING.md)
- Validation script (scripts/validate.sh)
- Updated .gitignore for Svelte, Rust, Python artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:06 -08:00
|
|
|
import WaveformPlayer from '$lib/components/WaveformPlayer.svelte';
|
|
|
|
|
import TranscriptEditor from '$lib/components/TranscriptEditor.svelte';
|
|
|
|
|
import SpeakerManager from '$lib/components/SpeakerManager.svelte';
|
|
|
|
|
import AIChatPanel from '$lib/components/AIChatPanel.svelte';
|
2026-02-26 15:53:09 -08:00
|
|
|
import ProgressOverlay from '$lib/components/ProgressOverlay.svelte';
|
Phase 6: Llama-server manager, settings UI, packaging, and polish
- Implement LlamaManager in Rust for llama-server lifecycle: spawn with
port allocation, health check, clean shutdown on Drop, model listing
- Add llama_start/stop/status/list_models Tauri commands
- Add load_settings/save_settings commands with JSON persistence
- Build SettingsModal with tabs for Transcription, AI Provider, Local AI
settings (model size, device, language, API keys, provider selection)
- Wire settings into pipeline calls (model, device, language, skip diarization)
- Configure Tauri packaging: asset protocol for local audio files,
CSP policy, bundle metadata, Linux .deb/.AppImage and Windows .msi config
- Add keyboard shortcuts: Space (play/pause), Ctrl+O (import),
Ctrl+, (settings), Escape (close menus/modals)
- Close export dropdown on outside click
- Tests: 30 Python, 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:38:23 -08:00
|
|
|
import SettingsModal from '$lib/components/SettingsModal.svelte';
|
2026-02-26 15:53:09 -08:00
|
|
|
import { segments, speakers } from '$lib/stores/transcript';
|
Phase 6: Llama-server manager, settings UI, packaging, and polish
- Implement LlamaManager in Rust for llama-server lifecycle: spawn with
port allocation, health check, clean shutdown on Drop, model listing
- Add llama_start/stop/status/list_models Tauri commands
- Add load_settings/save_settings commands with JSON persistence
- Build SettingsModal with tabs for Transcription, AI Provider, Local AI
settings (model size, device, language, API keys, provider selection)
- Wire settings into pipeline calls (model, device, language, skip diarization)
- Configure Tauri packaging: asset protocol for local audio files,
CSP policy, bundle metadata, Linux .deb/.AppImage and Windows .msi config
- Add keyboard shortcuts: Space (play/pause), Ctrl+O (import),
Ctrl+, (settings), Escape (close menus/modals)
- Close export dropdown on outside click
- Tests: 30 Python, 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:38:23 -08:00
|
|
|
import { settings, loadSettings } from '$lib/stores/settings';
|
2026-02-26 16:09:48 -08:00
|
|
|
import type { Segment, Speaker } from '$lib/types/transcript';
|
Phase 6: Llama-server manager, settings UI, packaging, and polish
- Implement LlamaManager in Rust for llama-server lifecycle: spawn with
port allocation, health check, clean shutdown on Drop, model listing
- Add llama_start/stop/status/list_models Tauri commands
- Add load_settings/save_settings commands with JSON persistence
- Build SettingsModal with tabs for Transcription, AI Provider, Local AI
settings (model size, device, language, API keys, provider selection)
- Wire settings into pipeline calls (model, device, language, skip diarization)
- Configure Tauri packaging: asset protocol for local audio files,
CSP policy, bundle metadata, Linux .deb/.AppImage and Windows .msi config
- Add keyboard shortcuts: Space (play/pause), Ctrl+O (import),
Ctrl+, (settings), Escape (close menus/modals)
- Close export dropdown on outside click
- Tests: 30 Python, 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:38:23 -08:00
|
|
|
import { onMount } from 'svelte';
|
2026-02-26 15:53:09 -08:00
|
|
|
|
|
|
|
|
let waveformPlayer: WaveformPlayer;
|
|
|
|
|
let audioUrl = $state('');
|
Phase 6: Llama-server manager, settings UI, packaging, and polish
- Implement LlamaManager in Rust for llama-server lifecycle: spawn with
port allocation, health check, clean shutdown on Drop, model listing
- Add llama_start/stop/status/list_models Tauri commands
- Add load_settings/save_settings commands with JSON persistence
- Build SettingsModal with tabs for Transcription, AI Provider, Local AI
settings (model size, device, language, API keys, provider selection)
- Wire settings into pipeline calls (model, device, language, skip diarization)
- Configure Tauri packaging: asset protocol for local audio files,
CSP policy, bundle metadata, Linux .deb/.AppImage and Windows .msi config
- Add keyboard shortcuts: Space (play/pause), Ctrl+O (import),
Ctrl+, (settings), Escape (close menus/modals)
- Close export dropdown on outside click
- Tests: 30 Python, 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:38:23 -08:00
|
|
|
let showSettings = $state(false);
|
|
|
|
|
|
|
|
|
|
onMount(() => {
|
|
|
|
|
loadSettings();
|
|
|
|
|
|
|
|
|
|
// Global keyboard shortcuts
|
|
|
|
|
function handleKeyDown(e: KeyboardEvent) {
|
|
|
|
|
// Don't trigger shortcuts when typing in inputs
|
|
|
|
|
const tag = (e.target as HTMLElement)?.tagName;
|
|
|
|
|
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;
|
|
|
|
|
|
|
|
|
|
if (e.key === ' ' && !e.ctrlKey && !e.metaKey) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
waveformPlayer?.togglePlayPause?.();
|
|
|
|
|
} else if (e.key === 'o' && (e.ctrlKey || e.metaKey)) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
handleFileImport();
|
|
|
|
|
} else if (e.key === ',' && (e.ctrlKey || e.metaKey)) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
showSettings = true;
|
|
|
|
|
} else if (e.key === 'Escape') {
|
|
|
|
|
showExportMenu = false;
|
|
|
|
|
showSettings = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close export dropdown on outside click
|
|
|
|
|
function handleClickOutside(e: MouseEvent) {
|
|
|
|
|
if (showExportMenu) {
|
|
|
|
|
const target = e.target as HTMLElement;
|
|
|
|
|
if (!target.closest('.export-dropdown')) {
|
|
|
|
|
showExportMenu = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.addEventListener('keydown', handleKeyDown);
|
|
|
|
|
document.addEventListener('click', handleClickOutside);
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
document.removeEventListener('keydown', handleKeyDown);
|
|
|
|
|
document.removeEventListener('click', handleClickOutside);
|
|
|
|
|
};
|
|
|
|
|
});
|
2026-02-26 15:53:09 -08:00
|
|
|
let isTranscribing = $state(false);
|
|
|
|
|
let transcriptionProgress = $state(0);
|
|
|
|
|
let transcriptionStage = $state('');
|
|
|
|
|
let transcriptionMessage = $state('');
|
|
|
|
|
|
2026-02-26 16:09:48 -08:00
|
|
|
// Speaker color palette for auto-assignment
|
|
|
|
|
const speakerColors = ['#e94560', '#4ecdc4', '#ffe66d', '#a8e6cf', '#ff8b94', '#c7ceea', '#ffd93d', '#6bcb77'];
|
|
|
|
|
|
2026-02-26 15:53:09 -08:00
|
|
|
function handleWordClick(timeMs: number) {
|
|
|
|
|
waveformPlayer?.seekTo(timeMs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function handleFileImport() {
|
2026-02-26 16:02:27 -08:00
|
|
|
const filePath = await open({
|
|
|
|
|
multiple: false,
|
|
|
|
|
filters: [{
|
|
|
|
|
name: 'Audio/Video',
|
|
|
|
|
extensions: ['mp3', 'wav', 'flac', 'ogg', 'm4a', 'aac', 'wma',
|
|
|
|
|
'mp4', 'mkv', 'avi', 'mov', 'webm'],
|
|
|
|
|
}],
|
|
|
|
|
});
|
2026-02-26 15:53:09 -08:00
|
|
|
if (!filePath) return;
|
|
|
|
|
|
2026-02-26 16:50:14 -08:00
|
|
|
// Convert file path to asset URL for wavesurfer
|
|
|
|
|
audioUrl = convertFileSrc(filePath);
|
2026-02-26 15:53:09 -08:00
|
|
|
waveformPlayer?.loadAudio(audioUrl);
|
|
|
|
|
|
2026-02-26 16:09:48 -08:00
|
|
|
// Start pipeline (transcription + diarization)
|
2026-02-26 15:53:09 -08:00
|
|
|
isTranscribing = true;
|
|
|
|
|
transcriptionProgress = 0;
|
|
|
|
|
transcriptionStage = 'Starting...';
|
2026-02-26 17:14:25 -08:00
|
|
|
transcriptionMessage = 'Initializing pipeline...';
|
|
|
|
|
|
|
|
|
|
// Listen for progress events from the sidecar
|
|
|
|
|
const unlisten = await listen<{
|
|
|
|
|
percent: number;
|
|
|
|
|
stage: string;
|
|
|
|
|
message: string;
|
|
|
|
|
}>('pipeline-progress', (event) => {
|
|
|
|
|
const { percent, stage, message } = event.payload;
|
|
|
|
|
if (typeof percent === 'number') transcriptionProgress = percent;
|
|
|
|
|
if (typeof stage === 'string') transcriptionStage = stage;
|
|
|
|
|
if (typeof message === 'string') transcriptionMessage = message;
|
|
|
|
|
});
|
2026-02-26 15:53:09 -08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const result = await invoke<{
|
|
|
|
|
segments: Array<{
|
|
|
|
|
text: string;
|
|
|
|
|
start_ms: number;
|
|
|
|
|
end_ms: number;
|
2026-02-26 16:09:48 -08:00
|
|
|
speaker: string | null;
|
2026-02-26 15:53:09 -08:00
|
|
|
words: Array<{
|
|
|
|
|
word: string;
|
|
|
|
|
start_ms: number;
|
|
|
|
|
end_ms: number;
|
|
|
|
|
confidence: number;
|
|
|
|
|
}>;
|
|
|
|
|
}>;
|
|
|
|
|
language: string;
|
|
|
|
|
duration_ms: number;
|
2026-02-26 16:09:48 -08:00
|
|
|
speakers: string[];
|
|
|
|
|
num_speakers: number;
|
Phase 6: Llama-server manager, settings UI, packaging, and polish
- Implement LlamaManager in Rust for llama-server lifecycle: spawn with
port allocation, health check, clean shutdown on Drop, model listing
- Add llama_start/stop/status/list_models Tauri commands
- Add load_settings/save_settings commands with JSON persistence
- Build SettingsModal with tabs for Transcription, AI Provider, Local AI
settings (model size, device, language, API keys, provider selection)
- Wire settings into pipeline calls (model, device, language, skip diarization)
- Configure Tauri packaging: asset protocol for local audio files,
CSP policy, bundle metadata, Linux .deb/.AppImage and Windows .msi config
- Add keyboard shortcuts: Space (play/pause), Ctrl+O (import),
Ctrl+, (settings), Escape (close menus/modals)
- Close export dropdown on outside click
- Tests: 30 Python, 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:38:23 -08:00
|
|
|
}>('run_pipeline', {
|
|
|
|
|
filePath,
|
|
|
|
|
model: $settings.transcription_model || undefined,
|
|
|
|
|
device: $settings.transcription_device || undefined,
|
|
|
|
|
language: $settings.transcription_language || undefined,
|
|
|
|
|
skipDiarization: $settings.skip_diarization || undefined,
|
|
|
|
|
});
|
2026-02-26 16:09:48 -08:00
|
|
|
|
|
|
|
|
// Create speaker entries from pipeline result
|
|
|
|
|
const newSpeakers: Speaker[] = (result.speakers || []).map((label, idx) => ({
|
|
|
|
|
id: `speaker-${idx}`,
|
|
|
|
|
project_id: '',
|
|
|
|
|
label,
|
|
|
|
|
display_name: null,
|
|
|
|
|
color: speakerColors[idx % speakerColors.length],
|
|
|
|
|
}));
|
|
|
|
|
speakers.set(newSpeakers);
|
|
|
|
|
|
|
|
|
|
// Build speaker label → id lookup
|
|
|
|
|
const speakerLookup = new Map(newSpeakers.map(s => [s.label, s.id]));
|
2026-02-26 15:53:09 -08:00
|
|
|
|
|
|
|
|
// Convert result to our store format
|
|
|
|
|
const newSegments: Segment[] = result.segments.map((seg, idx) => ({
|
|
|
|
|
id: `seg-${idx}`,
|
|
|
|
|
project_id: '',
|
|
|
|
|
media_file_id: '',
|
2026-02-26 16:09:48 -08:00
|
|
|
speaker_id: seg.speaker ? (speakerLookup.get(seg.speaker) ?? null) : null,
|
2026-02-26 15:53:09 -08:00
|
|
|
start_ms: seg.start_ms,
|
|
|
|
|
end_ms: seg.end_ms,
|
|
|
|
|
text: seg.text,
|
|
|
|
|
original_text: null,
|
|
|
|
|
confidence: null,
|
|
|
|
|
is_edited: false,
|
|
|
|
|
edited_at: null,
|
|
|
|
|
segment_index: idx,
|
|
|
|
|
words: seg.words.map((w, widx) => ({
|
|
|
|
|
id: `word-${idx}-${widx}`,
|
|
|
|
|
segment_id: `seg-${idx}`,
|
|
|
|
|
word: w.word,
|
|
|
|
|
start_ms: w.start_ms,
|
|
|
|
|
end_ms: w.end_ms,
|
|
|
|
|
confidence: w.confidence,
|
|
|
|
|
word_index: widx,
|
|
|
|
|
})),
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
segments.set(newSegments);
|
|
|
|
|
} catch (err) {
|
2026-02-26 16:09:48 -08:00
|
|
|
console.error('Pipeline failed:', err);
|
|
|
|
|
alert(`Pipeline failed: ${err}`);
|
2026-02-26 15:53:09 -08:00
|
|
|
} finally {
|
2026-02-26 17:14:25 -08:00
|
|
|
unlisten();
|
2026-02-26 15:53:09 -08:00
|
|
|
isTranscribing = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
Phase 4: Export to SRT, WebVTT, ASS, plain text, and Markdown
- Implement ExportService using pysubs2 for caption formats (SRT, VTT, ASS)
and custom formatters for plain text and Markdown
- SRT exports with [Speaker]: prefix, WebVTT with <v Speaker> voice tags,
ASS with color-coded speaker styles
- Plain text groups by speaker with labels, Markdown adds timestamps
- Add export.start IPC handler and export_transcript Tauri command
- Add export dropdown menu in header (appears after transcription)
- Uses native save dialog for output file selection
- Add pysubs2 dependency
- Tests: 30 Python (6 export tests), 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:18:54 -08:00
|
|
|
|
|
|
|
|
const exportFormats = [
|
|
|
|
|
{ name: 'SubRip Subtitle', ext: 'srt', format: 'srt' },
|
|
|
|
|
{ name: 'WebVTT', ext: 'vtt', format: 'vtt' },
|
|
|
|
|
{ name: 'Advanced SubStation Alpha', ext: 'ass', format: 'ass' },
|
|
|
|
|
{ name: 'Plain Text', ext: 'txt', format: 'txt' },
|
|
|
|
|
{ name: 'Markdown', ext: 'md', format: 'md' },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let showExportMenu = $state(false);
|
|
|
|
|
|
|
|
|
|
async function handleExport(format: string, ext: string, filterName: string) {
|
|
|
|
|
showExportMenu = false;
|
|
|
|
|
|
|
|
|
|
const outputPath = await save({
|
|
|
|
|
filters: [{ name: filterName, extensions: [ext] }],
|
|
|
|
|
});
|
|
|
|
|
if (!outputPath) return;
|
|
|
|
|
|
|
|
|
|
// Build speaker lookup: speaker_id → display_name
|
|
|
|
|
const speakerMap: Record<string, string> = {};
|
|
|
|
|
for (const s of $speakers) {
|
|
|
|
|
speakerMap[s.label] = s.display_name || s.label;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build export segments from store
|
|
|
|
|
const exportSegments = $segments.map(seg => {
|
|
|
|
|
const speaker = $speakers.find(s => s.id === seg.speaker_id);
|
|
|
|
|
return {
|
|
|
|
|
text: seg.text,
|
|
|
|
|
start_ms: seg.start_ms,
|
|
|
|
|
end_ms: seg.end_ms,
|
|
|
|
|
speaker: speaker?.label ?? null,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await invoke('export_transcript', {
|
|
|
|
|
segments: exportSegments,
|
|
|
|
|
speakers: speakerMap,
|
|
|
|
|
format,
|
|
|
|
|
outputPath,
|
|
|
|
|
title: 'Voice to Notes Transcript',
|
|
|
|
|
});
|
|
|
|
|
alert(`Exported to ${outputPath}`);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Export failed:', err);
|
|
|
|
|
alert(`Export failed: ${err}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
Phase 1 foundation: Tauri shell, Python sidecar, SQLite database
Tauri v2 + Svelte + TypeScript frontend:
- App shell with workspace layout (waveform, transcript, speakers, AI chat)
- Placeholder components for all major UI areas
- Typed stores (project, transcript, playback, AI)
- TypeScript interfaces matching the database schema
- Tauri bridge service with typed invoke wrappers
- svelte-check passes with 0 errors
Rust backend:
- Tauri v2 app entry point with command registration
- SQLite database layer (rusqlite with bundled SQLite)
- Full schema: projects, media_files, speakers, segments, words,
ai_outputs, annotations (with indexes)
- Model structs with serde serialization
- CRUD queries for projects, speakers, segments, words
- Segment text editing preserves original text
- Schema versioning for future migrations
- 6 tests passing
- Command stubs for project, transcribe, export, AI, settings, system
- App state management
Python sidecar:
- JSON-line IPC protocol (stdin/stdout)
- Message types: IPCMessage, progress, error, ready
- Handler registry with routing and error handling
- Ping/pong handler for connectivity testing
- Service stubs: transcribe, diarize, pipeline, AI, export
- Provider stubs: local (llama-server), OpenAI, Anthropic, LiteLLM
- Hardware detection stubs
- 14 tests passing, ruff clean
Also adds:
- Testing strategy document (docs/TESTING.md)
- Validation script (scripts/validate.sh)
- Updated .gitignore for Svelte, Rust, Python artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:06 -08:00
|
|
|
</script>
|
|
|
|
|
|
2026-02-26 15:53:09 -08:00
|
|
|
<div class="app-header">
|
|
|
|
|
<h1>Voice to Notes</h1>
|
|
|
|
|
<div class="header-actions">
|
|
|
|
|
<button class="import-btn" onclick={handleFileImport}>
|
|
|
|
|
Import Audio/Video
|
|
|
|
|
</button>
|
Phase 6: Llama-server manager, settings UI, packaging, and polish
- Implement LlamaManager in Rust for llama-server lifecycle: spawn with
port allocation, health check, clean shutdown on Drop, model listing
- Add llama_start/stop/status/list_models Tauri commands
- Add load_settings/save_settings commands with JSON persistence
- Build SettingsModal with tabs for Transcription, AI Provider, Local AI
settings (model size, device, language, API keys, provider selection)
- Wire settings into pipeline calls (model, device, language, skip diarization)
- Configure Tauri packaging: asset protocol for local audio files,
CSP policy, bundle metadata, Linux .deb/.AppImage and Windows .msi config
- Add keyboard shortcuts: Space (play/pause), Ctrl+O (import),
Ctrl+, (settings), Escape (close menus/modals)
- Close export dropdown on outside click
- Tests: 30 Python, 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:38:23 -08:00
|
|
|
<button class="settings-btn" onclick={() => showSettings = true} title="Settings">
|
|
|
|
|
Settings
|
|
|
|
|
</button>
|
Phase 4: Export to SRT, WebVTT, ASS, plain text, and Markdown
- Implement ExportService using pysubs2 for caption formats (SRT, VTT, ASS)
and custom formatters for plain text and Markdown
- SRT exports with [Speaker]: prefix, WebVTT with <v Speaker> voice tags,
ASS with color-coded speaker styles
- Plain text groups by speaker with labels, Markdown adds timestamps
- Add export.start IPC handler and export_transcript Tauri command
- Add export dropdown menu in header (appears after transcription)
- Uses native save dialog for output file selection
- Add pysubs2 dependency
- Tests: 30 Python (6 export tests), 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:18:54 -08:00
|
|
|
{#if $segments.length > 0}
|
|
|
|
|
<div class="export-dropdown">
|
|
|
|
|
<button class="export-btn" onclick={() => showExportMenu = !showExportMenu}>
|
|
|
|
|
Export
|
|
|
|
|
</button>
|
|
|
|
|
{#if showExportMenu}
|
|
|
|
|
<div class="export-menu">
|
|
|
|
|
{#each exportFormats as fmt}
|
|
|
|
|
<button class="export-option" onclick={() => handleExport(fmt.format, fmt.ext, fmt.name)}>
|
|
|
|
|
{fmt.name} (.{fmt.ext})
|
|
|
|
|
</button>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
2026-02-26 15:53:09 -08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
Phase 1 foundation: Tauri shell, Python sidecar, SQLite database
Tauri v2 + Svelte + TypeScript frontend:
- App shell with workspace layout (waveform, transcript, speakers, AI chat)
- Placeholder components for all major UI areas
- Typed stores (project, transcript, playback, AI)
- TypeScript interfaces matching the database schema
- Tauri bridge service with typed invoke wrappers
- svelte-check passes with 0 errors
Rust backend:
- Tauri v2 app entry point with command registration
- SQLite database layer (rusqlite with bundled SQLite)
- Full schema: projects, media_files, speakers, segments, words,
ai_outputs, annotations (with indexes)
- Model structs with serde serialization
- CRUD queries for projects, speakers, segments, words
- Segment text editing preserves original text
- Schema versioning for future migrations
- 6 tests passing
- Command stubs for project, transcribe, export, AI, settings, system
- App state management
Python sidecar:
- JSON-line IPC protocol (stdin/stdout)
- Message types: IPCMessage, progress, error, ready
- Handler registry with routing and error handling
- Ping/pong handler for connectivity testing
- Service stubs: transcribe, diarize, pipeline, AI, export
- Provider stubs: local (llama-server), OpenAI, Anthropic, LiteLLM
- Hardware detection stubs
- 14 tests passing, ruff clean
Also adds:
- Testing strategy document (docs/TESTING.md)
- Validation script (scripts/validate.sh)
- Updated .gitignore for Svelte, Rust, Python artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:06 -08:00
|
|
|
<div class="workspace">
|
|
|
|
|
<div class="main-content">
|
2026-02-26 15:53:09 -08:00
|
|
|
<WaveformPlayer bind:this={waveformPlayer} {audioUrl} />
|
|
|
|
|
<TranscriptEditor onWordClick={handleWordClick} />
|
Phase 1 foundation: Tauri shell, Python sidecar, SQLite database
Tauri v2 + Svelte + TypeScript frontend:
- App shell with workspace layout (waveform, transcript, speakers, AI chat)
- Placeholder components for all major UI areas
- Typed stores (project, transcript, playback, AI)
- TypeScript interfaces matching the database schema
- Tauri bridge service with typed invoke wrappers
- svelte-check passes with 0 errors
Rust backend:
- Tauri v2 app entry point with command registration
- SQLite database layer (rusqlite with bundled SQLite)
- Full schema: projects, media_files, speakers, segments, words,
ai_outputs, annotations (with indexes)
- Model structs with serde serialization
- CRUD queries for projects, speakers, segments, words
- Segment text editing preserves original text
- Schema versioning for future migrations
- 6 tests passing
- Command stubs for project, transcribe, export, AI, settings, system
- App state management
Python sidecar:
- JSON-line IPC protocol (stdin/stdout)
- Message types: IPCMessage, progress, error, ready
- Handler registry with routing and error handling
- Ping/pong handler for connectivity testing
- Service stubs: transcribe, diarize, pipeline, AI, export
- Provider stubs: local (llama-server), OpenAI, Anthropic, LiteLLM
- Hardware detection stubs
- 14 tests passing, ruff clean
Also adds:
- Testing strategy document (docs/TESTING.md)
- Validation script (scripts/validate.sh)
- Updated .gitignore for Svelte, Rust, Python artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:06 -08:00
|
|
|
</div>
|
|
|
|
|
<div class="sidebar-right">
|
|
|
|
|
<SpeakerManager />
|
|
|
|
|
<AIChatPanel />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-02-26 15:53:09 -08:00
|
|
|
<ProgressOverlay
|
|
|
|
|
visible={isTranscribing}
|
|
|
|
|
percent={transcriptionProgress}
|
|
|
|
|
stage={transcriptionStage}
|
|
|
|
|
message={transcriptionMessage}
|
|
|
|
|
/>
|
|
|
|
|
|
Phase 6: Llama-server manager, settings UI, packaging, and polish
- Implement LlamaManager in Rust for llama-server lifecycle: spawn with
port allocation, health check, clean shutdown on Drop, model listing
- Add llama_start/stop/status/list_models Tauri commands
- Add load_settings/save_settings commands with JSON persistence
- Build SettingsModal with tabs for Transcription, AI Provider, Local AI
settings (model size, device, language, API keys, provider selection)
- Wire settings into pipeline calls (model, device, language, skip diarization)
- Configure Tauri packaging: asset protocol for local audio files,
CSP policy, bundle metadata, Linux .deb/.AppImage and Windows .msi config
- Add keyboard shortcuts: Space (play/pause), Ctrl+O (import),
Ctrl+, (settings), Escape (close menus/modals)
- Close export dropdown on outside click
- Tests: 30 Python, 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:38:23 -08:00
|
|
|
<SettingsModal
|
|
|
|
|
visible={showSettings}
|
|
|
|
|
onClose={() => showSettings = false}
|
|
|
|
|
/>
|
|
|
|
|
|
Phase 1 foundation: Tauri shell, Python sidecar, SQLite database
Tauri v2 + Svelte + TypeScript frontend:
- App shell with workspace layout (waveform, transcript, speakers, AI chat)
- Placeholder components for all major UI areas
- Typed stores (project, transcript, playback, AI)
- TypeScript interfaces matching the database schema
- Tauri bridge service with typed invoke wrappers
- svelte-check passes with 0 errors
Rust backend:
- Tauri v2 app entry point with command registration
- SQLite database layer (rusqlite with bundled SQLite)
- Full schema: projects, media_files, speakers, segments, words,
ai_outputs, annotations (with indexes)
- Model structs with serde serialization
- CRUD queries for projects, speakers, segments, words
- Segment text editing preserves original text
- Schema versioning for future migrations
- 6 tests passing
- Command stubs for project, transcribe, export, AI, settings, system
- App state management
Python sidecar:
- JSON-line IPC protocol (stdin/stdout)
- Message types: IPCMessage, progress, error, ready
- Handler registry with routing and error handling
- Ping/pong handler for connectivity testing
- Service stubs: transcribe, diarize, pipeline, AI, export
- Provider stubs: local (llama-server), OpenAI, Anthropic, LiteLLM
- Hardware detection stubs
- 14 tests passing, ruff clean
Also adds:
- Testing strategy document (docs/TESTING.md)
- Validation script (scripts/validate.sh)
- Updated .gitignore for Svelte, Rust, Python artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:06 -08:00
|
|
|
<style>
|
2026-02-26 15:53:09 -08:00
|
|
|
.app-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
|
background: #0f3460;
|
|
|
|
|
color: #e0e0e0;
|
|
|
|
|
}
|
|
|
|
|
h1 {
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
.import-btn {
|
|
|
|
|
background: #e94560;
|
|
|
|
|
border: none;
|
|
|
|
|
color: white;
|
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
.import-btn:hover {
|
|
|
|
|
background: #d63851;
|
|
|
|
|
}
|
Phase 4: Export to SRT, WebVTT, ASS, plain text, and Markdown
- Implement ExportService using pysubs2 for caption formats (SRT, VTT, ASS)
and custom formatters for plain text and Markdown
- SRT exports with [Speaker]: prefix, WebVTT with <v Speaker> voice tags,
ASS with color-coded speaker styles
- Plain text groups by speaker with labels, Markdown adds timestamps
- Add export.start IPC handler and export_transcript Tauri command
- Add export dropdown menu in header (appears after transcription)
- Uses native save dialog for output file selection
- Add pysubs2 dependency
- Tests: 30 Python (6 export tests), 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:18:54 -08:00
|
|
|
.header-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
Phase 6: Llama-server manager, settings UI, packaging, and polish
- Implement LlamaManager in Rust for llama-server lifecycle: spawn with
port allocation, health check, clean shutdown on Drop, model listing
- Add llama_start/stop/status/list_models Tauri commands
- Add load_settings/save_settings commands with JSON persistence
- Build SettingsModal with tabs for Transcription, AI Provider, Local AI
settings (model size, device, language, API keys, provider selection)
- Wire settings into pipeline calls (model, device, language, skip diarization)
- Configure Tauri packaging: asset protocol for local audio files,
CSP policy, bundle metadata, Linux .deb/.AppImage and Windows .msi config
- Add keyboard shortcuts: Space (play/pause), Ctrl+O (import),
Ctrl+, (settings), Escape (close menus/modals)
- Close export dropdown on outside click
- Tests: 30 Python, 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:38:23 -08:00
|
|
|
.settings-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: 1px solid #4a5568;
|
|
|
|
|
color: #e0e0e0;
|
|
|
|
|
padding: 0.5rem 0.75rem;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
}
|
|
|
|
|
.settings-btn:hover {
|
|
|
|
|
background: rgba(255,255,255,0.05);
|
|
|
|
|
border-color: #e94560;
|
|
|
|
|
}
|
Phase 4: Export to SRT, WebVTT, ASS, plain text, and Markdown
- Implement ExportService using pysubs2 for caption formats (SRT, VTT, ASS)
and custom formatters for plain text and Markdown
- SRT exports with [Speaker]: prefix, WebVTT with <v Speaker> voice tags,
ASS with color-coded speaker styles
- Plain text groups by speaker with labels, Markdown adds timestamps
- Add export.start IPC handler and export_transcript Tauri command
- Add export dropdown menu in header (appears after transcription)
- Uses native save dialog for output file selection
- Add pysubs2 dependency
- Tests: 30 Python (6 export tests), 6 Rust, 0 Svelte errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:18:54 -08:00
|
|
|
.export-dropdown {
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
.export-btn {
|
|
|
|
|
background: #0f3460;
|
|
|
|
|
border: 1px solid #4a5568;
|
|
|
|
|
color: #e0e0e0;
|
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
.export-btn:hover {
|
|
|
|
|
background: #1a4a7a;
|
|
|
|
|
}
|
|
|
|
|
.export-menu {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 100%;
|
|
|
|
|
right: 0;
|
|
|
|
|
margin-top: 0.25rem;
|
|
|
|
|
background: #16213e;
|
|
|
|
|
border: 1px solid #4a5568;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
min-width: 220px;
|
|
|
|
|
}
|
|
|
|
|
.export-option {
|
|
|
|
|
display: block;
|
|
|
|
|
width: 100%;
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: #e0e0e0;
|
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
|
text-align: left;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
}
|
|
|
|
|
.export-option:hover {
|
|
|
|
|
background: rgba(233, 69, 96, 0.2);
|
|
|
|
|
}
|
Phase 1 foundation: Tauri shell, Python sidecar, SQLite database
Tauri v2 + Svelte + TypeScript frontend:
- App shell with workspace layout (waveform, transcript, speakers, AI chat)
- Placeholder components for all major UI areas
- Typed stores (project, transcript, playback, AI)
- TypeScript interfaces matching the database schema
- Tauri bridge service with typed invoke wrappers
- svelte-check passes with 0 errors
Rust backend:
- Tauri v2 app entry point with command registration
- SQLite database layer (rusqlite with bundled SQLite)
- Full schema: projects, media_files, speakers, segments, words,
ai_outputs, annotations (with indexes)
- Model structs with serde serialization
- CRUD queries for projects, speakers, segments, words
- Segment text editing preserves original text
- Schema versioning for future migrations
- 6 tests passing
- Command stubs for project, transcribe, export, AI, settings, system
- App state management
Python sidecar:
- JSON-line IPC protocol (stdin/stdout)
- Message types: IPCMessage, progress, error, ready
- Handler registry with routing and error handling
- Ping/pong handler for connectivity testing
- Service stubs: transcribe, diarize, pipeline, AI, export
- Provider stubs: local (llama-server), OpenAI, Anthropic, LiteLLM
- Hardware detection stubs
- 14 tests passing, ruff clean
Also adds:
- Testing strategy document (docs/TESTING.md)
- Validation script (scripts/validate.sh)
- Updated .gitignore for Svelte, Rust, Python artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:06 -08:00
|
|
|
.workspace {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
padding: 1rem;
|
2026-02-26 15:53:09 -08:00
|
|
|
height: calc(100vh - 3.5rem);
|
|
|
|
|
background: #0a0a23;
|
Phase 1 foundation: Tauri shell, Python sidecar, SQLite database
Tauri v2 + Svelte + TypeScript frontend:
- App shell with workspace layout (waveform, transcript, speakers, AI chat)
- Placeholder components for all major UI areas
- Typed stores (project, transcript, playback, AI)
- TypeScript interfaces matching the database schema
- Tauri bridge service with typed invoke wrappers
- svelte-check passes with 0 errors
Rust backend:
- Tauri v2 app entry point with command registration
- SQLite database layer (rusqlite with bundled SQLite)
- Full schema: projects, media_files, speakers, segments, words,
ai_outputs, annotations (with indexes)
- Model structs with serde serialization
- CRUD queries for projects, speakers, segments, words
- Segment text editing preserves original text
- Schema versioning for future migrations
- 6 tests passing
- Command stubs for project, transcribe, export, AI, settings, system
- App state management
Python sidecar:
- JSON-line IPC protocol (stdin/stdout)
- Message types: IPCMessage, progress, error, ready
- Handler registry with routing and error handling
- Ping/pong handler for connectivity testing
- Service stubs: transcribe, diarize, pipeline, AI, export
- Provider stubs: local (llama-server), OpenAI, Anthropic, LiteLLM
- Hardware detection stubs
- 14 tests passing, ruff clean
Also adds:
- Testing strategy document (docs/TESTING.md)
- Validation script (scripts/validate.sh)
- Updated .gitignore for Svelte, Rust, Python artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:06 -08:00
|
|
|
}
|
|
|
|
|
.main-content {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 1rem;
|
2026-02-26 15:53:09 -08:00
|
|
|
min-width: 0;
|
Phase 1 foundation: Tauri shell, Python sidecar, SQLite database
Tauri v2 + Svelte + TypeScript frontend:
- App shell with workspace layout (waveform, transcript, speakers, AI chat)
- Placeholder components for all major UI areas
- Typed stores (project, transcript, playback, AI)
- TypeScript interfaces matching the database schema
- Tauri bridge service with typed invoke wrappers
- svelte-check passes with 0 errors
Rust backend:
- Tauri v2 app entry point with command registration
- SQLite database layer (rusqlite with bundled SQLite)
- Full schema: projects, media_files, speakers, segments, words,
ai_outputs, annotations (with indexes)
- Model structs with serde serialization
- CRUD queries for projects, speakers, segments, words
- Segment text editing preserves original text
- Schema versioning for future migrations
- 6 tests passing
- Command stubs for project, transcribe, export, AI, settings, system
- App state management
Python sidecar:
- JSON-line IPC protocol (stdin/stdout)
- Message types: IPCMessage, progress, error, ready
- Handler registry with routing and error handling
- Ping/pong handler for connectivity testing
- Service stubs: transcribe, diarize, pipeline, AI, export
- Provider stubs: local (llama-server), OpenAI, Anthropic, LiteLLM
- Hardware detection stubs
- 14 tests passing, ruff clean
Also adds:
- Testing strategy document (docs/TESTING.md)
- Validation script (scripts/validate.sh)
- Updated .gitignore for Svelte, Rust, Python artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:06 -08:00
|
|
|
}
|
|
|
|
|
.sidebar-right {
|
|
|
|
|
width: 300px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 1rem;
|
2026-02-26 15:53:09 -08:00
|
|
|
flex-shrink: 0;
|
Phase 1 foundation: Tauri shell, Python sidecar, SQLite database
Tauri v2 + Svelte + TypeScript frontend:
- App shell with workspace layout (waveform, transcript, speakers, AI chat)
- Placeholder components for all major UI areas
- Typed stores (project, transcript, playback, AI)
- TypeScript interfaces matching the database schema
- Tauri bridge service with typed invoke wrappers
- svelte-check passes with 0 errors
Rust backend:
- Tauri v2 app entry point with command registration
- SQLite database layer (rusqlite with bundled SQLite)
- Full schema: projects, media_files, speakers, segments, words,
ai_outputs, annotations (with indexes)
- Model structs with serde serialization
- CRUD queries for projects, speakers, segments, words
- Segment text editing preserves original text
- Schema versioning for future migrations
- 6 tests passing
- Command stubs for project, transcribe, export, AI, settings, system
- App state management
Python sidecar:
- JSON-line IPC protocol (stdin/stdout)
- Message types: IPCMessage, progress, error, ready
- Handler registry with routing and error handling
- Ping/pong handler for connectivity testing
- Service stubs: transcribe, diarize, pipeline, AI, export
- Provider stubs: local (llama-server), OpenAI, Anthropic, LiteLLM
- Hardware detection stubs
- 14 tests passing, ruff clean
Also adds:
- Testing strategy document (docs/TESTING.md)
- Validation script (scripts/validate.sh)
- Updated .gitignore for Svelte, Rust, Python artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:06 -08:00
|
|
|
}
|
|
|
|
|
</style>
|