Roadmap P4.19: painel de estatísticas (+ recolhe P7/P8 que ficaram fora da main) - #9
Merged
Conversation
O detalhe so abria a direita. Um botao na toolbar passa a alternar entre a direita e abaixo: a direita funciona melhor em telas largas, abaixo e melhor para stack traces longos e telas estreitas. O icone aponta para ONDE o painel vai (seta para baixo / para a direita), e nao onde ele esta — a lib nao tem icones de painel, e a seta remove a ambiguidade. O botao so aparece quando ha um detalhe aberto para reposicionar. O splitter recebe @key na orientacao: ele mede e fixa os tamanhos dos panes na inicializacao, entao reaproveitar a instancia deixaria as larguras da disposicao anterior aplicadas na nova. A preferencia e persistida em ui.json — e nao no settings.json de proposito: o SettingsService dispara Changed ao salvar e o LogStore reage recarregando todos os arquivos, o que seria absurdo para uma preferencia visual. O enum e gravado por nome (Bottom), ja que o arquivo e editavel a mao. Validado no app: alterna nos dois sentidos e a escolha sobrevive a reabertura. 6 testes novos, incluindo arquivo corrompido e valor desconhecido caindo no padrao. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Alem da lista, um botao na toolbar alterna para uma tabela com ordenacao, agrupamento (arrastando a coluna para a faixa acima), filtro por coluna e redimensionamento. A escolha e persistida. Colunas derivadas do CONTEUDO: logs CLEF carregam propriedades estruturadas que variam conforme a aplicacao que os gerou, entao fixa-las na mao nao funcionaria. O LogColumnDiscovery amostra os eventos carregados, conta a frequencia de cada propriedade e oferece como coluna as que aparecem o suficiente, com o nome ja legivel - "MachineName" vira "Machine Name". O desempate por nome mantem a ordem estavel entre aberturas. O seletor de colunas e proprio, e nao o embutido (AllowColumnVisibility=false). Motivo: a coluna aplica a visibilidade no proprio OnInitialized, entao toda vez que o grid e recriado - o que acontece ao abrir/fechar o detalhe, ja que o layout troca de splitter - as escolhas do menu interno se perdiam. Mantendo o conjunto do lado do app, ele sobrevive a recriacao e ainda persiste entre execucoes. Tentei estabilizar a arvore mantendo o splitter sempre montado com o pane do detalhe colapsado, mas isso reintroduz a race de medicao do splitter aninhado (o conteudo mede 0px e some). Por isso a renderizacao condicional foi mantida e o problema foi resolvido pelo lado do estado. 19 testes novos: descoberta (frequencia, corte de raras, exclusao das que ja tem coluna fixa, teto, estabilidade, amostragem), rotulo legivel e valor da celula (sem as aspas que o ScalarValue adiciona). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
A versao vivia solta no csproj e ficou em 1.0.0 enquanto a Store ja estava em 1.0.2 — o titulo da janela, que le a versao do assembly, mostrava um numero diferente do publicado. Directory.Build.props passa a ser a fonte unica: - o csproj herda Version/FileVersion/AssemblyVersion (verificado: o assembly agora sai 1.1.0.0); - publish-store-package.ps1 usa esse valor quando -Version nao e informado; - release.yml passa /p:Version com a versao da tag, entao o exe dentro do pacote carrega a mesma versao carimbada no manifesto — antes o manifesto ia com a versao da tag e o assembly ficava em 1.0.0. Higiene: - o tipo de arquivo registrado virou 'Clef Log File'; 'Reader Log File' era sobra de copy/paste e divergia do rotulo do Package.appxmanifest; - RuntimeIdentifiers fica so em win-x64: e o que o CI publica, o que o pacote da Store contem e o que o README anuncia. Declarar win-x86 sugeria um suporte que nao existe. Nao suprimi o aviso MSB3277 remanescente: ele vem da unificacao de WindowsBase pelo proprio pacote WebView2 e mascarar conflitos de assembly com NoWarn e pior do que conviver com o ruido. Os avisos CS do nosso codigo seguem zerados. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A lista montava um no no DOM por evento da pagina. O roadmap pedia para medir antes: com paginas de ate 100 a virtualizacao renderia pouco, entao o ganho de verdade vinha junto com paginas maiores. A lista passa a usar OmniVirtualize (Height=null: o pai ja limita a altura e faz o scroll; sem altura limitada o Virtualize renderiza tudo) e o seletor ganha 250/500/1000 itens por pagina. Verificado com 5000 eventos: pagina de 1000 rola sem travar. Correcao encontrada durante esse teste: OmniPagination e zero-based e o app conta a partir de 1 — que e o numero mostrado no "X-Y de Z". Sem conversao, a pagina 1 aparecia como "2 / 100" e a ultima pagina ficava inalcancavel, porque o clamp do app ia ate UltimaPagina enquanto o componente esperava UltimaPagina - 1. A traducao ficou na fronteira, dentro da LogToolbar. Bug meu, introduzido na migracao do P2 ao trocar os botoes manuais pelo componente. Verificado: "1-50 de 5000" com "1 / 100", e o proximo leva a "51-100" / "2 / 100". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nao houve passe de a11y depois da migracao. Os componentes da Omni ja vem tratados, mas os elementos clef-* proprios nao. Lista de eventos: era uma pilha de div com @OnClick - nao recebia foco, nao respondia ao teclado e o leitor de tela nao a anunciava como lista selecionavel. Agora e role=listbox com itens role=option, tabindex e aria-selected, ativaveis por Enter/Espaco. Cada item ganhou um aria-label montado com nivel, hora, mensagem e a marca de excecao: sem ele o item seria lido como textos soltos e o nivel, que visualmente e um badge colorido, se perderia. Itens de grupo viraram button de verdade em vez de div com @OnClick - foco, teclado e semantica de graca. Nao ha nada interativo aninhado, entao a troca e segura; o CSS ganhou o reset de aparencia correspondente. Indicador de foco: nenhum elemento clef-* tinha. Adicionado via :focus-visible, que mostra o anel so na navegacao por teclado, sem poluir o clique de mouse. O contraste ja tinha sido tratado na migracao (WCAG AA medido no DOM nos dois temas), entao ficou de fora deste passe. Nao consegui confirmar o anel de foco na tela: a maquina bloqueou durante o teste. As mudancas sao estruturais e compilam, mas a verificacao visual fica pendente. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Roadmap P4: virtualização, acessibilidade, versionamento e higiene (+ bug de paginação)
Terceiro modo de visualizacao, ao lado de lista e tabela: uma visao agregada do
conjunto FILTRADO — nao do arquivo inteiro. Assim os numeros respondem ao que
esta em tela: filtrou por periodo, as estatisticas acompanham.
O painel traz total, erros e taxa de erro; contagem por nivel; timeline com o
volume e, sobreposta, a serie de erros (e o que revela os picos); e rankings de
origens, mensagens e excecoes.
Duas decisoes de agregacao que fazem a diferenca:
- Mensagens sao agrupadas pelo TEMPLATE, nao pelo texto renderizado. Sem isso,
600 execucoes de "Pedido {Id} processado" viram 600 linhas de contagem 1 e o
ranking nao mostra nada. Verificado no app: as 600 colapsaram em 1 entrada.
- Excecoes sao agrupadas pela primeira linha (tipo + mensagem). O stack trace
inteiro e unico por ocorrencia e tambem nao agruparia nada.
Os niveis saem em ordem de gravidade, e nao por contagem: a lista fica estavel
entre filtragens, entao o olho encontra "Error" sempre no mesmo lugar.
Tudo e clicavel e aplica o filtro correspondente — e o que torna o painel um
ponto de partida da investigacao, e nao um relatorio. A fatia da timeline se
ajusta ao periodo coberto; verificado no app: ao clicar em ERROR, o conjunto caiu
de 600 para 61 eventos e a timeline re-fatiou de 31,5 para 3 minutos.
O calculo so roda no modo que o usa, para nao gastar trabalho a toa na lista e na
tabela.
18 testes: ordem por gravidade, nivel desconhecido, agrupamento por template e
por primeira linha da excecao, SourceContext com fallback para o arquivo, numero
de fatias, separacao de erros na timeline, o evento do limite superior caindo na
ultima fatia, tudo no mesmo instante, fatia encolhendo com o periodo e conjunto
vazio.
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 conclui o item 19 do roadmap adicionando um terceiro modo de visualização (“Stats”) com agregações (resumo, timeline e rankings) sobre o conjunto filtrado, além de incorporar mudanças de P7/P8 (visão em tabela, preferências de UI, acessibilidade, versionamento e ajustes de CI).
Changes:
- Adiciona o painel de estatísticas (agregações puras em
LogStatistics+ UILogStatsPanel) e integra como novoLogViewMode. - Introduz visão em tabela com descoberta dinâmica de colunas (grid + seletor/persistência de colunas).
- Move preferências visuais para
ui.json, melhora acessibilidade/teclado/foco e unifica versionamento viaDirectory.Build.props+ ajustes em scripts/workflows.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| test/ClefExplorer.Tests/UiPreferencesTests.cs | Testes de persistência/robustez do ui.json e preferências de layout. |
| test/ClefExplorer.Tests/LogStatisticsTests.cs | Testes das agregações do painel de estatísticas (níveis, mensagens, exceções, timeline). |
| test/ClefExplorer.Tests/LogColumnDiscoveryTests.cs | Testes de descoberta/humanização/format de colunas derivadas de propriedades. |
| src/wwwroot/css/app.css | Estilos para grid, foco visível e painel de estatísticas. |
| src/Services/UiPreferencesService.cs | Serviço para persistir preferências de UI em ui.json. |
| src/Services/FileAssociationService.cs | Ajusta rótulo do tipo de arquivo registrado no registry. |
| src/Program.cs | Registra UiPreferencesService no DI. |
| src/Models/UiEnums.cs | Adiciona enums de UI (LogViewMode, DetailPanelPosition). |
| src/Helpers/LogStatistics.cs | Implementa agregações e timeline do conjunto filtrado. |
| src/Helpers/LogGridColumns.cs | Define colunas fixas e defaults do modo tabela. |
| src/Helpers/LogColumnDiscovery.cs | Descobre colunas por frequência e formata valores/humaniza nomes. |
| src/Components/LogViewer.razor | Integra os 3 modos (lista/tabela/stats), recalcula colunas/stats conforme filtro. |
| src/Components/LogToolbar.razor | UI para alternar modo de visualização + ajuste de paginação zero-based. |
| src/Components/LogStatsPanel.razor | UI do painel de estatísticas com cards, rankings e chart. |
| src/Components/LogList.razor | Virtualização + ARIA/teclado para lista de eventos. |
| src/Components/LogHeader.razor | Amplia opções de tamanho de página (até 1000) após virtualização. |
| src/Components/LogGroupManager.razor | Troca item clicável de grupo para <button> com semântica/teclado. |
| src/Components/LogGrid.razor | Nova visão em tabela (OmniDataGrid) + seletor de colunas customizado. |
| src/ClefExplorer.csproj | Ajusta RIDs (só win-x64) e remove versões locais (vem do props). |
| ROADMAP.md | Marca itens 18–23 como concluídos/parciais conforme implementado. |
| publish-store-package.ps1 | Permite herdar versão do Directory.Build.props quando omitida. |
| Directory.Build.props | Fonte única da versão (ClefVersion/Version/FileVersion/AssemblyVersion). |
| .github/workflows/release.yml | Carimba manifesto e passa versão para build do pacote da Store. |
| .github/workflows/ci.yml | Faz CI rodar para PRs sem restringir base branch. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Versao do assembly nao acompanhava o override (2 apontamentos): FileVersion e AssemblyVersion derivavam de $(ClefVersion), entao um /p:Version=2.0.0 mudava so o pacote e o assembly continuava na versao do arquivo — exatamente a divergencia que a fonte unica existe para evitar. Passam a derivar de $(Version). Provado: build normal sai 1.1.0.0; com /p:Version=2.5.0 sai 2.5.0.0. Junto: o publish-store-package.ps1 carimbava o manifesto mas nao passava versao alguma para o MSBuild, entao o exe dentro do pacote ficava na versao do arquivo. Agora passa /p:Version. Em ambos os pontos (script e release.yml) a versao vai com 3 partes, porque o Directory.Build.props acrescenta o ".0" — passar 4 produziria 5. O parametro -Version do script continuava [Parameter(Mandatory)], entao o PowerShell pediria o valor no prompt e o fallback recem-adicionado para o Directory.Build.props nunca seria alcancado. Removido. LogStatistics.Compute aceitava buckets/topCount invalidos: buckets = 0 dividiria por zero e criaria um vetor vazio, com indice negativo em seguida. Agora valida na entrada, longe da exceção obscura la dentro. 4 testes novos. OmniVirtualize recebia Height="null" — em Razor isso e a STRING "null", nao um null. Funcionava por acidente (o browser ignora a altura invalida e o pai dimensiona, que era a intencao), mas por motivo errado. Corrigido para Height="@null". 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 o item 19 — painel de estatísticas do ROADMAP.md, o último item grande em aberto. Com ele, o P4 fica concluído exceto pelo i18n (item 22), que depende de uma decisão de produto.
O painel
Terceiro modo de visualização, ao lado de lista e tabela. Mostra uma visão agregada do conjunto filtrado — não do arquivo inteiro — então os números respondem ao que está em tela.
Duas decisões de agregação que fazem a diferença
Mensagens são agrupadas pelo
MessageTemplate, não pelo texto renderizado. Sem isso, 600 execuções de"Pedido {Id} processado"viram 600 linhas de contagem 1 e o ranking não mostra nada. Verificado no app: as 600 colapsaram em 1 entrada.Exceções são agrupadas pela primeira linha (tipo + mensagem). O stack trace inteiro é único por ocorrência e também não agruparia nada.
Os níveis saem em ordem de gravidade, não por contagem — a lista fica estável entre filtragens, então o olho encontra "Error" sempre no mesmo lugar.
Tudo é clicável
Clicar num nível, origem, mensagem ou exceção aplica o filtro correspondente. É o que torna o painel um ponto de partida da investigação em vez de um relatório.
Verificado no app: clicando em
ERROR, o conjunto caiu de 600 para 61 eventos, o resumo recalculou para 100% de taxa de erro, e a timeline se re-fatiou de 31,5 min para 3 min — ajustando automaticamente ao novo período.Verificação
CS*.SourceContextcom fallback para o arquivo, número de fatias, separação de erros, o evento do limite superior caindo na última fatia, tudo no mesmo instante, fatia encolhendo com o período e conjunto vazio.Notas para o revisor
LogStatisticsé puro (sem dependência de UI), por isso todo o comportamento é testável direto.🤖 Generated with Claude Code