163 lines
6.1 KiB
Markdown
163 lines
6.1 KiB
Markdown
# Implementação — DT-10: Painel de Debug do Comando Gerado
|
|
|
|
**Data:** 01/03/2026
|
|
**Status:** ✅ Implementado
|
|
**Prioridade:** Baixa — melhoria de DX para usuário técnico
|
|
**Arquivos afetados:**
|
|
|
|
- `src/ui/components/execution_panel.rs`
|
|
- `src/ui/app.rs`
|
|
- `PRD.md` (documentação ao concluir)
|
|
|
|
---
|
|
|
|
## Objetivo
|
|
|
|
Exibir, de forma colapsável na `ExecutionPanel`, o comando exato (`ffmpeg …` ou `mkvmerge …`) que foi enviado ao processo externo na última geração. Permite que o usuário técnico inspecione, copie e reproduza o comando manualmente para diagnóstico.
|
|
|
|
---
|
|
|
|
## Viabilidade
|
|
|
|
| Ponto | Situação |
|
|
| ------------------------------------------------ | ------------------------------------------------ | --- | ---------------------------------- |
|
|
| `args: Vec<String>` já construído antes do spawn | `start_generation()` — linha ~244 de `app.rs` |
|
|
| `ExecutionPanel` já usa `ui.collapsing()` | Padrão reutilizado do "Log detalhado" |
|
|
| Nenhuma camada nova necessária | Mudanças em 2 arquivos apenas |
|
|
| Sem impacto em testes existentes | Campo additive — nenhuma assinatura pública muda |
|
|
| Botão "Copiar" sem dependência adicional | `ui.output_mut( | o | o.copied_text = …)` nativo do egui |
|
|
|
|
**Estimativa:** ~30 linhas adicionadas. Risco zero de regressão.
|
|
|
|
---
|
|
|
|
## Passo 1 — Campo `last_command` em `ExecutionPanel`
|
|
|
|
**Arquivo:** `src/ui/components/execution_panel.rs`
|
|
|
|
Adicionar o campo à struct:
|
|
|
|
```rust
|
|
pub struct ExecutionPanel {
|
|
pub state: ExecutionState,
|
|
pub log_lines: Vec<String>,
|
|
/// Comando completo enviado ao FFmpeg/mkvmerge na última geração.
|
|
/// Exemplo: "ffmpeg -i input.mkv -c copy ... output.mkv"
|
|
pub last_command: Option<String>,
|
|
}
|
|
```
|
|
|
|
Inicializar em `ExecutionPanel::new()`:
|
|
|
|
```rust
|
|
pub fn new() -> Self {
|
|
ExecutionPanel {
|
|
state: ExecutionState::Idle,
|
|
log_lines: Vec::new(),
|
|
last_command: None,
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Passo 2 — Formatar e armazenar o comando em `app.rs`
|
|
|
|
**Arquivo:** `src/ui/app.rs`
|
|
**Localização:** método `start_generation()`, logo após construir `args` e antes do `thread::spawn`
|
|
|
|
```rust
|
|
// Formatar comando legível para o painel de debug
|
|
let binary = if uses_mkvmerge { "mkvmerge" } else { "ffmpeg" };
|
|
let cmd_str = format!(
|
|
"{} {}",
|
|
binary,
|
|
args.iter()
|
|
.map(|a| if a.contains(' ') { format!("\"{}\"", a) } else { a.clone() })
|
|
.collect::<Vec<_>>()
|
|
.join(" ")
|
|
);
|
|
self.execution_panel.last_command = Some(cmd_str);
|
|
```
|
|
|
|
> **Atenção:** replicar o mesmo bloco em `start_batch_generation()` para cobrir o modo Lote (RF-09).
|
|
|
|
---
|
|
|
|
## Passo 3 — Renderizar o painel colapsável em `ExecutionPanel::ui()`
|
|
|
|
**Arquivo:** `src/ui/components/execution_panel.rs`
|
|
**Localização:** após o bloco `"Log detalhado"`, antes do `});` de fechamento do `ui.group`
|
|
|
|
```rust
|
|
if let Some(cmd) = &self.last_command {
|
|
ui.collapsing("🛠 Comando gerado", |ui| {
|
|
let mut cmd_str = cmd.as_str();
|
|
ui.add(
|
|
egui::TextEdit::multiline(&mut cmd_str)
|
|
.desired_rows(3)
|
|
.desired_width(f32::INFINITY)
|
|
.font(egui::TextStyle::Monospace),
|
|
);
|
|
if ui.small_button("📋 Copiar").clicked() {
|
|
ui.output_mut(|o| o.copied_text = cmd.clone());
|
|
}
|
|
});
|
|
}
|
|
```
|
|
|
|
**Comportamento esperado:**
|
|
|
|
- Colapsável fechado por padrão — não ocupa espaço visual desnecessário
|
|
- `TextEdit` somente leitura (variável local imutável no contexto do egui)
|
|
- Botão "📋 Copiar" coloca o comando no clipboard do sistema
|
|
- Argumento com espaço interno é envolvido em aspas para reprodutibilidade no terminal
|
|
|
|
---
|
|
|
|
## Passo 4 — Comportamento de limpeza
|
|
|
|
`last_command` **não** é limpo ao iniciar nova geração — ele é sobrescrito. O usuário vê sempre o comando da execução atual.
|
|
|
|
`last_command` **não** é limpo em `cancel_generation()` — o usuário pode querer inspecionar o comando de uma geração cancelada.
|
|
|
|
Não há necessidade de resetar em nenhum outro ponto.
|
|
|
|
---
|
|
|
|
## Passo 5 — Atualizar PRD e débitos técnicos
|
|
|
|
Ao concluir a implementação:
|
|
|
|
1. Marcar DT-10 como `✅ Resolvido` na tabela da seção 10 do `PRD.md`
|
|
2. Remover o item do backlog da seção 11
|
|
3. Adicionar critério de aceitação na seção 12:
|
|
- `[x] O comando FFmpeg/mkvmerge gerado é exibido em painel colapsável na ExecutionPanel (DT-10)`
|
|
|
|
---
|
|
|
|
## Ordem de execução recomendada
|
|
|
|
| # | Passo | Testável após |
|
|
| --- | --------------------------------------------------------------------- | ---------------------------------- |
|
|
| 1 | Adicionar campo + init em `ExecutionPanel` | `cargo check` |
|
|
| 2 | Renderizar painel colapsável (Passo 3) com valor hardcoded temporário | `cargo run` — inspeção visual |
|
|
| 3 | Wiring em `start_generation()` (Passo 2) | `cargo run` — geração real |
|
|
| 4 | Replicar wiring em `start_batch_generation()` | `cargo run` — modo lote |
|
|
| 5 | Remover hardcode temporário se usado; `cargo test` | 33 testes devem continuar passando |
|
|
| 6 | Atualizar PRD (Passo 5) | — |
|
|
|
|
---
|
|
|
|
## Critério de aceitação
|
|
|
|
- [x] O painel "🛠 Comando gerado" aparece na `ExecutionPanel` após a primeira geração
|
|
- [x] O painel está colapsado por padrão
|
|
- [x] O conteúdo exibe o binário (`ffmpeg` ou `mkvmerge`) seguido de todos os argumentos
|
|
- [x] Argumentos com espaços internos são envolvidos em aspas duplas
|
|
- [x] O botão "📋 Copiar" coloca o texto no clipboard
|
|
- [x] O comando é exibido também para gerações canceladas
|
|
- [x] O painel **não** aparece antes da primeira geração (estado `Idle` inicial)
|
|
- [x] Modo Lote exibe o comando do último item processado
|
|
- [x] `cargo test` — 68 testes passando sem regressão
|