chore: scaffold tauri transcription app
This commit is contained in:
commit
3b05cb22a0
18 files changed
+8054
No files matched your search
+34
@@ -0,0 +1,34 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Frontend build
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Tauri/Rust build
|
||||||
|
src-tauri/target/
|
||||||
|
src-tauri/gen/
|
||||||
|
|
||||||
|
# Local app data and large speech models
|
||||||
|
models/
|
||||||
|
*.gguf
|
||||||
|
*.nemo
|
||||||
|
*.wav
|
||||||
|
*.mp3
|
||||||
|
*.ogg
|
||||||
|
|
||||||
|
# Secrets and local config
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# OS/editor
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
.opencode/
|
||||||
|
|
||||||
|
# Superpowers local worktrees
|
||||||
|
.worktrees/
|
||||||
|
worktrees/
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
# Aplicativo de Transcrição Desktop MVP Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Criar o esqueleto funcional de um app desktop Windows/Linux de transcrição offline-first com Tauri, Rust, TypeScript, modal editável, catálogo de modelo Parakeet GGUF Q8, sidecar CrispASR preparado e CI/CD.
|
||||||
|
|
||||||
|
**Architecture:** O frontend TypeScript renderiza a tela/modal e chama comandos Tauri. O backend Rust mantém módulos pequenos para settings, modelos, transcrição, injeção, áudio e tray. Integrações nativas arriscadas ficam atrás de contratos e stubs explícitos para evoluir sem quebrar a arquitetura.
|
||||||
|
|
||||||
|
**Tech Stack:** Tauri 2, Rust 1.95, TypeScript, Vite, Vitest, GitHub Actions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```text
|
||||||
|
.github/workflows/ci.yml CI de lint/typecheck/test/build
|
||||||
|
.github/workflows/build.yml Builds desktop Windows/Linux manuais
|
||||||
|
.github/workflows/release.yml Release por tag v*.*.*
|
||||||
|
.gitignore Ignora builds, deps, modelos e segredos
|
||||||
|
README.md Visão geral, comandos e limitações
|
||||||
|
package.json Scripts frontend/Tauri
|
||||||
|
index.html Entrada Vite
|
||||||
|
src/main.ts Estado e eventos da UI
|
||||||
|
src/styles.css Modal/responsividade/acessibilidade
|
||||||
|
src/vite-env.d.ts Tipos Vite/Tauri
|
||||||
|
src-tauri/Cargo.toml Crates Rust/Tauri
|
||||||
|
src-tauri/build.rs Build script Tauri
|
||||||
|
src-tauri/tauri.conf.json Configuração Tauri e bundle
|
||||||
|
src-tauri/capabilities/default.json Permissões Tauri
|
||||||
|
src-tauri/icons/icon.png Ícone mínimo temporário
|
||||||
|
src-tauri/src/main.rs Entrada nativa
|
||||||
|
src-tauri/src/lib.rs Bootstrap Tauri
|
||||||
|
src-tauri/src/app_state.rs Estado compartilhado
|
||||||
|
src-tauri/src/ui_commands.rs Comandos Tauri
|
||||||
|
src-tauri/src/tray.rs Tray/menu
|
||||||
|
src-tauri/src/settings/mod.rs Settings e defaults
|
||||||
|
src-tauri/src/models/mod.rs Catálogo/status de modelos
|
||||||
|
src-tauri/src/transcription/mod.rs Contratos de transcrição
|
||||||
|
src-tauri/src/transcription/crispasr.rs Sidecar CrispASR preparado
|
||||||
|
src-tauri/src/transcription/openai.rs Adaptador OpenAI-compatible preparado
|
||||||
|
src-tauri/src/injection/mod.rs Contratos/fallback de inserção
|
||||||
|
src-tauri/src/audio/mod.rs Estado de gravação MVP
|
||||||
|
src-tauri/src/error.rs Erros serializáveis
|
||||||
|
```
|
||||||
|
|
||||||
|
## Task 1: Scaffolding do projeto
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `.gitignore`
|
||||||
|
- Create: `package.json`
|
||||||
|
- Create: `index.html`
|
||||||
|
- Create: `src/vite-env.d.ts`
|
||||||
|
- Create: `src-tauri/Cargo.toml`
|
||||||
|
- Create: `src-tauri/build.rs`
|
||||||
|
- Create: `src-tauri/tauri.conf.json`
|
||||||
|
- Create: `src-tauri/capabilities/default.json`
|
||||||
|
- Create: `src-tauri/src/main.rs`
|
||||||
|
- Create: `src-tauri/src/lib.rs`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Criar configuração base do frontend e Tauri**
|
||||||
|
|
||||||
|
Criar arquivos com scripts `dev`, `build`, `test`, `typecheck`, `tauri:dev` e `tauri:build`. Usar Tauri 2 e Vite.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Instalar dependências npm**
|
||||||
|
|
||||||
|
Run: `npm install`
|
||||||
|
|
||||||
|
Expected: `package-lock.json` criado e dependências instaladas.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verificar build frontend vazio**
|
||||||
|
|
||||||
|
Run: `npm run typecheck && npm run build`
|
||||||
|
|
||||||
|
Expected: TypeScript e Vite passam.
|
||||||
|
|
||||||
|
## Task 2: Contratos backend e testes Rust
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `src-tauri/src/error.rs`
|
||||||
|
- Create: `src-tauri/src/settings/mod.rs`
|
||||||
|
- Create: `src-tauri/src/models/mod.rs`
|
||||||
|
- Create: `src-tauri/src/transcription/mod.rs`
|
||||||
|
- Create: `src-tauri/src/transcription/crispasr.rs`
|
||||||
|
- Create: `src-tauri/src/transcription/openai.rs`
|
||||||
|
- Create: `src-tauri/src/injection/mod.rs`
|
||||||
|
- Create: `src-tauri/src/audio/mod.rs`
|
||||||
|
- Modify: `src-tauri/src/lib.rs`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Escrever tipos e testes unitários primeiro**
|
||||||
|
|
||||||
|
Testes devem cobrir:
|
||||||
|
- modelo Parakeet Q8 existe no catálogo;
|
||||||
|
- idioma manual é opcional por padrão;
|
||||||
|
- Wayland retorna inserção parcial/manual;
|
||||||
|
- CrispASR sem modelo configurado retorna erro claro.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Rodar testes para validar contratos**
|
||||||
|
|
||||||
|
Run: `cargo test --manifest-path src-tauri/Cargo.toml`
|
||||||
|
|
||||||
|
Expected: todos os testes passam após implementação mínima.
|
||||||
|
|
||||||
|
## Task 3: Comandos Tauri e estado do app
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `src-tauri/src/app_state.rs`
|
||||||
|
- Create: `src-tauri/src/ui_commands.rs`
|
||||||
|
- Modify: `src-tauri/src/lib.rs`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Expor comandos pequenos para frontend**
|
||||||
|
|
||||||
|
Comandos:
|
||||||
|
- `get_app_snapshot`
|
||||||
|
- `start_recording`
|
||||||
|
- `stop_recording_and_transcribe`
|
||||||
|
- `copy_ready_text`
|
||||||
|
- `request_insert_text`
|
||||||
|
- `list_models`
|
||||||
|
- `get_settings`
|
||||||
|
- `save_settings`
|
||||||
|
|
||||||
|
- [ ] **Step 2: Testar build Rust**
|
||||||
|
|
||||||
|
Run: `cargo test --manifest-path src-tauri/Cargo.toml`
|
||||||
|
|
||||||
|
Expected: comandos compilam e testes continuam passando.
|
||||||
|
|
||||||
|
## Task 4: UI do modal flutuante
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `src/main.ts`
|
||||||
|
- Create: `src/styles.css`
|
||||||
|
- Modify: `index.html`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Construir UI sem framework pesado**
|
||||||
|
|
||||||
|
Criar interface com:
|
||||||
|
- status;
|
||||||
|
- textarea editável;
|
||||||
|
- botões gravar/parar, continuar, copiar, inserir e salvar;
|
||||||
|
- painel de modelo/configuração resumida;
|
||||||
|
- mensagens de fallback.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Testar frontend**
|
||||||
|
|
||||||
|
Run: `npm run typecheck && npm run test -- --run && npm run build`
|
||||||
|
|
||||||
|
Expected: typecheck, Vitest e build passam.
|
||||||
|
|
||||||
|
## Task 5: Tray, assets e empacotamento
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `src-tauri/src/tray.rs`
|
||||||
|
- Create: `src-tauri/icons/icon.png`
|
||||||
|
- Modify: `src-tauri/src/lib.rs`
|
||||||
|
- Modify: `src-tauri/tauri.conf.json`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Adicionar tray básico**
|
||||||
|
|
||||||
|
Menu:
|
||||||
|
- Mostrar app;
|
||||||
|
- Iniciar gravação;
|
||||||
|
- Sair.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verificar build Tauri até onde o ambiente permitir**
|
||||||
|
|
||||||
|
Run: `npm run tauri:build`
|
||||||
|
|
||||||
|
Expected: se dependências do sistema estiverem instaladas, build gera bundle; se faltar WebView/NSIS/AppImage tooling, documentar o bloqueio e manter `cargo test` + `npm run build` verdes.
|
||||||
|
|
||||||
|
## Task 6: CI/CD e documentação
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `.github/workflows/ci.yml`
|
||||||
|
- Create: `.github/workflows/build.yml`
|
||||||
|
- Create: `.github/workflows/release.yml`
|
||||||
|
- Create: `README.md`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Criar workflows**
|
||||||
|
|
||||||
|
CI roda em PR/push para `main`. Build é manual. Release roda em tags `v*.*.*` e publica artefatos.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Documentar arquitetura e limitações**
|
||||||
|
|
||||||
|
README deve explicar:
|
||||||
|
- offline-first;
|
||||||
|
- modelo Parakeet GGUF Q8;
|
||||||
|
- CrispASR sidecar;
|
||||||
|
- suporte Windows/Linux;
|
||||||
|
- Wayland parcial;
|
||||||
|
- comandos locais.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verificação final**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
- `npm run typecheck`
|
||||||
|
- `npm run test -- --run`
|
||||||
|
- `npm run build`
|
||||||
|
- `cargo test --manifest-path src-tauri/Cargo.toml`
|
||||||
|
|
||||||
|
Expected: todos passam.
|
||||||
|
|
||||||
|
## Self-review
|
||||||
|
|
||||||
|
- Spec coverage: cobre organização, modal, inserção, modelo offline, CrispASR sidecar e CI/CD.
|
||||||
|
- Placeholder scan: não há etapas com TBD/TODO; integrações fora do MVP estão explicitamente isoladas.
|
||||||
|
- Type consistency: comandos e módulos nomeados de forma consistente com a estrutura proposta.
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
# Design do Aplicativo de Transcrição Desktop
|
||||||
|
|
||||||
|
## Objetivo
|
||||||
|
|
||||||
|
Construir um aplicativo desktop de transcrição para Windows e Linux, com foco inicial em operação offline. O usuário inicia e encerra gravações por hotkey global, revisa a transcrição em um modal flutuante editável e confirma a inserção no aplicativo de destino. O app deve permanecer residente na bandeja/barra do sistema e ser organizado para manutenção simples, builds automatizados e evolução incremental.
|
||||||
|
|
||||||
|
## Requisitos aprovados
|
||||||
|
|
||||||
|
- Plataforma: Windows e Linux.
|
||||||
|
- Base técnica: Tauri + Rust com frontend web mínimo em TypeScript.
|
||||||
|
- Linux: suporte completo no X11 quando possível; suporte parcial e honesto no Wayland.
|
||||||
|
- Transcrição: offline-first.
|
||||||
|
- Modelo principal: `cstr/parakeet-tdt-0.6b-v3-GGUF`, arquivo `parakeet-tdt-0.6b-v3-q8_0.gguf`.
|
||||||
|
- Runtime local: CrispASR como sidecar.
|
||||||
|
- UX de revisão: sempre exibir modal editável antes de inserir texto.
|
||||||
|
- Inserção: padrão por clipboard + colar, com modo manual/fallback.
|
||||||
|
- App residente: tray/system tray com ações principais.
|
||||||
|
- CI/CD: preparado para GitHub Actions, com comandos portáveis para adaptação futura ao GitLab CI.
|
||||||
|
|
||||||
|
## Arquitetura
|
||||||
|
|
||||||
|
O projeto usa uma arquitetura modular simples. O frontend Tauri renderiza o modal, configurações e estado visível. O backend Rust expõe comandos Tauri pequenos e delega cada responsabilidade a módulos focados.
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/ Frontend TypeScript/CSS
|
||||||
|
src-tauri/src/
|
||||||
|
audio/ Estado e gravação simulada/MVP
|
||||||
|
transcription/ Contrato comum, CrispASR sidecar e API OpenAI-compatible
|
||||||
|
injection/ Estratégias de inserção/cópia de texto
|
||||||
|
models/ Catálogo, status e metadados de modelos offline
|
||||||
|
settings/ Configuração local serializável
|
||||||
|
tray.rs Ícone da bandeja e menu
|
||||||
|
ui_commands.rs Comandos Tauri expostos ao frontend
|
||||||
|
app_state.rs Estado compartilhado mínimo
|
||||||
|
lib.rs Bootstrap Tauri
|
||||||
|
main.rs Entrada nativa
|
||||||
|
```
|
||||||
|
|
||||||
|
A implementação inicial entrega um esqueleto funcional e testável. Integrações que dependem de APIs nativas frágeis, como hotkey global e injeção real em Wayland, ficam isoladas por contratos para serem implementadas em fatias futuras sem reestruturar o app.
|
||||||
|
|
||||||
|
## Modal flutuante
|
||||||
|
|
||||||
|
O modal é a interface principal após cada gravação. Ele sempre abre antes de qualquer inserção no aplicativo de destino.
|
||||||
|
|
||||||
|
Componentes:
|
||||||
|
|
||||||
|
- Cabeçalho com estado atual: pronto, gravando, transcrevendo ou revisando.
|
||||||
|
- Metadados: backend, modelo e idioma.
|
||||||
|
- Área editável para texto transcrito.
|
||||||
|
- Ações: iniciar/parar gravação, continuar, copiar, inserir, salvar.
|
||||||
|
- Mensagens de erro e fallback.
|
||||||
|
|
||||||
|
Fluxo principal:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Pronto -> Gravando -> Transcrevendo -> Revisando -> Inserido/Copiado/Salvo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inserção de texto
|
||||||
|
|
||||||
|
A inserção padrão será `ClipboardPaste`: salvar clipboard atual, copiar transcrição, enviar colar quando o sistema suportar e restaurar clipboard depois de um atraso seguro. No MVP, a implementação real de colar é representada por contrato e fallback manual; o texto é copiado para o clipboard pelo frontend quando o usuário pressiona copiar.
|
||||||
|
|
||||||
|
Política por sistema:
|
||||||
|
|
||||||
|
- Windows: usar Win32 APIs em implementação futura (`GetForegroundWindow`, `SetForegroundWindow`, `SendInput`).
|
||||||
|
- Linux X11: usar X11 ou fallback com ferramenta opcional detectada.
|
||||||
|
- Linux Wayland: tratar como parcial; se automação for bloqueada, copiar texto e orientar Ctrl+V.
|
||||||
|
|
||||||
|
## Modelos offline
|
||||||
|
|
||||||
|
O gerenciador de modelos começa com catálogo estático e validação por metadados.
|
||||||
|
|
||||||
|
Modelo principal:
|
||||||
|
|
||||||
|
- ID: `parakeet-tdt-0.6b-v3-q8_0`
|
||||||
|
- Repositório: `cstr/parakeet-tdt-0.6b-v3-GGUF`
|
||||||
|
- Arquivo: `parakeet-tdt-0.6b-v3-q8_0.gguf`
|
||||||
|
- Tamanho aproximado: 711 MiB / 745,121,600 bytes conforme metadados do repositório.
|
||||||
|
- Idiomas: modelo base Parakeet v3 com detecção automática e suporte a 25 idiomas europeus, incluindo português.
|
||||||
|
|
||||||
|
O app não embute o modelo no instalador principal. O instalador fica leve; o modelo é baixado pelo app ou instalado manualmente em diretório local configurado.
|
||||||
|
|
||||||
|
## Transcrição local com CrispASR
|
||||||
|
|
||||||
|
CrispASR será executado como sidecar empacotado por Tauri. O contrato de transcrição aceita caminho de áudio e opções, e retorna texto, idioma detectado opcional, backend, modelo e duração.
|
||||||
|
|
||||||
|
No MVP, se o sidecar ou o modelo ainda não estiver configurado, o backend retorna mensagem clara de erro/pendência para o modal. Isso permite validar UI, settings e pipeline sem depender imediatamente do binário externo.
|
||||||
|
|
||||||
|
## API OpenAI-compatible
|
||||||
|
|
||||||
|
A integração com API externa fica em adaptador separado. Campos previstos:
|
||||||
|
|
||||||
|
- endpoint base;
|
||||||
|
- API key;
|
||||||
|
- modelo;
|
||||||
|
- idioma opcional;
|
||||||
|
- timeout.
|
||||||
|
|
||||||
|
Esse adaptador não é o padrão inicial. Ele existe para manter a arquitetura pronta para fallback online sem contaminar o fluxo offline.
|
||||||
|
|
||||||
|
## CI/CD
|
||||||
|
|
||||||
|
Workflows previstos:
|
||||||
|
|
||||||
|
- `ci.yml`: lint, typecheck, testes Rust, testes frontend e build web.
|
||||||
|
- `build.yml`: build desktop por matriz Windows/Linux.
|
||||||
|
- `release.yml`: publicação automática em GitHub Releases por tag `v*.*.*`.
|
||||||
|
|
||||||
|
A lógica principal fica em scripts npm/cargo para facilitar migração para GitLab CI.
|
||||||
|
|
||||||
|
## Escopo do MVP implementado nesta primeira fatia
|
||||||
|
|
||||||
|
- Estrutura de projeto Tauri + Rust + TypeScript.
|
||||||
|
- Módulos backend com contratos e testes unitários.
|
||||||
|
- Catálogo do modelo Parakeet GGUF Q8.
|
||||||
|
- Configuração local padrão.
|
||||||
|
- Modal funcional no frontend com revisão, cópia e simulação de fluxo.
|
||||||
|
- Tray com menu básico.
|
||||||
|
- Workflows de CI/build/release.
|
||||||
|
- Documentação de desenvolvimento e arquitetura.
|
||||||
|
|
||||||
|
## Fora do escopo imediato
|
||||||
|
|
||||||
|
- Captura real de áudio por dispositivo.
|
||||||
|
- Hotkey global real.
|
||||||
|
- Execução real do CrispASR em todos os SOs.
|
||||||
|
- Injeção real em aplicativos externos.
|
||||||
|
- Download real com barra de progresso.
|
||||||
|
|
||||||
|
Essas partes ficam isoladas em módulos para serem implementadas sem alterar a arquitetura principal.
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="pt-BR">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Transcript</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main id="app"></main>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Generated
+2269
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "transcript-desktop",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --host 127.0.0.1 --port 1420",
|
||||||
|
"build": "tsc --noEmit && vite build",
|
||||||
|
"test": "vitest",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"tauri:dev": "tauri dev",
|
||||||
|
"tauri:build": "tauri build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "^2.9.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tauri-apps/cli": "^2.9.0",
|
||||||
|
"jsdom": "^29.1.1",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
|
"vite": "^7.2.6",
|
||||||
|
"vitest": "^4.0.15"
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+5118
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "transcript-desktop"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Offline-first desktop transcription app for Windows and Linux"
|
||||||
|
authors = ["Transcript contributors"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "transcript_desktop_lib"
|
||||||
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "2", features = [] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
tauri = { version = "2", features = ["tray-icon", "image-png"] }
|
||||||
|
tauri-plugin-opener = "2"
|
||||||
|
thiserror = "2"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
tauri_build::build();
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
|
"identifier": "default",
|
||||||
|
"description": "Permissões padrão do aplicativo Transcript.",
|
||||||
|
"windows": ["main"],
|
||||||
|
"permissions": ["core:default", "opener:default"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
|
pub fn run() {
|
||||||
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_opener::init())
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("erro ao executar o aplicativo Tauri");
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
transcript_desktop_lib::run();
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
|
"productName": "Transcript",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"identifier": "com.transcript.desktop",
|
||||||
|
"build": {
|
||||||
|
"beforeDevCommand": "npm run dev",
|
||||||
|
"devUrl": "http://127.0.0.1:1420",
|
||||||
|
"beforeBuildCommand": "npm run build",
|
||||||
|
"frontendDist": "../dist"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "Transcript",
|
||||||
|
"label": "main",
|
||||||
|
"width": 600,
|
||||||
|
"height": 560,
|
||||||
|
"minWidth": 360,
|
||||||
|
"minHeight": 420,
|
||||||
|
"resizable": true,
|
||||||
|
"alwaysOnTop": true,
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"security": {
|
||||||
|
"csp": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": "all",
|
||||||
|
"externalBin": []
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
import './styles.css';
|
||||||
|
|
||||||
|
const app = document.querySelector<HTMLDivElement>('#app');
|
||||||
|
|
||||||
|
if (!app) {
|
||||||
|
throw new Error('Elemento #app não encontrado.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const shell = document.createElement('section');
|
||||||
|
shell.className = 'shell';
|
||||||
|
shell.setAttribute('aria-labelledby', 'app-title');
|
||||||
|
|
||||||
|
const hero = document.createElement('header');
|
||||||
|
hero.className = 'hero';
|
||||||
|
|
||||||
|
const eyebrow = document.createElement('p');
|
||||||
|
eyebrow.className = 'eyebrow';
|
||||||
|
eyebrow.textContent = 'Transcript Desktop';
|
||||||
|
|
||||||
|
const title = document.createElement('h1');
|
||||||
|
title.id = 'app-title';
|
||||||
|
title.textContent = 'Transcrição offline para Windows e Linux';
|
||||||
|
|
||||||
|
const subtitle = document.createElement('p');
|
||||||
|
subtitle.className = 'subtitle';
|
||||||
|
subtitle.textContent =
|
||||||
|
'Estrutura inicial Tauri + Rust preparada para modal editável, CrispASR sidecar e modelo Parakeet GGUF Q8.';
|
||||||
|
|
||||||
|
hero.append(eyebrow, title, subtitle);
|
||||||
|
|
||||||
|
const card = document.createElement('article');
|
||||||
|
card.className = 'card';
|
||||||
|
card.setAttribute('aria-label', 'Estado inicial');
|
||||||
|
|
||||||
|
const statusDot = document.createElement('span');
|
||||||
|
statusDot.className = 'status-dot';
|
||||||
|
statusDot.setAttribute('aria-hidden', 'true');
|
||||||
|
|
||||||
|
const cardText = document.createElement('div');
|
||||||
|
const cardTitle = document.createElement('h2');
|
||||||
|
cardTitle.textContent = 'Pronto para configurar o fluxo';
|
||||||
|
const cardDescription = document.createElement('p');
|
||||||
|
cardDescription.textContent = 'Os módulos nativos serão conectados nas próximas fatias.';
|
||||||
|
cardText.append(cardTitle, cardDescription);
|
||||||
|
|
||||||
|
card.append(statusDot, cardText);
|
||||||
|
shell.append(hero, card);
|
||||||
|
app.append(shell);
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
:root {
|
||||||
|
color: #101418;
|
||||||
|
background: #f5f1ea;
|
||||||
|
font-family:
|
||||||
|
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||||
|
sans-serif;
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
min-width: 320px;
|
||||||
|
min-height: 100vh;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top left, rgba(119, 93, 72, 0.16), transparent 32rem),
|
||||||
|
#f5f1ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
textarea,
|
||||||
|
input,
|
||||||
|
select {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell {
|
||||||
|
width: min(100%, 760px);
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 3rem 1.25rem;
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
margin: 0;
|
||||||
|
color: #7a4f38;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
max-width: 12ch;
|
||||||
|
font-size: clamp(2.5rem, 8vw, 5rem);
|
||||||
|
line-height: 0.9;
|
||||||
|
letter-spacing: -0.07em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
max-width: 42rem;
|
||||||
|
color: #586069;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 1rem;
|
||||||
|
border: 1px solid rgba(16, 20, 24, 0.12);
|
||||||
|
border-radius: 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.72);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card p {
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
color: #66707a;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 0.75rem;
|
||||||
|
height: 0.75rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #1f8f5f;
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(31, 143, 95, 0.16);
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
|
"types": ["vitest/globals"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
clearScreen: false,
|
||||||
|
server: {
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 1420,
|
||||||
|
strictPort: true,
|
||||||
|
},
|
||||||
|
envPrefix: ['VITE_', 'TAURI_'],
|
||||||
|
test: {
|
||||||
|
environment: 'jsdom',
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in new issue
Block a user