pragma Singleton // ───────────────────────────────────────────────────────────────────────────── // How much of the Claude subscription this account has used. // // The collector writes one display-ready record and this only ever reads it. // That split is the point: adding a second agent later is a collector, not a // change here or in the widget, and nothing in QML ever sees a credential. // // The record carries its own status, so this can tell the three cases apart: // the collector has never run, it ran and the session was stale, or it has // real numbers. The widget hides for the first two, which is right -- a bar // indicator that says "unknown" is worse than an empty space. // ───────────────────────────────────────────────────────────────────────────── import Quickshell import Quickshell.Io import QtQuick import qs.config Singleton { id: root // "ok" | "stale" | "unavailable" | "" (never collected) property string status: "" property string detail: "" // Percentages, 0-100, or -1 when the endpoint did not report one. property int fiveHourUsed: -1 property int weekUsed: -1 property string weekResetsAt: "" property string tier: "" readonly property bool available: root.status === "ok" && (root.fiveHourUsed >= 0 || root.weekUsed >= 0) // The number worth showing when there is only room for one: whichever // window is closer to its limit is the one about to interrupt you. readonly property int headline: Math.max(root.fiveHourUsed, root.weekUsed) readonly property string helperPath: Quickshell.shellDir + "/scripts/panama-agent-usage" readonly property string statePath: (Quickshell.env("XDG_STATE_HOME") || `${Quickshell.env("HOME")}/.local/state`) + "/panama/agent-usage.json" // Minutes, never a repaint. Usage moves slowly and the collector makes a // network call; anything faster would be spending someone's battery to // watch a number that changes a few times an hour. Timer { interval: 5 * 60 * 1000 running: Settings.showAgentUsage repeat: true triggeredOnStart: true onTriggered: collect.running = true } Process { id: collect command: [root.helperPath] onExited: record.reload() } FileView { id: record path: root.statePath printErrors: false watchChanges: true onFileChanged: this.reload() onLoaded: { try { const parsed = JSON.parse(this.text()); root.status = String(parsed.status ?? ""); root.detail = String(parsed.detail ?? ""); const usage = parsed.usage; if (usage) { root.fiveHourUsed = Number.isFinite(usage.fiveHour?.used) ? usage.fiveHour.used : -1; root.weekUsed = Number.isFinite(usage.week?.used) ? usage.week.used : -1; root.weekResetsAt = String(usage.week?.resetsAt ?? ""); root.tier = String(usage.tier ?? ""); } else { root.fiveHourUsed = -1; root.weekUsed = -1; } } catch (error) { root.status = ""; } } onLoadFailed: root.status = ""; } }