diff --git a/CHANGELOG.md b/CHANGELOG.md index 764826d..cd75e52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ Formato baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/); versões seguem SemVer. +## [0.6.0] - 2026-10-18 + +### Adicionado +- **Saúde do disco no estilo do CrystalDiskInfo:** a tela do dispositivo mostra o estado (Bom, Atenção, Ruim) com a % de vida útil restante em SSD, temperatura, horas ligado, vezes ligado, total lido e total gravado. Também traz a ficha do dispositivo (modelo, firmware, nº de série, interface, modo de transferência, capacidade, rotação, formato e recursos como TRIM) e a tabela completa de atributos SMART, com ID, valor atual, pior, limite e bruto, e um indicador por atributo. Funciona com discos ATA/SATA e NVMe (nos NVMe, os campos do log de saúde viram a tabela). +- **Partições:** ao abrir uma partição (`/dev/sda1`, `/dev/nvme0n1p2`, `/dev/disk2s1`), o SMART é lido do disco que a contém, e a tela avisa isso. +- **Comando `guara health`** no terminal, com saída legível ou `--json`. + +### Alterado +- Um atributo SMART com valor normalizado no limite de falha (ou abaixo dele) agora marca o disco como "Ruim", mesmo que a autoavaliação geral ainda passe. +- **Nova logo:** a cabeça do lobo-guará recortada das trilhas de um disco, inteiras de um lado (recuperado) e quebradas do outro (perdido), nas cores do app. Na interface ela acompanha o tema escolhido (claro ou escuro). + ## [0.5.0] - 2026-10-18 ### Adicionado diff --git a/Cargo.lock b/Cargo.lock index f921203..3ba6481 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -317,7 +317,7 @@ dependencies = [ [[package]] name = "guara-cli" -version = "0.5.0" +version = "0.6.0" dependencies = [ "clap", "ctrlc", @@ -329,7 +329,7 @@ dependencies = [ [[package]] name = "guara-core" -version = "0.5.0" +version = "0.6.0" dependencies = [ "flate2", "image", diff --git a/Cargo.toml b/Cargo.toml index 0951116..c484105 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["crates/guara-core", "crates/guara-cli"] [workspace.package] -version = "0.5.0" +version = "0.6.0" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/lucasrguerra/guara" diff --git a/README.md b/README.md index 9aeb394..710b77b 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ Ler discos brutos exige privilégios: **administrador** (Windows), `sudo` (Linux ```bash sudo guara devices # discos e pendrives (avisa TRIM e volumes montados) sudo guara inspect /dev/sdb # partições, espaço não particionado, cifragem +sudo guara health /dev/sdb # saúde do disco (SMART), requer smartmontools sudo guara scan /dev/sdb --session s.json --list # varredura profunda sudo guara scan /dev/sdb --quick # só metadados (segundos) guara scan disco.img --category image,document --min-confidence medium @@ -117,6 +118,12 @@ Veja [CONTRIBUTING.md](CONTRIBUTING.md). Versões seguem [SemVer](https://semver [MIT](LICENSE-MIT) ou [Apache-2.0](LICENSE-APACHE), à sua escolha. +## Novidades da 0.6.0 + +- Saúde do disco completa, no estilo do CrystalDiskInfo: estado com % de vida útil, temperatura, horas e vezes ligado, total lido e gravado, ficha do dispositivo (modelo, firmware, interface, modo de transferência, rotação, recursos) e a tabela inteira de atributos SMART (ATA e NVMe). +- Novo comando `guara health` no terminal. +- Nova logo: a cabeça do guará recortada das trilhas de um disco. + ## Novidades da 0.5.0 - Visualizador de bancos SQLite com recuperação de linhas apagadas. diff --git a/apps/gui/src-tauri/Cargo.toml b/apps/gui/src-tauri/Cargo.toml index 94f820f..77e1179 100644 --- a/apps/gui/src-tauri/Cargo.toml +++ b/apps/gui/src-tauri/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "guara-gui" description = "Guará: interface gráfica de recuperação de arquivos" -version = "0.5.0" +version = "0.6.0" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/lucasrguerra/guara" diff --git a/apps/gui/src-tauri/icons/128x128.png b/apps/gui/src-tauri/icons/128x128.png index 4ca70f7..6e74fb0 100644 Binary files a/apps/gui/src-tauri/icons/128x128.png and b/apps/gui/src-tauri/icons/128x128.png differ diff --git a/apps/gui/src-tauri/icons/128x128@2x.png b/apps/gui/src-tauri/icons/128x128@2x.png index 5d0b22e..e8afcdf 100644 Binary files a/apps/gui/src-tauri/icons/128x128@2x.png and b/apps/gui/src-tauri/icons/128x128@2x.png differ diff --git a/apps/gui/src-tauri/icons/32x32.png b/apps/gui/src-tauri/icons/32x32.png index d1faf21..ba6098b 100644 Binary files a/apps/gui/src-tauri/icons/32x32.png and b/apps/gui/src-tauri/icons/32x32.png differ diff --git a/apps/gui/src-tauri/icons/icon.ico b/apps/gui/src-tauri/icons/icon.ico index cbb8e7c..31ea6e4 100644 Binary files a/apps/gui/src-tauri/icons/icon.ico and b/apps/gui/src-tauri/icons/icon.ico differ diff --git a/apps/gui/src-tauri/icons/icon.png b/apps/gui/src-tauri/icons/icon.png index a2bc21e..13429ea 100644 Binary files a/apps/gui/src-tauri/icons/icon.png and b/apps/gui/src-tauri/icons/icon.png differ diff --git a/apps/gui/src-tauri/icons/source.png b/apps/gui/src-tauri/icons/source.png index a2bc21e..58b1f34 100644 Binary files a/apps/gui/src-tauri/icons/source.png and b/apps/gui/src-tauri/icons/source.png differ diff --git a/apps/gui/src-tauri/tauri.conf.json b/apps/gui/src-tauri/tauri.conf.json index e239672..086d624 100644 --- a/apps/gui/src-tauri/tauri.conf.json +++ b/apps/gui/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Guara", - "version": "0.5.0", + "version": "0.6.0", "identifier": "dev.guara.app", "build": { "frontendDist": "../ui" diff --git a/apps/gui/ui/app.css b/apps/gui/ui/app.css index aa3426e..e53772c 100644 --- a/apps/gui/ui/app.css +++ b/apps/gui/ui/app.css @@ -494,3 +494,25 @@ dialog.lightbox[open] { display: grid; } .xp-tiles { display: grid; grid-template-columns: repeat(3, 1fr); width: 100%; height: 100%; } .xp-tiles img { width: 100%; height: 100%; display: block; } .xp-pin { position: absolute; width: 14px; height: 14px; margin: -7px 0 0 -7px; border-radius: 50%; background: #0f8f8f; border: 3px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,.4); } + +/* saúde do disco (SMART) */ +.h-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 10px; } +.h-tile { background: var(--surface); border-radius: 14px; box-shadow: var(--shadow); padding: 12px 14px; display: flex; flex-direction: column; gap: 2px; } +.h-tile small { color: var(--text-3); font-size: 12px; } +.h-tile b { font-size: 18px; font-variant-numeric: tabular-nums; } +.h-tile.warning b { color: var(--warn); } .h-tile.failing b { color: var(--bad); } +.h-status.ok { background: var(--ok-soft); } .h-status.ok b { color: var(--ok); } +.h-status.warning { background: var(--warn-soft); } .h-status.failing { background: var(--bad-soft); } +.h-status b span { font-weight: 500; opacity: .8; } +.h-info { margin-top: 14px; background: var(--surface); border-radius: 14px; box-shadow: var(--shadow); padding: 14px 16px; } +.h-scroll { overflow-x: auto; background: var(--surface); border-radius: 14px; box-shadow: var(--shadow); } +.h-table { width: 100%; border-collapse: collapse; font-size: 13px; font-variant-numeric: tabular-nums; } +.h-table th { text-align: left; color: var(--text-3); font-weight: 500; padding: 8px 10px; border-bottom: 1px solid var(--line, rgba(127,127,127,.2)); white-space: nowrap; } +.h-table td { padding: 6px 10px; border-bottom: 1px solid var(--line, rgba(127,127,127,.12)); white-space: nowrap; } +.h-table tr:last-child td { border-bottom: 0; } +.h-table .dim { color: var(--text-3); } +.h-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--ok); } +.h-dot.warning { background: var(--warn); } .h-dot.failing { background: var(--bad); } + +/* logo inline: segue o tema escolhido no app, não o do sistema */ +.logo .a { stroke: var(--accent); } .logo .b { stroke: var(--accent-2); } .logo .t { fill: var(--text); } diff --git a/apps/gui/ui/app.js b/apps/gui/ui/app.js index 6cc7fd1..4c096b2 100644 --- a/apps/gui/ui/app.js +++ b/apps/gui/ui/app.js @@ -287,15 +287,46 @@ async function openDevice(d) { rpc('health', { path: d.path }).then((h) => { if (S.device === d) { S.health = h; paintHealth(); } }).catch(() => {}); } +const HEALTH_WORD = { ok: 'Bom', warning: 'Atenção', failing: 'Ruim' }; + function paintHealth() { const el = $('#healthArea'); const h = S.health; - if (!el || !h || !h.available) { if (el) el.innerHTML = ''; return; } + if (!el || !h) { if (el) el.innerHTML = ''; return; } + if (!h.available) { + el.innerHTML = `
${esc(h.summary)}
| ID | Atributo | Atual | Pior | Limite | Bruto | |
|---|---|---|---|---|---|---|
| ${esc(a.id)} | ${esc(a.name)} | ${dash(a.value)} | ${dash(a.worst)} | ${dash(a.thresh)} | ${esc(a.raw)} |
O SMART é do disco inteiro: os dados abaixo são de ${esc(h.disk)}, que contém ${esc(h.partition)}.
` : ''; + // o que motivou o alerta (os campos da ficha já aparecem abaixo) + const known = new Set(['Modelo', 'Nº de série', 'Horas ligado', 'Temperatura', 'Autoavaliação SMART', 'Vida útil usada']); + const why = (h.details || []).filter(([k]) => !known.has(k)).map(([k, v]) => `