Transcript Desktop
Ditado offline para Windows e Linux: grave com um atalho global, revise o texto e cole no app em foco.
📌 O problema
Ditar texto em qualquer aplicativo do desktop exige gravar, transcrever e colar o resultado por conta própria, e boa parte das opções depende de serviço online. O Transcript fica residente na bandeja, grava por atalho global, transcreve localmente e deixa o texto editável antes de inseri-lo no aplicativo em foco.
✨ O que faz
- Alterna a gravação com o atalho global
Ctrl+Shift+Space(eventotranscript://toggle-recordingpara o frontend). - Captura o microfone em WAV (cpal + hound) na pasta de dados do app.
- Transcreve localmente chamando o sidecar
sherpa-onnx-offlinecom o modelo Parakeet TDT 0.6B int8 e guarda um diagnóstico de cada tentativa (etapa, exit code, final de stdout/stderr). - Inclui adaptadores para o sidecar CrispASR (modelo GGUF legado) e para APIs compatíveis com OpenAI.
- Mostra o texto num editor, com os botões Copiar e Inserir, para revisar antes de colar.
- Cola no app em foco: copia para o clipboard e aciona
Ctrl+V(PowerShell no Windows,xdotoolno X11). - Mantém ícone na bandeja com o menu Mostrar app, Iniciar gravação e Sair; o clique esquerdo mostra a janela.
- Lista o catálogo de modelos com status e baixa o modelo padrão.
🚀 Quickstart
| Ferramenta | Versão |
|---|---|
| Node.js + npm | 22 (usada nos workflows; não há .nvmrc nem engines) |
| Rust (cargo) | stable, edition 2021 (workflows usam dtolnay/rust-toolchain@stable) |
| Tauri CLI | ^2.9.0 (devDependency) |
| Linux (build) | libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libasound2-dev |
| Linux X11 (colar) | xdotool, opcional |
tar com bzip2 |
usado por scripts/setup-fixtures.mjs |
npm ci
node scripts/setup-sidecar.mjs # baixa sherpa-onnx-offline para src-tauri/binaries/
npm run tauri:dev # app em desenvolvimento (Vite em 127.0.0.1:1420)
npm run tauri:build # bundles em src-tauri/target/release/bundle/
O modelo não vai no instalador: o app o baixa para a pasta de dados (%LOCALAPPDATA%\transcript\models no Windows, ~/.local/share/transcript/models no Linux) pelo comando Tauri download_default_model_command. As gravações ficam em recordings/ na mesma pasta.
Não há .env.example e nenhuma variável é obrigatória. As opcionais:
| Variável | Obrigatória | Descrição |
|---|---|---|
SHERPA_ONNX_SIDECAR |
Não | Caminho do sidecar; tem prioridade sobre src-tauri/binaries/ e sobre o PATH. |
SHERPA_ONNX_VERSION |
Não | Versão do release sherpa-onnx baixada por setup-sidecar.mjs (padrão v1.12.23). |
SHERPA_ONNX_TRIPLE |
Não | Target triple do sidecar (padrão: detectado via rustc -vV). |
🧱 Arquitetura
flowchart LR
HK["Hotkey global Ctrl+Shift+Space"] --> UI["Frontend TypeScript"]
UI --> CMD["Comandos Tauri em Rust"]
CMD --> REC["Gravação WAV com cpal e hound"]
REC --> ASR["Sidecar sherpa-onnx-offline"]
ASR --> UI
UI -->|Inserir| INS["Clipboard e Ctrl+V no app em foco"]
O hotkey emite um evento que o frontend escuta para iniciar ou parar a gravação. Ao parar, stop_recording_and_transcribe fecha o WAV, roda o sidecar e devolve o texto num snapshot para o editor. Só depois da revisão o botão Inserir chama request_insert_text, que copia o texto e tenta colar. As threads do sidecar são limitadas a 1–4.
Caminhos de transcrição
| Caminho | Arquivo | Estado no código |
|---|---|---|
| Sidecar sherpa-onnx (local) | transcription/sherpa_onnx.rs |
É o único chamado por stop_recording_and_transcribe. Procura o binário em SHERPA_ONNX_SIDECAR, depois em src-tauri/binaries/sherpa-onnx-offline-<triple>, depois no PATH. |
| Sidecar CrispASR (local) | transcription/crispasr.rs |
Adaptador que executa <sidecar> -m <modelo.gguf> -f <audio.wav> -t <threads>. Hoje nenhum comando o instancia. O spec original recomendava compilar o parakeet-main do CrispASR (branch parakeet). |
| API OpenAI-compatible | transcription/openai.rs |
Adaptador de envio do WAV. settings guarda mode (localFirst ou apiOnly, padrão apiOnly) e o endpoint padrão http://localhost:8022/v1/audio/transcriptions. |
Modelos
| ID | Formato | Tamanho no catálogo | Idiomas | Uso |
|---|---|---|---|---|
sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8 |
tarball .tar.bz2 do release asr-models do k2-fsa/sherpa-onnx (encoder, decoder, joiner int8 + tokens.txt) |
~640 MB | 25 idiomas europeus, incluindo pt |
Padrão; recomendado para PC simples |
parakeet-tdt-0.6b-v3-q8_0 |
GGUF Q8_0, repositório cstr/parakeet-tdt-0.6b-v3-GGUF |
745121600 bytes | pt, en |
Legado, para o adaptador CrispASR |
Compatibilidade por sistema
| Sistema | Inserção no app em foco | Fallback |
|---|---|---|
| Windows | Clipboard + Ctrl+V via PowerShell SendKeys |
Se o Ctrl+V falhar, o texto fica copiado e a mensagem avisa. |
| Linux X11 | Clipboard + xdotool key --clearmodifiers ctrl+v |
Sem xdotool, o texto fica copiado para colar manualmente. |
| Linux Wayland | Só copia (suporte parcial) | Sem colagem automática: cole com Ctrl+V. |
| Linux sem sessão detectada | Só copia | Cole manualmente no aplicativo desejado. |
A sessão é detectada por XDG_SESSION_TYPE, WAYLAND_DISPLAY e DISPLAY. O método de inserção configurável é clipboardPaste (padrão), keyTyping ou manualCopy.
🗂️ Estrutura
src/ Frontend TypeScript (app.ts, main.ts, styles.css, app.test.ts)
src-tauri/src/
audio/ Captura do microfone em WAV
transcription/ sherpa_onnx.rs, crispasr.rs, openai.rs
injection/ Clipboard, paste automático e fallback manual
models/ Catálogo e download de modelos
settings/ Configuração padrão
tray.rs shortcuts.rs ui_commands.rs app_state.rs diagnostics.rs error.rs
src-tauri/binaries/ Sidecar sherpa-onnx-offline e DLLs onnxruntime (Windows)
src-tauri/tests/fixtures/ Modelo ONNX grande para teste real (o clone passa de 1 GB)
scripts/ setup-sidecar.mjs, setup-fixtures.mjs
.github/workflows/ ci.yml, build.yml, release.yml
🧪 Scripts
| Comando | O que faz |
|---|---|
npm run dev |
Vite em 127.0.0.1:1420, só o frontend. |
npm run build |
tsc --noEmit e vite build. |
npm test |
vitest (ambiente jsdom); em modo não interativo use npm test -- --run, como o CI. |
npm run typecheck |
tsc --noEmit. |
npm run tauri:dev |
tauri dev. |
npm run tauri:build |
tauri build. |
cargo test --manifest-path src-tauri/Cargo.toml |
Testes Rust. |
node scripts/setup-sidecar.mjs |
Baixa o release *-shared do sherpa-onnx e extrai sherpa-onnx-offline para src-tauri/binaries/. Plataformas: Linux x64/arm64, macOS, Windows x64. |
node scripts/setup-fixtures.mjs |
Baixa parakeet-sample.wav para src-tauri/tests/fixtures/. |
Workflows (.github/workflows/)
| Arquivo | Gatilho | O que faz |
|---|---|---|
ci.yml |
PR e push em main |
Em ubuntu-22.04 com Node 22: npm ci, typecheck, npm run test -- --run, build e cargo test. Depois prepara sidecar, fixture e modelo, e roda o teste real de transcrição com --ignored. |
build.yml |
Push em main e manual |
Matriz windows-latest e ubuntu-22.04: npm run tauri:build e upload de src-tauri/target/release/bundle/**/* (windows-bundles, linux-bundles). |
release.yml |
Tag v*.*.* e manual (input tag) |
tauri-apps/tauri-action@v0 nas mesmas duas plataformas; publica a GitHub Release Transcript <tag>, sem rascunho e sem pré-release. |
📚 Documentação
- Design do app
- sherpa-onnx como sidecar
- Redesign do frontend
- Planos: MVP, integrações nativas, redesign
- Tauri: commands, system tray, sidecars, pipelines GitHub, pré-requisitos Linux
- Modelos: NVIDIA Parakeet v3, cstr GGUF
🛣️ Limitações e próximos passos
- whisper.cpp: só aparece no spec do redesign, e
bundle.resourcesaponta pararesources/whisper/, pasta que não existe no repositório. Não há código nem modelo Whisper no catálogo;whisper-1é só um exemplo de nome de modelo na API. - O app ainda não escolhe o caminho de transcrição por
mode:stop_recording_and_transcribesempre usa o sherpa-onnx. tauri.conf.jsonnão declarabundle.externalBin, embora o spec preveja o sidecar empacotado no instalador.- Divergências vistas na leitura do código, que não foi compilado: o frontend invoca
stop_recording,transcribe_pending,minimize_window,close_window,save_openai_api_keyeclear_openai_api_key, ausentes doinvoke_handlerdelib.rs;openai.rsimportacrate::secrets, quelib.rsnão declara;ui_commands.rslêsettings.transcription.model_idesettingsusashortcuts::DEFAULT_HOTKEY, e nenhum dos dois existe. - O hotkey
Ctrl+Shift+Spaceé fixo emshortcuts.rs, e o item Iniciar gravação da bandeja só mostra a janela. - O catálogo aponta para o modelo
v2-int8, mas oci.ymlbaixa ov3-int8(~670 MB) e cita o testereal_transcription_returns_known_text, que não está emsherpa_onnx.rs. - As versões divergem:
0.1.0empackage.jsoneCargo.toml,0.3.1emtauri.conf.json.
🤝 Contribuindo
Mantenha módulos pequenos, com responsabilidades claras. Antes de abrir o PR, estes comandos devem passar:
npm run typecheck && npm test -- --run && cargo test --manifest-path src-tauri/Cargo.toml
📄 Licença
Sem licença definida — todos os direitos reservados.