Build Wisp: on-device transcription studio (web + native, one codebase)
CI / test (push) Has been cancelled
CI / deploy-web (push) Has been cancelled
CI / build-apk (push) Has been cancelled

Private, offline speech-to-text that runs Whisper on the user's own device —
free, no account, no per-minute fees. Replaces Otter.ai / Rev.

- Pure, tested engine: chunking, overlap timestamp-stitching, exports
  (SRT/VTT/TXT/MD/JSON), WAV codec, resampler, job queue, model catalog (142 tests).
- Platform-abstracted TranscriptionEngine: transformers.js on web (loaded from
  CDN at runtime to dodge Metro's onnxruntime-web bundling limits), whisper.rn
  on native. Shared pipeline orchestrates decode -> chunk -> transcribe -> stitch.
- Cross-platform StorageRepo (Dexie web / expo-sqlite native), Zod-validated.
- UI: library + search, import, live-progress transcription, synced click-to-seek
  editor, multi-format export; model picker + privacy in settings.
- Web ships as a single-page PWA with COOP/COEP isolation for threaded WASM;
  Docker (nginx) image + Traefik compose for wisp.briggen.dev.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 17:54:21 +02:00
parent 97996c9846
commit 9f42ee2460
72 changed files with 7293 additions and 421 deletions
+49
View File
@@ -0,0 +1,49 @@
// Core domain types shared across the whole transcription engine.
// Kept tiny and dependency-free so every pure module can import from here
// without pulling in React Native, Expo, or any platform code.
/** Whisper always works at 16 kHz mono. */
export const WHISPER_SAMPLE_RATE = 16000 as const;
/**
* A contiguous transcribed segment. Times are in SECONDS and, once a media
* file has been fully processed, ABSOLUTE to the whole media (post-stitch).
* Engine implementations emit chunk-local times (0-based per chunk); the pure
* pipeline offsets and stitches them into absolute time.
*/
export interface Segment {
/** Inclusive start time in seconds. */
start: number;
/** Exclusive end time in seconds. */
end: number;
/** Recognized text for this segment (already trimmed). */
text: string;
/** Optional confidence in [0,1], derived from avg token logprob when available. */
confidence?: number;
}
/** Decoded audio ready for Whisper: 16 kHz, mono, normalized to [-1, 1]. */
export interface PcmAudio {
readonly sampleRate: typeof WHISPER_SAMPLE_RATE;
readonly samples: Float32Array;
}
/** The Whisper model variants we expose. `.en` = English-only (smaller/faster). */
export type ModelId =
| 'tiny.en'
| 'tiny'
| 'base.en'
| 'base'
| 'small.en'
| 'small';
export interface TranscribeOptions {
modelId: ModelId;
/** ISO language code; undefined => auto-detect (multilingual models only). */
language?: string;
/** Translate the result to English (multilingual models only). */
translate?: boolean;
}
/** Which compute backend an engine resolved to, for UX and model gating. */
export type Backend = 'coreml' | 'metal' | 'cpu' | 'webgpu' | 'wasm';