@cad0p/pi-usage 0.11.3 → 0.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,136 @@
1
+ import QtQuick
2
+ import Quickshell
3
+ import Quickshell.Io
4
+ import qs.Commons
5
+ import qs.Ui
6
+ import "UsageLogic.js" as Logic
7
+
8
+ // AI Usage: one bar icon and one popup for every AI subscription that has a
9
+ // usage record, each provider a collapsible section of a single scrolling
10
+ // list. Derived from Omarchy's built-in Agents panel (plugins/agents, MIT, by
11
+ // Omarchy): the same records, rows and type, with the tab switch replaced by
12
+ // sections and multi-account providers drawn account by account.
13
+ Panel {
14
+ id: root
15
+ moduleName: "cad0p.pi-usage"
16
+ ipcTarget: "cad0p.pi-usage"
17
+ manageIpc: false
18
+
19
+ readonly property color foreground: bar ? bar.foreground : Color.foreground
20
+ readonly property color urgent: bar ? bar.urgent : Color.urgent
21
+ readonly property string fontFamily: bar ? bar.fontFamily : Style.font.family
22
+ readonly property string glyph: "󱚣"
23
+
24
+ readonly property var providers: usage.providers
25
+
26
+ // The icon lights up when any headline window is at 90% or a prepaid
27
+ // balance is down to its last 10%, the built-in's alarm thresholds.
28
+ readonly property bool alarming: {
29
+ for (var i = 0; i < providers.length; i++) {
30
+ var h = Logic.headline(providers[i])
31
+ if ((h && h.percent >= 0.9) || Logic.balanceAlarming(providers[i].balance)) return true
32
+ }
33
+ return false
34
+ }
35
+
36
+ // Countdowns and "updated" read this instead of Date.now() so the panel
37
+ // keeps telling the truth while it sits open.
38
+ property double nowMs: Date.now()
39
+
40
+ function refreshNow() { usage.refreshAll() }
41
+
42
+ function launchAgent() {
43
+ if (root.bar) root.bar.run("omarchy-agent --pick")
44
+ root.close()
45
+ }
46
+
47
+ // Nothing to report, nothing in the bar, as with the built-in widget.
48
+ visible: Logic.pluginVisible(providers)
49
+ implicitWidth: button.implicitWidth
50
+ implicitHeight: button.implicitHeight
51
+
52
+ onOpenedChanged: if (opened) {
53
+ nowMs = Date.now()
54
+ content.resetView()
55
+ usage.refreshOnOpen()
56
+ Qt.callLater(function() { keyCatcher.forceActiveFocus() })
57
+ }
58
+
59
+ Main {
60
+ id: usage
61
+ settings: root.settings
62
+ bar: root.bar
63
+ pluginDir: Qt.resolvedUrl(".")
64
+ }
65
+
66
+ Timer {
67
+ interval: 30000
68
+ running: root.opened
69
+ repeat: true
70
+ onTriggered: root.nowMs = Date.now()
71
+ }
72
+
73
+ IpcHandler {
74
+ target: root.ipcTarget
75
+ function open(): void { root.open() }
76
+ function close(): void { root.close() }
77
+ function show(): void { root.open() }
78
+ function hide(): void { root.close() }
79
+ function toggle(): void { root.toggle() }
80
+ function refresh(): string { root.refreshNow(); return "ok" }
81
+ function expand(id: string): string { usage.setExpanded(id, true); return "ok" }
82
+ function collapse(id: string): string { usage.setExpanded(id, false); return "ok" }
83
+ }
84
+
85
+ BarIconButton {
86
+ id: button
87
+ anchors.fill: parent
88
+ bar: root.bar
89
+ text: root.glyph
90
+ active: root.alarming
91
+ onPressed: function(buttonCode) {
92
+ if (buttonCode === Qt.RightButton) root.launchAgent()
93
+ else if (buttonCode === Qt.MiddleButton) root.refreshNow()
94
+ else root.toggle()
95
+ }
96
+ }
97
+
98
+ KeyboardPanel {
99
+ id: panel
100
+ anchorItem: button
101
+ owner: root
102
+ bar: root.bar
103
+ open: root.opened
104
+ focusTarget: keyCatcher
105
+ contentWidth: panel.fittedContentWidth(Style.space(380))
106
+ contentHeight: panel.fittedContentHeight(content.contentImplicitHeight, Style.space(640))
107
+
108
+ PanelKeyCatcher {
109
+ id: keyCatcher
110
+ anchors.fill: parent
111
+
112
+ // j/k or arrows walk the section headers (paging through an open one
113
+ // taller than the panel), Enter/Space fold or unfold, l/h unfold and
114
+ // fold, r refreshes, Tab moves to the neighboring bar panel.
115
+ onMoveRequested: function(dx, dy) {
116
+ if (dy !== 0) content.moveCursor(dy)
117
+ if (dx !== 0) content.setCursorExpanded(dx > 0)
118
+ }
119
+ onActivateRequested: content.toggleCursor()
120
+ onCloseRequested: root.close()
121
+ onTabRequested: function(direction) { root.switchPanel(direction) }
122
+ onTextKey: function(t) { if (t === "r" || t === "R") root.refreshNow() }
123
+
124
+ UsageContent {
125
+ id: content
126
+ anchors.fill: parent
127
+ usage: usage
128
+ foreground: root.foreground
129
+ urgent: root.urgent
130
+ fontFamily: root.fontFamily
131
+ nowMs: root.nowMs
132
+ fallbackGlyph: root.glyph
133
+ }
134
+ }
135
+ }
136
+ }
@@ -0,0 +1,40 @@
1
+ # AI Usage (`cad0p.pi-usage`)
2
+
3
+ One Omarchy bar icon and one scrolling panel for every AI subscription that has
4
+ a usage record in `${XDG_STATE_HOME:-~/.local/state}/omarchy/agents/usage/`:
5
+ pi-usage's `pi-*.json` and Omarchy's own `claude`, `codex` and `fireworks`
6
+ records alike. Every provider is a collapsible section instead of a tab; a
7
+ multi-account provider shows every window of every account.
8
+
9
+ The plugin does **not** support the built-in Agents widget's synced cross-device
10
+ aggregation (`syncMode`, `syncDir`, `syncFileName`, `syncDeviceId`). Users who
11
+ rely on synced aggregation should keep the built-in widget; this plugin is not a
12
+ full replacement for it.
13
+
14
+ Omarchy's own collectors follow the `omarchyCollectors` setting: `Off` never
15
+ runs them, `Auto` only while the built-in Agents widget is off the bar — the
16
+ `retryAdvised` retry included.
17
+
18
+ Part of [`@cad0p/pi-usage`](../../README.md#the-ai-usage-bar-plugin), which
19
+ documents installation, keys, settings and IPC. Install with
20
+ `./install.sh --omarchy-plugin`, then `omarchy plugin enable cad0p.pi-usage`.
21
+
22
+ The installer links the plugin directory; Omarchy loads that symlink, but
23
+ `omarchy plugin validate` rejects symlinked plugin folders. Validate the real
24
+ package path instead, for example
25
+ `omarchy plugin validate ~/.pi/agent/npm/node_modules/@cad0p/pi-usage/omarchy/cad0p.pi-usage`,
26
+ or validate the directory in a source checkout—not the
27
+ `~/.config/omarchy/plugins/cad0p.pi-usage` link.
28
+
29
+ | File | Role |
30
+ |---|---|
31
+ | `Panel.qml` | Bar button, popup, keyboard and IPC (`omarchy-shell cad0p.pi-usage …`) |
32
+ | `UsageContent.qml` | The scrolling list of provider sections and their rows |
33
+ | `Main.qml` | Record discovery and watching, expansion state, refresh |
34
+ | `Agent.qml` | Per-record file watcher |
35
+ | `UsageLogic.js` | Pure logic (headline, ordering, formatting), tested with `node --test` |
36
+ | `assets/` | Provider marks, `<family>.svg` with a `-light` twin where needed |
37
+
38
+ Derived from Omarchy's built-in Agents plugin (`shell/plugins/agents`, MIT,
39
+ by Omarchy): its record contract, rows, meters, type scale and the Claude,
40
+ Codex and Fireworks marks. MIT, like the package.