Roadmap P2: drag & drop, tail ao vivo, exportação, instância única e estado da janela - #5
Merged
Merged
Conversation
…/P2.11) Arrastar e soltar (P2.7): o README anunciava a funcionalidade, mas ela nao existia. O WebView2 engole o drop por padrao e a API web nao expoe o caminho completo do arquivo; desabilitando AllowExternalDrop, o evento chega ao formulario, que le os caminhos reais via DataFormats.FileDrop. Aceita arquivos e pastas misturados, encaminhando para LoadFromPathsAsync. Instancia unica e multiplos arquivos (P2.10): Program.cs usava apenas args[0], entao selecionar cinco .clef no Explorer abria cinco janelas, cada uma com um arquivo. Agora todos os argumentos sao carregados e um mutex nomeado + named pipe garantem uma janela por usuario: instancias seguintes entregam seus caminhos a existente, trazem a janela para frente e encerram. Se o mutex falhar, o app segue como instancia independente — perder o "instancia unica" e melhor do que nao abrir. Estado da janela (P2.11): tamanho/posicao/maximizado passam a ser preservados entre execucoes (antes abria sempre em 1200x800). Ao restaurar, valida se a posicao ainda cabe em algum monitor conectado — sem isso, desconectar um monitor secundario faria a janela reabrir fora da area visivel. Guarda RestoreBounds quando maximizada, para o tamanho "normal" sobreviver. Permissoes do WebView2 (P3.17, aproveitando a mesma area): PermissionRequested liberava TUDO (camera, microfone, geolocalizacao...). Passa a negar por padrao, permitindo apenas ClipboardRead, usada pelo botao "copiar stack trace". LogStore ganha o evento PathsLoaded, disparado apenas quando um conjunto NOVO de caminhos e carregado — e nao quando o usuario (des)marca arquivos na arvore. Assim a UI limpa a selecao de arquivos visiveis nas cargas externas (drop, linha de comando, outra instancia) sem desestabilizar a arvore durante a marcacao, que foi a causa de um bug anterior. 10 testes novos para a validacao de monitores e a persistencia do placement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nao havia como tirar nada do app: so existia 'copiar stack trace' no detalhe. Depois de montar um filtro util (ex.: todos os erros de uma correlacao), o usuario nao conseguia compartilhar o resultado. Botao Exportar na toolbar, que exporta o conjunto FILTRADO inteiro — nao apenas a pagina exibida, que e o que o usuario ve mas raramente o que quer compartilhar. O formato vem da extensao escolhida no dialogo: - CSV com escape RFC 4180 (aspas duplicadas; campo entre aspas quando contem virgula, aspas ou quebra de linha — o caso comum de stack traces). - CLEF (um JSON por linha), de modo que o arquivo exportado possa ser reaberto pelo proprio ClefExplorer. Omite '@l' para Information, prefere o MessageTemplate ao texto renderizado e mantem acentos legiveis. - Texto puro, uma linha por evento com a excecao abaixo. IFilePickerService ganha PickSaveFileAsync. A serializacao roda em background e o resultado vira notificacao de sucesso ou de erro. 20 testes cobrindo escape do CSV, formato CLEF, texto e a deducao do formato pela extensao. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
O app carregava um snapshot estatico: acompanhar uma aplicacao rodando exigia reabrir o arquivo na mao — justamente o caso de uso mais comum durante um debug. Botao 'Ao vivo' na toolbar. Quando ligado, o LogStore sonda os arquivos carregados a cada segundo e le apenas o que foi acrescentado desde a ultima leitura (offset por arquivo), em vez de reprocessar tudo. Optei por sondagem em vez de FileSystemWatcher: o watcher perde eventos quando o buffer estoura e depende de o escritor atualizar os metadados, o que loggers com buffer nem sempre fazem na hora. Comparar o tamanho do arquivo e barato e previsivel — e ja e naturalmente debounced. Detalhes que os testes cobrem: - so consome ate a ultima quebra de linha: uma linha ainda sendo escrita e JSON incompleto e envenenaria o parser; - arquivo menor que o offset (rotacao/truncamento) recomeca do zero; - eventos ja carregados nao sao relidos; - .gz e ignorado (arquivo fechado, nao cresce); - os padroes de linha ignorada continuam valendo no modo ao vivo; - uma varredura por vez, para tiques nao empilharem leituras. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
There was a problem hiding this comment.
Pull request overview
Este PR implementa o conjunto de features essenciais (P2) do roadmap do ClefExplorer e aproveita para endurecer permissões do WebView2, adicionando também cobertura de testes para as novas capacidades (tail, exportação e persistência/validação de posicionamento da janela).
Changes:
- Adiciona modo “Ao vivo” (tail) no
LogStorecom leitura incremental por offset e polling. - Implementa exportação do conjunto filtrado (CSV/CLEF/texto) e integra na toolbar.
- Adiciona instância única, suporte a múltiplos caminhos via args / outra instância / drag&drop, e persistência de estado da janela (com validação contra monitores atuais) + hardening de permissões do WebView2.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/ClefExplorer.Tests/WindowPlacementTests.cs | Testes para persistência e validação de visibilidade do placement da janela. |
| test/ClefExplorer.Tests/LogStoreTests.cs | Novos testes cobrindo o modo tail (linhas novas, truncamento, ignorados, etc.). |
| test/ClefExplorer.Tests/LogExporterTests.cs | Testes de contrato para exportação em CSV/CLEF/texto e seleção de formato. |
| src/wwwroot/css/app.css | Estilo para ações à direita na toolbar (exportar + paginação). |
| src/Services/WinFormsFilePickerService.cs | Adiciona “save file picker” para suportar exportação. |
| src/Services/WindowPlacementService.cs | Serviço de load/save do placement e validação contra monitores. |
| src/Services/SingleInstance.cs | Implementação de instância única com mutex + named pipe. |
| src/Services/LogStore.cs | Evento PathsLoaded e implementação do tail com offsets e timer. |
| src/Services/LogExporter.cs | Serialização para CSV/CLEF/texto e helpers de formato/extensão. |
| src/Services/IFilePickerService.cs | Novo método PickSaveFileAsync para exportação. |
| src/Program.cs | Integra instância única, múltiplos args e registra WindowPlacementService. |
| src/Models/WindowPlacement.cs | Modelo persistido do placement (X/Y/W/H/Maximized) + regra IsUsable. |
| src/MainForm.cs | Drag&drop, recebimento de paths de outra instância, window placement, WebView2 permissions. |
| src/Components/LogViewer.razor | Botões “Ao vivo” e “Exportar”, reset de seleção via PathsLoaded, lógica de export. |
| src/Components/LogToolbar.razor | UI da toolbar com “Ao vivo”, “Exportar” e paginação. |
| ROADMAP.md | Marca P2 como concluído e atualiza status/registro do roadmap. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
afernandes
added a commit
that referenced
this pull request
Jul 28, 2026
Com 'pull_request: branches: [main]', PRs empilhados — que apontam para outra branch de feature — ficavam sem CI: aconteceu no #5 e no #7, onde o gate de testes so passou a valer depois de disparar o workflow na mao. Removido o filtro de base; o gatilho de push continua restrito a main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resumo
Implementa as features essenciais (P2) do ROADMAP.md, mais o endurecimento de permissões do WebView2 (item 17, que caiu na mesma área do código).
O que entrou
7. Arrastar e soltar arquivos/pastas
O README anunciava a funcionalidade, mas ela não existia. O detalhe não óbvio: o WebView2 engole o drop por padrão, e a API web não expõe o caminho completo do arquivo. Desligando
AllowExternalDrop, o evento chega ao formulário, que recebe os caminhos reais viaDataFormats.FileDrop. Aceita arquivos e pastas misturados.8. Modo "Ao vivo" (tail)
O app carregava um snapshot estático — acompanhar uma aplicação rodando exigia reabrir o arquivo na mão, justamente o caso de uso mais comum durante um debug. Agora um botão na toolbar liga a sondagem: a cada segundo o
LogStorelê apenas o que foi acrescentado desde a última leitura (offset por arquivo).Optei por sondagem em vez de
FileSystemWatcher(o roadmap sugeria o watcher): ele perde eventos quando o buffer estoura e depende de o escritor atualizar os metadados, o que loggers com buffer nem sempre fazem na hora. Comparar o tamanho do arquivo é barato, previsível e já naturalmente debounced.Casos cobertos por teste: linha ainda sendo escrita é ignorada (JSON incompleto envenenaria o parser), arquivo truncado/rotacionado recomeça do zero, eventos já carregados não são relidos,
.gzé pulado, padrões de linha ignorada continuam valendo.9. Exportar eventos filtrados
Só existia "copiar stack trace" de um evento. Agora um botão exporta o conjunto filtrado inteiro — não a página exibida, que é o que o usuário vê mas raramente o que quer compartilhar. Formato deduzido da extensão:
10. Instância única + múltiplos arquivos
Program.csusava apenasargs[0]: selecionar cinco.clefno Explorer abria cinco janelas. Agora todos os argumentos são carregados, e um mutex nomeado + named pipe garantem uma janela por usuário — instâncias seguintes entregam seus caminhos à existente e encerram. Se o mutex falhar, o app segue como instância independente (perder o "instância única" é melhor que não abrir).11. Estado da janela
Tamanho/posição/maximizado preservados entre execuções (antes: sempre 1200x800). Ao restaurar, valida se a posição ainda cabe em algum monitor conectado — sem isso, desconectar um monitor secundário faria a janela reabrir fora da área visível.
17. Permissões do WebView2
PermissionRequestedliberava tudo (câmera, microfone, geolocalização…). Passa a negar por padrão, permitindo apenasClipboardRead, usada pelo botão "copiar stack trace".Mudança de suporte no
LogStoreNovo evento
PathsLoaded, disparado apenas quando um conjunto novo de caminhos é carregado — e não quando o usuário (des)marca arquivos na árvore. Assim a UI limpa a seleção de arquivos visíveis nas cargas externas (drop, linha de comando, outra instância) sem desestabilizar a árvore durante a marcação, que foi a causa de um bug anterior.Verificação
CS*no build..clefde uma vez → uma janela com os dois carregados;260,140 900x560;window.jsongravado limpo (sem a propriedade calculada).AllowExternalDropfalhar (não falhou).Notas para o revisor
Timerdo tail é qualificado comoSystem.Threading.Timer: oSystem.Windows.Forms.Timertambém está em escopo e exigiria a bomba de mensagens da UI.\n; como esse byte não aparece no meio de uma sequência UTF-8 multibyte, o corte nunca parte um caractere.prompt()) e P4.🤖 Generated with Claude Code