# Relatório de Handoff — TecJustiça Transcribe → Electron

> Documento técnico para o desenvolvedor que reescreverá a aplicação como app Electron para Windows.
> Versão atual do protótipo Python: **0.1.17**

---

## 1. Visão Geral

**TecJustiça Transcribe** é uma aplicação desktop para transcrição automática de audiências judiciais usando WhisperX (ASR + alinhamento forçado + diarização de falantes).

### O que o app faz

1. Recebe um arquivo de áudio/vídeo (MP4, WAV, MP3, M4A, OGG, FLAC)
2. Transcreve o conteúdo com WhisperX (baseado em Faster Whisper / CTranslate2)
3. Alinha timestamps no nível de palavra
4. Identifica falantes (diarização via pyannote.audio)
5. Gera 3 formatos de saída: `.txt` (texto puro), `.srt` (legendas), `.json` (dados completos)
6. Exibe player de mídia sincronizado com transcrição clicável

### Stack atual

| Camada | Tecnologia |
|--------|-----------|
| Linguagem | Python 3.10–3.13 |
| ASR | WhisperX 3.3+ (Faster Whisper + pyannote.audio) |
| GPU | PyTorch + CUDA (via nvidia-smi) |
| GUI desktop | NiceGUI 2.0+ (Quasar/Vue3) + pywebview 5.0+ (WebKitGTK) |
| GUI bindings | PyGObject 3.50+ / pycairo 1.25+ |
| CLI | Click 8.0+ / Rich 13.0+ |
| Build | Hatchling |
| Instalador | Bash script (`install.sh`) com `uv tool install` |
| Config | JSON em `~/.config/tecjustica/config.json` |

### Entry points

```
tecjustica-transcribe  →  src/tecjustica_transcribe/cli.py:main      (CLI)
tecjustica-gui         →  src/tecjustica_transcribe/gui/app.py:main  (GUI desktop)
```

---

## 2. Arquitetura

### Separação core vs GUI

O projeto foi desenhado com separação clara entre lógica pura (Python puro, sem UI) e camada de apresentação. **O core inteiro pode ser reutilizado como subprocesso Python no Electron.**

```
src/tecjustica_transcribe/
├── __init__.py              # __version__ = "0.1.17"
├── cli.py                   # CLI Click — supressão de warnings, entry point
├── diagnostico.py           # Wrapper Rich sobre core/checks (init interativo)
├── transcrever.py           # Wrapper Rich sobre core/transcription (CLI)
│
├── core/                    # ★ LÓGICA PURA — sem dependência de UI ★
│   ├── checks.py            # Detecção: Python, NVIDIA, CUDA, GPU/VRAM, ffmpeg, token HF
│   ├── config.py            # Carregar/salvar config JSON (~/.config/tecjustica/)
│   ├── models.py            # Listar/baixar/deletar modelos WhisperX (HuggingFace Hub)
│   └── transcription.py     # Pipeline WhisperX completo (6 etapas com callback)
│
└── gui/                     # ★ CAMADA DE APRESENTAÇÃO — NiceGUI + pywebview ★
    ├── app.py               # Layout VS Code-like, CSS, detecção WSL2, main()
    └── pages/
        ├── transcricao.py   # Upload + transcrição + player + texto clicável
        ├── modelos.py       # Download/exclusão de modelos
        ├── configuracoes.py # Token HF, pasta de saída, device, batch_size
        └── diagnostico.py   # Verificações do sistema (tabela de checks)
```

### Diagrama de dependência

```
┌─────────────────────────────────────────────────────────┐
│                    GUI (NiceGUI)                         │
│  app.py → pages/{transcricao,modelos,configuracoes,...}  │
└────────────────────────┬────────────────────────────────┘
                         │ importa
┌────────────────────────▼────────────────────────────────┐
│                    core/ (Python puro)                    │
│  transcription.py  checks.py  config.py  models.py      │
└────────────────────────┬────────────────────────────────┘
                         │ importa
┌────────────────────────▼────────────────────────────────┐
│              Dependências externas                        │
│  whisperx, torch, faster_whisper, pyannote.audio,        │
│  huggingface_hub, ffmpeg (binário)                       │
└─────────────────────────────────────────────────────────┘
```

---

## 3. Pipeline WhisperX — As 6 Etapas

Arquivo: `core/transcription.py` — função `executar_pipeline()`

| # | Etapa | O que faz | Progresso |
|---|-------|-----------|-----------|
| 1 | **modelo** | `whisperx.load_model(modelo, device, compute_type, language)` | 10% |
| 2 | **audio** | `whisperx.load_audio(arquivo)` — decodifica via ffmpeg | 25% |
| 3 | **transcricao** | `model.transcribe(audio, batch_size=N)` — ASR batched | 50% |
| 4 | **alinhamento** | `whisperx.load_align_model()` + `whisperx.align()` — timestamps word-level | 70% |
| 5 | **diarizacao** | `DiarizationPipeline(token=hf_token)` + `assign_word_speakers()` | 90% |
| 6 | **salvando** | Gera `.srt`, `.txt`, `.json` | 100% |

### Callback de progresso

```python
on_progress: Callable[[str, str], None]  # (etapa, mensagem)
```

A GUI usa uma `queue.Queue` para comunicar progresso da thread worker para o timer do NiceGUI (polling a cada 0.5s). **No Electron, o equivalente seria IPC messages do processo Python → renderer.**

### Auto batch_size por VRAM

```python
def _obter_batch_size() -> int:
    vram_gb = torch.cuda.get_device_properties(0).total_memory / (1024**3)
    if vram_gb >= 10: return 16
    if vram_gb >= 6:  return 8
    return 4  # fallback (CPU ou VRAM baixa)
```

### Compute type

- GPU (CUDA): `float16`
- CPU: `int8`

### Formatos de saída

| Formato | Conteúdo |
|---------|----------|
| `.txt` | Texto puro com identificação de falantes (`[SPEAKER_00]`) |
| `.srt` | Legendas com timestamps `HH:MM:SS,mmm → HH:MM:SS,mmm` e falantes |
| `.json` | Array de segments com `start`, `end`, `text`, `speaker`, `words` |

---

## 4. Detecção de Ambiente

Arquivo: `core/checks.py` — 6 verificações executadas em sequência.

| Check | Como detecta | Critério de OK |
|-------|-------------|----------------|
| **Python** | `platform.python_version()` | 3.10 ≤ versão < 3.14 |
| **Driver NVIDIA** | `nvidia-smi --query-gpu=driver_version` (subprocess) | returncode == 0 |
| **CUDA** | `torch.cuda.is_available()` + `torch.version.cuda` | CUDA disponível |
| **GPU/VRAM** | `torch.cuda.get_device_name(0)` + `get_device_properties(0).total_memory` | VRAM ≥ 6 GB |
| **ffmpeg** | `ffmpeg -version` (subprocess) | returncode == 0 |
| **Token HF** | `config.get("hf_token")` do JSON | Token não-vazio |

### Para o Electron

- **nvidia-smi**: funciona igual no Windows — `nvidia-smi.exe` está no PATH quando o driver está instalado
- **torch.cuda**: funciona com PyTorch+CUDA para Windows
- **ffmpeg**: precisa estar no PATH ou ser empacotado junto. No Windows, baixar o binário estático do ffmpeg.org
- **Python**: o Electron vai embuti-lo ou usar um venv empacotado

---

## 5. Gestão de Modelos

Arquivo: `core/models.py`

### Catálogo de modelos

| Nome | Repositório HuggingFace | Tamanho |
|------|------------------------|---------|
| `large-v2` | `Systran/faster-whisper-large-v2` | ~3.1 GB |
| `medium` | `Systran/faster-whisper-medium` | ~1.5 GB |
| `small` | `Systran/faster-whisper-small` | ~500 MB |
| `tiny` | `Systran/faster-whisper-tiny` | ~150 MB |

### Cache

- Diretório: `~/.cache/huggingface/hub/`
- Pattern: `models--Systran--faster-whisper-{nome}/`
- Download via `huggingface_hub.snapshot_download(repo_id=repo)`
- Exclusão via `shutil.rmtree(cache_path)`

### Modelos de diarização (implícitos)

O pyannote.audio baixa automaticamente seus modelos na primeira execução:
- `pyannote/speaker-diarization-3.1` — requer aceitar termos no HuggingFace
- `pyannote/segmentation-3.0`

Esses modelos também ficam em `~/.cache/huggingface/hub/` e precisam do token HF para download.

---

## 6. Sistema de Configuração

Arquivo: `core/config.py`

### Localização

```
~/.config/tecjustica/config.json
```

### Estrutura do JSON

```json
{
  "hf_token": "hf_...",
  "output_dir": "./transcricoes",
  "device": "auto",
  "batch_size": 0
}
```

| Campo | Tipo | Default | Descrição |
|-------|------|---------|-----------|
| `hf_token` | string | `""` | Token HuggingFace (obrigatório para diarização) |
| `output_dir` | string | `"./transcricoes"` | Pasta de saída dos arquivos gerados |
| `device` | string | `"auto"` | `"auto"`, `"cuda"`, ou `"cpu"` |
| `batch_size` | int | `0` | 0 = auto (ajusta por VRAM), ou valor fixo 1–32 |

### API

```python
carregar_config() -> dict       # Lê JSON do disco (retorna {} se não existe)
salvar_config(config: dict)     # Salva JSON (cria diretório se necessário)
obter_token_hf() -> str | None  # Shortcut para ler token
salvar_token_hf(token: str)     # Shortcut para salvar token
```

### Para o Electron

No Windows, substituir `~/.config/tecjustica/` por `%APPDATA%/TecJustica/` ou usar `electron.app.getPath('userData')`.

---

## 7. GUI Atual — 4 Páginas

Arquivo: `gui/app.py` + `gui/pages/`

### Layout

Inspirado no VS Code: title bar + activity bar lateral + conteúdo + status bar.

```
┌──────────────────────────────────────────────┐
│  ⚖ TecJustiça Transcribe        (title bar)  │
├────────┬─────────────────────────────────────┤
│ 🎤 Trans│  ┌─────────────────────────────┐   │
│ 📦 Model│  │  Conteúdo da página ativa   │   │
│ ⚙ Config│  │                             │   │
│ 💻 Sist │  │                             │   │
│        │  └─────────────────────────────┘   │
├────────┴─────────────────────────────────────┤
│  GPU RTX 3060 (12.0 GB) | CUDA 12.1 | v0.1.17│
└──────────────────────────────────────────────┘
```

### Tema CSS

Variáveis CSS definidas em `VSCODE_CSS` (327 linhas) no `app.py`:

```css
--bg: #1e1e1e;          /* fundo principal */
--sidebar: #252526;      /* painéis/cards */
--activitybar: #333333;  /* barra lateral */
--titlebar: #323233;     /* barra de título */
--statusbar: #007acc;    /* barra de status (azul VS Code) */
--border: #3c3c3c;       /* bordas */
--text: #cccccc;         /* texto principal */
--text-dim: #858585;     /* texto secundário */
--accent: #0e639c;       /* cor de destaque */
--success: #4ec9b0;      /* verde */
--error: #f44747;        /* vermelho */
--warning: #cca700;      /* amarelo */
```

Fonte monospace: JetBrains Mono (importada via Google Fonts).

### Páginas

#### 7.1 Transcrição (`pages/transcricao.py`)

- Input de caminho de arquivo (texto ou upload drag-and-drop, max 500 MB)
- Seleção de modelo (large-v2, medium, small, tiny)
- Pasta de saída customizável
- Toggle de diarização
- Barra de progresso com 6 etapas visuais (ícones: spinner → check)
- Painel de resultado com caminhos dos arquivos gerados
- **Player de mídia sincronizado**: vídeo (`.mp4`, `.mkv`, `.avi`, `.mov`, `.webm`) ou áudio
  - URL gerada via `app.add_media_file()` (NiceGUI serve o arquivo localmente)
  - Transcrição clicável: cada segmento mostra timestamp + falante + texto
  - Clicar num segmento faz `player.seek(start)` + `player.play()`
  - Highlight do segmento ativo via `timeupdate` event (throttled 0.5s)

#### 7.2 Modelos (`pages/modelos.py`)

- Lista os 4 modelos com status (baixado/não baixado) e tamanho
- Botão "Baixar" (thread em background) com notificação de progresso
- Botão "Excluir" com diálogo de confirmação

#### 7.3 Configurações (`pages/configuracoes.py`)

- Token HuggingFace (campo password com toggle de visibilidade)
- Link para obter token e aceitar termos do pyannote
- Pasta de saída padrão
- Seleção de device (auto/cuda/cpu)
- Batch size (0 = auto, ou 1–32)
- Caminho do arquivo de configuração exibido

#### 7.4 Sistema/Diagnóstico (`pages/diagnostico.py`)

- Executa as 6 verificações de `core/checks.py`
- Exibe tabela com ícone ✅/❌, nome e detalhe
- Resumo: "Tudo pronto" ou "N problema(s) encontrado(s)"
- Botão "Re-verificar"

### Detecção de sistema na GUI

A info de GPU/CUDA é carregada em background thread (evita bloquear startup):

```python
threading.Thread(target=_load_system_info_async, daemon=True).start()
```

A status bar faz polling (timer 1s) até a info chegar, depois para.

---

## 8. Sistema de Instalação

Arquivo: `install.sh`

### Fluxo do instalador

1. **Preflight**: verifica Linux x86_64
2. **Detecta WSL2**: `grep -qi microsoft /proc/version`
3. **Verifica GPU NVIDIA**: `nvidia-smi` (aviso, não bloqueia)
4. **Instala dependências do sistema** (via `apt-get`):
   - `ffmpeg` — decodificação de áudio/vídeo
   - `libgirepository-2.0-dev`, `gcc`, `libcairo2-dev`, `pkg-config`, `python3-dev` — PyGObject/pycairo
   - `gir1.2-webkit2-4.1` — WebKitGTK para pywebview
   - `gstreamer1.0-plugins-bad`, `gstreamer1.0-libav` — codecs de mídia no WebKitGTK
5. **Instala uv** se ausente (gerenciador de pacotes Python)
6. **Instala pacote**: `uv tool install tecjustica-transcribe[gui] --force --python ">=3.10,<3.14"`
7. **Configura PATH**: adiciona `~/.local/bin` ao shell RC
8. **Cria atalho .desktop**: ícone SVG + arquivo `.desktop` para menu de aplicativos
9. **Mensagem final**: orienta a rodar `tecjustica-transcribe init`

### Dependências Python (pyproject.toml)

**Core:**
```
whisperx>=3.3.0
click>=8.0
rich>=13.0
```

**GUI (extra `[gui]`):**
```
nicegui>=2.0
pywebview>=5.0
PyGObject>=3.50
pycairo>=1.25
```

**Transitivas críticas (instaladas pelo whisperx):**
- `torch` + `torchaudio` (com CUDA)
- `faster-whisper` (CTranslate2)
- `pyannote.audio` (diarização)
- `huggingface_hub` (download de modelos)
- `onnxruntime` (inferência)

---

## 9. Gargalos e Lições Aprendidas

### 9.1 WebKitGTK + GStreamer — O maior gargalo

**Problema**: pywebview no Linux usa WebKitGTK, que depende do GStreamer para reproduzir mídia. Sem os plugins certos (`gstreamer1.0-plugins-bad`, `gstreamer1.0-libav`), vídeos MP4 simplesmente não tocam — sem erro, sem feedback.

**Impacto**: Exigiu instalar 2 pacotes extras do sistema (centenas de MBs de deps transitivas).

**Lição para Electron**: Chromium já tem codecs embutidos (H.264, AAC, WebM). O player `<video>` e `<audio>` funcionarão nativamente sem dependências extras. **Este é o maior ganho da migração para Electron.**

### 9.2 Warnings Python incontroláveis

**Problema**: PyTorch, pyannote, torchcodec e onnxruntime emitem dezenas de warnings no import — UserWarning, TensorFloat-32, Lightning migration notices, e mensagens C++ no stderr.

**Solução atual** (`cli.py`):
```python
# Filtros Python
warnings.filterwarnings("ignore", category=UserWarning, module="pyannote")
warnings.filterwarnings("ignore", category=UserWarning, module="torchcodec")
warnings.filterwarnings("ignore", category=UserWarning, module="torch")
warnings.filterwarnings("ignore", message=".*TensorFloat-32.*")

# Loggers
logging.getLogger("lightning.pytorch").setLevel(logging.ERROR)
logging.getLogger("whisperx").setLevel(logging.WARNING)

# ONNX Runtime (env vars antes do import)
os.environ["ORT_LOG_LEVEL"] = "3"
os.environ["ONNXRUNTIME_LOG_SEVERITY_LEVEL"] = "3"

# Para warnings C++ no stderr (última linha de defesa)
def _suprimir_stderr():
    devnull = os.open(os.devnull, os.O_WRONLY)
    os.dup2(devnull, 2)
```

**Lição para Electron**: O processo Python será um child process. Redirecionar seu stderr para um log file ou `/dev/null` é mais limpo. No Windows, usar `NUL` em vez de `/dev/null`.

### 9.3 OOM (Out of Memory) na GPU

**Problema**: Áudios longos (2+ horas de audiência) com `batch_size` alto podem estourar a VRAM.

**Solução atual**: Auto batch_size baseado na VRAM detectada + try/catch com mensagem amigável.

**Lição para Electron**: Manter o auto batch_size. Considerar adicionar um monitor de VRAM em tempo real (via `torch.cuda.memory_allocated()`) e abortar antes do OOM real.

### 9.4 Threading e comunicação GUI ↔ Worker

**Problema**: NiceGUI roda no asyncio event loop. Operações pesadas (whisperx) precisam rodar em thread separada. A comunicação é feita via `queue.Queue` + `ui.timer` polling.

**Solução atual**:
- Thread worker põe mensagens na fila: `("progress", etapa, msg)`, `("done", result)`, `("error", msg)`
- Timer NiceGUI (0.5s) drena a fila e atualiza UI

**Lição para Electron**: Usar IPC nativo do Electron (`ipcMain`/`ipcRenderer`). O processo Python pode escrever JSON lines no stdout, e o main process do Electron lê e encaminha para o renderer. Ou usar um socket/named pipe.

### 9.5 GUI sobrevivendo ao terminal

**Problema**: Se o usuário fecha o terminal que lançou a GUI, o app morria junto (SIGHUP).

**Solução atual**:
```python
signal.signal(signal.SIGHUP, signal.SIG_IGN)  # ignora SIGHUP
# + redireciona stdout/stderr para log file
os.dup2(_log_file.fileno(), 1)
os.dup2(_log_file.fileno(), 2)
```

**Lição para Electron**: Não se aplica — Electron gerencia seu próprio ciclo de vida. Logs devem ir para `electron-log` ou similar.

### 9.6 Servir mídia local

**Problema**: NiceGUI precisa servir arquivos de mídia via HTTP para o componente `<video>`/`<audio>`. Cada `app.add_media_file()` registra uma rota única.

**Lição para Electron**: Usar `file://` protocol ou custom protocol handler (`protocol.registerFileProtocol`). Muito mais simples e sem overhead de HTTP.

### 9.7 Tamanho da instalação

**Dependências Python totalizam ~5–8 GB** (PyTorch com CUDA é o maior: ~2.5 GB). Mais os modelos whisper (~3.1 GB para large-v2).

**Lição para Electron**: Considerar empacotar um Python embarcado com venv pré-configurado, ou usar PyInstaller/cx_Freeze para criar um executável standalone. O tamanho total do instalador Windows será provavelmente 3–4 GB (comprimido).

---

## 10. Recomendações para o Electron

### 10.1 Arquitetura sugerida

```
┌─────────────────────────────────────────────┐
│              Electron Main Process           │
│  - Gerencia janela, menu, tray              │
│  - Spawna processo Python como child        │
│  - IPC: stdin/stdout JSON lines             │
└────────────────┬────────────────────────────┘
                 │ IPC (JSON lines via stdio)
┌────────────────▼────────────────────────────┐
│         Python Backend (child process)       │
│  - core/transcription.py (pipeline)         │
│  - core/checks.py (diagnóstico)             │
│  - core/models.py (gestão de modelos)       │
│  - core/config.py (configuração)            │
│  Reutilizar o core/ inteiro sem mudanças    │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│            Electron Renderer Process         │
│  - React/Vue/Svelte (ou vanilla)            │
│  - Player <video>/<audio> nativo            │
│  - CSS tema VS Code (reutilizar variáveis)  │
│  - Transcrição clicável sincronizada        │
└─────────────────────────────────────────────┘
```

### 10.2 IPC com Python

Protocolo sugerido — JSON lines no stdout do processo Python:

```json
{"type": "progress", "stage": "transcricao", "message": "Transcrevendo (batch_size=16)..."}
{"type": "progress", "stage": "diarizacao", "message": "Identificando falantes..."}
{"type": "result", "segments": [...], "files": {"srt": "...", "txt": "...", "json": "..."}}
{"type": "error", "message": "CUDA out of memory"}
{"type": "check", "name": "GPU", "ok": true, "detail": "RTX 3060 (12.0 GB)"}
```

Comandos do Electron → Python via stdin:

```json
{"action": "transcribe", "file": "C:\\Users\\...\\audiencia.mp4", "model": "large-v2", "diarize": true}
{"action": "check_system"}
{"action": "list_models"}
{"action": "download_model", "name": "large-v2"}
```

### 10.3 Vantagens do Chromium (que o WebKitGTK não tem)

| Feature | WebKitGTK (atual) | Chromium (Electron) |
|---------|-------------------|---------------------|
| Codecs H.264/AAC | Requer GStreamer plugins | Embutido |
| DevTools | Limitado | Chrome DevTools completo |
| Performance JS | Boa | Excelente (V8) |
| Compatibilidade CSS | Pode ter quirks | Referência |
| `<video>` controles | Dependem do GStreamer | Nativos e consistentes |
| File dialogs | GTK | Nativo do OS |

### 10.4 Empacotamento para Windows

**Opção 1: Python embutido**
- Empacotar `python-3.12-embed-amd64.zip` (oficial Python.org, ~15 MB)
- Criar venv com dependências pré-instaladas
- Instalar PyTorch+CUDA via wheel (separar GPU e CPU em builds diferentes)
- Usar `electron-builder` para gerar instalador NSIS

**Opção 2: PyInstaller para o backend**
- Gerar executável `.exe` do backend Python com PyInstaller
- O Electron spawna o `.exe` como child process
- Vantagem: único binário, sem gerenciar venv
- Desvantagem: build pesado (~3 GB), mais complexo de debugar

**Opção 3: Conda/Miniconda embutido**
- Mais robusto para dependências CUDA
- Já resolve conflitos PyTorch/CUDA automaticamente

### 10.5 Checklist de migração

- [ ] Extrair `core/` como pacote Python standalone (já está pronto)
- [ ] Criar wrapper JSON-lines em cima do `core/` para comunicação via stdio
- [ ] Scaffoldar app Electron com React (ou framework de escolha)
- [ ] Reimplementar as 4 páginas no frontend (CSS já está documentado acima)
- [ ] Implementar player de mídia com `<video>`/`<audio>` + transcrição clicável
- [ ] Implementar sincronização `timeupdate` → highlight de segmento ativo
- [ ] Substituir `~/.config/tecjustica/` por `%APPDATA%/TecJustica/` no Windows
- [ ] Empacotar Python + dependências + ffmpeg no instalador
- [ ] Testar em Windows 10/11 com GPU NVIDIA (CUDA)
- [ ] Testar fallback CPU (sem GPU)
- [ ] Criar instalador NSIS/MSI com electron-builder
- [ ] Adicionar auto-update (electron-updater)

### 10.6 Detalhes adicionais para referência

**Supressão de warnings no Windows**: As env vars `ORT_LOG_LEVEL=3` e `ONNXRUNTIME_LOG_SEVERITY_LEVEL=3` funcionam igual no Windows. Os `warnings.filterwarnings` do Python também.

**ffmpeg no Windows**: Baixar build estático de https://www.gyan.dev/ffmpeg/builds/ e incluir no pacote. O WhisperX chama ffmpeg via `subprocess`.

**Token HuggingFace**: O fluxo é idêntico — salvar no config JSON e passar para o `DiarizationPipeline`. O usuário precisa aceitar os termos do modelo `pyannote/speaker-diarization-3.1` no site do HuggingFace antes de usar.

**VRAM mínima**: 6 GB. Abaixo disso, o app avisa mas permite continuar (pode dar OOM). GPUs recomendadas: RTX 3060+ (12 GB) ou RTX 4060+ (8 GB).

---

## Apêndice: Dependências do Sistema (Linux → Windows)

| Linux (apt) | Propósito | Equivalente Windows |
|-------------|-----------|-------------------|
| `ffmpeg` | Decodificação áudio/vídeo | Binário estático empacotado |
| `gir1.2-webkit2-4.1` | WebKitGTK para pywebview | Desnecessário (Chromium) |
| `gstreamer1.0-plugins-bad` | Codecs no WebKitGTK | Desnecessário (Chromium) |
| `gstreamer1.0-libav` | Codecs no WebKitGTK | Desnecessário (Chromium) |
| `libgirepository-2.0-dev` | PyGObject | Desnecessário |
| `gcc`, `libcairo2-dev`, `pkg-config`, `python3-dev` | Build PyGObject/pycairo | Desnecessário |
| Driver NVIDIA | GPU compute | Driver NVIDIA para Windows |
| CUDA toolkit | Via PyTorch wheel | Via PyTorch wheel (cu121/cu124) |

> **Nota**: 6 das 8 dependências de sistema são exclusivas do WebKitGTK/pywebview e **desaparecem completamente** com Electron. Isso simplifica enormemente o instalador Windows.
