@juliantanx/aiusage-widget 1.3.4 → 1.5.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @juliantanx/aiusage-widget
2
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.
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
4
 
5
5
  English | [中文](./README_zh.md)
6
6
 
@@ -10,13 +10,13 @@ English | [中文](./README_zh.md)
10
10
  - **Today's token usage** — total tokens with input/output breakdown.
11
11
  - **Monthly totals** — rolling token count for the current month.
12
12
  - **Top model** — your most-used model today and its share.
13
- - **Dashboard launcher** — one click to open the full aiusage web dashboard.
13
+ - **Dashboard launcher** — open the full AIUsage web dashboard from the tray menu.
14
14
  - **Auto-refresh** — updates every 60 seconds automatically.
15
15
  - **Cross-platform** — Windows, macOS, and Linux.
16
16
 
17
17
  ## Prerequisites
18
18
 
19
- - [aiusage](https://github.com/juliantanx/aiusage) CLI installed and data parsed (`aiusage parse`)
19
+ - [AIUsage](https://github.com/juliantanx/aiusage) CLI installed and data parsed (`aiusage serve` auto-parses on startup)
20
20
  - Node.js >= 20
21
21
 
22
22
  ## Install
@@ -32,7 +32,7 @@ npm install -g @juliantanx/aiusage-widget
32
32
  aiusage-widget
33
33
  ```
34
34
 
35
- The widget reads from `~/.aiusage/cache.db`. Make sure you have run `aiusage parse` at least once so the database exists.
35
+ The widget reads from `~/.aiusage/cache.db`. The database is created automatically when you run `aiusage serve`.
36
36
 
37
37
  ### Running with PM2
38
38
 
@@ -41,7 +41,7 @@ To keep the widget running after closing the terminal and auto-start on boot:
41
41
  ```bash
42
42
  npm install -g pm2
43
43
 
44
- # Start aiusage server + widget as background services
44
+ # Start AIUsage server + widget as background services
45
45
  aiusage pm2-start
46
46
 
47
47
  # Linux / macOS: run directly; Windows: run the output command as Administrator
@@ -57,7 +57,8 @@ PM2 works on Windows, macOS, and Linux.
57
57
 
58
58
  **Panel actions:**
59
59
 
60
- - Click **Open Full Dashboard** to launch `aiusage serve` and open the web dashboard in your browser.
60
+ - Click the refresh icon in the panel header to reload local usage data immediately.
61
+ - Use **Open Dashboard** in the tray context menu to launch `aiusage serve` if needed and open the web dashboard in your browser.
61
62
 
62
63
  ## Build from Source
63
64
 
@@ -74,7 +75,7 @@ pnpm dev
74
75
 
75
76
  - **Runtime:** Electron
76
77
  - **UI:** Svelte + Vite
77
- - **Database:** better-sqlite3 (reads aiusage's local SQLite database)
78
+ - **Database:** better-sqlite3 (reads AIUsage's local SQLite database)
78
79
 
79
80
  ## License
80
81
 
package/README_zh.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @juliantanx/aiusage-widget
2
2
 
3
- 一个轻量级的系统托盘小组件,用于 [aiusage](https://github.com/juliantanx/aiusage),可以快速查看 AI 编程助手的 token 用量。
3
+ 一个轻量级的系统托盘小组件,用于 [AIUsage](https://github.com/juliantanx/aiusage),可以快速查看 AI 编程助手的 token 用量。
4
4
 
5
5
  [English](./README.md) | 中文
6
6
 
@@ -10,13 +10,13 @@
10
10
  - **今日 token 用量** — 总 token 数及输入/输出明细。
11
11
  - **月度总量** — 当月累计 token 数。
12
12
  - **热门模型** — 今日使用最多的模型及其占比。
13
- - **仪表盘启动** — 一键打开完整的 aiusage Web 仪表盘。
13
+ - **仪表盘启动** — 从托盘菜单打开完整的 AIUsage Web 仪表盘。
14
14
  - **自动刷新** — 每 60 秒自动更新数据。
15
15
  - **跨平台** — 支持 Windows、macOS 和 Linux。
16
16
 
17
17
  ## 前置条件
18
18
 
19
- - 已安装 [aiusage](https://github.com/juliantanx/aiusage) CLI 并完成数据解析(`aiusage parse`)
19
+ - 已安装 [AIUsage](https://github.com/juliantanx/aiusage) CLI 并完成数据解析(`aiusage serve` 启动时会自动解析)
20
20
  - Node.js >= 20
21
21
 
22
22
  ## 安装
@@ -32,7 +32,7 @@ npm install -g @juliantanx/aiusage-widget
32
32
  aiusage-widget
33
33
  ```
34
34
 
35
- 组件会读取 `~/.aiusage/cache.db`。请确保至少执行过一次 `aiusage parse`,使数据库文件存在。
35
+ 组件会读取 `~/.aiusage/cache.db`。运行 `aiusage serve` 时会自动创建数据库。
36
36
 
37
37
  ### 使用 PM2 后台运行
38
38
 
@@ -41,7 +41,7 @@ aiusage-widget
41
41
  ```bash
42
42
  npm install -g pm2
43
43
 
44
- # 一条命令启动 aiusage 服务 + 托盘组件为后台服务
44
+ # 一条命令启动 AIUsage 服务 + 托盘组件为后台服务
45
45
  aiusage pm2-start
46
46
 
47
47
  # Linux / macOS: 直接执行;Windows: 以管理员身份执行输出的命令
@@ -57,7 +57,8 @@ PM2 支持 Windows、macOS 和 Linux。
57
57
 
58
58
  **面板操作:**
59
59
 
60
- - 点击 **Open Full Dashboard** 可启动 `aiusage serve` 并在浏览器中打开 Web 仪表盘。
60
+ - 点击面板右上角的刷新图标,可立即重新读取本地用量数据。
61
+ - 在托盘右键菜单中点击 **Open Dashboard**,可按需启动 `aiusage serve` 并在浏览器中打开 Web 仪表盘。
61
62
 
62
63
  ## 从源码构建
63
64
 
@@ -74,7 +75,7 @@ pnpm dev
74
75
 
75
76
  - **运行时:** Electron
76
77
  - **UI:** Svelte + Vite
77
- - **数据库:** better-sqlite3(读取 aiusage 的本地 SQLite 数据库)
78
+ - **数据库:** better-sqlite3(读取 AIUsage 的本地 SQLite 数据库)
78
79
 
79
80
  ## 许可证
80
81
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const { copyFileSync, mkdirSync } = require('node:fs')
2
+ const { copyFileSync, mkdirSync, statSync, existsSync } = require('node:fs')
3
3
  const { dirname, join } = require('node:path')
4
4
  const { execFileSync } = require('node:child_process')
5
5
  const { rebuild } = require('@electron/rebuild')
@@ -14,7 +14,23 @@ const nativeSource = join(
14
14
  const nativeDir = join(widgetRoot, 'dist', 'native')
15
15
  const nativeTarget = join(nativeDir, 'better_sqlite3.node')
16
16
 
17
+ function isTargetFresh() {
18
+ try {
19
+ if (!existsSync(nativeTarget) || !existsSync(nativeSource)) return false
20
+ const srcTime = statSync(nativeSource).mtimeMs
21
+ const dstTime = statSync(nativeTarget).mtimeMs
22
+ return dstTime >= srcTime
23
+ } catch {
24
+ return false
25
+ }
26
+ }
27
+
17
28
  async function main() {
29
+ if (isTargetFresh() && !process.argv.includes('--force')) {
30
+ console.log('Native binding is up to date, skipping rebuild (use --force to override)')
31
+ return
32
+ }
33
+
18
34
  const electronVersion = require('electron/package.json').version
19
35
 
20
36
  await rebuild({
@@ -33,7 +49,7 @@ async function main() {
33
49
 
34
50
  execFileSync('npm', ['run', 'install'], {
35
51
  cwd: dirname(require.resolve('better-sqlite3/package.json')),
36
- stdio: 'inherit',
52
+ stdio: 'pipe',
37
53
  })
38
54
  }
39
55
 
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.currencies = exports.EXCHANGE_RATE_SOURCE = void 0;
4
+ exports.convertUsdCost = convertUsdCost;
5
+ exports.formatUsdCost = formatUsdCost;
6
+ exports.formatCurrency = formatCurrency;
7
+ exports.EXCHANGE_RATE_SOURCE = 'https://latest.currency-api.pages.dev/v1/currencies/usd.json';
8
+ exports.currencies = [
9
+ { code: 'USD', label: 'USD' },
10
+ { code: 'CNY', label: 'CNY' },
11
+ ];
12
+ function convertUsdCost(value, currency, exchangeRate) {
13
+ if (currency === 'USD')
14
+ return value;
15
+ if (exchangeRate?.rate && Number.isFinite(exchangeRate.rate))
16
+ return value * exchangeRate.rate;
17
+ return null;
18
+ }
19
+ function formatUsdCost(value, currency, locale, exchangeRate, compact = false) {
20
+ const converted = convertUsdCost(value, currency, exchangeRate);
21
+ return converted === null ? '--' : formatCurrency(converted, currency, locale, compact);
22
+ }
23
+ function formatCurrency(value, currency, locale, compact = false) {
24
+ const notation = compact && Math.abs(value) >= 1000 ? 'compact' : 'standard';
25
+ const fractionDigits = value >= 100 || notation === 'compact'
26
+ ? 0
27
+ : value >= 1
28
+ ? 2
29
+ : value > 0
30
+ ? 3
31
+ : 0;
32
+ return new Intl.NumberFormat(locale, {
33
+ style: 'currency',
34
+ currency,
35
+ currencyDisplay: 'narrowSymbol',
36
+ notation,
37
+ minimumFractionDigits: fractionDigits,
38
+ maximumFractionDigits: fractionDigits,
39
+ }).format(value);
40
+ }
package/dist/data.js CHANGED
@@ -1,24 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.queryWidgetData = queryWidgetData;
4
- function queryWidgetData(db) {
4
+ function queryWidgetData(db, rangeDays = 30) {
5
5
  const todayStart = getTodayStartMs();
6
- const monthStart = getMonthStartMs();
7
6
  const tomorrow = todayStart + 86400000;
8
- const nextMonthStart = getNextMonthStartMs();
7
+ const rangeStart = todayStart - (rangeDays - 1) * 86400000;
9
8
  const todayRow = db.prepare(`
10
9
  SELECT
11
10
  COALESCE(SUM(input_tokens), 0) AS input,
12
11
  COALESCE(SUM(output_tokens), 0) AS output,
13
- COALESCE(SUM(cache_read_tokens + cache_write_tokens + thinking_tokens), 0) AS other
12
+ COALESCE(SUM(cache_read_tokens), 0) AS cacheRead,
13
+ COALESCE(SUM(cache_write_tokens), 0) AS cacheWrite,
14
+ COALESCE(SUM(thinking_tokens), 0) AS thinking,
15
+ COALESCE(SUM(cost), 0) AS cost
14
16
  FROM records
15
17
  WHERE ts >= ? AND ts < ?
16
18
  `).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
+ const rangeRow = db.prepare(`
20
+ SELECT
21
+ COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_write_tokens + thinking_tokens), 0) AS total,
22
+ COALESCE(SUM(cost), 0) AS cost
19
23
  FROM records
20
24
  WHERE ts >= ? AND ts < ?
21
- `).get(monthStart, nextMonthStart);
25
+ `).get(rangeStart, tomorrow);
22
26
  const modelRows = db.prepare(`
23
27
  SELECT
24
28
  model,
@@ -37,16 +41,73 @@ function queryWidgetData(db) {
37
41
  share: totalTokens > 0 ? Math.round((top.tokens / totalTokens) * 100) : 0,
38
42
  };
39
43
  }
44
+ const toolRows = db.prepare(`
45
+ SELECT
46
+ tool,
47
+ SUM(input_tokens + output_tokens + cache_read_tokens + cache_write_tokens + thinking_tokens) AS tokens
48
+ FROM records
49
+ WHERE ts >= ? AND ts < ?
50
+ GROUP BY tool
51
+ ORDER BY tokens DESC
52
+ `).all(todayStart, tomorrow);
53
+ let topTool = null;
54
+ if (toolRows.length > 0) {
55
+ const totalTokens = toolRows.reduce((acc, r) => acc + r.tokens, 0);
56
+ const top = toolRows[0];
57
+ topTool = {
58
+ name: top.tool,
59
+ share: totalTokens > 0 ? Math.round((top.tokens / totalTokens) * 100) : 0,
60
+ };
61
+ }
62
+ // Daily history for the configured range
63
+ const dailyRows = db.prepare(`
64
+ SELECT
65
+ CAST((ts - ?) / 86400000 AS INTEGER) AS dayIndex,
66
+ SUM(input_tokens + output_tokens + cache_read_tokens + cache_write_tokens + thinking_tokens) AS tokens,
67
+ SUM(cost) AS cost
68
+ FROM records
69
+ WHERE ts >= ? AND ts < ?
70
+ GROUP BY dayIndex
71
+ ORDER BY dayIndex
72
+ `).all(rangeStart, rangeStart, tomorrow);
73
+ const dailyHistory = [];
74
+ const dailyMap = new Map(dailyRows.map(r => [r.dayIndex, r]));
75
+ for (let i = 0; i < rangeDays; i++) {
76
+ const dayMs = rangeStart + i * 86400000;
77
+ const d = new Date(dayMs);
78
+ const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
79
+ const row = dailyMap.get(i);
80
+ dailyHistory.push({
81
+ date,
82
+ tokens: row?.tokens ?? 0,
83
+ cost: row?.cost ?? 0,
84
+ });
85
+ }
86
+ // Session count today
87
+ const sessionRow = db.prepare(`
88
+ SELECT COUNT(DISTINCT session_id) AS cnt
89
+ FROM records
90
+ WHERE ts >= ? AND ts < ? AND session_id IS NOT NULL AND session_id != ''
91
+ `).get(todayStart, tomorrow);
40
92
  return {
41
93
  todayTokens: {
42
- total: todayRow.input + todayRow.output + todayRow.other,
94
+ total: todayRow.input + todayRow.output + todayRow.cacheRead + todayRow.cacheWrite + todayRow.thinking,
43
95
  input: todayRow.input,
44
96
  output: todayRow.output,
97
+ cacheRead: todayRow.cacheRead,
98
+ cacheWrite: todayRow.cacheWrite,
99
+ thinking: todayRow.thinking,
45
100
  },
46
- monthTokens: {
47
- total: monthRow.total,
101
+ todayCost: todayRow.cost,
102
+ rangeTokens: {
103
+ total: rangeRow.total,
48
104
  },
105
+ rangeCost: rangeRow.cost,
106
+ rangeDays,
49
107
  topModel,
108
+ topTool,
109
+ dailyHistory,
110
+ sessionCountToday: sessionRow.cnt,
50
111
  lastUpdated: Date.now(),
51
112
  };
52
113
  }
@@ -54,11 +115,3 @@ function getTodayStartMs() {
54
115
  const now = new Date();
55
116
  return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
56
117
  }
57
- function getMonthStartMs() {
58
- const now = new Date();
59
- return new Date(now.getFullYear(), now.getMonth(), 1).getTime();
60
- }
61
- function getNextMonthStartMs() {
62
- const now = new Date();
63
- return new Date(now.getFullYear(), now.getMonth() + 1, 1).getTime();
64
- }
package/dist/i18n.js ADDED
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.t = t;
4
+ const en = {
5
+ today: 'Today',
6
+ lastNDays: (n) => `Last ${n} days`,
7
+ tokenBreakdown: 'Token breakdown',
8
+ tokenBreakdownToday: 'Token breakdown (Today)',
9
+ trend: 'Trend',
10
+ topModel: 'Top Model',
11
+ topTool: 'Top Tool',
12
+ sessions: 'Sessions',
13
+ settings: 'Settings',
14
+ theme: 'Theme',
15
+ timeRange: 'Time range',
16
+ refreshInterval: 'Refresh interval',
17
+ display: 'Display',
18
+ currency: 'Currency',
19
+ exchangeRate: 'Exchange rate',
20
+ exchangeRateUpdated: (time) => `Updated ${time}`,
21
+ exchangeRateUnavailable: 'Exchange rate unavailable',
22
+ language: 'Language',
23
+ showCost: 'Show cost',
24
+ tokenBreakdownToggle: 'Token breakdown',
25
+ activityChart: 'Activity chart',
26
+ syncedAt: (time) => `Synced ${time}`,
27
+ themeSystem: 'System',
28
+ themeLight: 'Light',
29
+ themeDark: 'Dark',
30
+ showPanel: 'Show Panel',
31
+ openDashboard: 'Open Dashboard',
32
+ refresh: 'Refresh',
33
+ quit: 'Quit',
34
+ close: 'Close',
35
+ };
36
+ const zh = {
37
+ today: '今日',
38
+ lastNDays: (n) => `近 ${n} 天`,
39
+ tokenBreakdown: 'Token 分布',
40
+ tokenBreakdownToday: 'Token 分布 (今日)',
41
+ trend: '趋势',
42
+ topModel: '常用模型',
43
+ topTool: '常用工具',
44
+ sessions: '会话数',
45
+ settings: '设置',
46
+ theme: '主题',
47
+ timeRange: '时间范围',
48
+ refreshInterval: '刷新间隔',
49
+ display: '显示',
50
+ currency: '币种',
51
+ exchangeRate: '汇率',
52
+ exchangeRateUpdated: (time) => `更新于 ${time}`,
53
+ exchangeRateUnavailable: '汇率不可用',
54
+ language: '语言',
55
+ showCost: '显示费用',
56
+ tokenBreakdownToggle: 'Token 分布',
57
+ activityChart: '活动图表',
58
+ syncedAt: (time) => `同步于 ${time}`,
59
+ themeSystem: '跟随系统',
60
+ themeLight: '浅色',
61
+ themeDark: '深色',
62
+ showPanel: '显示面板',
63
+ openDashboard: '打开仪表盘',
64
+ refresh: '刷新',
65
+ quit: '退出',
66
+ close: '关闭',
67
+ };
68
+ const translations = { en, zh };
69
+ function t(locale) {
70
+ return translations[locale] ?? translations.en;
71
+ }
package/dist/main.js CHANGED
@@ -5,18 +5,30 @@ const node_path_1 = require("node:path");
5
5
  const node_fs_1 = require("node:fs");
6
6
  const node_os_1 = require("node:os");
7
7
  const node_module_1 = require("node:module");
8
+ const currency_1 = require("./currency");
8
9
  const data_1 = require("./data");
10
+ const i18n_1 = require("./i18n");
11
+ const settings_1 = require("./settings");
9
12
  const ui_1 = require("./ui");
10
13
  const nodeRequire = (0, node_module_1.createRequire)(__filename);
11
14
  const Database = nodeRequire('better-sqlite3');
12
15
  const DB_PATH = (0, node_path_1.join)((0, node_os_1.homedir)(), '.aiusage', 'cache.db');
13
16
  const PORT_FILE = (0, node_path_1.join)((0, node_os_1.homedir)(), '.aiusage', '.serve-port');
17
+ const FX_CACHE_FILE = (0, node_path_1.join)((0, node_os_1.homedir)(), '.aiusage', 'widget-exchange-rate.json');
14
18
  const DASHBOARD_PORT = 3847;
15
- const REFRESH_INTERVAL_MS = 60000;
19
+ const WINDOW_WIDTH = 380;
20
+ const DEFAULT_WINDOW_HEIGHT = 500;
21
+ const MIN_WINDOW_HEIGHT = 320;
22
+ const FX_CACHE_TTL_MS = 6 * 60 * 60 * 1000;
16
23
  let tray = null;
17
24
  let win = null;
18
25
  let db = null;
19
- electron_1.app.setName('aiusage Widget');
26
+ let refreshTimer = null;
27
+ let positionRetryTimers = [];
28
+ let settings = (0, settings_1.loadSettings)();
29
+ let exchangeRate = loadExchangeRateCache();
30
+ let exchangeRatePromise = null;
31
+ electron_1.app.setName('AIUsage Widget');
20
32
  // Prevent dock icon on macOS
21
33
  if (process.platform === 'darwin' && electron_1.app.dock) {
22
34
  electron_1.app.dock.hide();
@@ -28,11 +40,13 @@ electron_1.app.whenReady().then(() => {
28
40
  nativeBinding: (0, ui_1.getWidgetNativeBindingPath)(__dirname),
29
41
  });
30
42
  }
43
+ applyTheme(settings.theme);
31
44
  createTray();
32
45
  createWindow();
33
46
  startAutoRefresh();
47
+ void refreshExchangeRate();
34
48
  if ((0, ui_1.shouldShowWindowOnLaunch)(electron_1.app.isPackaged)) {
35
- showWindow();
49
+ showWindowWhenTrayReady();
36
50
  }
37
51
  });
38
52
  electron_1.app.on('window-all-closed', () => {
@@ -41,26 +55,31 @@ electron_1.app.on('window-all-closed', () => {
41
55
  electron_1.app.on('before-quit', () => {
42
56
  db?.close();
43
57
  });
58
+ function applyTheme(theme) {
59
+ electron_1.nativeTheme.themeSource = theme;
60
+ }
44
61
  function createTray() {
45
62
  const { buffer, scaleFactor } = (0, ui_1.getTrayIconNativeImage)();
46
63
  const icon = electron_1.nativeImage.createFromBuffer(buffer, scaleFactor ? { scaleFactor } : undefined);
47
64
  tray = new electron_1.Tray(icon);
48
- tray.setToolTip('aiusage Widget');
65
+ tray.setToolTip('AIUsage Widget');
49
66
  tray.on('click', () => toggleWindow());
50
67
  tray.on('right-click', () => {
68
+ const i18n = (0, i18n_1.t)(settings.locale);
51
69
  const menu = electron_1.Menu.buildFromTemplate([
52
- { label: 'Show Panel', click: () => showWindow() },
53
- { label: 'Refresh', click: () => pushDataUpdate() },
70
+ { label: i18n.showPanel, click: () => showWindow() },
71
+ { label: i18n.openDashboard, click: () => openDashboardAction() },
72
+ { label: i18n.refresh, click: () => pushDataUpdate() },
54
73
  { type: 'separator' },
55
- { label: 'Quit', click: () => { electron_1.app.exit(0); } },
74
+ { label: i18n.quit, click: () => { electron_1.app.exit(0); } },
56
75
  ]);
57
76
  tray.popUpContextMenu(menu);
58
77
  });
59
78
  }
60
79
  function createWindow() {
61
80
  win = new electron_1.BrowserWindow({
62
- width: 320,
63
- height: 300,
81
+ width: WINDOW_WIDTH,
82
+ height: DEFAULT_WINDOW_HEIGHT,
64
83
  show: false,
65
84
  frame: false,
66
85
  resizable: false,
@@ -82,10 +101,32 @@ function createWindow() {
82
101
  function showWindow() {
83
102
  if (!win)
84
103
  return;
85
- // Position near tray icon
104
+ positionWindowNearTray();
105
+ win.show();
106
+ win.focus();
107
+ pushDataUpdate();
108
+ schedulePositionRetries();
109
+ }
110
+ function showWindowWhenTrayReady(attempt = 0) {
111
+ if (!tray)
112
+ return;
113
+ const trayBounds = tray.getBounds();
114
+ if ((0, ui_1.hasUsableTrayBounds)({
115
+ platform: process.platform,
116
+ trayBounds,
117
+ displayBounds: electron_1.screen.getDisplayNearestPoint({ x: trayBounds.x, y: trayBounds.y }).workArea,
118
+ }) || attempt >= 12) {
119
+ showWindow();
120
+ return;
121
+ }
122
+ setTimeout(() => showWindowWhenTrayReady(attempt + 1), 80);
123
+ }
124
+ function positionWindowNearTray() {
125
+ if (!win || !tray)
126
+ return false;
86
127
  const trayBounds = tray.getBounds();
87
- const winBounds = win.getBounds();
88
128
  const displayBounds = electron_1.screen.getDisplayNearestPoint({ x: trayBounds.x, y: trayBounds.y }).workArea;
129
+ const winBounds = win.getBounds();
89
130
  const { x, y } = (0, ui_1.getWindowPosition)({
90
131
  platform: process.platform,
91
132
  trayBounds,
@@ -93,9 +134,21 @@ function showWindow() {
93
134
  displayBounds,
94
135
  });
95
136
  win.setPosition(x, y, false);
96
- win.show();
97
- win.focus();
98
- pushDataUpdate();
137
+ return (0, ui_1.hasUsableTrayBounds)({ platform: process.platform, trayBounds, displayBounds });
138
+ }
139
+ function schedulePositionRetries() {
140
+ for (const timer of positionRetryTimers)
141
+ clearTimeout(timer);
142
+ positionRetryTimers = [80, 200, 500, 1000].map((delay) => setTimeout(() => {
143
+ if (!win?.isVisible())
144
+ return;
145
+ const positionedWithRealTrayBounds = positionWindowNearTray();
146
+ if (positionedWithRealTrayBounds) {
147
+ for (const timer of positionRetryTimers)
148
+ clearTimeout(timer);
149
+ positionRetryTimers = [];
150
+ }
151
+ }, delay));
99
152
  }
100
153
  function toggleWindow() {
101
154
  if (win?.isVisible()) {
@@ -109,7 +162,7 @@ function pushDataUpdate() {
109
162
  if (!win || !db)
110
163
  return;
111
164
  try {
112
- const data = (0, data_1.queryWidgetData)(db);
165
+ const data = (0, data_1.queryWidgetData)(db, settings.rangeDays);
113
166
  win.webContents.send('widget:data-update', data);
114
167
  }
115
168
  catch {
@@ -117,29 +170,172 @@ function pushDataUpdate() {
117
170
  }
118
171
  }
119
172
  function startAutoRefresh() {
120
- setInterval(() => pushDataUpdate(), REFRESH_INTERVAL_MS);
173
+ if (refreshTimer)
174
+ clearInterval(refreshTimer);
175
+ refreshTimer = setInterval(() => pushDataUpdate(), settings.refreshIntervalSec * 1000);
121
176
  }
122
- // IPC handlers
123
- electron_1.ipcMain.handle('widget:get-data', () => {
124
- if (!db)
125
- return null;
126
- return (0, data_1.queryWidgetData)(db);
127
- });
128
- electron_1.ipcMain.handle('widget:open-dashboard', async () => {
177
+ async function openDashboardAction() {
129
178
  const port = getDashboardPort();
130
179
  const reachable = await isDashboardReachable(port);
131
180
  if (!reachable) {
132
181
  const result = await launchDashboard();
133
182
  if (!result.success) {
134
- electron_1.dialog.showErrorBox('aiusage Not Found', 'The aiusage CLI is not installed.\n\nInstall it with:\n npm install -g @juliantanx/aiusage\n\nThen try again.');
135
- return;
183
+ // CLI not found; attempt auto-install
184
+ notifyRenderer('install:status', { phase: 'installing' });
185
+ const installResult = await installAiusageCli();
186
+ if (!installResult.success) {
187
+ notifyRenderer('install:status', { phase: 'failed', error: installResult.error });
188
+ electron_1.dialog.showErrorBox('Installation Failed', `Could not install @juliantanx/aiusage automatically.\n\n${installResult.error ?? 'Unknown error'}\n\nTry manually:\n npm install -g @juliantanx/aiusage`);
189
+ return;
190
+ }
191
+ notifyRenderer('install:status', { phase: 'launching' });
192
+ const retryResult = await launchDashboard();
193
+ if (!retryResult.success) {
194
+ notifyRenderer('install:status', { phase: 'failed', error: retryResult.error });
195
+ electron_1.dialog.showErrorBox('Launch Failed', 'AIUsage was installed but the dashboard failed to start.\n\nTry running:\n aiusage serve');
196
+ return;
197
+ }
198
+ notifyRenderer('install:status', { phase: 'done' });
136
199
  }
137
200
  }
138
201
  electron_1.shell.openExternal(`http://localhost:${getDashboardPort()}`);
202
+ }
203
+ function notifyRenderer(channel, payload) {
204
+ if (win && !win.isDestroyed()) {
205
+ win.webContents.send(channel, payload);
206
+ }
207
+ }
208
+ function loadExchangeRateCache() {
209
+ try {
210
+ if ((0, node_fs_1.existsSync)(FX_CACHE_FILE)) {
211
+ const raw = JSON.parse((0, node_fs_1.readFileSync)(FX_CACHE_FILE, 'utf-8'));
212
+ if (raw.base === 'USD' && raw.target === 'CNY' && typeof raw.rate === 'number') {
213
+ return raw;
214
+ }
215
+ }
216
+ }
217
+ catch {
218
+ // Fall through to empty state
219
+ }
220
+ return {
221
+ base: 'USD',
222
+ target: 'CNY',
223
+ rate: null,
224
+ fetchedAt: null,
225
+ source: currency_1.EXCHANGE_RATE_SOURCE,
226
+ };
227
+ }
228
+ function saveExchangeRateCache(rate) {
229
+ const dir = (0, node_path_1.join)((0, node_os_1.homedir)(), '.aiusage');
230
+ if (!(0, node_fs_1.existsSync)(dir)) {
231
+ (0, node_fs_1.mkdirSync)(dir, { recursive: true });
232
+ }
233
+ (0, node_fs_1.writeFileSync)(FX_CACHE_FILE, JSON.stringify(rate, null, 2), 'utf-8');
234
+ }
235
+ async function refreshExchangeRate(force = false) {
236
+ const hasFreshRate = exchangeRate.rate !== null &&
237
+ exchangeRate.fetchedAt !== null &&
238
+ Date.now() - exchangeRate.fetchedAt < FX_CACHE_TTL_MS;
239
+ if (!force && hasFreshRate)
240
+ return exchangeRate;
241
+ if (exchangeRatePromise)
242
+ return exchangeRatePromise;
243
+ exchangeRatePromise = (async () => {
244
+ try {
245
+ const response = await fetch(currency_1.EXCHANGE_RATE_SOURCE);
246
+ if (!response.ok)
247
+ throw new Error(`HTTP ${response.status}`);
248
+ const payload = await response.json();
249
+ const rate = payload.usd?.cny;
250
+ if (typeof rate !== 'number' || !Number.isFinite(rate) || rate <= 0) {
251
+ throw new Error('USD/CNY rate missing');
252
+ }
253
+ exchangeRate = {
254
+ base: 'USD',
255
+ target: 'CNY',
256
+ rate,
257
+ fetchedAt: Date.now(),
258
+ date: payload.date,
259
+ source: currency_1.EXCHANGE_RATE_SOURCE,
260
+ };
261
+ saveExchangeRateCache(exchangeRate);
262
+ }
263
+ catch (error) {
264
+ exchangeRate = {
265
+ ...exchangeRate,
266
+ error: error instanceof Error ? error.message : 'Failed to fetch exchange rate',
267
+ source: currency_1.EXCHANGE_RATE_SOURCE,
268
+ };
269
+ }
270
+ finally {
271
+ exchangeRatePromise = null;
272
+ }
273
+ return exchangeRate;
274
+ })();
275
+ return exchangeRatePromise;
276
+ }
277
+ async function installAiusageCli() {
278
+ const { execFile } = nodeRequire('child_process');
279
+ // Try npm first, fall back to pnpm, then yarn
280
+ const managers = ['npm', 'pnpm', 'yarn'];
281
+ for (const pm of managers) {
282
+ const args = pm === 'yarn'
283
+ ? ['global', 'add', '@juliantanx/aiusage']
284
+ : ['install', '-g', '@juliantanx/aiusage'];
285
+ const result = await new Promise((resolve) => {
286
+ execFile(pm, args, { timeout: 120000, shell: true }, (err, _stdout, stderr) => {
287
+ if (err) {
288
+ resolve({ success: false, error: stderr || err.message });
289
+ }
290
+ else {
291
+ resolve({ success: true });
292
+ }
293
+ });
294
+ });
295
+ if (result.success)
296
+ return result;
297
+ // If this package manager isn't installed, try the next one
298
+ }
299
+ return { success: false, error: 'No package manager (npm/pnpm/yarn) could install @juliantanx/aiusage.' };
300
+ }
301
+ // IPC handlers
302
+ electron_1.ipcMain.handle('widget:get-data', () => {
303
+ if (!db)
304
+ return null;
305
+ return (0, data_1.queryWidgetData)(db);
306
+ });
307
+ electron_1.ipcMain.handle('widget:open-dashboard', async () => {
308
+ await openDashboardAction();
309
+ });
310
+ electron_1.ipcMain.handle('widget:get-settings', () => {
311
+ return settings;
312
+ });
313
+ electron_1.ipcMain.handle('widget:get-exchange-rate', async () => {
314
+ return refreshExchangeRate();
315
+ });
316
+ electron_1.ipcMain.handle('widget:save-settings', (_event, newSettings) => {
317
+ settings = newSettings;
318
+ (0, settings_1.saveSettings)(settings);
319
+ applyTheme(settings.theme);
320
+ startAutoRefresh();
321
+ return settings;
139
322
  });
140
323
  electron_1.ipcMain.on('widget:hide-window', () => {
141
324
  win?.hide();
142
325
  });
326
+ electron_1.ipcMain.on('widget:resize-window', (_event, height) => {
327
+ if (!win || !Number.isFinite(height))
328
+ return;
329
+ const bounds = win.getBounds();
330
+ const displayBounds = electron_1.screen.getDisplayNearestPoint({ x: bounds.x, y: bounds.y }).workArea;
331
+ const nextHeight = Math.min(Math.max(Math.ceil(height), MIN_WINDOW_HEIGHT), displayBounds.height);
332
+ if (Math.abs(bounds.height - nextHeight) < 2)
333
+ return;
334
+ win.setSize(WINDOW_WIDTH, nextHeight, false);
335
+ if (win.isVisible()) {
336
+ positionWindowNearTray();
337
+ }
338
+ });
143
339
  function getDashboardPort() {
144
340
  try {
145
341
  if ((0, node_fs_1.existsSync)(PORT_FILE)) {
@@ -159,7 +355,6 @@ async function isDashboardReachable(port) {
159
355
  resolve(res.statusCode !== undefined && res.statusCode < 500);
160
356
  });
161
357
  req.on('error', () => resolve(false));
162
- // 200ms is plenty for localhost; 1000ms caused each poll to block for 1s
163
358
  req.setTimeout(200, () => { req.destroy(); resolve(false); });
164
359
  });
165
360
  }
@@ -173,14 +368,11 @@ async function launchDashboard() {
173
368
  });
174
369
  let failed = false;
175
370
  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
371
  child.on('close', (code) => {
179
372
  if (code !== 0)
180
373
  failed = true;
181
374
  });
182
375
  child.unref();
183
- // Poll up to 5s (25 × 200ms) for the server to become reachable
184
376
  let attempts = 0;
185
377
  const check = async () => {
186
378
  if (failed) {
package/dist/preload.js CHANGED
@@ -5,8 +5,12 @@ electron_1.contextBridge.exposeInMainWorld('widget', {
5
5
  getData: () => electron_1.ipcRenderer.invoke('widget:get-data'),
6
6
  openDashboard: () => electron_1.ipcRenderer.invoke('widget:open-dashboard'),
7
7
  hideWindow: () => electron_1.ipcRenderer.send('widget:hide-window'),
8
+ resizeWindow: (height) => electron_1.ipcRenderer.send('widget:resize-window', height),
8
9
  onDataUpdate: (callback) => {
9
10
  electron_1.ipcRenderer.removeAllListeners('widget:data-update');
10
11
  electron_1.ipcRenderer.on('widget:data-update', (_event, data) => callback(data));
11
12
  },
13
+ getSettings: () => electron_1.ipcRenderer.invoke('widget:get-settings'),
14
+ saveSettings: (settings) => electron_1.ipcRenderer.invoke('widget:save-settings', settings),
15
+ getExchangeRate: () => electron_1.ipcRenderer.invoke('widget:get-exchange-rate'),
12
16
  });
@@ -0,0 +1 @@
1
+ var Fl=Object.defineProperty;var Al=(e,t,l)=>t in e?Fl(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l;var ut=(e,t,l)=>Al(e,typeof t!="symbol"?t+"":t,l);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))n(i);new MutationObserver(i=>{for(const s of i)if(s.type==="childList")for(const a of s.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&n(a)}).observe(document,{childList:!0,subtree:!0});function l(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function n(i){if(i.ep)return;i.ep=!0;const s=l(i);fetch(i.href,s)}})();function fe(){}function Tl(e){return e()}function Rt(){return Object.create(null)}function qe(e){e.forEach(Tl)}function Xe(e){return typeof e=="function"}function Ge(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}function El(e){return Object.keys(e).length===0}function r(e,t){e.appendChild(t)}function B(e,t,l){e.insertBefore(t,l||null)}function A(e){e.parentNode&&e.parentNode.removeChild(e)}function we(e,t){for(let l=0;l<e.length;l+=1)e[l]&&e[l].d(t)}function m(e){return document.createElement(e)}function G(e){return document.createElementNS("http://www.w3.org/2000/svg",e)}function U(e){return document.createTextNode(e)}function q(){return U(" ")}function ve(){return U("")}function ge(e,t,l,n){return e.addEventListener(t,l,n),()=>e.removeEventListener(t,l,n)}function o(e,t,l){l==null?e.removeAttribute(t):e.getAttribute(t)!==l&&e.setAttribute(t,l)}function Bl(e){return Array.from(e.childNodes)}function K(e,t){t=""+t,e.data!==t&&(e.data=t)}function Se(e,t,l,n){l==null?e.style.removeProperty(t):e.style.setProperty(t,l,"")}function X(e,t,l){e.classList.toggle(t,!!l)}function Nl(e,t,{bubbles:l=!1,cancelable:n=!1}={}){return new CustomEvent(e,{detail:t,bubbles:l,cancelable:n})}let Ze;function Qe(e){Ze=e}function yt(){if(!Ze)throw new Error("Function called outside component initialization");return Ze}function Cl(e){yt().$$.on_mount.push(e)}function Pl(e){yt().$$.after_update.push(e)}function Ol(){const e=yt();return(t,l,{cancelable:n=!1}={})=>{const i=e.$$.callbacks[t];if(i){const s=Nl(t,l,{cancelable:n});return i.slice().forEach(a=>{a.call(e,s)}),!s.defaultPrevented}return!0}}const ze=[],_t=[];let Ve=[];const Dt=[],Sl=Promise.resolve();let mt=!1;function Ml(){mt||(mt=!0,Sl.then(Ll))}function Ft(){return Ml(),Sl}function pt(e){Ve.push(e)}const ht=new Set;let We=0;function Ll(){if(We!==0)return;const e=Ze;do{try{for(;We<ze.length;){const t=ze[We];We++,Qe(t),Hl(t.$$)}}catch(t){throw ze.length=0,We=0,t}for(Qe(null),ze.length=0,We=0;_t.length;)_t.pop()();for(let t=0;t<Ve.length;t+=1){const l=Ve[t];ht.has(l)||(ht.add(l),l())}Ve.length=0}while(ze.length);for(;Dt.length;)Dt.pop()();mt=!1,ht.clear(),Qe(e)}function Hl(e){if(e.fragment!==null){e.update(),qe(e.before_update);const t=e.dirty;e.dirty=[-1],e.fragment&&e.fragment.p(e.ctx,t),e.after_update.forEach(pt)}}function Il(e){const t=[],l=[];Ve.forEach(n=>e.indexOf(n)===-1?t.push(n):l.push(n)),l.forEach(n=>n()),Ve=t}const tt=new Set;let Ae;function vt(){Ae={r:0,c:[],p:Ae}}function bt(){Ae.r||qe(Ae.c),Ae=Ae.p}function oe(e,t){e&&e.i&&(tt.delete(e),e.i(t))}function me(e,t,l,n){if(e&&e.o){if(tt.has(e))return;tt.add(e),Ae.c.push(()=>{tt.delete(e),n&&(l&&e.d(1),n())}),e.o(t)}else n&&n()}function le(e){return(e==null?void 0:e.length)!==void 0?e:Array.from(e)}function Ee(e){e&&e.c()}function Me(e,t,l){const{fragment:n,after_update:i}=e.$$;n&&n.m(t,l),pt(()=>{const s=e.$$.on_mount.map(Tl).filter(Xe);e.$$.on_destroy?e.$$.on_destroy.push(...s):qe(s),e.$$.on_mount=[]}),i.forEach(pt)}function Le(e,t){const l=e.$$;l.fragment!==null&&(Il(l.after_update),qe(l.on_destroy),l.fragment&&l.fragment.d(t),l.on_destroy=l.fragment=null,l.ctx=[])}function Ul(e,t){e.$$.dirty[0]===-1&&(ze.push(e),Ml(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<<t%31}function Ke(e,t,l,n,i,s,a=null,h=[-1]){const f=Ze;Qe(e);const u=e.$$={fragment:null,ctx:[],props:s,update:fe,not_equal:i,bound:Rt(),on_mount:[],on_destroy:[],on_disconnect:[],before_update:[],after_update:[],context:new Map(t.context||(f?f.$$.context:[])),callbacks:Rt(),dirty:h,skip_bound:!1,root:t.target||f.$$.root};a&&a(u.root);let c=!1;if(u.ctx=l?l(e,t.props||{},(d,b,...k)=>{const R=k.length?k[0]:b;return u.ctx&&i(u.ctx[d],u.ctx[d]=R)&&(!u.skip_bound&&u.bound[d]&&u.bound[d](R),c&&Ul(e,d)),b}):[],u.update(),c=!0,qe(u.before_update),u.fragment=n?n(u.ctx):!1,t.target){if(t.hydrate){const d=Bl(t.target);u.fragment&&u.fragment.l(d),d.forEach(A)}else u.fragment&&u.fragment.c();t.intro&&oe(e.$$.fragment),Me(e,t.target,t.anchor),Ll()}Qe(f)}class Ye{constructor(){ut(this,"$$");ut(this,"$$set")}$destroy(){Le(this,1),this.$destroy=fe}$on(t,l){if(!Xe(l))return fe;const n=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return n.push(l),()=>{const i=n.indexOf(l);i!==-1&&n.splice(i,1)}}$set(t){this.$$set&&!El(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}const jl="4";typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add(jl);function At(e){let t,l;return{c(){t=m("span"),l=U(e[6]),o(t,"class","status svelte-1c4hcx6")},m(n,i){B(n,t,i),r(t,l)},p(n,i){i&64&&K(l,n[6])},d(n){n&&A(t)}}}function Wl(e){let t,l,n,i,s,a,h,f,u,c,d,b,k,R,I,W,w,z,Y,O,L,E,F,P,T,D,V,J,H,g=e[6]&&At(e);return{c(){t=m("div"),l=m("div"),n=G("svg"),i=G("rect"),s=G("rect"),a=G("rect"),h=G("rect"),f=q(),u=m("span"),u.textContent="AIUsage",c=q(),g&&g.c(),d=q(),b=m("div"),k=m("button"),R=G("svg"),I=G("path"),W=G("path"),w=G("path"),z=G("path"),Y=q(),O=m("button"),L=G("svg"),E=G("circle"),F=G("path"),P=q(),T=m("button"),D=G("svg"),V=G("path"),o(i,"width","64"),o(i,"height","64"),o(i,"rx","14"),o(i,"fill","oklch(0.55 0.12 175)"),o(s,"x","10"),o(s,"y","38"),o(s,"width","12"),o(s,"height","16"),o(s,"rx","3"),o(s,"fill","white"),o(a,"x","26"),o(a,"y","26"),o(a,"width","12"),o(a,"height","28"),o(a,"rx","3"),o(a,"fill","white"),o(h,"x","42"),o(h,"y","14"),o(h,"width","12"),o(h,"height","40"),o(h,"rx","3"),o(h,"fill","white"),o(n,"xmlns","http://www.w3.org/2000/svg"),o(n,"width","14"),o(n,"height","14"),o(n,"viewBox","0 0 64 64"),Se(n,"display","block"),Se(n,"border-radius","3px"),o(u,"class","logo svelte-1c4hcx6"),o(l,"class","brand svelte-1c4hcx6"),o(I,"d","M2.5 8a5.5 5.5 0 0 1 9.36-3.93"),o(W,"d","M13.5 8a5.5 5.5 0 0 1-9.36 3.93"),o(w,"d","M12 1.5v3h-3"),o(z,"d","M4 11.5v3h3"),o(R,"width","13"),o(R,"height","13"),o(R,"viewBox","0 0 16 16"),o(R,"fill","none"),o(R,"stroke","currentColor"),o(R,"stroke-width","1.8"),o(R,"stroke-linecap","round"),o(k,"class","icon-btn svelte-1c4hcx6"),o(k,"title",e[3]),o(k,"aria-label",e[3]),o(E,"cx","12"),o(E,"cy","12"),o(E,"r","3"),o(F,"d","M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"),o(L,"width","13"),o(L,"height","13"),o(L,"viewBox","0 0 24 24"),o(L,"fill","none"),o(L,"stroke","currentColor"),o(L,"stroke-width","2"),o(L,"stroke-linecap","round"),o(L,"stroke-linejoin","round"),o(O,"class","icon-btn svelte-1c4hcx6"),o(O,"title",e[4]),o(O,"aria-label",e[4]),o(V,"d","M2 2l8 8M10 2l-8 8"),o(D,"width","11"),o(D,"height","11"),o(D,"viewBox","0 0 12 12"),o(D,"fill","none"),o(D,"stroke","currentColor"),o(D,"stroke-width","1.8"),o(D,"stroke-linecap","round"),o(T,"class","icon-btn svelte-1c4hcx6"),o(T,"title",e[5]),o(T,"aria-label",e[5]),o(b,"class","actions svelte-1c4hcx6"),o(t,"class","header svelte-1c4hcx6")},m(S,v){B(S,t,v),r(t,l),r(l,n),r(n,i),r(n,s),r(n,a),r(n,h),r(l,f),r(l,u),r(l,c),g&&g.m(l,null),r(t,d),r(t,b),r(b,k),r(k,R),r(R,I),r(R,W),r(R,w),r(R,z),r(b,Y),r(b,O),r(O,L),r(L,E),r(L,F),r(b,P),r(b,T),r(T,D),r(D,V),J||(H=[ge(k,"click",function(){Xe(e[0])&&e[0].apply(this,arguments)}),ge(O,"click",function(){Xe(e[2])&&e[2].apply(this,arguments)}),ge(T,"click",function(){Xe(e[1])&&e[1].apply(this,arguments)})],J=!0)},p(S,[v]){e=S,e[6]?g?g.p(e,v):(g=At(e),g.c(),g.m(l,null)):g&&(g.d(1),g=null),v&8&&o(k,"title",e[3]),v&8&&o(k,"aria-label",e[3]),v&16&&o(O,"title",e[4]),v&16&&o(O,"aria-label",e[4]),v&32&&o(T,"title",e[5]),v&32&&o(T,"aria-label",e[5])},i:fe,o:fe,d(S){S&&A(t),g&&g.d(),J=!1,qe(H)}}}function zl(e,t,l){let{onRefresh:n}=t,{onClose:i}=t,{onToggleSettings:s}=t,{refreshLabel:a}=t,{settingsLabel:h}=t,{closeLabel:f}=t,{statusText:u=""}=t;return e.$$set=c=>{"onRefresh"in c&&l(0,n=c.onRefresh),"onClose"in c&&l(1,i=c.onClose),"onToggleSettings"in c&&l(2,s=c.onToggleSettings),"refreshLabel"in c&&l(3,a=c.refreshLabel),"settingsLabel"in c&&l(4,h=c.settingsLabel),"closeLabel"in c&&l(5,f=c.closeLabel),"statusText"in c&&l(6,u=c.statusText)},[n,i,s,a,h,f,u]}class Vl extends Ye{constructor(t){super(),Ke(this,t,zl,Wl,Ge,{onRefresh:0,onClose:1,onToggleSettings:2,refreshLabel:3,settingsLabel:4,closeLabel:5,statusText:6})}}function Et(e){let t,l;return{c(){t=m("span"),l=U(e[2]),o(t,"class","sub svelte-36ktuv")},m(n,i){B(n,t,i),r(t,l)},p(n,i){i&4&&K(l,n[2])},d(n){n&&A(t)}}}function Gl(e){let t,l,n,i,s,a,h,f,u=e[2]&&Et(e);return{c(){t=m("div"),l=m("span"),n=U(e[0]),i=q(),s=m("div"),a=m("span"),h=U(e[1]),f=q(),u&&u.c(),o(l,"class","label svelte-36ktuv"),o(a,"class","value svelte-36ktuv"),X(a,"accent",e[3]),o(s,"class","right svelte-36ktuv"),o(t,"class","row svelte-36ktuv")},m(c,d){B(c,t,d),r(t,l),r(l,n),r(t,i),r(t,s),r(s,a),r(a,h),r(s,f),u&&u.m(s,null)},p(c,[d]){d&1&&K(n,c[0]),d&2&&K(h,c[1]),d&8&&X(a,"accent",c[3]),c[2]?u?u.p(c,d):(u=Et(c),u.c(),u.m(s,null)):u&&(u.d(1),u=null)},i:fe,o:fe,d(c){c&&A(t),u&&u.d()}}}function Kl(e,t,l){let{label:n}=t,{value:i}=t,{sub:s=""}=t,{accent:a=!1}=t;return e.$$set=h=>{"label"in h&&l(0,n=h.label),"value"in h&&l(1,i=h.value),"sub"in h&&l(2,s=h.sub),"accent"in h&&l(3,a=h.accent)},[n,i,s,a]}class dt extends Ye{constructor(t){super(),Ke(this,t,Kl,Gl,Ge,{label:0,value:1,sub:2,accent:3})}}function Bt(e,t,l){const n=e.slice();return n[7]=t[l],n}function Nt(e,t,l){const n=e.slice();return n[7]=t[l],n}function Yl(e){let t;return{c(){t=m("div"),t.textContent="No token data",o(t,"class","empty svelte-4y3y1j")},m(l,n){B(l,t,n)},p:fe,d(l){l&&A(t)}}}function Xl(e){let t,l,n,i=le(e[0]),s=[];for(let f=0;f<i.length;f+=1)s[f]=Pt(Nt(e,i,f));let a=le(e[0]),h=[];for(let f=0;f<a.length;f+=1)h[f]=Ot(Bt(e,a,f));return{c(){t=m("div");for(let f=0;f<s.length;f+=1)s[f].c();l=q(),n=m("div");for(let f=0;f<h.length;f+=1)h[f].c();o(t,"class","bar svelte-4y3y1j"),o(n,"class","legend svelte-4y3y1j")},m(f,u){B(f,t,u);for(let c=0;c<s.length;c+=1)s[c]&&s[c].m(t,null);B(f,l,u),B(f,n,u);for(let c=0;c<h.length;c+=1)h[c]&&h[c].m(n,null)},p(f,u){if(u&3){i=le(f[0]);let c;for(c=0;c<i.length;c+=1){const d=Nt(f,i,c);s[c]?s[c].p(d,u):(s[c]=Pt(d),s[c].c(),s[c].m(t,null))}for(;c<s.length;c+=1)s[c].d(1);s.length=i.length}if(u&1){a=le(f[0]);let c;for(c=0;c<a.length;c+=1){const d=Bt(f,a,c);h[c]?h[c].p(d,u):(h[c]=Ot(d),h[c].c(),h[c].m(n,null))}for(;c<h.length;c+=1)h[c].d(1);h.length=a.length}},d(f){f&&(A(t),A(l),A(n)),we(s,f),we(h,f)}}}function Pt(e){let t,l;return{c(){t=m("div"),o(t,"class","segment svelte-4y3y1j"),Se(t,"width",e[7].value/e[1]*100+"%"),Se(t,"background",e[7].color),o(t,"title",l=e[7].label+": "+lt(e[7].value))},m(n,i){B(n,t,i)},p(n,i){i&3&&Se(t,"width",n[7].value/n[1]*100+"%"),i&1&&Se(t,"background",n[7].color),i&1&&l!==(l=n[7].label+": "+lt(n[7].value))&&o(t,"title",l)},d(n){n&&A(t)}}}function Ot(e){let t,l,n,i,s=e[7].label+"",a,h,f,u=lt(e[7].value)+"",c,d;return{c(){t=m("div"),l=m("span"),n=q(),i=m("span"),a=U(s),h=q(),f=m("span"),c=U(u),d=q(),o(l,"class","dot svelte-4y3y1j"),Se(l,"background",e[7].color),o(i,"class","legend-label svelte-4y3y1j"),o(f,"class","legend-value svelte-4y3y1j"),o(t,"class","legend-item svelte-4y3y1j")},m(b,k){B(b,t,k),r(t,l),r(t,n),r(t,i),r(i,a),r(t,h),r(t,f),r(f,c),r(t,d)},p(b,k){k&1&&Se(l,"background",b[7].color),k&1&&s!==(s=b[7].label+"")&&K(a,s),k&1&&u!==(u=lt(b[7].value)+"")&&K(c,u)},d(b){b&&A(t)}}}function Ql(e){let t;function l(s,a){return s[1]>0?Xl:Yl}let n=l(e),i=n(e);return{c(){t=m("div"),i.c(),o(t,"class","breakdown svelte-4y3y1j")},m(s,a){B(s,t,a),i.m(t,null)},p(s,[a]){n===(n=l(s))&&i?i.p(s,a):(i.d(1),i=n(s),i&&(i.c(),i.m(t,null)))},i:fe,o:fe,d(s){s&&A(t),i.d()}}}function lt(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:String(e)}function Zl(e,t,l){let n,i,{input:s=0}=t,{output:a=0}=t,{cacheRead:h=0}=t,{cacheWrite:f=0}=t,{thinking:u=0}=t;return e.$$set=c=>{"input"in c&&l(2,s=c.input),"output"in c&&l(3,a=c.output),"cacheRead"in c&&l(4,h=c.cacheRead),"cacheWrite"in c&&l(5,f=c.cacheWrite),"thinking"in c&&l(6,u=c.thinking)},e.$$.update=()=>{e.$$.dirty&124&&l(1,n=s+a+h+f+u),e.$$.dirty&124&&l(0,i=[{label:"Input",value:s,color:"var(--chart-input)"},{label:"Output",value:a,color:"var(--chart-output)"},{label:"Cache R",value:h,color:"var(--chart-cache-read)"},{label:"Cache W",value:f,color:"var(--chart-cache-write)"},{label:"Think",value:u,color:"var(--chart-thinking)"}].filter(c=>c.value>0))},[i,n,s,a,h,f,u]}class Jl extends Ye{constructor(t){super(),Ke(this,t,Zl,Ql,Ge,{input:2,output:3,cacheRead:4,cacheWrite:5,thinking:6})}}const Ht=[{code:"USD",label:"USD"},{code:"CNY",label:"CNY"}];function ql(e,t,l){return t==="USD"?e:l!=null&&l.rate&&Number.isFinite(l.rate)?e*l.rate:null}function kt(e,t,l,n,i=!1){const s=ql(e,t,n);return s===null?"--":Rl(s,t,l,i)}function Rl(e,t,l,n=!1){const i=n&&Math.abs(e)>=1e3?"compact":"standard",s=e>=100||i==="compact"?0:e>=1?2:e>0?3:0;return new Intl.NumberFormat(l,{style:"currency",currency:t,currencyDisplay:"narrowSymbol",notation:i,minimumFractionDigits:s,maximumFractionDigits:s}).format(e)}function It(e,t,l){const n=e.slice();return n[33]=t[l],n}function gt(e){const t=e.slice(),l=t[0][t[6]];t[36]=l;const n=t[21]();t[37]=n;const i=t[23](t[6]);t[38]=i;const s=t[22](t[6]);return t[39]=s,t}function Ut(e,t,l){const n=e.slice();return n[40]=t[l],n}function jt(e,t,l){const n=e.slice();return n[40]=t[l],n[44]=l,n}function Wt(e){let t,l,n,i,s,a=nl(e[40])+"",h,f;return{c(){t=G("line"),s=G("text"),h=U(a),o(t,"x1",pe),o(t,"y1",l=e[15](e[40])),o(t,"x2",n=pe+e[9]),o(t,"y2",i=e[15](e[40])),o(t,"stroke","var(--border)"),o(t,"stroke-width","0.5"),o(t,"stroke-dasharray",e[44]===0?"none":"2,2"),o(s,"x",pe-7),o(s,"y",f=e[15](e[40])+3),o(s,"text-anchor","end"),o(s,"class","y-label svelte-15p89al")},m(u,c){B(u,t,c),B(u,s,c),r(s,h)},p(u,c){c[0]&2048&&l!==(l=u[15](u[40]))&&o(t,"y1",l),c[0]&512&&n!==(n=pe+u[9])&&o(t,"x2",n),c[0]&2048&&i!==(i=u[15](u[40]))&&o(t,"y2",i),c[0]&2048&&a!==(a=nl(u[40])+"")&&K(h,a),c[0]&2048&&f!==(f=u[15](u[40])+3)&&o(s,"y",f)},d(u){u&&(A(t),A(s))}}}function zt(e){let t,l=le(e[10]),n=[];for(let i=0;i<l.length;i+=1)n[i]=Vt(Ut(e,l,i));return{c(){for(let i=0;i<n.length;i+=1)n[i].c();t=ve()},m(i,s){for(let a=0;a<n.length;a+=1)n[a]&&n[a].m(i,s);B(i,t,s)},p(i,s){if(s[0]&198144){l=le(i[10]);let a;for(a=0;a<l.length;a+=1){const h=Ut(i,l,a);n[a]?n[a].p(h,s):(n[a]=Vt(h),n[a].c(),n[a].m(t.parentNode,t))}for(;a<n.length;a+=1)n[a].d(1);n.length=l.length}},d(i){i&&A(t),we(n,i)}}}function Vt(e){let t,l=e[17](e[40])+"",n,i,s;return{c(){t=G("text"),n=U(l),o(t,"x",i=pe+e[9]+7),o(t,"y",s=e[16](e[40])+3),o(t,"text-anchor","start"),o(t,"class","y-label y-label-cost svelte-15p89al")},m(a,h){B(a,t,h),r(t,n)},p(a,h){h[0]&1024&&l!==(l=a[17](a[40])+"")&&K(n,l),h[0]&512&&i!==(i=pe+a[9]+7)&&o(t,"x",i),h[0]&1024&&s!==(s=a[16](a[40])+3)&&o(t,"y",s)},d(a){a&&A(t)}}}function Gt(e){let t;return{c(){t=G("path"),o(t,"d",e[12]),o(t,"fill","url(#areaGrad)"),o(t,"clip-path","url(#chartClip)"),o(t,"class","area svelte-15p89al")},m(l,n){B(l,t,n)},p(l,n){n[0]&4096&&o(t,"d",l[12])},d(l){l&&A(t)}}}function Kt(e){let t;return{c(){t=G("path"),o(t,"d",e[13]),o(t,"fill","none"),o(t,"stroke","var(--chart-cache-read)"),o(t,"stroke-width","1.3"),o(t,"stroke-linecap","round"),o(t,"stroke-linejoin","round"),o(t,"clip-path","url(#chartClip)"),o(t,"class","line cost-line svelte-15p89al")},m(l,n){B(l,t,n)},p(l,n){n[0]&8192&&o(t,"d",l[13])},d(l){l&&A(t)}}}function Yt(e){let t;return{c(){t=G("path"),o(t,"d",e[4]),o(t,"fill","none"),o(t,"stroke","var(--accent)"),o(t,"stroke-width","1.8"),o(t,"stroke-linecap","round"),o(t,"stroke-linejoin","round"),o(t,"clip-path","url(#chartClip)"),o(t,"class","line svelte-15p89al")},m(l,n){B(l,t,n)},p(l,n){n[0]&16&&o(t,"d",l[4])},d(l){l&&A(t)}}}function Xt(e){let t,l,n,i;return{c(){t=G("line"),o(t,"x1",l=e[14](e[6])),o(t,"y1",ke),o(t,"x2",n=e[14](e[6])),o(t,"y2",i=ke+e[2]),o(t,"stroke","var(--text-muted)"),o(t,"stroke-width","0.5"),o(t,"stroke-dasharray","2,2")},m(s,a){B(s,t,a)},p(s,a){a[0]&64&&l!==(l=s[14](s[6]))&&o(t,"x1",l),a[0]&64&&n!==(n=s[14](s[6]))&&o(t,"x2",n),a[0]&4&&i!==(i=ke+s[2])&&o(t,"y2",i)},d(s){s&&A(t)}}}function Qt(e){let t,l,n,i,s=e[1]&&Zt(e);return{c(){t=G("circle"),s&&s.c(),i=ve(),o(t,"cx",l=e[14](e[6])),o(t,"cy",n=e[15](e[3][e[6]])),o(t,"r","4"),o(t,"fill","var(--bg)"),o(t,"stroke","var(--accent)"),o(t,"stroke-width","2")},m(a,h){B(a,t,h),s&&s.m(a,h),B(a,i,h)},p(a,h){h[0]&64&&l!==(l=a[14](a[6]))&&o(t,"cx",l),h[0]&72&&n!==(n=a[15](a[3][a[6]]))&&o(t,"cy",n),a[1]?s?s.p(a,h):(s=Zt(a),s.c(),s.m(i.parentNode,i)):s&&(s.d(1),s=null)},d(a){a&&(A(t),A(i)),s&&s.d(a)}}}function Zt(e){let t,l,n;return{c(){t=G("circle"),o(t,"cx",l=e[14](e[6])),o(t,"cy",n=e[16](e[5][e[6]])),o(t,"r","3"),o(t,"fill","var(--bg)"),o(t,"stroke","var(--chart-cache-read)"),o(t,"stroke-width","1.5")},m(i,s){B(i,t,s)},p(i,s){s[0]&64&&l!==(l=i[14](i[6]))&&o(t,"cx",l),s[0]&96&&n!==(n=i[16](i[5][i[6]]))&&o(t,"cy",n)},d(i){i&&A(t)}}}function Jt(e){let t,l,n,i=e[18](e[36].date)+"",s,a,h=ll(e[36].tokens)+"",f,u,c,d=e[1]&&e[36].cost>0&&$t(e);return{c(){t=G("foreignObject"),l=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n=document.createElementNS("http://www.w3.org/1999/xhtml","span"),s=U(i),a=document.createElementNS("http://www.w3.org/1999/xhtml","span"),f=U(h),d&&d.c(),o(n,"class","tooltip-date svelte-15p89al"),o(a,"class","tooltip-metric token-dot svelte-15p89al"),o(l,"class","tooltip svelte-15p89al"),o(l,"xmlns","http://www.w3.org/1999/xhtml"),X(l,"pointer-left",e[24](e[6])==="left"),X(l,"pointer-right",e[24](e[6])==="right"),o(t,"x",u=e[38]),o(t,"y",c=e[39]),o(t,"width",e[37]),o(t,"height","28"),o(t,"class","tooltip-fo svelte-15p89al")},m(b,k){B(b,t,k),r(t,l),r(l,n),r(n,s),r(l,a),r(a,f),d&&d.m(l,null)},p(b,k){k[0]&65&&i!==(i=b[18](b[36].date)+"")&&K(s,i),k[0]&65&&h!==(h=ll(b[36].tokens)+"")&&K(f,h),b[1]&&b[36].cost>0?d?d.p(b,k):(d=$t(b),d.c(),d.m(l,null)):d&&(d.d(1),d=null),k[0]&16777280&&X(l,"pointer-left",b[24](b[6])==="left"),k[0]&16777280&&X(l,"pointer-right",b[24](b[6])==="right"),k[0]&64&&u!==(u=b[38])&&o(t,"x",u),k[0]&64&&c!==(c=b[39])&&o(t,"y",c)},d(b){b&&A(t),d&&d.d()}}}function $t(e){let t,l=e[20](e[36].cost)+"",n;return{c(){t=document.createElementNS("http://www.w3.org/1999/xhtml","span"),n=U(l),o(t,"class","tooltip-metric cost-dot svelte-15p89al")},m(i,s){B(i,t,s),r(t,n)},p(i,s){s[0]&65&&l!==(l=i[20](i[36].cost)+"")&&K(n,l)},d(i){i&&A(t)}}}function xt(e){let t,l=e[19](e[0][e[33]].date)+"",n,i;return{c(){t=G("text"),n=U(l),o(t,"x",i=e[14](e[33])),o(t,"y",nt-2),o(t,"text-anchor","middle"),o(t,"class","tick-label svelte-15p89al")},m(s,a){B(s,t,a),r(t,n)},p(s,a){a[0]&257&&l!==(l=s[19](s[0][s[33]].date)+"")&&K(n,l),a[0]&256&&i!==(i=s[14](s[33]))&&o(t,"x",i)},d(s){s&&A(t)}}}function el(e){let t,l=e[0][e[33]]&&xt(e);return{c(){l&&l.c(),t=ve()},m(n,i){l&&l.m(n,i),B(n,t,i)},p(n,i){n[0][n[33]]?l?l.p(n,i):(l=xt(n),l.c(),l.m(t.parentNode,t)):l&&(l.d(1),l=null)},d(n){n&&A(t),l&&l.d(n)}}}function $l(e){let t,l,n,i,s,a,h,f,u,c,d,b,k,R,I,W,w,z,Y=le(e[11]),O=[];for(let g=0;g<Y.length;g+=1)O[g]=Wt(jt(e,Y,g));let L=e[1]&&zt(e),E=e[12]&&Gt(e),F=e[13]&&Kt(e),P=e[4]&&Yt(e),T=e[6]!==null&&Xt(e),D=e[6]!==null&&Qt(e),V=e[6]!==null&&e[0][e[6]]&&Jt(gt(e)),J=le(e[8]),H=[];for(let g=0;g<J.length;g+=1)H[g]=el(It(e,J,g));return{c(){t=m("div"),l=G("svg"),n=G("defs"),i=G("linearGradient"),s=G("stop"),a=G("stop"),h=G("clipPath"),f=G("rect");for(let g=0;g<O.length;g+=1)O[g].c();u=ve(),L&&L.c(),c=ve(),E&&E.c(),d=ve(),F&&F.c(),b=ve(),P&&P.c(),k=ve(),T&&T.c(),R=ve(),D&&D.c(),I=ve(),V&&V.c(),W=ve();for(let g=0;g<H.length;g+=1)H[g].c();o(s,"offset","0%"),o(s,"stop-color","var(--accent)"),o(s,"stop-opacity","0.2"),o(a,"offset","100%"),o(a,"stop-color","var(--accent)"),o(a,"stop-opacity","0.02"),o(i,"id","areaGrad"),o(i,"x1","0"),o(i,"y1","0"),o(i,"x2","0"),o(i,"y2","1"),o(f,"x",pe),o(f,"y",ke),o(f,"width",e[9]),o(f,"height",e[2]),o(h,"id","chartClip"),o(l,"viewBox","0 0 "+wt+" "+nt),o(l,"width","100%"),o(l,"height",nt),o(l,"class","chart-svg svelte-15p89al"),o(l,"role","img"),o(l,"aria-label","Token usage trend chart"),X(l,"mounted",e[7]),o(t,"class","chart-wrapper svelte-15p89al")},m(g,S){B(g,t,S),r(t,l),r(l,n),r(n,i),r(i,s),r(i,a),r(n,h),r(h,f);for(let v=0;v<O.length;v+=1)O[v]&&O[v].m(l,null);r(l,u),L&&L.m(l,null),r(l,c),E&&E.m(l,null),r(l,d),F&&F.m(l,null),r(l,b),P&&P.m(l,null),r(l,k),T&&T.m(l,null),r(l,R),D&&D.m(l,null),r(l,I),V&&V.m(l,null),r(l,W);for(let v=0;v<H.length;v+=1)H[v]&&H[v].m(l,null);w||(z=[ge(l,"mousemove",e[25]),ge(l,"mouseleave",e[26])],w=!0)},p(g,S){if(S[0]&512&&o(f,"width",g[9]),S[0]&4&&o(f,"height",g[2]),S[0]&35328){Y=le(g[11]);let v;for(v=0;v<Y.length;v+=1){const j=jt(g,Y,v);O[v]?O[v].p(j,S):(O[v]=Wt(j),O[v].c(),O[v].m(l,u))}for(;v<O.length;v+=1)O[v].d(1);O.length=Y.length}if(g[1]?L?L.p(g,S):(L=zt(g),L.c(),L.m(l,c)):L&&(L.d(1),L=null),g[12]?E?E.p(g,S):(E=Gt(g),E.c(),E.m(l,d)):E&&(E.d(1),E=null),g[13]?F?F.p(g,S):(F=Kt(g),F.c(),F.m(l,b)):F&&(F.d(1),F=null),g[4]?P?P.p(g,S):(P=Yt(g),P.c(),P.m(l,k)):P&&(P.d(1),P=null),g[6]!==null?T?T.p(g,S):(T=Xt(g),T.c(),T.m(l,R)):T&&(T.d(1),T=null),g[6]!==null?D?D.p(g,S):(D=Qt(g),D.c(),D.m(l,I)):D&&(D.d(1),D=null),g[6]!==null&&g[0][g[6]]?V?V.p(gt(g),S):(V=Jt(gt(g)),V.c(),V.m(l,W)):V&&(V.d(1),V=null),S[0]&540929){J=le(g[8]);let v;for(v=0;v<J.length;v+=1){const j=It(g,J,v);H[v]?H[v].p(j,S):(H[v]=el(j),H[v].c(),H[v].m(l,null))}for(;v<H.length;v+=1)H[v].d(1);H.length=J.length}S[0]&128&&X(l,"mounted",g[7])},i:fe,o:fe,d(g){g&&A(t),we(O,g),L&&L.d(),E&&E.d(),F&&F.d(),P&&P.d(),T&&T.d(),D&&D.d(),V&&V.d(),we(H,g),w=!1,qe(z)}}}const wt=352,nt=126,ke=14,xl=18,pe=34,en=36;function tl(e){if(e<=0)return 1;const t=Math.pow(10,Math.floor(Math.log10(e))),l=e/t;return(l<=1?1:l<=2?2:l<=5?5:10)*t}function ll(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:String(Math.round(e))}function nl(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e5?`${Math.round(e/1e3)}K`:e>=1e3?`${(e/1e3).toFixed(1)}K`:String(Math.round(e))}function ol(e){return new Date(e+"T00:00:00")}function tn(e,t,l){let n,i,s,a,h,f,u,c,d,b,k,R,{data:I=[]}=t,{showCost:W=!1}=t,{locale:w="en"}=t,{currency:z="USD"}=t,{exchangeRate:Y=null}=t,O=null,L=!1;Cl(()=>{requestAnimationFrame(()=>{l(7,L=!0)})});function E(C){return pe+C/Math.max(I.length-1,1)*h}function F(C){return ke+f-C/s*f}function P(C){return ke+f-C/a*f}function T(C,ne){if(C.length<2)return"";const y=C.length,N=C.map((M,te)=>E(te)),ee=C.map(M=>ne(M));if(y===2)return`M${N[0]},${ee[0]}L${N[1]},${ee[1]}`;const $=[],x=[];for(let M=0;M<y-1;M++)$.push((ee[M+1]-ee[M])/(N[M+1]-N[M]));x.push($[0]);for(let M=1;M<y-1;M++)$[M-1]*$[M]<=0?x.push(0):x.push(($[M-1]+$[M])/2);x.push($[y-2]);for(let M=0;M<y-1;M++)if(Math.abs($[M])<1e-12)x[M]=0,x[M+1]=0;else{const te=x[M]/$[M],_e=x[M+1]/$[M],ye=te*te+_e*_e;if(ye>9){const Te=3/Math.sqrt(ye);x[M]=Te*te*$[M],x[M+1]=Te*_e*$[M]}}let ue=`M${N[0].toFixed(1)},${ee[0].toFixed(1)}`;for(let M=0;M<y-1;M++){const te=(N[M+1]-N[M])/3,_e=N[M]+te,ye=ee[M]+x[M]*te,Te=N[M+1]-te,Je=ee[M+1]-x[M+1]*te;ue+=`C${_e.toFixed(1)},${ye.toFixed(1)},${Te.toFixed(1)},${Je.toFixed(1)},${N[M+1].toFixed(1)},${ee[M+1].toFixed(1)}`}return ue}function D(C){return Rl(C,z,w,!0)}function V(C){const ne=ol(C);return new Intl.DateTimeFormat(w,{month:w==="zh"?"numeric":"short",day:"numeric"}).format(ne)}function J(C){const ne=ol(C);return new Intl.DateTimeFormat(w,{month:"numeric",day:"numeric"}).format(ne)}function H(C){return kt(C,z,w,Y)}function g(){return W?156:116}function S(C){const ne=F(n[C]??0)-30;return Math.min(Math.max(ne,4),ke+f-28)}function v(C){const y=g(),N=E(C),ee=j(C)==="right"?N-y-8:N+8;return Math.min(Math.max(ee,pe),pe+h-y)}function j(C){return E(C)>pe+h/2?"right":"left"}function be(C){const y=C.currentTarget.getBoundingClientRect(),N=(C.clientX-y.left)/y.width*wt;let ee=1/0,$=0;for(let x=0;x<I.length;x++){const ue=Math.abs(E(x)-N);ue<ee&&(ee=ue,$=x)}l(6,O=$)}function Q(){l(6,O=null)}return e.$$set=C=>{"data"in C&&l(0,I=C.data),"showCost"in C&&l(1,W=C.showCost),"locale"in C&&l(27,w=C.locale),"currency"in C&&l(28,z=C.currency),"exchangeRate"in C&&l(29,Y=C.exchangeRate)},e.$$.update=()=>{e.$$.dirty[0]&1&&l(3,n=I.map(C=>C.tokens)),e.$$.dirty[0]&805306369&&l(5,i=I.map(C=>ql(C.cost,z,Y)??0)),e.$$.dirty[0]&8&&l(31,s=tl(Math.max(...n,1))),e.$$.dirty[0]&32&&l(30,a=tl(Math.max(...i,1))),e.$$.dirty[0]&8&&l(4,u=T(n,F)),e.$$.dirty[0]&34&&l(13,c=W?T(i,P):""),e.$$.dirty[0]&28&&l(12,d=u?`${u}L${E(n.length-1).toFixed(1)},${(ke+f).toFixed(1)}L${E(0).toFixed(1)},${(ke+f).toFixed(1)}Z`:""),e.$$.dirty[1]&1&&l(11,b=[0,s/2,s]),e.$$.dirty[0]&1073741824&&l(10,k=[0,a/2,a]),e.$$.dirty[0]&1&&l(8,R=I.length<=5?I.map((C,ne)=>ne):[0,Math.floor(I.length*.25),Math.floor(I.length*.5),Math.floor(I.length*.75),I.length-1])},l(9,h=wt-pe-en),l(2,f=nt-ke-xl),[I,W,f,n,u,i,O,L,R,h,k,b,d,c,E,F,P,D,V,J,H,g,S,v,j,be,Q,w,z,Y,a,s]}class ln extends Ye{constructor(t){super(),Ke(this,t,tn,$l,Ge,{data:0,showCost:1,locale:27,currency:28,exchangeRate:29},null,[-1,-1])}}const nn={today:"Today",lastNDays:e=>`Last ${e} days`,tokenBreakdown:"Token breakdown",tokenBreakdownToday:"Token breakdown (Today)",trend:"Trend",topModel:"Top Model",topTool:"Top Tool",sessions:"Sessions",settings:"Settings",theme:"Theme",timeRange:"Time range",refreshInterval:"Refresh interval",display:"Display",currency:"Currency",exchangeRate:"Exchange rate",exchangeRateUpdated:e=>`Updated ${e}`,exchangeRateUnavailable:"Exchange rate unavailable",language:"Language",showCost:"Show cost",tokenBreakdownToggle:"Token breakdown",activityChart:"Activity chart",syncedAt:e=>`Synced ${e}`,themeSystem:"System",themeLight:"Light",themeDark:"Dark",showPanel:"Show Panel",openDashboard:"Open Dashboard",refresh:"Refresh",quit:"Quit",close:"Close"},on={today:"今日",lastNDays:e=>`近 ${e} 天`,tokenBreakdown:"Token 分布",tokenBreakdownToday:"Token 分布 (今日)",trend:"趋势",topModel:"常用模型",topTool:"常用工具",sessions:"会话数",settings:"设置",theme:"主题",timeRange:"时间范围",refreshInterval:"刷新间隔",display:"显示",currency:"币种",exchangeRate:"汇率",exchangeRateUpdated:e=>`更新于 ${e}`,exchangeRateUnavailable:"汇率不可用",language:"语言",showCost:"显示费用",tokenBreakdownToggle:"Token 分布",activityChart:"活动图表",syncedAt:e=>`同步于 ${e}`,themeSystem:"跟随系统",themeLight:"浅色",themeDark:"深色",showPanel:"显示面板",openDashboard:"打开仪表盘",refresh:"刷新",quit:"退出",close:"关闭"},il={en:nn,zh:on};function Dl(e){return il[e]??il.en}function sl(e,t,l){const n=e.slice();return n[22]=t[l],n}function al(e,t,l){const n=e.slice();return n[25]=t[l],n}function rl(e,t,l){const n=e.slice();return n[28]=t[l],n}function cl(e,t,l){const n=e.slice();return n[31]=t[l],n}function fl(e,t,l){const n=e.slice();return n[34]=t[l],n}function ul(e){let t,l,n;function i(){return e[14](e[34])}return{c(){t=m("button"),t.textContent=`${e[34].label} `,o(t,"class","option-btn svelte-198qnem"),X(t,"active",e[1].locale===e[34].value)},m(s,a){B(s,t,a),l||(n=ge(t,"click",i),l=!0)},p(s,a){e=s,a[0]&2050&&X(t,"active",e[1].locale===e[34].value)},d(s){s&&A(t),l=!1,n()}}}function hl(e){let t,l,n;function i(){return e[15](e[31])}return{c(){t=m("button"),t.textContent=`${e[31].label} `,o(t,"class","option-btn svelte-198qnem"),X(t,"active",e[1].currency===e[31].code)},m(s,a){B(s,t,a),l||(n=ge(t,"click",i),l=!0)},p(s,a){e=s,a[0]&2&&X(t,"active",e[1].currency===e[31].code)},d(s){s&&A(t),l=!1,n()}}}function dl(e){var b;let t,l,n=e[2].exchangeRate+"",i,s,a,h,f=e[7](),u,c=f&&sn(e),d=((b=e[0])==null?void 0:b.error)&&gl(e);return{c(){var k;t=m("div"),l=m("span"),i=U(n),s=q(),a=m("strong"),a.textContent=`${e[6]()}`,h=q(),c&&c.c(),u=q(),d&&d.c(),o(a,"class","svelte-198qnem"),o(t,"class","rate-info svelte-198qnem"),X(t,"error",!((k=e[0])!=null&&k.rate))},m(k,R){B(k,t,R),r(t,l),r(l,i),r(t,s),r(t,a),r(t,h),c&&c.m(t,null),r(t,u),d&&d.m(t,null)},p(k,R){var I,W;R[0]&4&&n!==(n=k[2].exchangeRate+"")&&K(i,n),f&&c.p(k,R),(I=k[0])!=null&&I.error?d?d.p(k,R):(d=gl(k),d.c(),d.m(t,null)):d&&(d.d(1),d=null),R[0]&1&&X(t,"error",!((W=k[0])!=null&&W.rate))},d(k){k&&A(t),c&&c.d(),d&&d.d()}}}function sn(e){let t;return{c(){t=m("small"),t.textContent=`${e[7]()}`,o(t,"class","svelte-198qnem")},m(l,n){B(l,t,n)},p:fe,d(l){l&&A(t)}}}function gl(e){let t,l=e[0].error+"",n;return{c(){t=m("small"),n=U(l),o(t,"class","svelte-198qnem")},m(i,s){B(i,t,s),r(t,n)},p(i,s){s[0]&1&&l!==(l=i[0].error+"")&&K(n,l)},d(i){i&&A(t)}}}function _l(e){let t,l=e[28].label+"",n,i,s,a;function h(){return e[16](e[28])}return{c(){t=m("button"),n=U(l),i=q(),o(t,"class","option-btn svelte-198qnem"),X(t,"active",e[1].theme===e[28].value)},m(f,u){B(f,t,u),r(t,n),r(t,i),s||(a=ge(t,"click",h),s=!0)},p(f,u){e=f,u[0]&8&&l!==(l=e[28].label+"")&&K(n,l),u[0]&10&&X(t,"active",e[1].theme===e[28].value)},d(f){f&&A(t),s=!1,a()}}}function ml(e){let t,l,n;function i(){return e[17](e[25])}return{c(){t=m("button"),t.textContent=`${e[25].label} `,o(t,"class","option-btn svelte-198qnem"),X(t,"active",e[1].rangeDays===e[25].value)},m(s,a){B(s,t,a),l||(n=ge(t,"click",i),l=!0)},p(s,a){e=s,a[0]&1026&&X(t,"active",e[1].rangeDays===e[25].value)},d(s){s&&A(t),l=!1,n()}}}function pl(e){let t,l,n;function i(){return e[18](e[22])}return{c(){t=m("button"),t.textContent=`${e[22].label} `,o(t,"class","option-btn svelte-198qnem"),X(t,"active",e[1].refreshIntervalSec===e[22].value)},m(s,a){B(s,t,a),l||(n=ge(t,"click",i),l=!0)},p(s,a){e=s,a[0]&514&&X(t,"active",e[1].refreshIntervalSec===e[22].value)},d(s){s&&A(t),l=!1,n()}}}function an(e){let t,l,n,i=e[2].settings+"",s,a,h,f,u,c,d=e[2].language+"",b,k,R,I,W,w,z=e[2].currency+"",Y,O,L,E,F,P,T,D=e[2].theme+"",V,J,H,g,S,v,j=e[2].timeRange+"",be,Q,C,ne,y,N,ee=e[2].refreshInterval+"",$,x,ue,M,te,_e,ye=e[2].display+"",Te,Je,Ce,Be,ot,$e=e[2].showCost+"",it,Tt,Re,Ct,Ne,st,xe=e[2].tokenBreakdownToggle+"",at,St,De,Mt,Pe,rt,et=e[2].activityChart+"",ct,Lt,Fe,ft,qt,Oe=le(e[11]),ie=[];for(let p=0;p<Oe.length;p+=1)ie[p]=ul(fl(e,Oe,p));let He=le(Ht),se=[];for(let p=0;p<He.length;p+=1)se[p]=hl(cl(e,He,p));let he=e[1].currency==="CNY"&&dl(e),Ie=le(e[3]),ae=[];for(let p=0;p<Ie.length;p+=1)ae[p]=_l(rl(e,Ie,p));let Ue=le(e[10]),re=[];for(let p=0;p<Ue.length;p+=1)re[p]=ml(al(e,Ue,p));let je=le(e[9]),ce=[];for(let p=0;p<je.length;p+=1)ce[p]=pl(sl(e,je,p));return{c(){t=m("div"),l=m("div"),n=m("span"),s=U(i),a=q(),h=m("button"),h.innerHTML='<svg width="11" height="11" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><path d="M2 2l8 8M10 2l-8 8"></path></svg>',f=q(),u=m("div"),c=m("div"),b=U(d),k=q(),R=m("div");for(let p=0;p<ie.length;p+=1)ie[p].c();I=q(),W=m("div"),w=m("div"),Y=U(z),O=q(),L=m("div");for(let p=0;p<se.length;p+=1)se[p].c();E=q(),he&&he.c(),F=q(),P=m("div"),T=m("div"),V=U(D),J=q(),H=m("div");for(let p=0;p<ae.length;p+=1)ae[p].c();g=q(),S=m("div"),v=m("div"),be=U(j),Q=q(),C=m("div");for(let p=0;p<re.length;p+=1)re[p].c();ne=q(),y=m("div"),N=m("div"),$=U(ee),x=q(),ue=m("div");for(let p=0;p<ce.length;p+=1)ce[p].c();M=q(),te=m("div"),_e=m("div"),Te=U(ye),Je=q(),Ce=m("div"),Be=m("label"),ot=m("span"),it=U($e),Tt=q(),Re=m("button"),Re.innerHTML='<span class="toggle-thumb svelte-198qnem"></span>',Ct=q(),Ne=m("label"),st=m("span"),at=U(xe),St=q(),De=m("button"),De.innerHTML='<span class="toggle-thumb svelte-198qnem"></span>',Mt=q(),Pe=m("label"),rt=m("span"),ct=U(et),Lt=q(),Fe=m("button"),Fe.innerHTML='<span class="toggle-thumb svelte-198qnem"></span>',o(n,"class","settings-title svelte-198qnem"),o(h,"class","back-btn svelte-198qnem"),o(l,"class","settings-header svelte-198qnem"),o(c,"class","section-label svelte-198qnem"),o(R,"class","button-group svelte-198qnem"),o(u,"class","section svelte-198qnem"),o(w,"class","section-label svelte-198qnem"),o(L,"class","button-group svelte-198qnem"),o(W,"class","section svelte-198qnem"),o(T,"class","section-label svelte-198qnem"),o(H,"class","button-group svelte-198qnem"),o(P,"class","section svelte-198qnem"),o(v,"class","section-label svelte-198qnem"),o(C,"class","button-group svelte-198qnem"),o(S,"class","section svelte-198qnem"),o(N,"class","section-label svelte-198qnem"),o(ue,"class","button-group svelte-198qnem"),o(y,"class","section svelte-198qnem"),o(_e,"class","section-label svelte-198qnem"),o(Re,"class","toggle svelte-198qnem"),X(Re,"on",e[1].showCost),o(Be,"class","toggle-row svelte-198qnem"),o(De,"class","toggle svelte-198qnem"),X(De,"on",e[1].showTokenBreakdown),o(Ne,"class","toggle-row svelte-198qnem"),o(Fe,"class","toggle svelte-198qnem"),X(Fe,"on",e[1].showHeatmap),o(Pe,"class","toggle-row svelte-198qnem"),o(Ce,"class","toggles svelte-198qnem"),o(te,"class","section svelte-198qnem"),o(t,"class","settings svelte-198qnem")},m(p,Z){B(p,t,Z),r(t,l),r(l,n),r(n,s),r(l,a),r(l,h),r(t,f),r(t,u),r(u,c),r(c,b),r(u,k),r(u,R);for(let _=0;_<ie.length;_+=1)ie[_]&&ie[_].m(R,null);r(t,I),r(t,W),r(W,w),r(w,Y),r(W,O),r(W,L);for(let _=0;_<se.length;_+=1)se[_]&&se[_].m(L,null);r(W,E),he&&he.m(W,null),r(t,F),r(t,P),r(P,T),r(T,V),r(P,J),r(P,H);for(let _=0;_<ae.length;_+=1)ae[_]&&ae[_].m(H,null);r(t,g),r(t,S),r(S,v),r(v,be),r(S,Q),r(S,C);for(let _=0;_<re.length;_+=1)re[_]&&re[_].m(C,null);r(t,ne),r(t,y),r(y,N),r(N,$),r(y,x),r(y,ue);for(let _=0;_<ce.length;_+=1)ce[_]&&ce[_].m(ue,null);r(t,M),r(t,te),r(te,_e),r(_e,Te),r(te,Je),r(te,Ce),r(Ce,Be),r(Be,ot),r(ot,it),r(Be,Tt),r(Be,Re),r(Ce,Ct),r(Ce,Ne),r(Ne,st),r(st,at),r(Ne,St),r(Ne,De),r(Ce,Mt),r(Ce,Pe),r(Pe,rt),r(rt,ct),r(Pe,Lt),r(Pe,Fe),ft||(qt=[ge(h,"click",e[13]),ge(Re,"click",e[19]),ge(De,"click",e[20]),ge(Fe,"click",e[21])],ft=!0)},p(p,Z){if(Z[0]&4&&i!==(i=p[2].settings+"")&&K(s,i),Z[0]&4&&d!==(d=p[2].language+"")&&K(b,d),Z[0]&2082){Oe=le(p[11]);let _;for(_=0;_<Oe.length;_+=1){const de=fl(p,Oe,_);ie[_]?ie[_].p(de,Z):(ie[_]=ul(de),ie[_].c(),ie[_].m(R,null))}for(;_<ie.length;_+=1)ie[_].d(1);ie.length=Oe.length}if(Z[0]&4&&z!==(z=p[2].currency+"")&&K(Y,z),Z[0]&34){He=le(Ht);let _;for(_=0;_<He.length;_+=1){const de=cl(p,He,_);se[_]?se[_].p(de,Z):(se[_]=hl(de),se[_].c(),se[_].m(L,null))}for(;_<se.length;_+=1)se[_].d(1);se.length=He.length}if(p[1].currency==="CNY"?he?he.p(p,Z):(he=dl(p),he.c(),he.m(W,null)):he&&(he.d(1),he=null),Z[0]&4&&D!==(D=p[2].theme+"")&&K(V,D),Z[0]&42){Ie=le(p[3]);let _;for(_=0;_<Ie.length;_+=1){const de=rl(p,Ie,_);ae[_]?ae[_].p(de,Z):(ae[_]=_l(de),ae[_].c(),ae[_].m(H,null))}for(;_<ae.length;_+=1)ae[_].d(1);ae.length=Ie.length}if(Z[0]&4&&j!==(j=p[2].timeRange+"")&&K(be,j),Z[0]&1058){Ue=le(p[10]);let _;for(_=0;_<Ue.length;_+=1){const de=al(p,Ue,_);re[_]?re[_].p(de,Z):(re[_]=ml(de),re[_].c(),re[_].m(C,null))}for(;_<re.length;_+=1)re[_].d(1);re.length=Ue.length}if(Z[0]&4&&ee!==(ee=p[2].refreshInterval+"")&&K($,ee),Z[0]&546){je=le(p[9]);let _;for(_=0;_<je.length;_+=1){const de=sl(p,je,_);ce[_]?ce[_].p(de,Z):(ce[_]=pl(de),ce[_].c(),ce[_].m(ue,null))}for(;_<ce.length;_+=1)ce[_].d(1);ce.length=je.length}Z[0]&4&&ye!==(ye=p[2].display+"")&&K(Te,ye),Z[0]&4&&$e!==($e=p[2].showCost+"")&&K(it,$e),Z[0]&2&&X(Re,"on",p[1].showCost),Z[0]&4&&xe!==(xe=p[2].tokenBreakdownToggle+"")&&K(at,xe),Z[0]&2&&X(De,"on",p[1].showTokenBreakdown),Z[0]&4&&et!==(et=p[2].activityChart+"")&&K(ct,et),Z[0]&2&&X(Fe,"on",p[1].showHeatmap)},i:fe,o:fe,d(p){p&&A(t),we(ie,p),we(se,p),he&&he.d(),we(ae,p),we(re,p),we(ce,p),ft=!1,qe(qt)}}}function rn(e,t,l){let n,i,{settings:s}=t,{exchangeRate:a=null}=t;const h=Ol();let f={...s};function u(){h("save",f)}function c(){return a!=null&&a.rate?`1 USD = ${a.rate.toFixed(4)} CNY`:n.exchangeRateUnavailable}function d(){if(!(a!=null&&a.fetchedAt))return"";const T=new Date(a.fetchedAt),D=String(T.getHours()).padStart(2,"0"),V=String(T.getMinutes()).padStart(2,"0");return n.exchangeRateUpdated(`${D}:${V}`)}function b(T){typeof f[T]=="boolean"&&(l(1,f={...f,[T]:!f[T]}),u())}const k=[{label:"30s",value:30},{label:"1m",value:60},{label:"5m",value:300},{label:"10m",value:600}],R=[{label:"7d",value:7},{label:"14d",value:14},{label:"30d",value:30},{label:"60d",value:60},{label:"90d",value:90}],I=[{label:"English",value:"en"},{label:"中文",value:"zh"}],W=()=>h("close"),w=T=>{l(1,f={...f,locale:T.value}),u()},z=T=>{l(1,f={...f,currency:T.code}),u()},Y=T=>{l(1,f={...f,theme:T.value}),u()},O=T=>{l(1,f={...f,rangeDays:T.value}),u()},L=T=>{l(1,f={...f,refreshIntervalSec:T.value}),u()},E=()=>b("showCost"),F=()=>b("showTokenBreakdown"),P=()=>b("showHeatmap");return e.$$set=T=>{"settings"in T&&l(12,s=T.settings),"exchangeRate"in T&&l(0,a=T.exchangeRate)},e.$$.update=()=>{e.$$.dirty[0]&2&&l(2,n=Dl(f.locale??"en")),e.$$.dirty[0]&4&&l(3,i=[{label:n.themeSystem,value:"system"},{label:n.themeLight,value:"light"},{label:n.themeDark,value:"dark"}])},[a,f,n,i,h,u,c,d,b,k,R,I,s,W,w,z,Y,O,L,E,F,P]}class cn extends Ye{constructor(t){super(),Ke(this,t,rn,an,Ge,{settings:12,exchangeRate:0},null,[-1,-1])}}function fn(e){var be,Q,C,ne;let t,l,n,i,s,a=e[3].today+"",h,f,u,c,d,b,k,R,I,W,w,z,Y,O,L,E,F,P,T,D,V,J,H,g=((be=e[1])==null?void 0:be.showCost)&&vl(e),S=((Q=e[1])==null?void 0:Q.showCost)&&bl(e),v=((C=e[1])==null?void 0:C.showTokenBreakdown)&&e[0]&&kl(e),j=((ne=e[1])==null?void 0:ne.showHeatmap)&&e[0]&&wl(e);return P=new dt({props:{label:e[3].topModel,value:e[14],sub:e[13]}}),D=new dt({props:{label:e[3].topTool,value:e[12],sub:e[11]}}),J=new dt({props:{label:e[3].sessions,value:e[10]}}),{c(){t=m("div"),l=m("div"),n=m("div"),i=m("div"),s=m("span"),h=U(a),f=q(),u=m("span"),c=U(e[19]),d=q(),g&&g.c(),b=q(),k=m("div"),R=m("span"),I=U(e[15]),W=q(),w=m("span"),z=U(e[18]),Y=q(),S&&S.c(),O=q(),v&&v.c(),L=q(),j&&j.c(),E=q(),F=m("div"),Ee(P.$$.fragment),T=q(),Ee(D.$$.fragment),V=q(),Ee(J.$$.fragment),o(s,"class","metric-label svelte-1d2i7t8"),o(u,"class","metric-value svelte-1d2i7t8"),o(i,"class","metric svelte-1d2i7t8"),o(R,"class","metric-label svelte-1d2i7t8"),o(w,"class","metric-value svelte-1d2i7t8"),o(k,"class","metric svelte-1d2i7t8"),o(n,"class","metric-grid svelte-1d2i7t8"),o(l,"class","section svelte-1d2i7t8"),o(F,"class","section details svelte-1d2i7t8"),o(t,"class","content svelte-1d2i7t8")},m(y,N){B(y,t,N),r(t,l),r(l,n),r(n,i),r(i,s),r(s,h),r(i,f),r(i,u),r(u,c),r(i,d),g&&g.m(i,null),r(n,b),r(n,k),r(k,R),r(R,I),r(k,W),r(k,w),r(w,z),r(k,Y),S&&S.m(k,null),r(t,O),v&&v.m(t,null),r(t,L),j&&j.m(t,null),r(t,E),r(t,F),Me(P,F,null),r(F,T),Me(D,F,null),r(F,V),Me(J,F,null),H=!0},p(y,N){var ue,M,te,_e;(!H||N[0]&8)&&a!==(a=y[3].today+"")&&K(h,a),(!H||N[0]&524288)&&K(c,y[19]),(ue=y[1])!=null&&ue.showCost?g?g.p(y,N):(g=vl(y),g.c(),g.m(i,null)):g&&(g.d(1),g=null),(!H||N[0]&32768)&&K(I,y[15]),(!H||N[0]&262144)&&K(z,y[18]),(M=y[1])!=null&&M.showCost?S?S.p(y,N):(S=bl(y),S.c(),S.m(k,null)):S&&(S.d(1),S=null),(te=y[1])!=null&&te.showTokenBreakdown&&y[0]?v?(v.p(y,N),N[0]&3&&oe(v,1)):(v=kl(y),v.c(),oe(v,1),v.m(t,L)):v&&(vt(),me(v,1,1,()=>{v=null}),bt()),(_e=y[1])!=null&&_e.showHeatmap&&y[0]?j?(j.p(y,N),N[0]&3&&oe(j,1)):(j=wl(y),j.c(),oe(j,1),j.m(t,E)):j&&(vt(),me(j,1,1,()=>{j=null}),bt());const ee={};N[0]&8&&(ee.label=y[3].topModel),N[0]&16384&&(ee.value=y[14]),N[0]&8192&&(ee.sub=y[13]),P.$set(ee);const $={};N[0]&8&&($.label=y[3].topTool),N[0]&4096&&($.value=y[12]),N[0]&2048&&($.sub=y[11]),D.$set($);const x={};N[0]&8&&(x.label=y[3].sessions),N[0]&1024&&(x.value=y[10]),J.$set(x)},i(y){H||(oe(v),oe(j),oe(P.$$.fragment,y),oe(D.$$.fragment,y),oe(J.$$.fragment,y),H=!0)},o(y){me(v),me(j),me(P.$$.fragment,y),me(D.$$.fragment,y),me(J.$$.fragment,y),H=!1},d(y){y&&A(t),g&&g.d(),S&&S.d(),v&&v.d(),j&&j.d(),Le(P),Le(D),Le(J)}}}function un(e){let t,l;return t=new cn({props:{settings:e[1],exchangeRate:e[2]}}),t.$on("save",e[21]),t.$on("close",e[23]),{c(){Ee(t.$$.fragment)},m(n,i){Me(t,n,i),l=!0},p(n,i){const s={};i[0]&2&&(s.settings=n[1]),i[0]&4&&(s.exchangeRate=n[2]),t.$set(s)},i(n){l||(oe(t.$$.fragment,n),l=!0)},o(n){me(t.$$.fragment,n),l=!1},d(n){Le(t,n)}}}function vl(e){let t,l;return{c(){t=m("span"),l=U(e[17]),o(t,"class","metric-cost svelte-1d2i7t8")},m(n,i){B(n,t,i),r(t,l)},p(n,i){i[0]&131072&&K(l,n[17])},d(n){n&&A(t)}}}function bl(e){let t,l;return{c(){t=m("span"),l=U(e[16]),o(t,"class","metric-cost svelte-1d2i7t8")},m(n,i){B(n,t,i),r(t,l)},p(n,i){i[0]&65536&&K(l,n[16])},d(n){n&&A(t)}}}function kl(e){let t,l,n=e[3].tokenBreakdownToday+"",i,s,a,h;return a=new Jl({props:{input:e[0].todayTokens.input,output:e[0].todayTokens.output,cacheRead:e[0].todayTokens.cacheRead,cacheWrite:e[0].todayTokens.cacheWrite,thinking:e[0].todayTokens.thinking}}),{c(){t=m("div"),l=m("div"),i=U(n),s=q(),Ee(a.$$.fragment),o(l,"class","section-title svelte-1d2i7t8"),o(t,"class","section svelte-1d2i7t8")},m(f,u){B(f,t,u),r(t,l),r(l,i),r(t,s),Me(a,t,null),h=!0},p(f,u){(!h||u[0]&8)&&n!==(n=f[3].tokenBreakdownToday+"")&&K(i,n);const c={};u[0]&1&&(c.input=f[0].todayTokens.input),u[0]&1&&(c.output=f[0].todayTokens.output),u[0]&1&&(c.cacheRead=f[0].todayTokens.cacheRead),u[0]&1&&(c.cacheWrite=f[0].todayTokens.cacheWrite),u[0]&1&&(c.thinking=f[0].todayTokens.thinking),a.$set(c)},i(f){h||(oe(a.$$.fragment,f),h=!0)},o(f){me(a.$$.fragment,f),h=!1},d(f){f&&A(t),Le(a)}}}function wl(e){var f;let t,l,n=e[3].trend+"",i,s,a,h;return a=new ln({props:{data:e[0].dailyHistory,showCost:((f=e[1])==null?void 0:f.showCost)??!1,locale:e[4],currency:e[5],exchangeRate:e[2]}}),{c(){t=m("div"),l=m("div"),i=U(n),s=q(),Ee(a.$$.fragment),o(l,"class","section-title svelte-1d2i7t8"),o(t,"class","section svelte-1d2i7t8")},m(u,c){B(u,t,c),r(t,l),r(l,i),r(t,s),Me(a,t,null),h=!0},p(u,c){var b;(!h||c[0]&8)&&n!==(n=u[3].trend+"")&&K(i,n);const d={};c[0]&1&&(d.data=u[0].dailyHistory),c[0]&2&&(d.showCost=((b=u[1])==null?void 0:b.showCost)??!1),c[0]&16&&(d.locale=u[4]),c[0]&32&&(d.currency=u[5]),c[0]&4&&(d.exchangeRate=u[2]),a.$set(d)},i(u){h||(oe(a.$$.fragment,u),h=!0)},o(u){me(a.$$.fragment,u),h=!1},d(u){u&&A(t),Le(a)}}}function hn(e){let t,l,n,i,s,a;l=new Vl({props:{onRefresh:e[20],onClose:_n,onToggleSettings:e[22],refreshLabel:e[3].refresh,settingsLabel:e[3].settings,closeLabel:e[3].close,statusText:e[9]}});const h=[un,fn],f=[];function u(c,d){return c[7]&&c[1]?0:1}return i=u(e),s=f[i]=h[i](e),{c(){t=m("div"),Ee(l.$$.fragment),n=q(),s.c(),o(t,"class","panel svelte-1d2i7t8"),X(t,"loading",e[6])},m(c,d){B(c,t,d),Me(l,t,null),r(t,n),f[i].m(t,null),e[24](t),a=!0},p(c,d){const b={};d[0]&128&&(b.onToggleSettings=c[22]),d[0]&8&&(b.refreshLabel=c[3].refresh),d[0]&8&&(b.settingsLabel=c[3].settings),d[0]&8&&(b.closeLabel=c[3].close),d[0]&512&&(b.statusText=c[9]),l.$set(b);let k=i;i=u(c),i===k?f[i].p(c,d):(vt(),me(f[k],1,1,()=>{f[k]=null}),bt(),s=f[i],s?s.p(c,d):(s=f[i]=h[i](c),s.c()),oe(s,1),s.m(t,null)),(!a||d[0]&64)&&X(t,"loading",c[6])},i(c){a||(oe(l.$$.fragment,c),oe(s),a=!0)},o(c){me(l.$$.fragment,c),me(s),a=!1},d(c){c&&A(t),Le(l),f[i].d(),e[24](null)}}}function dn(){return typeof navigator<"u"&&navigator.language.toLowerCase().startsWith("zh")?"zh":"en"}function yl(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:String(e)}function gn(e){return e.replace(/^(claude-|gpt-|gemini-|deepseek-|qwen-)/i,"").replace(/-\d{8}$/,"")}function _n(){window.widget.hideWindow()}function mn(e,t,l){let n,i,s,a,h,f,u,c,d,b,k,R,I,W,w=null,z=null,Y=null,O=dn(),L=!0,E=!1,F,P=0;function T(Q){return i.lastNDays(Q)}function D(Q){const C=new Date(Q),ne=String(C.getHours()).padStart(2,"0"),y=String(C.getMinutes()).padStart(2,"0");return i.syncedAt(`${ne}:${y}`)}async function V(){l(6,L=!0),l(0,w=await window.widget.getData()),l(6,L=!1)}async function J(){l(1,z=await window.widget.getSettings())}async function H(){l(2,Y=await window.widget.getExchangeRate())}async function g(Q){l(1,z=await window.widget.saveSettings(Q.detail)),V()}function S(){if(!F)return;const Q=Math.ceil(F.getBoundingClientRect().height);Q<=0||Math.abs(Q-P)<2||(P=Q,window.widget.resizeWindow(Q))}Cl(()=>{V(),J(),H(),window.widget.onDataUpdate(C=>{l(0,w=C),l(6,L=!1)});const Q=new ResizeObserver(()=>S());return Q.observe(F),Ft().then(S),()=>Q.disconnect()}),Pl(()=>{Ft().then(S)});const v=()=>{l(7,E=!E)},j=()=>{l(7,E=!1)};function be(Q){_t[Q?"unshift":"push"](()=>{F=Q,l(8,F)})}return e.$$.update=()=>{var Q;e.$$.dirty[0]&2&&l(4,n=(z==null?void 0:z.locale)??O),e.$$.dirty[0]&16&&l(3,i=Dl(n)),e.$$.dirty[0]&2&&l(5,s=(z==null?void 0:z.currency)??"USD"),e.$$.dirty[0]&1&&l(19,a=w?yl(w.todayTokens.total):"--"),e.$$.dirty[0]&1&&l(18,h=w?yl(w.rangeTokens.total):"--"),e.$$.dirty[0]&53&&l(17,f=w?kt(w.todayCost,s,n,Y):"--"),e.$$.dirty[0]&53&&l(16,u=w?kt(w.rangeCost,s,n,Y):"--"),e.$$.dirty[0]&9&&l(15,c=w?T(w.rangeDays):i.lastNDays(30)),e.$$.dirty[0]&1&&l(14,d=w!=null&&w.topModel?gn(w.topModel.name):"--"),e.$$.dirty[0]&1&&l(13,b=w!=null&&w.topModel?`${w.topModel.share}%`:""),e.$$.dirty[0]&1&&l(12,k=((Q=w==null?void 0:w.topTool)==null?void 0:Q.name)??"--"),e.$$.dirty[0]&1&&l(11,R=w!=null&&w.topTool?`${w.topTool.share}%`:""),e.$$.dirty[0]&1&&l(10,I=w?String(w.sessionCountToday):"--"),e.$$.dirty[0]&1&&l(9,W=w?D(w.lastUpdated):"")},[w,z,Y,i,n,s,L,E,F,W,I,R,k,b,d,c,u,f,h,a,V,g,v,j,be]}class pn extends Ye{constructor(t){super(),Ke(this,t,mn,hn,Ge,{},null,[-1,-1])}}new pn({target:document.getElementById("app")});
@@ -0,0 +1 @@
1
+ .header.svelte-1c4hcx6{display:flex;align-items:center;justify-content:space-between;padding:9px 14px 8px;-webkit-app-region:drag}.brand.svelte-1c4hcx6{display:flex;align-items:center;gap:6px}.logo.svelte-1c4hcx6{font-size:12px;font-weight:600;color:var(--text-primary)}.status.svelte-1c4hcx6{font-family:Geist Mono,SF Mono,Menlo,monospace;font-size:8px;line-height:1;color:var(--text-muted);font-variant-numeric:tabular-nums;margin-left:2px;transform:translateY(1px)}.actions.svelte-1c4hcx6{display:flex;gap:2px;-webkit-app-region:no-drag}.icon-btn.svelte-1c4hcx6{width:24px;height:24px;border:none;border-radius:4px;background:transparent;color:var(--text-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .1s,color .1s;padding:0}.icon-btn.svelte-1c4hcx6:hover{background:var(--bg-hover);color:var(--text-primary)}.row.svelte-36ktuv{display:flex;align-items:baseline;justify-content:space-between;padding:6px 0}.label.svelte-36ktuv{font-size:11px;font-weight:550;letter-spacing:.04em;text-transform:uppercase;color:var(--text-muted)}.right.svelte-36ktuv{display:flex;align-items:baseline;gap:6px}.value.svelte-36ktuv{font-family:Geist Mono,SF Mono,Menlo,monospace;font-size:13px;font-weight:600;color:var(--text-primary);font-variant-numeric:tabular-nums}.value.accent.svelte-36ktuv{color:var(--accent)}.sub.svelte-36ktuv{font-family:Geist Mono,SF Mono,Menlo,monospace;font-size:10px;color:var(--text-muted);font-variant-numeric:tabular-nums}.breakdown.svelte-4y3y1j{padding:0}.bar.svelte-4y3y1j{display:flex;height:6px;border-radius:3px;overflow:hidden;gap:1px;margin-bottom:8px}.segment.svelte-4y3y1j{min-width:3px;border-radius:1px}.legend.svelte-4y3y1j{display:grid;grid-template-columns:1fr 1fr;gap:3px 12px}.legend-item.svelte-4y3y1j{display:flex;align-items:center;gap:4px}.dot.svelte-4y3y1j{width:6px;height:6px;border-radius:50%;flex-shrink:0}.legend-label.svelte-4y3y1j{font-size:10px;color:var(--text-muted);flex:1}.legend-value.svelte-4y3y1j{font-family:Geist Mono,SF Mono,Menlo,monospace;font-size:10px;font-weight:550;color:var(--text-secondary);font-variant-numeric:tabular-nums}.empty.svelte-4y3y1j{font-size:10px;color:var(--text-muted);text-align:center;padding:8px 0}.chart-wrapper.svelte-15p89al.svelte-15p89al{position:relative;-webkit-user-select:none;user-select:none}.chart-svg.svelte-15p89al.svelte-15p89al{display:block;cursor:crosshair;overflow:visible}.chart-svg.svelte-15p89al .line.svelte-15p89al{stroke-dasharray:2000;stroke-dashoffset:2000;transition:stroke-dashoffset .8s cubic-bezier(.16,1,.3,1)}.chart-svg.mounted.svelte-15p89al .line.svelte-15p89al{stroke-dashoffset:0}.chart-svg.svelte-15p89al .cost-line.svelte-15p89al{opacity:.75}.chart-svg.svelte-15p89al .area.svelte-15p89al{opacity:0;transition:opacity .6s cubic-bezier(.16,1,.3,1) .3s}.chart-svg.mounted.svelte-15p89al .area.svelte-15p89al{opacity:1}.tick-label.svelte-15p89al.svelte-15p89al{font-size:8px;fill:var(--text-muted);font-family:Geist Mono,SF Mono,Menlo,monospace;font-variant-numeric:tabular-nums}.y-label.svelte-15p89al.svelte-15p89al{font-size:7px;fill:var(--text-muted);opacity:.72;font-family:Geist Mono,SF Mono,Menlo,monospace;font-variant-numeric:tabular-nums}.y-label-cost.svelte-15p89al.svelte-15p89al{fill:var(--chart-cache-read);opacity:.78}.tooltip-fo.svelte-15p89al.svelte-15p89al{overflow:visible;pointer-events:none}.tooltip.svelte-15p89al.svelte-15p89al{display:flex;align-items:center;justify-content:center;gap:8px;height:24px;padding:0 8px;border:1px solid var(--border);border-radius:4px;background:var(--surface);box-shadow:0 4px 10px #0000001f;color:var(--text-primary);position:relative;box-sizing:border-box;font-family:Inter,-apple-system,sans-serif;pointer-events:none;white-space:nowrap}.tooltip.svelte-15p89al.svelte-15p89al:after{content:"";position:absolute;top:50%;width:6px;height:6px;border-right:1px solid var(--border);border-bottom:1px solid var(--border);background:var(--surface)}.tooltip.pointer-left.svelte-15p89al.svelte-15p89al:after{left:-4px;transform:translateY(-50%) rotate(135deg)}.tooltip.pointer-right.svelte-15p89al.svelte-15p89al:after{right:-4px;transform:translateY(-50%) rotate(-45deg)}.tooltip-date.svelte-15p89al.svelte-15p89al,.tooltip-metric.svelte-15p89al.svelte-15p89al{position:relative;z-index:1}.tooltip-date.svelte-15p89al.svelte-15p89al{font-size:9px;color:var(--text-secondary);line-height:1}.tooltip-metric.svelte-15p89al.svelte-15p89al{display:inline-flex;align-items:center;gap:4px;font-family:Geist Mono,SF Mono,Menlo,monospace;font-size:10px;font-weight:600;color:var(--text-primary);font-variant-numeric:tabular-nums;line-height:1}.tooltip-metric.svelte-15p89al.svelte-15p89al:before{content:"";width:5px;height:5px;border-radius:50%;background:var(--accent);display:inline-block}.cost-dot.svelte-15p89al.svelte-15p89al{color:var(--chart-cache-read)}.cost-dot.svelte-15p89al.svelte-15p89al:before{background:var(--chart-cache-read)}@media (prefers-reduced-motion: reduce){.chart-svg.svelte-15p89al .line.svelte-15p89al{stroke-dasharray:none;stroke-dashoffset:0;transition:none}.chart-svg.svelte-15p89al .area.svelte-15p89al{opacity:1;transition:none}}.settings.svelte-198qnem.svelte-198qnem{padding:0 14px 14px}.settings-header.svelte-198qnem.svelte-198qnem{display:flex;align-items:center;justify-content:space-between;padding:8px 0 10px}.settings-title.svelte-198qnem.svelte-198qnem{font-size:12px;font-weight:600;color:var(--text-primary)}.back-btn.svelte-198qnem.svelte-198qnem{width:24px;height:24px;border:none;border-radius:4px;background:transparent;color:var(--text-muted);cursor:pointer;display:flex;align-items:center;justify-content:center}.back-btn.svelte-198qnem.svelte-198qnem:hover{background:var(--bg-hover);color:var(--text-primary)}.section.svelte-198qnem.svelte-198qnem{margin-bottom:14px}.section-label.svelte-198qnem.svelte-198qnem{font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--text-muted);margin-bottom:6px}.button-group.svelte-198qnem.svelte-198qnem{display:flex;gap:0;border-radius:6px;overflow:hidden;border:1px solid var(--border)}.rate-info.svelte-198qnem.svelte-198qnem{display:grid;grid-template-columns:auto 1fr;gap:2px 8px;align-items:baseline;margin-top:6px;color:var(--text-muted);font-size:10px}.rate-info.svelte-198qnem strong.svelte-198qnem{justify-self:end;color:var(--text-secondary);font-family:Geist Mono,SF Mono,Menlo,monospace;font-size:10px;font-weight:600;font-variant-numeric:tabular-nums}.rate-info.svelte-198qnem small.svelte-198qnem{grid-column:1 / -1;justify-self:end;font-size:9px;color:var(--text-muted)}.rate-info.error.svelte-198qnem strong.svelte-198qnem{color:var(--text-muted)}.option-btn.svelte-198qnem.svelte-198qnem{flex:1;padding:6px 0;border:none;background:transparent;color:var(--text-secondary);font-size:11px;font-weight:500;cursor:pointer;transition:background .1s,color .1s}.option-btn.svelte-198qnem.svelte-198qnem:not(:last-child){border-right:1px solid var(--border)}.option-btn.active.svelte-198qnem.svelte-198qnem{background:var(--accent);color:#fff}.option-btn.svelte-198qnem.svelte-198qnem:hover:not(.active){background:var(--bg-hover)}.toggles.svelte-198qnem.svelte-198qnem{display:flex;flex-direction:column;gap:8px}.toggle-row.svelte-198qnem.svelte-198qnem{display:flex;align-items:center;justify-content:space-between;font-size:11px;color:var(--text-secondary);cursor:pointer}.toggle.svelte-198qnem.svelte-198qnem{position:relative;width:32px;height:18px;border-radius:9px;border:none;background:var(--border);cursor:pointer;transition:background .15s;padding:0}.toggle.on.svelte-198qnem.svelte-198qnem{background:var(--accent)}.toggle-thumb.svelte-198qnem.svelte-198qnem{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:#fff;transition:transform .15s;display:block}.toggle.on.svelte-198qnem .toggle-thumb.svelte-198qnem{transform:translate(14px)}*{box-sizing:border-box;margin:0;padding:0}html,body{width:100%;height:100%;overflow:hidden}body{background:transparent;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}:root{--bg:oklch(.985 .004 175);--surface:oklch(.995 .003 175);--bg-hover:oklch(.955 .008 175);--border:oklch(.92 .008 175);--border-medium:oklch(.87 .01 175);--text-primary:oklch(.18 .012 175);--text-secondary:oklch(.42 .015 175);--text-muted:oklch(.6 .012 175);--accent:oklch(.55 .12 175);--chart-input:oklch(.65 .14 175);--chart-output:oklch(.6 .15 250);--chart-cache-read:oklch(.7 .1 65);--chart-cache-write:oklch(.65 .12 310);--chart-thinking:oklch(.6 .16 300);--shadow:none}@media (prefers-color-scheme: dark){:root{--bg:oklch(.18 .008 175);--surface:oklch(.22 .006 175);--bg-hover:oklch(.26 .01 175);--border:oklch(.3 .01 175);--border-medium:oklch(.35 .012 175);--text-primary:oklch(.94 .006 175);--text-secondary:oklch(.76 .01 175);--text-muted:oklch(.58 .008 175);--accent:oklch(.65 .12 175);--chart-input:oklch(.65 .14 175);--chart-output:oklch(.6 .15 250);--chart-cache-read:oklch(.7 .1 65);--chart-cache-write:oklch(.65 .12 310);--chart-thinking:oklch(.6 .16 300);--shadow:none}}.panel.svelte-1d2i7t8{background:var(--bg);border-radius:10px;border:1px solid var(--border);overflow:hidden;width:100vw;box-shadow:var(--shadow);transition:opacity .15s}.panel.loading.svelte-1d2i7t8{opacity:.7}.content.svelte-1d2i7t8{padding:0 14px 8px}.section.svelte-1d2i7t8{padding:8px 0}.section.svelte-1d2i7t8:not(:last-child):not(.details){border-bottom:1px solid var(--border)}.section-title.svelte-1d2i7t8{font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--text-muted);margin-bottom:8px}.metric-grid.svelte-1d2i7t8{display:grid;grid-template-columns:1fr 1fr;gap:12px}.metric.svelte-1d2i7t8{display:flex;flex-direction:column;gap:2px}.metric-label.svelte-1d2i7t8{font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--text-muted)}.metric-value.svelte-1d2i7t8{font-family:Geist Mono,SF Mono,Menlo,monospace;font-size:20px;font-weight:700;color:var(--text-primary);font-variant-numeric:tabular-nums;line-height:1.2}.metric-cost.svelte-1d2i7t8{font-family:Geist Mono,SF Mono,Menlo,monospace;font-size:11px;font-weight:550;color:var(--accent);font-variant-numeric:tabular-nums}.details.svelte-1d2i7t8{padding:4px 0}
@@ -4,9 +4,9 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
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">
7
+ <title>AIUsage Widget</title>
8
+ <script type="module" crossorigin src="./assets/index-CM09fccb.js"></script>
9
+ <link rel="stylesheet" crossorigin href="./assets/index-iI9Ag7ZK.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="app"></div>
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadSettings = loadSettings;
4
+ exports.saveSettings = saveSettings;
5
+ exports.getDefaultSettings = getDefaultSettings;
6
+ const node_path_1 = require("node:path");
7
+ const node_os_1 = require("node:os");
8
+ const node_fs_1 = require("node:fs");
9
+ const SETTINGS_PATH = (0, node_path_1.join)((0, node_os_1.homedir)(), '.aiusage', 'widget-settings.json');
10
+ const DEFAULT_SETTINGS = {
11
+ theme: 'system',
12
+ refreshIntervalSec: 60,
13
+ rangeDays: 30,
14
+ showCost: true,
15
+ showHeatmap: true,
16
+ showTokenBreakdown: true,
17
+ locale: 'en',
18
+ currency: 'USD',
19
+ };
20
+ function loadSettings() {
21
+ try {
22
+ if ((0, node_fs_1.existsSync)(SETTINGS_PATH)) {
23
+ const raw = JSON.parse((0, node_fs_1.readFileSync)(SETTINGS_PATH, 'utf-8'));
24
+ return { ...DEFAULT_SETTINGS, ...raw };
25
+ }
26
+ }
27
+ catch {
28
+ // Fall through to defaults
29
+ }
30
+ return { ...DEFAULT_SETTINGS };
31
+ }
32
+ function saveSettings(settings) {
33
+ const dir = (0, node_path_1.join)((0, node_os_1.homedir)(), '.aiusage');
34
+ if (!(0, node_fs_1.existsSync)(dir)) {
35
+ (0, node_fs_1.mkdirSync)(dir, { recursive: true });
36
+ }
37
+ (0, node_fs_1.writeFileSync)(SETTINGS_PATH, JSON.stringify(settings, null, 2), 'utf-8');
38
+ }
39
+ function getDefaultSettings() {
40
+ return { ...DEFAULT_SETTINGS };
41
+ }
package/dist/ui.js CHANGED
@@ -4,6 +4,7 @@ exports.shouldShowWindowOnLaunch = shouldShowWindowOnLaunch;
4
4
  exports.shouldHideWindowOnBlur = shouldHideWindowOnBlur;
5
5
  exports.shouldHideWindowOnClose = shouldHideWindowOnClose;
6
6
  exports.getWindowPosition = getWindowPosition;
7
+ exports.hasUsableTrayBounds = hasUsableTrayBounds;
7
8
  exports.getWidgetNativeBindingPath = getWidgetNativeBindingPath;
8
9
  exports.getTrayIconNativeImage = getTrayIconNativeImage;
9
10
  const node_path_1 = require("node:path");
@@ -33,10 +34,11 @@ function shouldHideWindowOnClose(_) {
33
34
  return true;
34
35
  }
35
36
  function getWindowPosition({ platform, trayBounds, windowBounds, displayBounds, }) {
36
- const preferredX = Math.round(trayBounds.x + trayBounds.width / 2 - windowBounds.width / 2);
37
+ const anchorBounds = normalizeTrayBounds({ platform, trayBounds, displayBounds });
38
+ const preferredX = Math.round(anchorBounds.x + anchorBounds.width / 2 - windowBounds.width / 2);
37
39
  const preferredY = platform === 'darwin'
38
- ? trayBounds.y + trayBounds.height + 4
39
- : trayBounds.y - windowBounds.height - 4;
40
+ ? anchorBounds.y + anchorBounds.height + 4
41
+ : anchorBounds.y - windowBounds.height - 4;
40
42
  const maxX = Math.max(displayBounds.x, displayBounds.x + displayBounds.width - windowBounds.width);
41
43
  const maxY = Math.max(displayBounds.y, displayBounds.y + displayBounds.height - windowBounds.height);
42
44
  return {
@@ -44,6 +46,24 @@ function getWindowPosition({ platform, trayBounds, windowBounds, displayBounds,
44
46
  y: Math.min(Math.max(preferredY, displayBounds.y), maxY),
45
47
  };
46
48
  }
49
+ function hasUsableTrayBounds({ platform, trayBounds, displayBounds, }) {
50
+ if (platform !== 'darwin')
51
+ return true;
52
+ const isLeftBottomFallback = trayBounds.x <= displayBounds.x + 1 &&
53
+ trayBounds.y >= displayBounds.y + displayBounds.height - Math.max(trayBounds.height, 1) - 8;
54
+ const isEmpty = trayBounds.width <= 0 && trayBounds.height <= 0;
55
+ return !isLeftBottomFallback && !isEmpty;
56
+ }
57
+ function normalizeTrayBounds({ platform, trayBounds, displayBounds, }) {
58
+ if (hasUsableTrayBounds({ platform, trayBounds, displayBounds }))
59
+ return trayBounds;
60
+ return {
61
+ x: displayBounds.x + displayBounds.width - 48,
62
+ y: displayBounds.y,
63
+ width: 32,
64
+ height: 24,
65
+ };
66
+ }
47
67
  function getWidgetNativeBindingPath(baseDir) {
48
68
  return (0, node_path_1.join)(baseDir, 'native', 'better_sqlite3.node');
49
69
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@juliantanx/aiusage-widget",
3
- "version": "1.3.4",
3
+ "version": "1.5.0",
4
4
  "private": false,
5
- "description": "System tray widget for aiusage — view AI token usage from your system tray",
5
+ "description": "System tray widget for AIUsage — view AI token usage from your system tray",
6
6
  "keywords": [
7
7
  "ai",
8
8
  "aiusage",
@@ -1,2 +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 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")});
@@ -1 +0,0 @@
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}