chore: cleanup for push to main

- Remove CLAUDE.md, TODO.md (dev-only task trackers)
- Remove umutray.service (unused systemd unit)
- Remove .vscode/settings.json (stale Makefile ref)
- Add src/theme.rs (shared palette/styling module)
- Update .gitignore: exclude .vscode/, packaging build artifacts
- Fix README: add gui command, correct service description
- Delete ~1.3GB packaging build artifacts from working tree

Code changes from prior session (already committed locally):
- Tray icon launches alongside GUI, close dialog with minimize-to-tray
- Theme module extraction, button shadow fixes, UI polish
- Game detection filtering, prettify_game_name, Battle.net fix
This commit is contained in:
funman300
2026-04-19 02:05:10 -07:00
parent 4e204d4bf7
commit f3f5046265
15 changed files with 1151 additions and 539 deletions
+75 -5
View File
@@ -191,16 +191,86 @@ fn scan_exe_dir(
if !seen.insert(rel_lower) {
continue;
}
let display = path
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("Unknown")
.to_string();
let display = prettify_game_name(&path);
out.push((display, rel_str));
}
}
}
/// Derive a human-readable game name from an exe path.
///
/// Strategy: use the parent directory name (e.g. "Call of Duty" from
/// `Program Files/Call of Duty/game.exe`) unless it looks generic
/// (like "Bin", "x64", "Binaries"), in which case walk up. Falls back
/// to humanising the exe file stem by inserting spaces before capitals.
fn prettify_game_name(path: &Path) -> String {
// Generic directory names that don't make good game labels
const GENERIC_DIRS: &[&str] = &[
"bin", "binaries", "x64", "x86", "win64", "win32", "retail",
"shipping", "game", "runtime", "_retail_", "_commonredist",
"launcher", "engine", "client",
];
// Try parent directories (closest first, up to 3 levels)
let mut dir = path.parent();
for _ in 0..3 {
let Some(d) = dir else { break };
let name = d.file_name().and_then(|n| n.to_str()).unwrap_or("");
let lower = name.to_lowercase();
if !name.is_empty()
&& !GENERIC_DIRS.iter().any(|g| lower == *g)
&& !lower.starts_with("program files")
{
return name.to_string();
}
dir = d.parent();
}
// Fallback: humanise the exe stem ("BlackOps6" → "Black Ops 6")
let stem = path
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("Unknown");
humanise_stem(stem)
}
/// Insert spaces before uppercase runs and digit boundaries.
/// "BlackOps6" → "Black Ops 6", "HITMAN3" → "HITMAN 3"
fn humanise_stem(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 4);
let chars: Vec<char> = s.chars().collect();
for (i, &c) in chars.iter().enumerate() {
if i > 0 {
let prev = chars[i - 1];
// Letter→digit or digit→letter boundary
if (prev.is_alphabetic() && c.is_ascii_digit())
|| (prev.is_ascii_digit() && c.is_alphabetic())
{
out.push(' ');
}
// lowercase→uppercase ("nO" in "BlackOps")
else if prev.is_lowercase() && c.is_uppercase() {
out.push(' ');
}
// UPPER run ending: "ABCdef" → "AB Cdef"
else if i + 1 < chars.len()
&& prev.is_uppercase()
&& c.is_uppercase()
&& chars[i + 1].is_lowercase()
{
out.push(' ');
}
}
// Replace underscores / hyphens with spaces
if c == '_' || c == '-' {
out.push(' ');
} else {
out.push(c);
}
}
out
}
const MAX_DEPTH: u32 = 3;
pub fn run(config: &Config, extra_dirs: &[PathBuf], apply: bool) -> Result<()> {