feat: pré-visualização completa, tema, nova identidade e correções (0.2.0)
Release / Cria a release (push) Canceled after 0s
Release / CLI (linux-aarch64) (push) Canceled after 0s
Release / CLI (linux-x86_64) (push) Canceled after 0s
Release / CLI (macos-aarch64) (push) Canceled after 0s
Release / CLI (macos-x86_64) (push) Canceled after 0s
Release / CLI (windows-x86_64) (push) Canceled after 0s
Release / App desktop (linux-x86_64) (push) Canceled after 0s
Release / App desktop (macos-aarch64) (push) Canceled after 0s
Release / App desktop (macos-x86_64) (push) Canceled after 0s
Release / App desktop (windows-x86_64) (push) Canceled after 0s
Release / Publica a release (push) Canceled after 0s

- Preview de imagem, vídeo/MP4, áudio, PDF (pdf.js), texto, Office/ODF, ZIP e hex,
  lido sob demanda com Range; tela cheia com navegação por teclado.
- Seletor de tema Auto/Claro/Escuro persistido (vale também como administrador).
- Identidade verde-azulada com logo de pixels que formam um lobo.
- Corrige reinício como administrador no AppImage (usa $APPIMAGE) e mostra o motivo.
- Corrige barra inferior piscando (render idempotente, barra criada uma vez).
- Corrige FAT32 em mídias pequenas não reconhecido.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas RayanandClaude Sonnet 5.5 committed 2026-10-18 15:34:01 -03:00
1 parent 510f1bc48e
commit c5c94a9956
32 files changed
+1666 -227

No files matched your search

+71
View File
@@ -231,3 +231,74 @@ fn pausa_e_retoma_encontra_o_mesmo() {
b.sort();
assert_eq!(a, b);
}
/// Streaming de pré-visualização: Range devolve exatamente os bytes do arquivo recuperado.
#[test]
fn file_slice_com_range_bate_com_o_arquivo_recuperado() {
use std::io::Read;
let Some(dir) = fixtures() else { return };
let path = dir.join("ntfs.img");
let eng = service::Engine::new();
let job = eng
.start_scan(
path.to_str().unwrap(),
ScanOptions {
mode: ScanMode::Quick,
..Default::default()
},
)
.unwrap();
loop {
let st = eng.status(&job).unwrap();
if st["status"] != "running" {
break;
}
std::thread::sleep(std::time::Duration::from_millis(20));
}
let res = eng
.results(
&job,
&service::Query {
query: Some("relatorio.pdf".into()),
..Default::default()
},
)
.unwrap();
let id = res["items"][0]["id"].as_u64().unwrap();
let size = res["items"][0]["size"].as_u64().unwrap();
let want = expected(&dir, "ntfs")
.into_iter()
.find(|(_, p)| p.ends_with("relatorio.pdf"))
.unwrap()
.0;
let full = eng.file_slice(&job, id, None).unwrap();
assert_eq!((full.status(), full.len()), (200, size));
let mut all = vec![];
full.reader().read_to_end(&mut all).unwrap();
assert_eq!(sha256(&all), want);
let part = eng.file_slice(&job, id, Some("bytes=100-199")).unwrap();
assert_eq!((part.status(), part.len()), (206, 100));
assert_eq!(part.read_all(1 << 20).unwrap(), all[100..200]);
let tail = eng.file_slice(&job, id, Some("bytes=-50")).unwrap();
assert_eq!(tail.read_all(1 << 20).unwrap(), all[all.len() - 50..]);
assert_eq!(
eng.file_slice(&job, id, Some(&format!("bytes={size}-")))
.err(),
Some(416)
);
assert_eq!(eng.file_slice(&job, 999_999, None).err(), Some(404));
}
#[test]
fn configuracoes_de_tema_persistem() {
let dir = tempfile::tempdir().unwrap();
std::env::set_var("GUARA_CONFIG_DIR", dir.path());
assert_eq!(settings::load().theme, "auto");
settings::save(&settings::Settings {
theme: "dark".into(),
})
.unwrap();
assert_eq!(settings::load().theme, "dark");
}
+55
View File
@@ -86,3 +86,58 @@ fn varredura_rapida_nao_faz_carving() {
7
);
}
/// Regressão: mkfs.vfat -F 32 em mídia pequena (< 65525 clusters) é FAT32 pelo BPB.
#[test]
fn fat32_pequeno_com_poucos_clusters_e_reconhecido() {
use std::process::Command;
let dir = tempfile::tempdir().unwrap();
let img = dir.path().join("p.img");
let ok = Command::new("truncate")
.args(["-s", "32M"])
.arg(&img)
.status()
.map(|s| s.success())
.unwrap_or(false)
&& Command::new("mkfs.vfat")
.args(["-F", "32"])
.arg(&img)
.output()
.map(|o| o.status.success())
.unwrap_or(false);
let file = dir.path().join("arquivo.txt");
std::fs::write(&file, b"conteudo de teste\n".repeat(500)).unwrap();
let ok = ok
&& Command::new("mcopy")
.arg("-i")
.arg(&img)
.arg(&file)
.arg("::/")
.status()
.map(|s| s.success())
.unwrap_or(false)
&& Command::new("mdel")
.arg("-i")
.arg(&img)
.arg("::/arquivo.txt")
.status()
.map(|s| s.success())
.unwrap_or(false);
if !ok {
eprintln!("SKIP: mtools/dosfstools ausentes");
return;
}
let res = scan_path(
&img,
&ScanOptions {
mode: ScanMode::Quick,
..Default::default()
},
);
let layout = res.layout.unwrap();
assert_eq!(layout.partitions[0].fs, FsKind::Fat32);
assert_eq!(res.found.len(), 1);
let out = tempfile::tempdir().unwrap();
recover_all(&img, &res.found, out.path());
assert!(hashes_in(out.path()).contains_key(&sha256(&b"conteudo de teste\n".repeat(500))));
}