Commit graph

8 commits

Author SHA1 Message Date
AgentSeal
5281a08e06 fix(desktop): fall back to top-right when tray click has no usable coords
The GNOME AppIndicator extension delivers Activate with (0, 0) instead of
real screen coordinates, which made the popover land in the top-left corner
because the centering math treated the bogus origin as a legitimate anchor.
Treat (0, 0) as no-anchor and reuse the top-right fallback the non-Linux
path already uses, so the popover lands near the StatusNotifier area on
hosts that don't pass coordinates.

Also cfg-gate the Emitter import since it is only used in the non-Linux
tray menu path.
2026-04-18 04:03:02 -07:00
AgentSeal
ea5e311d4a feat(desktop): replace Linux tray with ksni for Mac-style click UX
Tauri's Linux tray uses libappindicator, which by design never fires
left-click events to the app and exposes no icon screen position. That
forced a menu-first UX on GNOME and made anchoring the popover near the
icon impossible (tauri-apps/tauri#7283, closed not-planned).

Drop libappindicator on Linux and talk StatusNotifierItem directly via
the ksni crate:

* new src-tauri/src/tray_linux.rs implements ksni::Tray. activate(x, y)
  and secondary_activate(x, y) arrive with real screen coordinates. The
  implementation exports no menu on purpose, so SNI hosts (notably the
  gnome-shell-extension-appindicator) call Activate on left click instead
  of opening a context menu.
* LinuxTrayHandle wraps the async ksni::Handle with a sync Mutex so the
  Tauri command handler can push title updates without naming the
  generic Handle<CodeburnTray> across module boundaries.
* lib.rs gates TrayIconBuilder behind cfg(not(target_os = "linux")) and
  spawns ksni on the Tokio runtime Tauri already owns. Tray click events
  go through codeburn://tray-activate with {x, y} so the positioning logic
  can anchor the popover directly below the click, clamped to the monitor.
* new set_tray_title command pushes the hero cost to the tray label on
  every payload fetch. On Linux that's the SNI title next to the icon;
  on other platforms it's the TrayIcon::set_title.
* new quit_app command + popover-footer × button gives Linux users a way
  to exit without a tray menu.
* empty-state copy already landed in a previous commit. Combined with the
  footer Quit button, the popover is now self-contained on Linux.

Windows is unaffected: TrayIconBuilder there fires left-click events
correctly and set_title/tooltip work out of the box.

Known limitation: on vanilla GNOME without AppIndicator support, there is
no tray to click. Documentation will link to the AppIndicator extension
install, same caveat Slack, Discord and 1Password ship with.
2026-04-18 03:59:13 -07:00
AgentSeal
86bdbfcd1c fix(desktop): anchor popover to top-right using configured width
window.outer_size() returns (0, 0) on the first show, so the previous
positioning snapped to top-left when the window had not been rendered yet.
Derive the target x from the popover width declared in tauri.conf.json and
scale margins by the monitor's scale factor so HiDPI displays land in the
right spot.
2026-04-18 03:44:32 -07:00
AgentSeal
d29f2b6101 feat(desktop): anchor popover to top-right on show
Linux window managers ignore tray icon screen coordinates, so we cannot
attach the popover to the icon the way macOS does. Snap it to the top-right
of the primary monitor on every show so it feels anchored to the tray area
on GNOME, KDE and Unity, which all host StatusNotifier icons at the top
right.
2026-04-18 03:39:33 -07:00
AgentSeal
1eef50a64d feat(desktop): add Show Dashboard tray item and empty-state copy
Linux tray implementations often do not deliver a distinct left-click event
through the AppIndicator path, so the popover could not be opened on GNOME.
Add an explicit Show Dashboard menu item that calls toggle_popover so the
UI is reachable via the tray menu on every platform.

Also render a friendly empty state in the popover when no session data is
found, pointing at the supported source paths instead of showing a bare
$0 hero with an empty activity list.
2026-04-18 03:35:37 -07:00
AgentSeal
d8ff4dcca7 fix(desktop): pass extras slice to Command::args without deref
cmd.args(*extras) on &[&str] fails to compile on stable Rust because
[&str] is unsized. Drop the deref so the slice passes through.
2026-04-18 03:30:49 -07:00
Resham Joshi
9d4da7c0fd feat(desktop): Linux provisioner script + optional unattended autoinstall
desktop/Scripts/provision-linux.sh: one-shot apt + Node 20 + Rust stable
+ codeburn CLI + repo clone + npm install. Leaves the user one command
away from `npm run tauri dev`.

desktop/Scripts/autoinstall/: cloud-init user-data + meta-data plus a
README for building the CIDATA ISO. Mount that ISO alongside the Ubuntu
Server ISO in UTM and the installer runs without any prompts. Default
test credentials codeburn/codeburn; README calls out changing them for
non-throwaway VMs.
2026-04-17 18:11:03 -07:00
Resham Joshi
8590087392 feat(desktop): scaffold Tauri 2.x tray app for Linux + Windows
Adds desktop/ with a native tray app that mirrors the macOS popover via
a shared tokens.json and the same codeburn status --format menubar-json
data source. Same security posture as the Swift app: argv-validated CLI
spawn, O_NOFOLLOW cache writes, flock on config.json, FX rate clamping
to [0.0001, 1_000_000].

Stack:
- Tauri 2.x (Rust) for tray + window lifecycle, shells out to the CLI
- React + TypeScript + Vite for the popover UI
- libayatana-appindicator on Linux, system tray on Windows
- Produces .deb / .rpm / .AppImage on ubuntu-latest, .msi on
  windows-latest. Both workflows run on free GitHub Actions minutes.

Rust modules (src-tauri/src/):
- lib.rs:    tray icon, menu events, popover toggle, state wiring
- cli.rs:    CodeburnCli with argv allowlist and bounded pipe drain
             (20 MB stdout / 256 KB stderr / 60 s wall time)
- config.rs: flock-guarded read-modify-write of ~/.config/codeburn/config.json
- fx.rs:     Frankfurter fetch with 24 h disk cache, bounds check

Frontend:
- App.tsx with agent tabs, period switcher, hero cost, activity rows,
  optimize findings CTA, footer (currency picker / refresh / Open
  Full Report). Listens for `codeburn://refresh` tray events.
- lib/payload.ts mirrors the CLI's MenubarPayload shape
- lib/currency.ts mirrors the Swift Double.asCurrency helpers
- styles.css with design tokens as CSS custom properties

CLI:
- `codeburn menubar` now platform-dispatches: macOS (.app zip),
  Linux (.AppImage into ~/.local/bin), Windows (.msi via msiexec).
  macOS behaviour preserved exactly.

Release workflow:
- .github/workflows/release-desktop-linux.yml triggers on `linux-v*`
  tags, builds all three Linux formats, uploads to GitHub Releases.

Scaffold verified:
- cargo check -> clean
- tsc --noEmit -> clean
- npm run build (CLI) -> 205 KB
- Existing test suite: 230 / 230 still pass
2026-04-17 17:53:10 -07:00