mirror of
https://github.com/tiennm99/claude-code-usage-bubble.git
synced 2026-06-07 04:10:01 +00:00
aa6217d2cf
Every Rust module under src/ that previously contained upstream-derivative
code has been replaced by a from-scratch implementation:
diag/ log + simplelog file appender (was: diagnose.rs)
os/ color, dpi, registry, string, theme (was: theme.rs, native_interop.rs)
net/ WinHTTP-based HTTP client (was: ureq + native-tls)
i18n/ TOML-embedded locale tables (was: localization/*.rs)
usage/ trait UsageProvider + ClaudeProvider + ChatGptProvider + refresh
orchestrator + registry (was: poller.rs, models.rs)
creds/ trait CredentialSource + local/WSL/Codex impls (was: poller.rs)
tray/ stateless tray manager + tiny-skia anti-aliased badge renderer
(was: tray_icon.rs)
update/ release fetch + inline cmd /c handoff installer
(was: updater.rs's helper-exe pattern)
Application files (app.rs, bubble.rs, panel.rs, settings.rs) migrated to
the new modules. main.rs declares only the new modules.
NOTICE deleted; LICENSE is plain Apache-2.0; README updated to credit
inspiration rather than claim derivation. Cargo.toml drops ureq + native-tls
+ winres in favour of log + simplelog + thiserror + toml + tiny-skia +
embed-resource. Build script swapped to embed-resource via res/icon.rc.
External contracts preserved unchanged: Anthropic + ChatGPT endpoints and
headers, ~/.claude/.credentials.json + Codex auth.json paths, WSL bridging
via wsl.exe, CLI-driven token refresh, GitHub Releases JSON shape, Windows
registry path for startup, single-instance mutex name.
Phase docs: plans/260516-0707-cleanroom-rewrite/.
74 lines
2.2 KiB
Rust
74 lines
2.2 KiB
Rust
// Discover the user's preferred Windows UI language.
|
|
//
|
|
// We try three sources in priority order and return the first non-empty
|
|
// result. Callers normalise the returned BCP-47-ish code against the
|
|
// list of locales we actually ship.
|
|
|
|
use windows::core::PWSTR;
|
|
use windows::Win32::Globalization::{
|
|
GetUserDefaultLocaleName, GetUserDefaultUILanguage, GetUserPreferredUILanguages,
|
|
LCIDToLocaleName, LOCALE_ALLOW_NEUTRAL_NAMES, MAX_LOCALE_NAME, MUI_LANGUAGE_NAME,
|
|
};
|
|
|
|
/// First non-empty locale code from the user's preferences. May be
|
|
/// `Some("en-US")` style; callers do prefix normalisation.
|
|
pub fn detect_system_locale() -> Option<String> {
|
|
preferred_ui()
|
|
.into_iter()
|
|
.next()
|
|
.or_else(default_ui_language)
|
|
.or_else(default_locale_name)
|
|
}
|
|
|
|
fn preferred_ui() -> Vec<String> {
|
|
unsafe {
|
|
let mut count: u32 = 0;
|
|
let mut buf_len: u32 = 0;
|
|
if GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &mut count, PWSTR::null(), &mut buf_len)
|
|
.is_err()
|
|
|| buf_len == 0
|
|
{
|
|
return Vec::new();
|
|
}
|
|
let mut buffer = vec![0u16; buf_len as usize];
|
|
if GetUserPreferredUILanguages(
|
|
MUI_LANGUAGE_NAME,
|
|
&mut count,
|
|
PWSTR(buffer.as_mut_ptr()),
|
|
&mut buf_len,
|
|
)
|
|
.is_err()
|
|
{
|
|
return Vec::new();
|
|
}
|
|
buffer
|
|
.split(|u| *u == 0)
|
|
.filter(|s| !s.is_empty())
|
|
.map(String::from_utf16_lossy)
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
fn default_ui_language() -> Option<String> {
|
|
unsafe {
|
|
let lcid = GetUserDefaultUILanguage();
|
|
let mut buf = [0u16; MAX_LOCALE_NAME as usize];
|
|
let len = LCIDToLocaleName(lcid as u32, Some(&mut buf), LOCALE_ALLOW_NEUTRAL_NAMES);
|
|
if len <= 1 {
|
|
return None;
|
|
}
|
|
Some(String::from_utf16_lossy(&buf[..(len as usize - 1)]))
|
|
}
|
|
}
|
|
|
|
fn default_locale_name() -> Option<String> {
|
|
unsafe {
|
|
let mut buf = [0u16; MAX_LOCALE_NAME as usize];
|
|
let len = GetUserDefaultLocaleName(&mut buf);
|
|
if len <= 1 {
|
|
return None;
|
|
}
|
|
Some(String::from_utf16_lossy(&buf[..(len as usize - 1)]))
|
|
}
|
|
}
|