Phase 0 of UI/UX polish pass. Surgical changes, no substrate migration yet.
- Extract bar_fill_color + accent_color_for into new src/usage_color.rs so
the bubble, panel, and tray badge agree on a single 4-band usage ramp.
- Panel: color each bar from its own percent (was using max(5h, 7d) for
both rows, so a healthy 5h bar turned red whenever 7d was full).
- Light-mode amber #B47A20 (was #E0A040, failed WCAG AA at 2.4:1).
- Codex identity: switch from white/charcoal to OpenAI teal #10A37F
across bubble, panel stripe, and tray sweep so the surfaces share one
brand color and the tray badge stops reading as "loading spinner".
- Panel: drop WS_BORDER, add DwmSetWindowAttribute(DWMWCP_ROUND) for
Win11 rounded corners. Idempotent re-apply on every show() so the
attribute survives any future destroy/recreate path. Silently no-ops
on Win10.
Both the in-app restart and the auto-update install previously
shelled out to cmd.exe so the new instance could wait for the old
one to release the singleton mutex and the locked exe file. On
some Windows configurations the `start ""` inside `cmd /c ...` can
flash a console window despite CREATE_NO_WINDOW + DETACHED_PROCESS
flags. The replacement spawns the child binary directly via
CreateProcessW; since the main exe is built with
windows_subsystem = "windows", no console is ever allocated.
- New `src/update/handoff.rs` exposes `spawn_detached`,
`wait_for_parent_exit`, and `cleanup_stale_old_exes`.
- New CLI flags `--wait-pid <pid>` and `--updated-to <version>`
parsed early in `main`; the child waits up to 5s on the parent
PID via OpenProcess+WaitForSingleObject before falling through
to a 3s mutex-acquisition retry.
- `restart_app` and `install::begin` both spawn detached children
using the new helper.
- Update install now uses MoveFileExW twice (rename running exe
sideways, then move staged exe into place with
MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED so portable
installs on non-system drives still work). Rollback restores
the backup if either the swap OR the post-swap detached spawn
fails, and a MessageBoxW modal surfaces the backup path if the
rollback itself fails.
- First launch after an auto-update shows a blue-info tray
balloon "Updated to vX.Y.Z" via a new `tray::notify_info` (the
existing `tray::notify` is split into `notify_warning` +
`notify_info` sharing a `notify_inner`).
- Startup sweeps stale `<exe>.old.<pid>` siblings left by past
in-place updates.
- Three new `LocaleStrings` fields translated across all 8
supported locales (en/nl/es/fr/de/ja/ko/zh-TW).
Appends ' · v{CARGO_PKG_VERSION}' to every state of the version-action
menu item so users can see what they are running without opening an
About dialog. Reads as 'Check for updates · v0.1.7', 'Up to date ·
v0.1.7', etc. Winget channel decoration is preserved as a trailing
parenthetical.
Bumps version to 0.1.7.
The legacy poller.rs and updater.rs modules they served were deleted
in the clean-room rewrite; everything now goes through net::winhttp.
Grep confirms no source references to either crate. Removing them
trims the dep graph noticeably.
Bumps version to 0.1.6.
- Threshold balloons fire the cycle utilization crosses 80% or 95%
on either provider, with the title showing "{Provider} · {N}%" and
body translated per shipped locale. Reuses the existing
BALLOON_COOLDOWN so notifications stay calm.
- Dark/light auto-follow: bubble's WM_SETTINGCHANGE handler now calls
app::recheck_theme(), which re-reads HKCU\…\Personalize, updates
state.is_dark if changed, and triggers a UI repaint + tray refresh.
Windows posts WM_SETTINGCHANGE to every top-level window when the
user toggles light/dark in Settings, so the bubble repaints in
near-real-time.
- Adds 2 new i18n keys (threshold_80_body, threshold_95_body) across
all eight shipped locales.
Bumps version to 0.1.5.
- P0: pull blocking HTTPS out from under the global mutex. AppState's
http and registry now live behind Arc<Client> and Arc<Mutex<Registry>>;
do_poll, attempt_refresh, and version_action's Apply branch clone
these out, drop lock_state, then do their I/O. Apply now spawns a
worker thread that posts WM_APP_UPDATE_APPLIED back to the message-
only window when the cmd handoff is launched, so the UI no longer
freezes for the duration of the download.
- P1: bubble.rs paint_text_layer saves and restores the DC's previous
HFONT before DeleteObject. The old code's DeleteObject on a still-
selected HFONT silently failed and leaked one handle per paint frame
(up to ~12/s under the ≥95% pulse animation).
- P1: replace 5x CreatePopupMenu().unwrap() with let-else early returns
that destroy any half-built menus and log. GDI exhaustion no longer
panics the UI thread.
- P1: at-most-one-in-flight gate (static AtomicBool) on the poll thread
so rapid Refresh clicks don't stack concurrent HTTPS calls.
- P1: token-expired balloon now picks the title/body for the provider
that actually failed, instead of always falling back to Claude when
show_claude_code is on.
- P1: panel place_near honors SM_XVIRTUALSCREEN / SM_YVIRTUALSCREEN so
multi-monitor setups with a secondary display left of the primary
no longer mis-clamp the panel position.
- P1: COUNTDOWN_TEMPLATE bumped from "999d" to "999시간" — Korean has
the widest suffix among shipped locales and was overflowing the
countdown column.
Bumps version to 0.1.4.
GitHub's Releases API exposes a `digest: "sha256:..."` field on every
asset since 2024. We now parse it, hash the downloaded bytes locally,
and abort with ChecksumMismatch if they disagree. Releases that predate
the field (none currently exist for this repo) skip verification rather
than fail, so v0.1.0 / v0.1.1 / v0.1.2 still update normally.
cmd.exe expands `%var%` even inside double-quoted arguments, which
would let a path like `C:\Users\%PATHEXT%\bubble.exe` substitute the
expansion. Real Windows paths with `%` are vanishingly rare, so we
fail fast with UnsafePath rather than ship a bespoke cmd-escape
implementation.
Bumps version to 0.1.3.
Rust's std::process::Command escapes inner double quotes as \" when
wrapping the args(["/c", &cmd]) array. cmd.exe does not understand the
\" escape, so the swap-and-restart command got mangled: `start ""
"PATH"` arrived as `start \"\" \"PATH\"`, which the cmd parser
collapsed into `start \ PATH` — producing the "Windows cannot find
'\'" dialog and aborting the update.
Switching to raw_arg lets us hand cmd.exe the literal command line it
expects. The two quote characters cmd needs to keep are the outer pair
wrapping the whole /c argument; cmd's "more than two quotes, special
chars present" branch then preserves the inner path quotes intact.
Bumps version to 0.1.2 since this is the first updater fix that ships
through the updater itself for any future v0.1.2+ user.