@juliantanx/aiusage-widget 1.3.1 → 1.3.3
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.
- package/LICENSE +21 -0
- package/README.md +81 -79
- package/README_zh.md +81 -79
- package/bin/launcher.js +40 -40
- package/bin/prepare-native.js +43 -0
- package/dist/data.js +19 -19
- package/dist/main.js +27 -24
- package/dist/native/better_sqlite3.node +0 -0
- package/dist/renderer/assets/index-CArY3S1D.js +2 -0
- package/dist/renderer/assets/index-D6oVYB8t.css +1 -0
- package/dist/renderer/index.html +14 -14
- package/dist/ui.js +43 -18
- package/package.json +16 -14
- package/dist/renderer/assets/index-2X4hbwJe.css +0 -1
- package/dist/renderer/assets/index-Bb9mog0W.js +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 juliantanx
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,79 +1,81 @@
|
|
|
1
|
-
# @juliantanx/aiusage-widget
|
|
2
|
-
|
|
3
|
-
A lightweight system tray widget for [aiusage](https://github.com/juliantanx/aiusage) that shows your AI coding assistant token usage at a glance.
|
|
4
|
-
|
|
5
|
-
English | [中文](./README_zh.md)
|
|
6
|
-
|
|
7
|
-
## Features
|
|
8
|
-
|
|
9
|
-
- **System tray integration** — lives in your system tray, click to toggle a compact stats panel.
|
|
10
|
-
- **Today's token usage** — total tokens with input/output breakdown.
|
|
11
|
-
- **Monthly totals** — rolling token count for the current month.
|
|
12
|
-
- **Top model** — your most-used model today and its share.
|
|
13
|
-
- **Dashboard launcher** — one click to open the full aiusage web dashboard.
|
|
14
|
-
- **Auto-refresh** — updates every 60 seconds automatically.
|
|
15
|
-
- **Cross-platform** — Windows, macOS, and Linux.
|
|
16
|
-
|
|
17
|
-
## Prerequisites
|
|
18
|
-
|
|
19
|
-
- [aiusage](https://github.com/juliantanx/aiusage) CLI installed and data parsed (`aiusage parse`)
|
|
20
|
-
- Node.js >=
|
|
21
|
-
|
|
22
|
-
## Install
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npm install -g @juliantanx/aiusage-widget
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Usage
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
# Start the widget (runs in background, adds a tray icon)
|
|
32
|
-
aiusage-widget
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
The widget reads from `~/.aiusage/cache.db`. Make sure you have run `aiusage parse` at least once so the database exists.
|
|
36
|
-
|
|
37
|
-
### Running with PM2
|
|
38
|
-
|
|
39
|
-
To keep the widget running after closing the terminal and auto-start on boot:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
npm install -g pm2
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
pm2
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
**Panel
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
pnpm
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
- **
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
1
|
+
# @juliantanx/aiusage-widget
|
|
2
|
+
|
|
3
|
+
A lightweight system tray widget for [aiusage](https://github.com/juliantanx/aiusage) that shows your AI coding assistant token usage at a glance.
|
|
4
|
+
|
|
5
|
+
English | [中文](./README_zh.md)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **System tray integration** — lives in your system tray, click to toggle a compact stats panel.
|
|
10
|
+
- **Today's token usage** — total tokens with input/output breakdown.
|
|
11
|
+
- **Monthly totals** — rolling token count for the current month.
|
|
12
|
+
- **Top model** — your most-used model today and its share.
|
|
13
|
+
- **Dashboard launcher** — one click to open the full aiusage web dashboard.
|
|
14
|
+
- **Auto-refresh** — updates every 60 seconds automatically.
|
|
15
|
+
- **Cross-platform** — Windows, macOS, and Linux.
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- [aiusage](https://github.com/juliantanx/aiusage) CLI installed and data parsed (`aiusage parse`)
|
|
20
|
+
- Node.js >= 20
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @juliantanx/aiusage-widget
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Start the widget (runs in background, adds a tray icon)
|
|
32
|
+
aiusage-widget
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The widget reads from `~/.aiusage/cache.db`. Make sure you have run `aiusage parse` at least once so the database exists.
|
|
36
|
+
|
|
37
|
+
### Running with PM2
|
|
38
|
+
|
|
39
|
+
To keep the widget running after closing the terminal and auto-start on boot:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install -g pm2
|
|
43
|
+
|
|
44
|
+
# Start aiusage server + widget as background services
|
|
45
|
+
aiusage pm2-start
|
|
46
|
+
|
|
47
|
+
# Linux / macOS: run directly; Windows: run the output command as Administrator
|
|
48
|
+
pm2 startup
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
PM2 works on Windows, macOS, and Linux.
|
|
52
|
+
|
|
53
|
+
**Tray interactions:**
|
|
54
|
+
|
|
55
|
+
- **Left-click** — toggle the stats panel.
|
|
56
|
+
- **Right-click** — context menu with Show Panel, Refresh, and Quit.
|
|
57
|
+
|
|
58
|
+
**Panel actions:**
|
|
59
|
+
|
|
60
|
+
- Click **Open Full Dashboard** to launch `aiusage serve` and open the web dashboard in your browser.
|
|
61
|
+
|
|
62
|
+
## Build from Source
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
git clone https://github.com/juliantanx/aiusage.git
|
|
66
|
+
cd aiusage
|
|
67
|
+
pnpm install
|
|
68
|
+
pnpm build
|
|
69
|
+
cd packages/widget
|
|
70
|
+
pnpm dev
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Tech Stack
|
|
74
|
+
|
|
75
|
+
- **Runtime:** Electron
|
|
76
|
+
- **UI:** Svelte + Vite
|
|
77
|
+
- **Database:** better-sqlite3 (reads aiusage's local SQLite database)
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT
|
package/README_zh.md
CHANGED
|
@@ -1,79 +1,81 @@
|
|
|
1
|
-
# @juliantanx/aiusage-widget
|
|
2
|
-
|
|
3
|
-
一个轻量级的系统托盘小组件,用于 [aiusage](https://github.com/juliantanx/aiusage),可以快速查看 AI 编程助手的 token 用量。
|
|
4
|
-
|
|
5
|
-
[English](./README.md) | 中文
|
|
6
|
-
|
|
7
|
-
## 功能
|
|
8
|
-
|
|
9
|
-
- **系统托盘集成** — 常驻系统托盘,点击即可弹出简洁的统计面板。
|
|
10
|
-
- **今日 token 用量** — 总 token 数及输入/输出明细。
|
|
11
|
-
- **月度总量** — 当月累计 token 数。
|
|
12
|
-
- **热门模型** — 今日使用最多的模型及其占比。
|
|
13
|
-
- **仪表盘启动** — 一键打开完整的 aiusage Web 仪表盘。
|
|
14
|
-
- **自动刷新** — 每 60 秒自动更新数据。
|
|
15
|
-
- **跨平台** — 支持 Windows、macOS 和 Linux。
|
|
16
|
-
|
|
17
|
-
## 前置条件
|
|
18
|
-
|
|
19
|
-
- 已安装 [aiusage](https://github.com/juliantanx/aiusage) CLI 并完成数据解析(`aiusage parse`)
|
|
20
|
-
- Node.js >=
|
|
21
|
-
|
|
22
|
-
## 安装
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npm install -g @juliantanx/aiusage-widget
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## 使用
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
# 启动组件(后台运行,添加系统托盘图标)
|
|
32
|
-
aiusage-widget
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
组件会读取 `~/.aiusage/cache.db`。请确保至少执行过一次 `aiusage parse`,使数据库文件存在。
|
|
36
|
-
|
|
37
|
-
### 使用 PM2 后台运行
|
|
38
|
-
|
|
39
|
-
如需关闭终端后保持组件运行,并实现开机自启:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
npm install -g pm2
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
pm2
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
pnpm
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
1
|
+
# @juliantanx/aiusage-widget
|
|
2
|
+
|
|
3
|
+
一个轻量级的系统托盘小组件,用于 [aiusage](https://github.com/juliantanx/aiusage),可以快速查看 AI 编程助手的 token 用量。
|
|
4
|
+
|
|
5
|
+
[English](./README.md) | 中文
|
|
6
|
+
|
|
7
|
+
## 功能
|
|
8
|
+
|
|
9
|
+
- **系统托盘集成** — 常驻系统托盘,点击即可弹出简洁的统计面板。
|
|
10
|
+
- **今日 token 用量** — 总 token 数及输入/输出明细。
|
|
11
|
+
- **月度总量** — 当月累计 token 数。
|
|
12
|
+
- **热门模型** — 今日使用最多的模型及其占比。
|
|
13
|
+
- **仪表盘启动** — 一键打开完整的 aiusage Web 仪表盘。
|
|
14
|
+
- **自动刷新** — 每 60 秒自动更新数据。
|
|
15
|
+
- **跨平台** — 支持 Windows、macOS 和 Linux。
|
|
16
|
+
|
|
17
|
+
## 前置条件
|
|
18
|
+
|
|
19
|
+
- 已安装 [aiusage](https://github.com/juliantanx/aiusage) CLI 并完成数据解析(`aiusage parse`)
|
|
20
|
+
- Node.js >= 20
|
|
21
|
+
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @juliantanx/aiusage-widget
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 使用
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# 启动组件(后台运行,添加系统托盘图标)
|
|
32
|
+
aiusage-widget
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
组件会读取 `~/.aiusage/cache.db`。请确保至少执行过一次 `aiusage parse`,使数据库文件存在。
|
|
36
|
+
|
|
37
|
+
### 使用 PM2 后台运行
|
|
38
|
+
|
|
39
|
+
如需关闭终端后保持组件运行,并实现开机自启:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install -g pm2
|
|
43
|
+
|
|
44
|
+
# 一条命令启动 aiusage 服务 + 托盘组件为后台服务
|
|
45
|
+
aiusage pm2-start
|
|
46
|
+
|
|
47
|
+
# Linux / macOS: 直接执行;Windows: 以管理员身份执行输出的命令
|
|
48
|
+
pm2 startup
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
PM2 支持 Windows、macOS 和 Linux。
|
|
52
|
+
|
|
53
|
+
**托盘操作:**
|
|
54
|
+
|
|
55
|
+
- **左键点击** — 切换统计面板的显示/隐藏。
|
|
56
|
+
- **右键点击** — 弹出上下文菜单,包含显示面板、刷新和退出。
|
|
57
|
+
|
|
58
|
+
**面板操作:**
|
|
59
|
+
|
|
60
|
+
- 点击 **Open Full Dashboard** 可启动 `aiusage serve` 并在浏览器中打开 Web 仪表盘。
|
|
61
|
+
|
|
62
|
+
## 从源码构建
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
git clone https://github.com/juliantanx/aiusage.git
|
|
66
|
+
cd aiusage
|
|
67
|
+
pnpm install
|
|
68
|
+
pnpm build
|
|
69
|
+
cd packages/widget
|
|
70
|
+
pnpm dev
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 技术栈
|
|
74
|
+
|
|
75
|
+
- **运行时:** Electron
|
|
76
|
+
- **UI:** Svelte + Vite
|
|
77
|
+
- **数据库:** better-sqlite3(读取 aiusage 的本地 SQLite 数据库)
|
|
78
|
+
|
|
79
|
+
## 许可证
|
|
80
|
+
|
|
81
|
+
MIT
|
package/bin/launcher.js
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const { spawn } = require('child_process')
|
|
3
|
-
const { writeFileSync, mkdirSync }
|
|
4
|
-
const { homedir } = require('os')
|
|
5
|
-
const electron = require('electron')
|
|
6
|
-
const path = require('path')
|
|
7
|
-
|
|
8
|
-
const foreground = process.argv.includes('--foreground')
|
|
9
|
-
const aiusageDir = path.join(homedir(), '.aiusage')
|
|
10
|
-
const pidPath = path.join(aiusageDir, 'widget.pid')
|
|
11
|
-
|
|
12
|
-
if (foreground) {
|
|
13
|
-
// PM2 / service mode: run in foreground so the process manager can monitor it
|
|
14
|
-
const child = spawn(
|
|
15
|
-
String(electron),
|
|
16
|
-
[path.join(__dirname, '..', 'dist', 'main.js')],
|
|
17
|
-
{ stdio: 'inherit' }
|
|
18
|
-
)
|
|
19
|
-
child.on('exit', (code) => process.exit(code ?? 0))
|
|
20
|
-
process.on('SIGINT', () => child.kill('SIGINT'))
|
|
21
|
-
process.on('SIGTERM', () => child.kill('SIGTERM'))
|
|
22
|
-
} else {
|
|
23
|
-
// Normal mode: detach so closing the terminal doesn't kill the widget
|
|
24
|
-
const child = spawn(
|
|
25
|
-
String(electron),
|
|
26
|
-
[path.join(__dirname, '..', 'dist', 'main.js')],
|
|
27
|
-
{ detached: true, stdio: 'ignore' }
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
if (child.pid) {
|
|
31
|
-
try {
|
|
32
|
-
mkdirSync(aiusageDir, { recursive: true })
|
|
33
|
-
writeFileSync(pidPath, String(child.pid), { encoding: 'utf-8' })
|
|
34
|
-
} catch {
|
|
35
|
-
// Non-fatal: widget still starts, deduplication just won't work
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
child.unref()
|
|
40
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawn } = require('child_process')
|
|
3
|
+
const { writeFileSync, mkdirSync } = require('fs')
|
|
4
|
+
const { homedir } = require('os')
|
|
5
|
+
const electron = require('electron')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
|
|
8
|
+
const foreground = process.argv.includes('--foreground')
|
|
9
|
+
const aiusageDir = path.join(homedir(), '.aiusage')
|
|
10
|
+
const pidPath = path.join(aiusageDir, 'widget.pid')
|
|
11
|
+
|
|
12
|
+
if (foreground) {
|
|
13
|
+
// PM2 / service mode: run in foreground so the process manager can monitor it
|
|
14
|
+
const child = spawn(
|
|
15
|
+
String(electron),
|
|
16
|
+
[path.join(__dirname, '..', 'dist', 'main.js')],
|
|
17
|
+
{ stdio: 'inherit' }
|
|
18
|
+
)
|
|
19
|
+
child.on('exit', (code) => process.exit(code ?? 0))
|
|
20
|
+
process.on('SIGINT', () => child.kill('SIGINT'))
|
|
21
|
+
process.on('SIGTERM', () => child.kill('SIGTERM'))
|
|
22
|
+
} else {
|
|
23
|
+
// Normal mode: detach so closing the terminal doesn't kill the widget
|
|
24
|
+
const child = spawn(
|
|
25
|
+
String(electron),
|
|
26
|
+
[path.join(__dirname, '..', 'dist', 'main.js')],
|
|
27
|
+
{ detached: true, stdio: 'ignore' }
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if (child.pid) {
|
|
31
|
+
try {
|
|
32
|
+
mkdirSync(aiusageDir, { recursive: true })
|
|
33
|
+
writeFileSync(pidPath, String(child.pid), { encoding: 'utf-8' })
|
|
34
|
+
} catch {
|
|
35
|
+
// Non-fatal: widget still starts, deduplication just won't work
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
child.unref()
|
|
40
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { copyFileSync, mkdirSync } = require('node:fs')
|
|
3
|
+
const { dirname, join } = require('node:path')
|
|
4
|
+
const { execFileSync } = require('node:child_process')
|
|
5
|
+
const { rebuild } = require('@electron/rebuild')
|
|
6
|
+
|
|
7
|
+
const widgetRoot = join(__dirname, '..')
|
|
8
|
+
const nativeSource = join(
|
|
9
|
+
dirname(require.resolve('better-sqlite3/package.json')),
|
|
10
|
+
'build',
|
|
11
|
+
'Release',
|
|
12
|
+
'better_sqlite3.node'
|
|
13
|
+
)
|
|
14
|
+
const nativeDir = join(widgetRoot, 'dist', 'native')
|
|
15
|
+
const nativeTarget = join(nativeDir, 'better_sqlite3.node')
|
|
16
|
+
|
|
17
|
+
async function main() {
|
|
18
|
+
const electronVersion = require('electron/package.json').version
|
|
19
|
+
|
|
20
|
+
await rebuild({
|
|
21
|
+
buildPath: widgetRoot,
|
|
22
|
+
electronVersion,
|
|
23
|
+
onlyModules: ['better-sqlite3'],
|
|
24
|
+
force: true,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
mkdirSync(nativeDir, { recursive: true })
|
|
28
|
+
copyFileSync(nativeSource, nativeTarget)
|
|
29
|
+
|
|
30
|
+
if (process.platform === 'darwin') {
|
|
31
|
+
execFileSync('codesign', ['--force', '--sign', '-', nativeTarget], { stdio: 'inherit' })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
execFileSync('npm', ['run', 'install'], {
|
|
35
|
+
cwd: dirname(require.resolve('better-sqlite3/package.json')),
|
|
36
|
+
stdio: 'inherit',
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
main().catch((error) => {
|
|
41
|
+
console.error(error)
|
|
42
|
+
process.exit(1)
|
|
43
|
+
})
|
package/dist/data.js
CHANGED
|
@@ -6,27 +6,27 @@ function queryWidgetData(db) {
|
|
|
6
6
|
const monthStart = getMonthStartMs();
|
|
7
7
|
const tomorrow = todayStart + 86400000;
|
|
8
8
|
const nextMonthStart = getNextMonthStartMs();
|
|
9
|
-
const todayRow = db.prepare(`
|
|
10
|
-
SELECT
|
|
11
|
-
COALESCE(SUM(input_tokens), 0) AS input,
|
|
12
|
-
COALESCE(SUM(output_tokens), 0) AS output,
|
|
13
|
-
COALESCE(SUM(cache_read_tokens + cache_write_tokens + thinking_tokens), 0) AS other
|
|
14
|
-
FROM records
|
|
15
|
-
WHERE ts >= ? AND ts < ?
|
|
9
|
+
const todayRow = db.prepare(`
|
|
10
|
+
SELECT
|
|
11
|
+
COALESCE(SUM(input_tokens), 0) AS input,
|
|
12
|
+
COALESCE(SUM(output_tokens), 0) AS output,
|
|
13
|
+
COALESCE(SUM(cache_read_tokens + cache_write_tokens + thinking_tokens), 0) AS other
|
|
14
|
+
FROM records
|
|
15
|
+
WHERE ts >= ? AND ts < ?
|
|
16
16
|
`).get(todayStart, tomorrow);
|
|
17
|
-
const monthRow = db.prepare(`
|
|
18
|
-
SELECT COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_write_tokens + thinking_tokens), 0) AS total
|
|
19
|
-
FROM records
|
|
20
|
-
WHERE ts >= ? AND ts < ?
|
|
17
|
+
const monthRow = db.prepare(`
|
|
18
|
+
SELECT COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_write_tokens + thinking_tokens), 0) AS total
|
|
19
|
+
FROM records
|
|
20
|
+
WHERE ts >= ? AND ts < ?
|
|
21
21
|
`).get(monthStart, nextMonthStart);
|
|
22
|
-
const modelRows = db.prepare(`
|
|
23
|
-
SELECT
|
|
24
|
-
model,
|
|
25
|
-
SUM(input_tokens + output_tokens + cache_read_tokens + cache_write_tokens + thinking_tokens) AS tokens
|
|
26
|
-
FROM records
|
|
27
|
-
WHERE ts >= ? AND ts < ?
|
|
28
|
-
GROUP BY model
|
|
29
|
-
ORDER BY tokens DESC
|
|
22
|
+
const modelRows = db.prepare(`
|
|
23
|
+
SELECT
|
|
24
|
+
model,
|
|
25
|
+
SUM(input_tokens + output_tokens + cache_read_tokens + cache_write_tokens + thinking_tokens) AS tokens
|
|
26
|
+
FROM records
|
|
27
|
+
WHERE ts >= ? AND ts < ?
|
|
28
|
+
GROUP BY model
|
|
29
|
+
ORDER BY tokens DESC
|
|
30
30
|
`).all(todayStart, tomorrow);
|
|
31
31
|
let topModel = null;
|
|
32
32
|
if (modelRows.length > 0) {
|
package/dist/main.js
CHANGED
|
@@ -23,7 +23,10 @@ if (process.platform === 'darwin' && electron_1.app.dock) {
|
|
|
23
23
|
}
|
|
24
24
|
electron_1.app.whenReady().then(() => {
|
|
25
25
|
if ((0, node_fs_1.existsSync)(DB_PATH)) {
|
|
26
|
-
db = new Database(DB_PATH, {
|
|
26
|
+
db = new Database(DB_PATH, {
|
|
27
|
+
readonly: true,
|
|
28
|
+
nativeBinding: (0, ui_1.getWidgetNativeBindingPath)(__dirname),
|
|
29
|
+
});
|
|
27
30
|
}
|
|
28
31
|
createTray();
|
|
29
32
|
createWindow();
|
|
@@ -39,13 +42,10 @@ electron_1.app.on('before-quit', () => {
|
|
|
39
42
|
db?.close();
|
|
40
43
|
});
|
|
41
44
|
function createTray() {
|
|
42
|
-
const
|
|
45
|
+
const { buffer, scaleFactor } = (0, ui_1.getTrayIconNativeImage)();
|
|
46
|
+
const icon = electron_1.nativeImage.createFromBuffer(buffer, scaleFactor ? { scaleFactor } : undefined);
|
|
43
47
|
tray = new electron_1.Tray(icon);
|
|
44
48
|
tray.setToolTip('aiusage Widget');
|
|
45
|
-
// On macOS, SVG icons silently fail — use a text label as the visible entry
|
|
46
|
-
if (process.platform === 'darwin') {
|
|
47
|
-
tray.setTitle('⚡');
|
|
48
|
-
}
|
|
49
49
|
tray.on('click', () => toggleWindow());
|
|
50
50
|
tray.on('right-click', () => {
|
|
51
51
|
const menu = electron_1.Menu.buildFromTemplate([
|
|
@@ -85,10 +85,13 @@ function showWindow() {
|
|
|
85
85
|
// Position near tray icon
|
|
86
86
|
const trayBounds = tray.getBounds();
|
|
87
87
|
const winBounds = win.getBounds();
|
|
88
|
-
const
|
|
89
|
-
const y =
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
const displayBounds = electron_1.screen.getDisplayNearestPoint({ x: trayBounds.x, y: trayBounds.y }).workArea;
|
|
89
|
+
const { x, y } = (0, ui_1.getWindowPosition)({
|
|
90
|
+
platform: process.platform,
|
|
91
|
+
trayBounds,
|
|
92
|
+
windowBounds: winBounds,
|
|
93
|
+
displayBounds,
|
|
94
|
+
});
|
|
92
95
|
win.setPosition(x, y, false);
|
|
93
96
|
win.show();
|
|
94
97
|
win.focus();
|
|
@@ -156,7 +159,8 @@ async function isDashboardReachable(port) {
|
|
|
156
159
|
resolve(res.statusCode !== undefined && res.statusCode < 500);
|
|
157
160
|
});
|
|
158
161
|
req.on('error', () => resolve(false));
|
|
159
|
-
|
|
162
|
+
// 200ms is plenty for localhost; 1000ms caused each poll to block for 1s
|
|
163
|
+
req.setTimeout(200, () => { req.destroy(); resolve(false); });
|
|
160
164
|
});
|
|
161
165
|
}
|
|
162
166
|
async function launchDashboard() {
|
|
@@ -167,21 +171,20 @@ async function launchDashboard() {
|
|
|
167
171
|
stdio: 'ignore',
|
|
168
172
|
shell: true,
|
|
169
173
|
});
|
|
170
|
-
let
|
|
171
|
-
child.on('error', (
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
174
|
+
let failed = false;
|
|
175
|
+
child.on('error', () => { failed = true; });
|
|
176
|
+
// shell:true suppresses ENOENT on the error event — the shell itself spawns fine
|
|
177
|
+
// but exits immediately (code 127) when the command isn't found. Detect that here.
|
|
178
|
+
child.on('close', (code) => {
|
|
179
|
+
if (code !== 0)
|
|
180
|
+
failed = true;
|
|
178
181
|
});
|
|
179
182
|
child.unref();
|
|
180
|
-
//
|
|
183
|
+
// Poll up to 5s (25 × 200ms) for the server to become reachable
|
|
181
184
|
let attempts = 0;
|
|
182
185
|
const check = async () => {
|
|
183
|
-
if (
|
|
184
|
-
resolve({ success: false, error:
|
|
186
|
+
if (failed) {
|
|
187
|
+
resolve({ success: false, error: 'aiusage command not found' });
|
|
185
188
|
return;
|
|
186
189
|
}
|
|
187
190
|
if (await isDashboardReachable(getDashboardPort())) {
|
|
@@ -189,11 +192,11 @@ async function launchDashboard() {
|
|
|
189
192
|
return;
|
|
190
193
|
}
|
|
191
194
|
attempts++;
|
|
192
|
-
if (attempts >=
|
|
195
|
+
if (attempts >= 25) {
|
|
193
196
|
resolve({ success: false, error: 'Server failed to start within 5 seconds' });
|
|
194
197
|
return;
|
|
195
198
|
}
|
|
196
|
-
setTimeout(check,
|
|
199
|
+
setTimeout(check, 200);
|
|
197
200
|
};
|
|
198
201
|
check();
|
|
199
202
|
});
|
|
Binary file
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var se=Object.defineProperty;var ie=(e,t,n)=>t in e?se(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var z=(e,t,n)=>ie(e,typeof t!="symbol"?t+"":t,n);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))o(r);new MutationObserver(r=>{for(const s of r)if(s.type==="childList")for(const f of s.addedNodes)f.tagName==="LINK"&&f.rel==="modulepreload"&&o(f)}).observe(document,{childList:!0,subtree:!0});function n(r){const s={};return r.integrity&&(s.integrity=r.integrity),r.referrerPolicy&&(s.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?s.credentials="include":r.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function o(r){if(r.ep)return;r.ep=!0;const s=n(r);fetch(r.href,s)}})();function w(){}function re(e){return e()}function X(){return Object.create(null)}function T(e){e.forEach(re)}function R(e){return typeof e=="function"}function Y(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}function le(e){return Object.keys(e).length===0}function d(e,t){e.appendChild(t)}function I(e,t,n){e.insertBefore(t,n||null)}function M(e){e.parentNode&&e.parentNode.removeChild(e)}function g(e){return document.createElement(e)}function F(e){return document.createTextNode(e)}function _(){return F(" ")}function K(e,t,n,o){return e.addEventListener(t,n,o),()=>e.removeEventListener(t,n,o)}function p(e,t,n){n==null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}function fe(e){return Array.from(e.childNodes)}function U(e,t){t=""+t,e.data!==t&&(e.data=t)}function Z(e,t,n){e.classList.toggle(t,!!n)}let L;function x(e){L=e}function ce(){if(!L)throw new Error("Function called outside component initialization");return L}function ue(e){ce().$$.on_mount.push(e)}const $=[],ee=[];let k=[];const te=[],ae=Promise.resolve();let V=!1;function de(){V||(V=!0,ae.then(oe))}function W(e){k.push(e)}const B=new Set;let v=0;function oe(){if(v!==0)return;const e=L;do{try{for(;v<$.length;){const t=$[v];v++,x(t),he(t.$$)}}catch(t){throw $.length=0,v=0,t}for(x(null),$.length=0,v=0;ee.length;)ee.pop()();for(let t=0;t<k.length;t+=1){const n=k[t];B.has(n)||(B.add(n),n())}k.length=0}while($.length);for(;te.length;)te.pop()();V=!1,B.clear(),x(e)}function he(e){if(e.fragment!==null){e.update(),T(e.before_update);const t=e.dirty;e.dirty=[-1],e.fragment&&e.fragment.p(e.ctx,t),e.after_update.forEach(W)}}function pe(e){const t=[],n=[];k.forEach(o=>e.indexOf(o)===-1?t.push(o):n.push(o)),n.forEach(o=>o()),k=t}const P=new Set;let ge;function O(e,t){e&&e.i&&(P.delete(e),e.i(t))}function N(e,t,n,o){if(e&&e.o){if(P.has(e))return;P.add(e),ge.c.push(()=>{P.delete(e)}),e.o(t)}}function A(e){e&&e.c()}function E(e,t,n){const{fragment:o,after_update:r}=e.$$;o&&o.m(t,n),W(()=>{const s=e.$$.on_mount.map(re).filter(R);e.$$.on_destroy?e.$$.on_destroy.push(...s):T(s),e.$$.on_mount=[]}),r.forEach(W)}function C(e,t){const n=e.$$;n.fragment!==null&&(pe(n.after_update),T(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function me(e,t){e.$$.dirty[0]===-1&&($.push(e),de(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<<t%31}function G(e,t,n,o,r,s,f=null,h=[-1]){const l=L;x(e);const i=e.$$={fragment:null,ctx:[],props:s,update:w,not_equal:r,bound:X(),on_mount:[],on_destroy:[],on_disconnect:[],before_update:[],after_update:[],context:new Map(t.context||(l?l.$$.context:[])),callbacks:X(),dirty:h,skip_bound:!1,root:t.target||l.$$.root};f&&f(i.root);let u=!1;if(i.ctx=n?n(e,t.props||{},(c,m,...b)=>{const S=b.length?b[0]:m;return i.ctx&&r(i.ctx[c],i.ctx[c]=S)&&(!i.skip_bound&&i.bound[c]&&i.bound[c](S),u&&me(e,c)),m}):[],i.update(),u=!0,T(i.before_update),i.fragment=o?o(i.ctx):!1,t.target){if(t.hydrate){const c=fe(t.target);i.fragment&&i.fragment.l(c),c.forEach(M)}else i.fragment&&i.fragment.c();t.intro&&O(e.$$.fragment),E(e,t.target,t.anchor),oe()}x(l)}class J{constructor(){z(this,"$$");z(this,"$$set")}$destroy(){C(this,1),this.$destroy=w}$on(t,n){if(!R(n))return w;const o=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return o.push(n),()=>{const r=o.indexOf(n);r!==-1&&o.splice(r,1)}}$set(t){this.$$set&&!le(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}const ye="4";typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add(ye);function _e(e){let t,n,o,r,s,f,h,l,i;return{c(){t=g("div"),n=g("span"),n.innerHTML=`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 64 64" style="display:block;border-radius:4px"><rect width="64" height="64" rx="14" fill="#0d9488"></rect><rect x="10" y="38" width="12" height="16" rx="3" fill="white"></rect><rect x="26" y="26" width="12" height="28" rx="3" fill="white"></rect><rect x="42" y="14" width="12" height="40" rx="3" fill="white"></rect></svg>
|
|
2
|
+
aiusage`,o=_(),r=g("div"),s=g("button"),s.textContent="↻",f=_(),h=g("button"),h.textContent="✕",p(n,"class","logo svelte-soepgf"),p(s,"class","icon-btn svelte-soepgf"),p(s,"title","Refresh"),p(h,"class","icon-btn svelte-soepgf"),p(h,"title","Close"),p(r,"class","actions svelte-soepgf"),p(t,"class","header svelte-soepgf")},m(u,c){I(u,t,c),d(t,n),d(t,o),d(t,r),d(r,s),d(r,f),d(r,h),l||(i=[K(s,"click",function(){R(e[0])&&e[0].apply(this,arguments)}),K(h,"click",function(){R(e[1])&&e[1].apply(this,arguments)})],l=!0)},p(u,[c]){e=u},i:w,o:w,d(u){u&&M(t),l=!1,T(i)}}}function we(e,t,n){let{onRefresh:o}=t,{onClose:r}=t;return e.$$set=s=>{"onRefresh"in s&&n(0,o=s.onRefresh),"onClose"in s&&n(1,r=s.onClose)},[o,r]}class be extends J{constructor(t){super(),G(this,t,we,_e,Y,{onRefresh:0,onClose:1})}}function ne(e){let t,n;return{c(){t=g("span"),n=F(e[2]),p(t,"class","secondary svelte-savwyt")},m(o,r){I(o,t,r),d(t,n)},p(o,r){r&4&&U(n,o[2])},d(o){o&&M(t)}}}function ve(e){let t,n,o,r,s,f,h,l,i=e[2]&&ne(e);return{c(){t=g("div"),n=g("div"),o=F(e[0]),r=_(),s=g("div"),f=g("span"),h=F(e[1]),l=_(),i&&i.c(),p(n,"class","section-label svelte-savwyt"),p(f,"class","primary svelte-savwyt"),p(s,"class","values svelte-savwyt"),p(t,"class","section svelte-savwyt")},m(u,c){I(u,t,c),d(t,n),d(n,o),d(t,r),d(t,s),d(s,f),d(f,h),d(s,l),i&&i.m(s,null)},p(u,[c]){c&1&&U(o,u[0]),c&2&&U(h,u[1]),u[2]?i?i.p(u,c):(i=ne(u),i.c(),i.m(s,null)):i&&(i.d(1),i=null)},i:w,o:w,d(u){u&&M(t),i&&i.d()}}}function $e(e,t,n){let{label:o}=t,{primary:r}=t,{secondary:s=""}=t;return e.$$set=f=>{"label"in f&&n(0,o=f.label),"primary"in f&&n(1,r=f.primary),"secondary"in f&&n(2,s=f.secondary)},[o,r,s]}class H extends J{constructor(t){super(),G(this,t,$e,ve,Y,{label:0,primary:1,secondary:2})}}function ke(e){let t,n,o,r,s,f,h,l,i,u,c,m,b,S;return n=new be({props:{onRefresh:e[6],onClose:Se}}),r=new H({props:{label:"TODAY",primary:e[5],secondary:e[4]}}),f=new H({props:{label:"THIS MONTH",primary:e[3]}}),l=new H({props:{label:"TOP MODEL",primary:e[2],secondary:e[1]}}),{c(){t=g("div"),A(n.$$.fragment),o=_(),A(r.$$.fragment),s=_(),A(f.$$.fragment),h=_(),A(l.$$.fragment),i=_(),u=g("div"),c=g("button"),c.textContent="Open Full Dashboard →",p(c,"class","open-btn svelte-z2p4qk"),p(u,"class","footer svelte-z2p4qk"),p(t,"class","panel svelte-z2p4qk"),Z(t,"loading",e[0])},m(a,y){I(a,t,y),E(n,t,null),d(t,o),E(r,t,null),d(t,s),E(f,t,null),d(t,h),E(l,t,null),d(t,i),d(t,u),d(u,c),m=!0,b||(S=K(c,"click",Oe),b=!0)},p(a,[y]){const j={};y&32&&(j.primary=a[5]),y&16&&(j.secondary=a[4]),r.$set(j);const Q={};y&8&&(Q.primary=a[3]),f.$set(Q);const q={};y&4&&(q.primary=a[2]),y&2&&(q.secondary=a[1]),l.$set(q),(!m||y&1)&&Z(t,"loading",a[0])},i(a){m||(O(n.$$.fragment,a),O(r.$$.fragment,a),O(f.$$.fragment,a),O(l.$$.fragment,a),m=!0)},o(a){N(n.$$.fragment,a),N(r.$$.fragment,a),N(f.$$.fragment,a),N(l.$$.fragment,a),m=!1},d(a){a&&M(t),C(n),C(r),C(f),C(l),b=!1,S()}}}function D(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:String(e)}function Se(){window.widget.hideWindow()}async function Oe(){await window.widget.openDashboard()}function Ee(e,t,n){let o,r,s,f,h,l=null,i=!0;async function u(){n(0,i=!0),n(7,l=await window.widget.getData()),n(0,i=!1)}return ue(()=>{u(),window.widget.onDataUpdate(c=>{n(7,l=c),n(0,i=!1)})}),e.$$.update=()=>{var c;e.$$.dirty&128&&n(5,o=l?D(l.todayTokens.total):"—"),e.$$.dirty&128&&n(4,r=l?`↑${D(l.todayTokens.input)} ↓${D(l.todayTokens.output)}`:""),e.$$.dirty&128&&n(3,s=l?D(l.monthTokens.total):"—"),e.$$.dirty&128&&n(2,f=((c=l==null?void 0:l.topModel)==null?void 0:c.name)??"—"),e.$$.dirty&128&&n(1,h=l!=null&&l.topModel?`${l.topModel.share}%`:"")},[i,h,f,s,r,o,u,l]}class Ce extends J{constructor(t){super(),G(this,t,Ee,ke,Y,{})}}new Ce({target:document.getElementById("app")});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.header.svelte-soepgf{display:flex;align-items:center;justify-content:space-between;padding:12px 14px 10px;border-bottom:1px solid var(--border);-webkit-app-region:drag}.logo.svelte-soepgf{display:flex;align-items:center;gap:6px;font-size:13px;font-weight:600;color:var(--text-primary);letter-spacing:.01em}.actions.svelte-soepgf{display:flex;gap:4px;-webkit-app-region:no-drag}.icon-btn.svelte-soepgf{width:24px;height:24px;border:none;border-radius:4px;background:transparent;color:var(--text-muted);cursor:pointer;font-size:13px;display:flex;align-items:center;justify-content:center;transition:background .1s,color .1s;padding:0}.icon-btn.svelte-soepgf:hover{background:var(--bg-hover);color:var(--text-primary)}.section.svelte-savwyt{padding:12px 14px;border-bottom:1px solid var(--border)}.section-label.svelte-savwyt{font-size:10px;font-weight:700;letter-spacing:.08em;color:var(--text-muted);text-transform:uppercase;margin-bottom:5px}.values.svelte-savwyt{display:flex;align-items:baseline;gap:8px}.primary.svelte-savwyt{font-size:16px;font-weight:600;color:var(--text-primary);font-variant-numeric:tabular-nums}.secondary.svelte-savwyt{font-size:11px;color:var(--text-muted);font-variant-numeric:tabular-nums}*{box-sizing:border-box;margin:0;padding:0}body{background:transparent;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;-webkit-font-smoothing:antialiased}:root{--bg:#1a1a1f;--bg-hover:rgba(255, 255, 255, .06);--border:rgba(255, 255, 255, .08);--text-primary:#f0f0f2;--text-muted:rgba(240, 240, 242, .45);--accent:#6c8eff}@media (prefers-color-scheme: light){:root{--bg:#ffffff;--bg-hover:rgba(0, 0, 0, .05);--border:rgba(0, 0, 0, .08);--text-primary:#0f0f12;--text-muted:rgba(15, 15, 18, .45);--accent:#3b5bdb}}.panel.svelte-z2p4qk{background:var(--bg);border-radius:12px;border:1px solid var(--border);overflow:hidden;width:320px;box-shadow:0 8px 32px #0000004d,0 2px 8px #0003;transition:opacity .15s}.panel.loading.svelte-z2p4qk{opacity:.7}.footer.svelte-z2p4qk{padding:10px 14px 14px}.open-btn.svelte-z2p4qk{width:100%;padding:9px 14px;border:1px solid var(--accent);border-radius:7px;background:transparent;color:var(--accent);font-size:12px;font-weight:600;cursor:pointer;transition:background .15s;letter-spacing:.01em}.open-btn.svelte-z2p4qk:hover{background:#6c8eff1a}
|
package/dist/renderer/index.html
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" />
|
|
7
|
-
<title>aiusage Widget</title>
|
|
8
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
10
|
-
</head>
|
|
11
|
-
<body>
|
|
12
|
-
<div id="app"></div>
|
|
13
|
-
</body>
|
|
14
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" />
|
|
7
|
+
<title>aiusage Widget</title>
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-CArY3S1D.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-D6oVYB8t.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="app"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
package/dist/ui.js
CHANGED
|
@@ -3,19 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.shouldShowWindowOnLaunch = shouldShowWindowOnLaunch;
|
|
4
4
|
exports.shouldHideWindowOnBlur = shouldHideWindowOnBlur;
|
|
5
5
|
exports.shouldHideWindowOnClose = shouldHideWindowOnClose;
|
|
6
|
-
exports.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
'
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
'
|
|
6
|
+
exports.getWindowPosition = getWindowPosition;
|
|
7
|
+
exports.getWidgetNativeBindingPath = getWidgetNativeBindingPath;
|
|
8
|
+
exports.getTrayIconNativeImage = getTrayIconNativeImage;
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
// macOS: 32x32 PNG rendered at @2x — displayed as 16x16 logical on Retina via scaleFactor:2
|
|
11
|
+
const TRAY_ICON_PNG_MAC_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAsklEQVR4nO2XQQ7EIAhFuQBX8HQe' +
|
|
12
|
+
'hu7Z9U6z8TZu2jSxjekwYxpUuoCEjRr+EzEBgMqQKSDTgkwJmTIybZ08l5hH7ACSIVPsLPoPJkri' +
|
|
13
|
+
'o4XvHuu0z7i5lIkA5V1mi5++QCkOK4AERum/ngFGBF7TZ7vbsSadHQLwyxzAAboBtKp8OEBLwAEc' +
|
|
14
|
+
'QA2grXI1gFbAARzgKcBXSzYRIItNaesbavcrT69oy20HE/PR7BXDqeV4vgPnux87hsqGswAAAABJ' +
|
|
15
|
+
'RU5ErkJggg==';
|
|
16
|
+
// Windows: 16x16 PNG
|
|
17
|
+
const TRAY_ICON_PNG_WIN_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAaElEQVR4nGNgYGBg4J3Skco7peMa' +
|
|
18
|
+
'75SO/0RikNpUBiTNxGpEx6kMJNqM4RIGYhVbr5j/v+3UETAGsWHiRBsA0ggDIDbtDUB3MskGoGug' +
|
|
19
|
+
'vQGEnEzQAEIaBrcB17CFASE+clKmLDNRmp0B5XqLc//RXPMAAAAASUVORK5CYII=';
|
|
20
|
+
// Linux: 22x22 PNG
|
|
21
|
+
const TRAY_ICON_PNG_LINUX_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAt0lEQVR4nGNggALeKR3ZvFM6zvJO' +
|
|
22
|
+
'6fjDO6XjP4n4D1RvNgMy4J3SsYAMw3DhBcgupZahMJzNAPUCtQ0+y0BmmIKx1Yp5cIwe5gzkGtp+' +
|
|
23
|
+
'6sh/ZADiI8uTbfDUC6dRDAbxB5fBMrP7wZiqBt//+B5uAIhNFYNBrkQHILFhbnDuvu3/P/z4AccQ' +
|
|
24
|
+
'PlUMxqZ4ZBn8hwYG/0EpNk2WzgYrgGEQHyRefXQfXAzExqcWis/SrqCnWdVEq8oUAE+RyDWaecD7' +
|
|
25
|
+
'AAAAAElFTkSuQmCC';
|
|
19
26
|
function shouldShowWindowOnLaunch(isPackaged) {
|
|
20
27
|
return !isPackaged;
|
|
21
28
|
}
|
|
@@ -25,10 +32,28 @@ function shouldHideWindowOnBlur(isPackaged) {
|
|
|
25
32
|
function shouldHideWindowOnClose(_) {
|
|
26
33
|
return true;
|
|
27
34
|
}
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
function getWindowPosition({ platform, trayBounds, windowBounds, displayBounds, }) {
|
|
36
|
+
const preferredX = Math.round(trayBounds.x + trayBounds.width / 2 - windowBounds.width / 2);
|
|
37
|
+
const preferredY = platform === 'darwin'
|
|
38
|
+
? trayBounds.y + trayBounds.height + 4
|
|
39
|
+
: trayBounds.y - windowBounds.height - 4;
|
|
40
|
+
const maxX = Math.max(displayBounds.x, displayBounds.x + displayBounds.width - windowBounds.width);
|
|
41
|
+
const maxY = Math.max(displayBounds.y, displayBounds.y + displayBounds.height - windowBounds.height);
|
|
42
|
+
return {
|
|
43
|
+
x: Math.min(Math.max(preferredX, displayBounds.x), maxX),
|
|
44
|
+
y: Math.min(Math.max(preferredY, displayBounds.y), maxY),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function getWidgetNativeBindingPath(baseDir) {
|
|
48
|
+
return (0, node_path_1.join)(baseDir, 'native', 'better_sqlite3.node');
|
|
49
|
+
}
|
|
50
|
+
function getTrayIconNativeImage() {
|
|
51
|
+
if (process.platform === 'darwin') {
|
|
52
|
+
// 32x32 PNG at scaleFactor 2 = renders as 16x16 logical, sharp on Retina
|
|
53
|
+
return { buffer: Buffer.from(TRAY_ICON_PNG_MAC_BASE64, 'base64'), scaleFactor: 2 };
|
|
54
|
+
}
|
|
55
|
+
if (process.platform === 'linux') {
|
|
56
|
+
return { buffer: Buffer.from(TRAY_ICON_PNG_LINUX_BASE64, 'base64') };
|
|
32
57
|
}
|
|
33
|
-
return
|
|
58
|
+
return { buffer: Buffer.from(TRAY_ICON_PNG_WIN_BASE64, 'base64') };
|
|
34
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juliantanx/aiusage-widget",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "System tray widget for aiusage — view AI token usage from your system tray",
|
|
6
6
|
"keywords": [
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"engines": {
|
|
25
|
-
"node": ">=
|
|
25
|
+
"node": ">=20"
|
|
26
26
|
},
|
|
27
27
|
"main": "dist/main.js",
|
|
28
28
|
"files": [
|
|
@@ -34,29 +34,31 @@
|
|
|
34
34
|
"bin": {
|
|
35
35
|
"aiusage-widget": "bin/launcher.js"
|
|
36
36
|
},
|
|
37
|
-
"scripts": {
|
|
38
|
-
"rebuild": "pnpm run rebuild:electron",
|
|
39
|
-
"rebuild:electron": "node -e \"const {execSync}=require('child_process'); const electronVersion=require('electron/package.json').version; const cmd='npm rebuild better-sqlite3 --runtime=electron --target=' + electronVersion + ' --dist-url=https://electronjs.org/headers'; execSync(cmd, {stdio:'inherit'})\"",
|
|
40
|
-
"rebuild:node": "npm rebuild better-sqlite3",
|
|
41
|
-
"dev": "pnpm run rebuild:electron && concurrently --names \"vite,tsc,electron\" --kill-others --success command-electron \"vite build --watch\" \"tsc -p tsconfig.json --watch\" \"pnpm exec wait-on dist/main.js && pnpm exec electron .\"",
|
|
42
|
-
"build": "rm -rf dist/renderer && vite build && tsc -p tsconfig.json",
|
|
43
|
-
"pack": "pnpm build && electron-builder",
|
|
44
|
-
"test": "pnpm run rebuild:node && vitest run"
|
|
45
|
-
},
|
|
46
37
|
"dependencies": {
|
|
47
|
-
"better-sqlite3": "^
|
|
38
|
+
"better-sqlite3": "^12.0.0"
|
|
48
39
|
},
|
|
49
40
|
"devDependencies": {
|
|
50
|
-
"electron": "^
|
|
41
|
+
"@electron/rebuild": "^3.6.1",
|
|
51
42
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
|
52
43
|
"@types/better-sqlite3": "*",
|
|
53
44
|
"@types/node": "*",
|
|
54
45
|
"concurrently": "^8.0.0",
|
|
46
|
+
"electron": "^33.0.0",
|
|
55
47
|
"electron-builder": "^25.0.0",
|
|
56
48
|
"svelte": "^4.0.0",
|
|
57
49
|
"typescript": "^5.0.0",
|
|
58
50
|
"vite": "^5.0.0",
|
|
59
51
|
"vitest": "^2.0.0",
|
|
60
52
|
"wait-on": "^7.0.0"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"rebuild": "pnpm run rebuild:electron",
|
|
56
|
+
"rebuild:electron": "node -e \"const {rebuild}=require('@electron/rebuild');const v=require('electron/package.json').version;rebuild({buildPath:process.cwd(),electronVersion:v,onlyModules:['better-sqlite3'],force:true}).then(()=>process.exit(0)).catch(e=>{console.error(e);process.exit(1)})\"",
|
|
57
|
+
"prepare:native": "node bin/prepare-native.js",
|
|
58
|
+
"rebuild:node": "npm rebuild better-sqlite3",
|
|
59
|
+
"dev": "pnpm run prepare:native && concurrently --names \"vite,tsc,electron\" --kill-others --success command-electron \"vite build --watch\" \"tsc -p tsconfig.json --watch\" \"node -e \\\"require('wait-on')({resources:['dist/main.js']},()=>{const c=require('child_process').spawn(require('electron'),['.'],{stdio:'inherit'});c.on('exit',(code,signal)=>process.exit(code ?? (signal ? 1 : 0)))})\\\"\"",
|
|
60
|
+
"build": "rm -rf dist/renderer && vite build && tsc -p tsconfig.json && pnpm run prepare:native",
|
|
61
|
+
"pack": "pnpm build && electron-builder",
|
|
62
|
+
"test": "pnpm run rebuild:node && vitest run"
|
|
61
63
|
}
|
|
62
|
-
}
|
|
64
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.header.svelte-14gtt2i{display:flex;align-items:center;justify-content:space-between;padding:12px 14px 10px;border-bottom:1px solid var(--border);-webkit-app-region:drag}.logo.svelte-14gtt2i{font-size:13px;font-weight:600;color:var(--text-primary);letter-spacing:.01em}.actions.svelte-14gtt2i{display:flex;gap:4px;-webkit-app-region:no-drag}.icon-btn.svelte-14gtt2i{width:24px;height:24px;border:none;border-radius:4px;background:transparent;color:var(--text-muted);cursor:pointer;font-size:13px;display:flex;align-items:center;justify-content:center;transition:background .1s,color .1s;padding:0}.icon-btn.svelte-14gtt2i:hover{background:var(--bg-hover);color:var(--text-primary)}.section.svelte-savwyt{padding:12px 14px;border-bottom:1px solid var(--border)}.section-label.svelte-savwyt{font-size:10px;font-weight:700;letter-spacing:.08em;color:var(--text-muted);text-transform:uppercase;margin-bottom:5px}.values.svelte-savwyt{display:flex;align-items:baseline;gap:8px}.primary.svelte-savwyt{font-size:16px;font-weight:600;color:var(--text-primary);font-variant-numeric:tabular-nums}.secondary.svelte-savwyt{font-size:11px;color:var(--text-muted);font-variant-numeric:tabular-nums}*{box-sizing:border-box;margin:0;padding:0}body{background:transparent;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;-webkit-font-smoothing:antialiased}:root{--bg:#1a1a1f;--bg-hover:rgba(255, 255, 255, .06);--border:rgba(255, 255, 255, .08);--text-primary:#f0f0f2;--text-muted:rgba(240, 240, 242, .45);--accent:#6c8eff}@media (prefers-color-scheme: light){:root{--bg:#ffffff;--bg-hover:rgba(0, 0, 0, .05);--border:rgba(0, 0, 0, .08);--text-primary:#0f0f12;--text-muted:rgba(15, 15, 18, .45);--accent:#3b5bdb}}.panel.svelte-z2p4qk{background:var(--bg);border-radius:12px;border:1px solid var(--border);overflow:hidden;width:320px;box-shadow:0 8px 32px #0000004d,0 2px 8px #0003;transition:opacity .15s}.panel.loading.svelte-z2p4qk{opacity:.7}.footer.svelte-z2p4qk{padding:10px 14px 14px}.open-btn.svelte-z2p4qk{width:100%;padding:9px 14px;border:1px solid var(--accent);border-radius:7px;background:transparent;color:var(--accent);font-size:12px;font-weight:600;cursor:pointer;transition:background .15s;letter-spacing:.01em}.open-btn.svelte-z2p4qk:hover{background:#6c8eff1a}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var se=Object.defineProperty;var ie=(e,t,n)=>t in e?se(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var x=(e,t,n)=>ie(e,typeof t!="symbol"?t+"":t,n);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))o(r);new MutationObserver(r=>{for(const s of r)if(s.type==="childList")for(const u of s.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&o(u)}).observe(document,{childList:!0,subtree:!0});function n(r){const s={};return r.integrity&&(s.integrity=r.integrity),r.referrerPolicy&&(s.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?s.credentials="include":r.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function o(r){if(r.ep)return;r.ep=!0;const s=n(r);fetch(r.href,s)}})();function w(){}function re(e){return e()}function X(){return Object.create(null)}function M(e){e.forEach(re)}function F(e){return typeof e=="function"}function Y(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}function le(e){return Object.keys(e).length===0}function d(e,t){e.appendChild(t)}function j(e,t,n){e.insertBefore(t,n||null)}function N(e){e.parentNode&&e.parentNode.removeChild(e)}function h(e){return document.createElement(e)}function I(e){return document.createTextNode(e)}function _(){return I(" ")}function K(e,t,n,o){return e.addEventListener(t,n,o),()=>e.removeEventListener(t,n,o)}function p(e,t,n){n==null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}function ue(e){return Array.from(e.childNodes)}function U(e,t){t=""+t,e.data!==t&&(e.data=t)}function Z(e,t,n){e.classList.toggle(t,!!n)}let T;function L(e){T=e}function ae(){if(!T)throw new Error("Function called outside component initialization");return T}function fe(e){ae().$$.on_mount.push(e)}const v=[],ee=[];let S=[];const te=[],ce=Promise.resolve();let V=!1;function de(){V||(V=!0,ce.then(oe))}function W(e){S.push(e)}const B=new Set;let $=0;function oe(){if($!==0)return;const e=T;do{try{for(;$<v.length;){const t=v[$];$++,L(t),me(t.$$)}}catch(t){throw v.length=0,$=0,t}for(L(null),v.length=0,$=0;ee.length;)ee.pop()();for(let t=0;t<S.length;t+=1){const n=S[t];B.has(n)||(B.add(n),n())}S.length=0}while(v.length);for(;te.length;)te.pop()();V=!1,B.clear(),L(e)}function me(e){if(e.fragment!==null){e.update(),M(e.before_update);const t=e.dirty;e.dirty=[-1],e.fragment&&e.fragment.p(e.ctx,t),e.after_update.forEach(W)}}function pe(e){const t=[],n=[];S.forEach(o=>e.indexOf(o)===-1?t.push(o):n.push(o)),n.forEach(o=>o()),S=t}const R=new Set;let he;function O(e,t){e&&e.i&&(R.delete(e),e.i(t))}function A(e,t,n,o){if(e&&e.o){if(R.has(e))return;R.add(e),he.c.push(()=>{R.delete(e)}),e.o(t)}}function D(e){e&&e.c()}function C(e,t,n){const{fragment:o,after_update:r}=e.$$;o&&o.m(t,n),W(()=>{const s=e.$$.on_mount.map(re).filter(F);e.$$.on_destroy?e.$$.on_destroy.push(...s):M(s),e.$$.on_mount=[]}),r.forEach(W)}function E(e,t){const n=e.$$;n.fragment!==null&&(pe(n.after_update),M(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function ge(e,t){e.$$.dirty[0]===-1&&(v.push(e),de(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<<t%31}function G(e,t,n,o,r,s,u=null,m=[-1]){const l=T;L(e);const i=e.$$={fragment:null,ctx:[],props:s,update:w,not_equal:r,bound:X(),on_mount:[],on_destroy:[],on_disconnect:[],before_update:[],after_update:[],context:new Map(t.context||(l?l.$$.context:[])),callbacks:X(),dirty:m,skip_bound:!1,root:t.target||l.$$.root};u&&u(i.root);let f=!1;if(i.ctx=n?n(e,t.props||{},(a,g,...b)=>{const k=b.length?b[0]:g;return i.ctx&&r(i.ctx[a],i.ctx[a]=k)&&(!i.skip_bound&&i.bound[a]&&i.bound[a](k),f&&ge(e,a)),g}):[],i.update(),f=!0,M(i.before_update),i.fragment=o?o(i.ctx):!1,t.target){if(t.hydrate){const a=ue(t.target);i.fragment&&i.fragment.l(a),a.forEach(N)}else i.fragment&&i.fragment.c();t.intro&&O(e.$$.fragment),C(e,t.target,t.anchor),oe()}L(l)}class J{constructor(){x(this,"$$");x(this,"$$set")}$destroy(){E(this,1),this.$destroy=w}$on(t,n){if(!F(n))return w;const o=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return o.push(n),()=>{const r=o.indexOf(n);r!==-1&&o.splice(r,1)}}$set(t){this.$$set&&!le(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}const ye="4";typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add(ye);function _e(e){let t,n,o,r,s,u,m,l,i;return{c(){t=h("div"),n=h("span"),n.textContent="⚡ aiusage",o=_(),r=h("div"),s=h("button"),s.textContent="↻",u=_(),m=h("button"),m.textContent="✕",p(n,"class","logo svelte-14gtt2i"),p(s,"class","icon-btn svelte-14gtt2i"),p(s,"title","Refresh"),p(m,"class","icon-btn svelte-14gtt2i"),p(m,"title","Close"),p(r,"class","actions svelte-14gtt2i"),p(t,"class","header svelte-14gtt2i")},m(f,a){j(f,t,a),d(t,n),d(t,o),d(t,r),d(r,s),d(r,u),d(r,m),l||(i=[K(s,"click",function(){F(e[0])&&e[0].apply(this,arguments)}),K(m,"click",function(){F(e[1])&&e[1].apply(this,arguments)})],l=!0)},p(f,[a]){e=f},i:w,o:w,d(f){f&&N(t),l=!1,M(i)}}}function we(e,t,n){let{onRefresh:o}=t,{onClose:r}=t;return e.$$set=s=>{"onRefresh"in s&&n(0,o=s.onRefresh),"onClose"in s&&n(1,r=s.onClose)},[o,r]}class be extends J{constructor(t){super(),G(this,t,we,_e,Y,{onRefresh:0,onClose:1})}}function ne(e){let t,n;return{c(){t=h("span"),n=I(e[2]),p(t,"class","secondary svelte-savwyt")},m(o,r){j(o,t,r),d(t,n)},p(o,r){r&4&&U(n,o[2])},d(o){o&&N(t)}}}function $e(e){let t,n,o,r,s,u,m,l,i=e[2]&&ne(e);return{c(){t=h("div"),n=h("div"),o=I(e[0]),r=_(),s=h("div"),u=h("span"),m=I(e[1]),l=_(),i&&i.c(),p(n,"class","section-label svelte-savwyt"),p(u,"class","primary svelte-savwyt"),p(s,"class","values svelte-savwyt"),p(t,"class","section svelte-savwyt")},m(f,a){j(f,t,a),d(t,n),d(n,o),d(t,r),d(t,s),d(s,u),d(u,m),d(s,l),i&&i.m(s,null)},p(f,[a]){a&1&&U(o,f[0]),a&2&&U(m,f[1]),f[2]?i?i.p(f,a):(i=ne(f),i.c(),i.m(s,null)):i&&(i.d(1),i=null)},i:w,o:w,d(f){f&&N(t),i&&i.d()}}}function ve(e,t,n){let{label:o}=t,{primary:r}=t,{secondary:s=""}=t;return e.$$set=u=>{"label"in u&&n(0,o=u.label),"primary"in u&&n(1,r=u.primary),"secondary"in u&&n(2,s=u.secondary)},[o,r,s]}class H extends J{constructor(t){super(),G(this,t,ve,$e,Y,{label:0,primary:1,secondary:2})}}function Se(e){let t,n,o,r,s,u,m,l,i,f,a,g,b,k;return n=new be({props:{onRefresh:e[6],onClose:ke}}),r=new H({props:{label:"TODAY",primary:e[5],secondary:e[4]}}),u=new H({props:{label:"THIS MONTH",primary:e[3]}}),l=new H({props:{label:"TOP MODEL",primary:e[2],secondary:e[1]}}),{c(){t=h("div"),D(n.$$.fragment),o=_(),D(r.$$.fragment),s=_(),D(u.$$.fragment),m=_(),D(l.$$.fragment),i=_(),f=h("div"),a=h("button"),a.textContent="Open Full Dashboard →",p(a,"class","open-btn svelte-z2p4qk"),p(f,"class","footer svelte-z2p4qk"),p(t,"class","panel svelte-z2p4qk"),Z(t,"loading",e[0])},m(c,y){j(c,t,y),C(n,t,null),d(t,o),C(r,t,null),d(t,s),C(u,t,null),d(t,m),C(l,t,null),d(t,i),d(t,f),d(f,a),g=!0,b||(k=K(a,"click",Oe),b=!0)},p(c,[y]){const q={};y&32&&(q.primary=c[5]),y&16&&(q.secondary=c[4]),r.$set(q);const Q={};y&8&&(Q.primary=c[3]),u.$set(Q);const z={};y&4&&(z.primary=c[2]),y&2&&(z.secondary=c[1]),l.$set(z),(!g||y&1)&&Z(t,"loading",c[0])},i(c){g||(O(n.$$.fragment,c),O(r.$$.fragment,c),O(u.$$.fragment,c),O(l.$$.fragment,c),g=!0)},o(c){A(n.$$.fragment,c),A(r.$$.fragment,c),A(u.$$.fragment,c),A(l.$$.fragment,c),g=!1},d(c){c&&N(t),E(n),E(r),E(u),E(l),b=!1,k()}}}function P(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:String(e)}function ke(){window.widget.hideWindow()}async function Oe(){await window.widget.openDashboard()}function Ce(e,t,n){let o,r,s,u,m,l=null,i=!0;async function f(){n(0,i=!0),n(7,l=await window.widget.getData()),n(0,i=!1)}return fe(()=>{f(),window.widget.onDataUpdate(a=>{n(7,l=a),n(0,i=!1)})}),e.$$.update=()=>{var a;e.$$.dirty&128&&n(5,o=l?P(l.todayTokens.total):"—"),e.$$.dirty&128&&n(4,r=l?`↑${P(l.todayTokens.input)} ↓${P(l.todayTokens.output)}`:""),e.$$.dirty&128&&n(3,s=l?P(l.monthTokens.total):"—"),e.$$.dirty&128&&n(2,u=((a=l==null?void 0:l.topModel)==null?void 0:a.name)??"—"),e.$$.dirty&128&&n(1,m=l!=null&&l.topModel?`${l.topModel.share}%`:"")},[i,m,u,s,r,o,f,l]}class Ee extends J{constructor(t){super(),G(this,t,Ce,Se,Y,{})}}new Ee({target:document.getElementById("app")});
|