Skip to content

✨ feat(status-bar): app version + a quiet update ping - #45

Merged
winlp4ever merged 2 commits into
mainfrom
feat/version-badge
Aug 6, 2026
Merged

✨ feat(status-bar): app version + a quiet update ping#45
winlp4ever merged 2 commits into
mainfrom
feat/version-badge

Conversation

@winlp4ever

Copy link
Copy Markdown
Contributor

First cut of the version/update QoL — deliberately minimal and non-invasive, in the always-visible status bar (bottom-right).

What you get

  • Idle: dim smterm 0.1.24 at the far right of the status bar. Silent.
  • Update available: it becomes an accented, clickable ping (reuses the existing .dot.pulse) → clicking opens the release page. Tooltip: "Update available — v0.1.25 (you have v0.1.24)."

Placed in the status bar (not the sidebar) because the sidebar is unmounted when collapsed — a ping there would vanish. Status bar is always rendered.

How (off any hot path, no signing)

  • app:versionapp.getVersion().
  • app:check-update → main hits the GitHub releases API (vcmf/smterm), compares to the running version, returns { current, latest, updateAvailable, url }. Runs on launch + every 6h (so a window left open for days still notices). Any failure — offline, rate-limited, 8s timeout — returns updateAvailable:false, so the badge just stays quiet. Pure network in main; nowhere near the terminal.

Tests

make check + typecheck green — 435 tests (+12):

  • version.ts: semver compare incl. prerelease ordering (final > rc), leading v/build suffix, unparseable → no false update.
  • update-check.ts: release-JSON parsing (tag/url/missing).
  • status-bar.tsx: shows dim version when current; shows the pinging, linking badge when an update exists; stays silent when the check fails.

Not in scope (the bigger story we discussed)

Auto-download/apply, delta updates (your .blockmaps are already published), CDN mirror for faster installs, and the code-signing decision that unblocks silent macOS updates — all deferred. This is just the "know your version / know an update exists" seed.

Adds the running version to the far-right of the always-visible status bar:
dim "smterm 0.1.24" by default, and when a newer release exists it becomes an
accented, clickable ping (reusing .dot.pulse) → opens the release page.

The check is best-effort and off any hot path: main hits the GitHub releases API
on launch + every 6h; any failure (offline / rate-limited / timeout) just leaves
the badge silent. Pure semver compare + release parsing are unit-tested; the
status-bar states have component tests.

@winlp4ever winlp4ever left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran /code-review (high effort). Well-built feature — verified the substantive parts: version.ts semver compare is correct incl. prerelease ordering (final > rc, numeric < alphanumeric, fewer-fields-lower), leading-v/+build handling, and unparseable → 0 (no false update); the GitHub check degrades gracefully on every failure path (offline / non-200 / bad JSON / 8s timeout → quiet), sets the required User-Agent, and /releases/latest correctly ignores prereleases; off the hot path, IPC wired full-stack, tsc clean, 12 tests pass. Three findings, all low/low-med.

Comment thread src/components/status-bar.test.tsx Outdated
render(<StatusBar />)
expect(await screen.findByText("macOS")).toBeInTheDocument()
expect(screen.getByText("UTF-8")).toBeInTheDocument()
describe("StatusBar version badge", () => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1 (test-coverage — top) — the rewrite deleted the existing StatusBar tests. This file's describe("StatusBar") previously covered the platform label + UTF-8, running/waiting session counts, and git-branch shown-in-repo / hidden-out-of-repo. Rewriting it to describe("StatusBar version badge") replaced those rather than adding to them. The component still renders all of that (its code is unchanged), but I checked and no other component test exercises it — so a future regression in the branch/counts/platform rendering would go uncaught. Restore the platform/counts/branch cases alongside the new version-badge ones.

Comment thread src/components/status-bar.tsx Outdated
void ipc.platformInfo().then((info) => setPlatform(info.label))
void ipc.appVersion().then(setVersion)
// Check on launch, then re-check every 6h so a window left open for days still notices.
const check = () => void ipc.checkUpdate().then(setUpdate)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#2 (correctness) — a failed periodic re-check hides a real update. const check = () => void ipc.checkUpdate().then(setUpdate) sets state unconditionally, and a failed check returns the quiet status ({updateAvailable:false, latest:null}). So: the launch check finds an update → badge pings; 6h later the machine is briefly offline → the re-check returns quietsetUpdate(quiet) → the ping vanishes; another 6h later it reappears. A transient network blip on a re-check silently drops a real, still-valid update notification. Update state only on a successful check (or keep updateAvailable:true sticky until a newer successful result supersedes it).

Comment thread src/components/status-bar.tsx Outdated
// Check on launch, then re-check every 6h so a window left open for days still notices.
const check = () => void ipc.checkUpdate().then(setUpdate)
check()
const t = setInterval(check, 6 * 60 * 60 * 1000)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3 (correctness, low) — the 6h setInterval is unreliable across system sleep. For the stated "a window left open for days still notices" case, a laptop that sleeps between ticks throttles/defers the timer, so it resumes only after wake and the cadence drifts well past 6h — a mostly-sleeping machine may re-check far less than intended. The on-launch check covers the common path, so impact is low; consider also re-checking on powerMonitor resume / window focus rather than relying on a long wall-clock interval for the 'days open' scenario.

- #1: restore the original StatusBar tests (platform/UTF-8, running/waiting
  counts, git-branch shown/hidden) that the rewrite had replaced; keep them
  alongside the new version-badge cases.
- #2: a failed re-check no longer clears a real update ping. checkUpdate results
  fold through a pure applyUpdateResult that ignores a failed check (latest null)
  and only lets a successful result supersede — so a transient offline blip on
  the 6h re-check can't drop a still-valid notification.
- #3: also re-check on window focus (throttled to the 6h refresh), so a laptop
  that slept through the wall-clock interval still notices on wake/return.
@winlp4ever

Copy link
Copy Markdown
Contributor Author

All three addressed:

  • v0: copy/paste, find, PTY reattach, packaging + release CI #1 (deleted tests) — restored the original StatusBar cases (platform/UTF-8, running/waiting counts, git-branch shown-in-repo / hidden-out-of-repo) alongside the new version-badge ones. My rewrite had replaced them; they're back.
  • fix: share zsh/bash history across panes (cmux-like) #2 (failed re-check hides a real update) — check results now fold through a pure applyUpdateResult(prev, next) that ignores a failed check (latest === null) and only lets a successful result supersede. So the offline-blip-on-re-check → ping-vanishes sequence can't happen. Unit-tested (keeps prior on fail; supersedes on success) — did it purely rather than fight fake-timers + findBy.
  • perf: repair WebGL atlas/framebuffer garble (focus / DPR / resize) #3 (interval unreliable across sleep) — added a window-focus re-check, throttled to the 6h refresh, so a laptop that slept through the interval re-checks on wake/return. Kept the interval for the always-focused case.

make check + typecheck green — 441 tests.

@winlp4ever
winlp4ever merged commit a572215 into main Aug 6, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant