ai-battery 0.1.3 → 0.1.5
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 +36 -6
- package/bin/ai-battery-hud +15 -83
- package/bin/ai-battery-hud.js +30 -6
- package/bin/ai-battery-hud.ps1 +20 -1
- package/bin/ai-battery-macos-status.applescript +65 -9
- package/bin/ai-battery-run-win.js +300 -0
- package/bin/ai-battery.js +1225 -96
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -50,8 +50,8 @@ Codex 86% │ 5h 18:09 │ 7d 82% ┃ Claude 4% │ 5h 18:10 │ 7d 71%
|
|
|
50
50
|
| `ai-battery` | 지원 | 지원 | 지원 | 지원 | Node.js 18 이상이 필요합니다. |
|
|
51
51
|
| `ai-battery --watch` | 지원 | 지원 | 지원 | 지원 | 터미널 안에서 주기적으로 갱신합니다. |
|
|
52
52
|
| Claude statusLine | 지원 | 지원 | 지원 | 지원 | Claude Code `statusLine`에 `node <script>` 명령을 저장합니다. |
|
|
53
|
-
| Codex terminal row |
|
|
54
|
-
| `ai-battery setup codex` |
|
|
53
|
+
| Codex terminal row | 지원 | 지원 | 지원 | 지원 | Windows는 `node-pty`/ConPTY가 있으면 reserved row, 없으면 overlay fallback을 사용합니다. WSL/Linux/macOS는 POSIX PTY와 `python3`를 사용합니다. |
|
|
54
|
+
| `ai-battery setup codex` | 지원 | 지원 | 지원 | 지원 | Windows는 `codex.cmd` wrapper, WSL/Linux/macOS는 POSIX shell wrapper를 설치합니다. |
|
|
55
55
|
| `ai-battery hud` | 지원 | 지원 | 미지원 | 지원 | Windows/WSL은 PowerShell/WinForms HUD, macOS는 menu bar status item입니다. |
|
|
56
56
|
|
|
57
57
|
실행 중 감지(흰색/회색 표시)는 Linux/WSL은 `/proc`, macOS는 `ps`, Windows는 PowerShell 프로세스 목록을 사용합니다.
|
|
@@ -107,6 +107,7 @@ ai-battery --version
|
|
|
107
107
|
ai-battery --provider codex
|
|
108
108
|
ai-battery --provider claude
|
|
109
109
|
ai-battery setup
|
|
110
|
+
ai-battery uninstall
|
|
110
111
|
ai-battery doctor
|
|
111
112
|
ai-battery hud
|
|
112
113
|
ai-battery off codex
|
|
@@ -124,9 +125,36 @@ ai-battery on codex
|
|
|
124
125
|
|
|
125
126
|
`doctor`는 설치 상태와 함께 npm latest 버전을 확인합니다. 네트워크가 막혀 있으면 버전 확인만 건너뛰고 나머지 진단은 계속 표시합니다.
|
|
126
127
|
|
|
128
|
+
## Uninstall
|
|
129
|
+
|
|
130
|
+
`off`는 표시만 숨기는 설정이고, `uninstall`은 `setup`과 HUD autostart가 만든 통합 지점을 제거합니다.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
ai-battery uninstall
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
일부만 제거할 수도 있습니다.
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
ai-battery uninstall codex
|
|
140
|
+
ai-battery uninstall claude
|
|
141
|
+
ai-battery uninstall hud
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
이 명령은 AI Battery가 관리 마커를 넣은 Codex wrapper, Claude `statusLine`, HUD/menu bar autostart와 실행 중인 HUD를 정리합니다. 다른 도구가 만든 `codex` 파일이나 Claude `statusLine`은 건드리지 않습니다. 이전 버전이나 `--force`로 기존 파일을 백업한 경우에는 가능한 한 원래 파일/symlink를 복원합니다. 현재 이미 AI Battery wrapper 안에서 실행 중인 Codex 세션의 terminal row는 그 세션을 종료해야 사라집니다.
|
|
145
|
+
|
|
146
|
+
최신 npm은 패키지의 uninstall lifecycle을 실행하지 않기 때문에 `npm uninstall ai-battery` 또는 `npm uninstall -g ai-battery`만으로는 외부 통합 지점을 자동 정리할 수 없습니다. 완전히 제거하려면 npm 패키지를 지우기 전에 먼저 실행하세요.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
ai-battery uninstall
|
|
150
|
+
npm uninstall -g ai-battery
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
이미 npm 패키지를 먼저 지운 뒤라면, 다시 설치한 다음 `ai-battery uninstall`을 실행하거나 아래 항목을 직접 확인해 제거해야 합니다: AI Battery가 만든 Codex wrapper, shell rc의 `# >>> ai-battery setup >>>` block, Claude `statusLine`, HUD/menu bar autostart.
|
|
154
|
+
|
|
127
155
|
## Setup
|
|
128
156
|
|
|
129
|
-
`setup`은 한 번만 실행합니다. Claude Code에는 statusLine hook을 설치하고, Codex에는
|
|
157
|
+
`setup`은 한 번만 실행합니다. Claude Code에는 statusLine hook을 설치하고, Codex에는 platform wrapper를 설치해서 이후에는 추가 명령 없이 원래처럼 실행하게 합니다.
|
|
130
158
|
|
|
131
159
|
```bash
|
|
132
160
|
ai-battery setup
|
|
@@ -139,7 +167,9 @@ ai-battery setup claude
|
|
|
139
167
|
ai-battery setup codex
|
|
140
168
|
```
|
|
141
169
|
|
|
142
|
-
Codex wrapper는 기존 `codex` 명령을 직접 덮어쓰지 않습니다. `~/.local/bin/codex`에 관리형 wrapper를 만들고, 필요한 경우 셸 설정에 `~/.local/bin
|
|
170
|
+
Codex wrapper는 기존 `codex` 명령을 직접 덮어쓰지 않습니다. `~/.local/bin`이 이미 PATH에서 원본 `codex`보다 앞에 있고 `~/.local/bin/codex`가 비어 있거나 AI Battery 관리 파일이면 그 위치에 wrapper를 둬서 바로 잡히게 합니다. 그렇지 않으면 `~/.local/share/ai-battery/bin/codex`에 관리형 wrapper를 만들고, 필요한 경우 셸 설정에 이 디렉터리를 PATH 앞쪽으로 추가합니다. `~/.local/bin/codex` 같은 공용 위치에 이미 다른 파일이 있으면 덮어쓰지 않습니다. 새 터미널부터 `codex`가 자동으로 AI Battery 하단 행과 함께 실행됩니다. 같은 터미널에서 이미 `codex`를 실행한 적이 있으면 셸 캐시 때문에 `hash -r`이 한 번 필요할 수 있고, PATH 추가가 필요한 경우에는 `setup` 출력에 표시되는 `source ...` 명령을 실행하세요.
|
|
171
|
+
|
|
172
|
+
Windows native `cmd`/PowerShell에서는 `codex.cmd` wrapper가 Windows runner를 실행합니다. `node-pty`가 설치되어 있으면 ConPTY로 Codex 화면을 한 줄 짧게 띄워 하단 row를 예약하고, `node-pty`가 없거나 로드되지 않으면 같은 콘솔의 하단에 overlay row를 다시 그리는 fallback을 사용합니다. Claude statusLine은 일반 `cmd`/PowerShell 프롬프트가 아니라 Claude Code 안에서만 표시됩니다.
|
|
143
173
|
|
|
144
174
|
Codex 하단 행이 보이지 않으면 진단을 실행합니다.
|
|
145
175
|
|
|
@@ -209,7 +239,7 @@ Claude가 한 번 이상 statusLine payload를 전달해야 Claude의 사용량
|
|
|
209
239
|
|
|
210
240
|
## Desktop HUD
|
|
211
241
|
|
|
212
|
-
일반 터미널 위에 외부 프로세스가 안전하게 status line을 덧그리는 방식은 안정적이지 않습니다. 그래서 Windows에서는 floating overlay를, macOS에서는 상단 menu bar status item을 제공합니다. Windows native에서는 WSL 없이 PowerShell/WinForms로 바로 실행되고, WSL에서는 `powershell.exe`를 통해 같은 HUD를 띄웁니다. macOS에서는
|
|
242
|
+
일반 터미널 위에 외부 프로세스가 안전하게 status line을 덧그리는 방식은 안정적이지 않습니다. 그래서 Windows에서는 floating overlay를, macOS에서는 상단 menu bar status item을 제공합니다. Windows native에서는 WSL 없이 PowerShell/WinForms로 바로 실행되고, WSL에서는 `powershell.exe`를 통해 같은 HUD를 띄웁니다. macOS에서는 투명 배경의 작은 SVG 이미지로 Codex와 Claude 로고, 짧은 미터, 퍼센트만 표시하고, 클릭하면 자세한 상태를 볼 수 있습니다.
|
|
213
243
|
|
|
214
244
|
```bash
|
|
215
245
|
ai-battery hud
|
|
@@ -275,7 +305,7 @@ Codex는 최근 세션 로그에서 `rate_limits` 이벤트를 찾습니다. Cla
|
|
|
275
305
|
|
|
276
306
|
## Source Environment
|
|
277
307
|
|
|
278
|
-
기본 CLI는 Node.js 18 이상이 있으면 Windows native, WSL, Linux, macOS에서 실행됩니다.
|
|
308
|
+
기본 CLI는 Node.js 18 이상이 있으면 Windows native, WSL, Linux, macOS에서 실행됩니다. Windows native의 Codex terminal row는 Node runner를 사용하며, optional `node-pty`가 있으면 ConPTY reserved row를 사용하고 없으면 overlay fallback을 사용합니다. WSL/Linux/macOS의 `ai-battery-run`은 Python 3와 POSIX PTY를 사용합니다. HUD는 Windows/WSL에서는 PowerShell/WinForms, macOS에서는 내장 `osascript`와 AppleScriptObjC를 사용합니다.
|
|
279
309
|
|
|
280
310
|
Codex 데이터는 기본적으로 `~/.codex/sessions`를 읽습니다. 다른 위치를 쓰고 있다면 `CODEX_HOME`을 설정하세요.
|
|
281
311
|
|
package/bin/ai-battery-hud
CHANGED
|
@@ -1,89 +1,21 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
set -
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
fi
|
|
12
|
-
|
|
13
|
-
FOREGROUND=0
|
|
14
|
-
ONCE=0
|
|
15
|
-
STOP=0
|
|
16
|
-
FILTERED_ARGS=()
|
|
17
|
-
for arg in "$@"; do
|
|
18
|
-
case "$arg" in
|
|
19
|
-
-Foreground|--foreground)
|
|
20
|
-
FOREGROUND=1
|
|
21
|
-
;;
|
|
22
|
-
-Movable|--movable)
|
|
23
|
-
FILTERED_ARGS+=("-Movable")
|
|
24
|
-
;;
|
|
25
|
-
-Once|--once)
|
|
26
|
-
ONCE=1
|
|
27
|
-
FILTERED_ARGS+=("$arg")
|
|
28
|
-
;;
|
|
29
|
-
-Stop|--stop)
|
|
30
|
-
STOP=1
|
|
31
|
-
FILTERED_ARGS+=("-StopExisting")
|
|
32
|
-
;;
|
|
33
|
-
*)
|
|
34
|
-
FILTERED_ARGS+=("$arg")
|
|
35
|
-
;;
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
script=$0
|
|
5
|
+
while [ -h "$script" ]; do
|
|
6
|
+
dir=$(CDPATH= cd -- "$(dirname -- "$script")" && pwd)
|
|
7
|
+
link=$(readlink "$script")
|
|
8
|
+
case "$link" in
|
|
9
|
+
/*) script=$link ;;
|
|
10
|
+
*) script=$dir/$link ;;
|
|
36
11
|
esac
|
|
37
12
|
done
|
|
38
13
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if [[ -n "${CLAUDEX_BATTERY_COMMAND:-}" ]]; then
|
|
42
|
-
AI_BATTERY_COMMAND="$CLAUDEX_BATTERY_COMMAND"
|
|
43
|
-
fi
|
|
14
|
+
script_dir=$(CDPATH= cd -- "$(dirname -- "$script")" && pwd)
|
|
44
15
|
|
|
45
|
-
if
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
echo "ai-battery-hud: node was not found in WSL PATH." >&2
|
|
49
|
-
exit 1
|
|
50
|
-
fi
|
|
51
|
-
printf -v WSL_HOME_QUOTED "%q" "$HOME"
|
|
52
|
-
printf -v NODE_BIN_QUOTED "%q" "$NODE_BIN"
|
|
53
|
-
printf -v AI_BATTERY_JS_QUOTED "%q" "$SCRIPT_DIR/ai-battery.js"
|
|
54
|
-
ENV_PREFIX=()
|
|
55
|
-
for name in AI_BATTERY_STATE_DIR CLAUDEX_BATTERY_STATE_DIR AI_BATTERY_COLUMNS CLAUDEX_BATTERY_COLUMNS AI_BATTERY_COLUMN_GUARD CLAUDEX_BATTERY_COLUMN_GUARD CODEX_HOME; do
|
|
56
|
-
if [[ -n "${!name:-}" ]]; then
|
|
57
|
-
printf -v value_quoted "%q" "${!name}"
|
|
58
|
-
ENV_PREFIX+=("$name=$value_quoted")
|
|
59
|
-
fi
|
|
60
|
-
done
|
|
61
|
-
AI_BATTERY_COMMAND="${ENV_PREFIX[*]} HOME=$WSL_HOME_QUOTED $NODE_BIN_QUOTED $AI_BATTERY_JS_QUOTED --json"
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
PS_ARGS=(-NoProfile -ExecutionPolicy Bypass -File "$HUD_PS1_WIN" -BatteryCommand "$AI_BATTERY_COMMAND" "${FILTERED_ARGS[@]}")
|
|
65
|
-
|
|
66
|
-
if [[ "$FOREGROUND" == "1" || "$ONCE" == "1" || "$STOP" == "1" ]]; then
|
|
67
|
-
exec powershell.exe "${PS_ARGS[@]}"
|
|
16
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
17
|
+
echo "ai-battery-hud: node was not found on PATH." >&2
|
|
18
|
+
exit 1
|
|
68
19
|
fi
|
|
69
20
|
|
|
70
|
-
|
|
71
|
-
local value="${1//\'/\'\'}"
|
|
72
|
-
printf "'%s'" "$value"
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
win_arg_quote() {
|
|
76
|
-
local value="${1//\"/\\\"}"
|
|
77
|
-
printf '"%s"' "$value"
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
PS_CMDLINE=""
|
|
81
|
-
for arg in "${PS_ARGS[@]}"; do
|
|
82
|
-
if [[ -n "$PS_CMDLINE" ]]; then
|
|
83
|
-
PS_CMDLINE+=" "
|
|
84
|
-
fi
|
|
85
|
-
PS_CMDLINE+="$(win_arg_quote "$arg")"
|
|
86
|
-
done
|
|
87
|
-
|
|
88
|
-
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "Start-Process -WindowStyle Hidden -FilePath 'powershell.exe' -ArgumentList $(ps_quote "$PS_CMDLINE")" >/dev/null 2>&1
|
|
89
|
-
echo "AI Battery HUD started or already running. Drag it to place it; right-click and choose Exit to close."
|
|
21
|
+
exec node "$script_dir/ai-battery-hud.js" "$@"
|
package/bin/ai-battery-hud.js
CHANGED
|
@@ -78,6 +78,15 @@ function sleepSync(ms) {
|
|
|
78
78
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
function waitForFile(filePath, timeoutMs = 3500) {
|
|
82
|
+
const start = Date.now();
|
|
83
|
+
while (Date.now() - start < timeoutMs) {
|
|
84
|
+
if (fs.existsSync(filePath)) return true;
|
|
85
|
+
sleepSync(100);
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
81
90
|
function snapshotHasWeekly(jsonText) {
|
|
82
91
|
try {
|
|
83
92
|
const snapshot = JSON.parse(jsonText);
|
|
@@ -188,7 +197,8 @@ function macCommands(options) {
|
|
|
188
197
|
const providerArgs = macProviderArgs(options.provider);
|
|
189
198
|
const envPrefix = relevantEnvPrefix();
|
|
190
199
|
return {
|
|
191
|
-
title: `${envPrefix} ${node} ${batteryJs} --menu-bar${providerArgs} 2>/dev/null`,
|
|
200
|
+
title: `${envPrefix} ${node} ${batteryJs} --menu-bar-image${providerArgs} 2>/dev/null`,
|
|
201
|
+
detailImage: `${envPrefix} ${node} ${batteryJs} --menu-detail-image${providerArgs} 2>/dev/null`,
|
|
192
202
|
detail: `${envPrefix} ${node} ${batteryJs} --no-color${providerArgs} 2>/dev/null`
|
|
193
203
|
};
|
|
194
204
|
}
|
|
@@ -313,14 +323,16 @@ function runMacHud(cliArgs) {
|
|
|
313
323
|
process.exit(result.status ?? 0);
|
|
314
324
|
}
|
|
315
325
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
326
|
+
const existingPids = macHudPids();
|
|
327
|
+
if (existingPids.length) {
|
|
328
|
+
spawnSync("kill", existingPids.map(String), { stdio: "ignore" });
|
|
329
|
+
sleepSync(400);
|
|
319
330
|
}
|
|
320
331
|
|
|
321
332
|
const osascriptArgs = [
|
|
322
333
|
macStatusPath,
|
|
323
334
|
commands.title,
|
|
335
|
+
commands.detailImage,
|
|
324
336
|
commands.detail,
|
|
325
337
|
String(options.interval)
|
|
326
338
|
];
|
|
@@ -335,7 +347,7 @@ function runMacHud(cliArgs) {
|
|
|
335
347
|
stdio: "ignore"
|
|
336
348
|
});
|
|
337
349
|
child.unref();
|
|
338
|
-
console.log("AI Battery menu bar started. Click the menu bar item for details; use
|
|
350
|
+
console.log(`${existingPids.length ? "AI Battery menu bar restarted" : "AI Battery menu bar started"}. Click the menu bar item for details; use "ai-battery hud stop" to close it.`);
|
|
339
351
|
process.exit(0);
|
|
340
352
|
}
|
|
341
353
|
|
|
@@ -511,6 +523,9 @@ if (subcommand === "autostart") {
|
|
|
511
523
|
}
|
|
512
524
|
|
|
513
525
|
const initialJson = stop ? null : prefetchInitialJson(batteryCommand, useWsl);
|
|
526
|
+
const readyPath = (!useWsl && process.platform === "win32" && !foreground && !once && !stop)
|
|
527
|
+
? path.join(os.tmpdir(), `ai-battery-hud-ready-${process.pid}-${Date.now()}.json`)
|
|
528
|
+
: null;
|
|
514
529
|
|
|
515
530
|
const psArgs = [
|
|
516
531
|
"-NoProfile",
|
|
@@ -523,6 +538,10 @@ const psArgs = [
|
|
|
523
538
|
...filteredArgs
|
|
524
539
|
];
|
|
525
540
|
|
|
541
|
+
if (readyPath) {
|
|
542
|
+
psArgs.push("-ReadyPath", readyPath);
|
|
543
|
+
}
|
|
544
|
+
|
|
526
545
|
if (initialJson) {
|
|
527
546
|
psArgs.push("-InitialJsonBase64", Buffer.from(initialJson, "utf8").toString("base64"));
|
|
528
547
|
}
|
|
@@ -560,4 +579,9 @@ if (useWsl) {
|
|
|
560
579
|
child.unref();
|
|
561
580
|
}
|
|
562
581
|
|
|
563
|
-
|
|
582
|
+
if (readyPath && !waitForFile(readyPath)) {
|
|
583
|
+
console.log("AI Battery HUD start requested, but no visible window was confirmed. Run: ai-battery hud --foreground");
|
|
584
|
+
} else {
|
|
585
|
+
console.log("AI Battery HUD started. Drag it to place it; right-click and choose Exit to close.");
|
|
586
|
+
}
|
|
587
|
+
if (readyPath) fs.rmSync(readyPath, { force: true });
|
package/bin/ai-battery-hud.ps1
CHANGED
|
@@ -12,7 +12,8 @@ param(
|
|
|
12
12
|
[switch]$ClickThrough,
|
|
13
13
|
[switch]$StopExisting,
|
|
14
14
|
[switch]$UseWsl,
|
|
15
|
-
[switch]$Once
|
|
15
|
+
[switch]$Once,
|
|
16
|
+
[string]$ReadyPath = ""
|
|
16
17
|
)
|
|
17
18
|
|
|
18
19
|
$ErrorActionPreference = "Stop"
|
|
@@ -112,6 +113,22 @@ $script:hudAnchorY = "top"
|
|
|
112
113
|
$script:codexRowVisible = $true
|
|
113
114
|
$script:claudeRowVisible = $true
|
|
114
115
|
|
|
116
|
+
function Signal-HudReady {
|
|
117
|
+
if ([string]::IsNullOrWhiteSpace($ReadyPath)) { return }
|
|
118
|
+
try {
|
|
119
|
+
$dir = Split-Path -Parent $ReadyPath
|
|
120
|
+
if ($dir) { New-Item -ItemType Directory -Force -Path $dir | Out-Null }
|
|
121
|
+
@{
|
|
122
|
+
Ready = $true
|
|
123
|
+
Pid = $PID
|
|
124
|
+
At = [datetime]::UtcNow.ToString("o")
|
|
125
|
+
Mode = $Mode
|
|
126
|
+
} | ConvertTo-Json -Compress | Set-Content -Encoding UTF8 -Path $ReadyPath
|
|
127
|
+
} catch {
|
|
128
|
+
# Readiness is diagnostic only.
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
115
132
|
function Read-HudPlacement {
|
|
116
133
|
foreach ($path in @((Get-HudStatePath), (Get-LegacyHudStatePath))) {
|
|
117
134
|
try {
|
|
@@ -969,6 +986,7 @@ if ($Mode -eq "tray") {
|
|
|
969
986
|
$timer.add_Tick({ Update-Tray })
|
|
970
987
|
Update-Tray
|
|
971
988
|
$timer.Start()
|
|
989
|
+
Signal-HudReady
|
|
972
990
|
[System.Windows.Forms.Application]::Run($context)
|
|
973
991
|
Release-SingleInstance
|
|
974
992
|
exit 0
|
|
@@ -1807,6 +1825,7 @@ $form.add_SizeChanged({ Sync-HitFormBounds })
|
|
|
1807
1825
|
$form.add_Shown({
|
|
1808
1826
|
Show-HitForm
|
|
1809
1827
|
Update-HudVisibilityForFullscreen
|
|
1828
|
+
Signal-HudReady
|
|
1810
1829
|
})
|
|
1811
1830
|
|
|
1812
1831
|
Set-Position
|
|
@@ -4,9 +4,11 @@ use scripting additions
|
|
|
4
4
|
|
|
5
5
|
property statusItem : missing value
|
|
6
6
|
property detailItem : missing value
|
|
7
|
+
property detailView : missing value
|
|
7
8
|
property refreshTimer : missing value
|
|
8
9
|
property titleCommand : ""
|
|
9
10
|
property detailCommand : ""
|
|
11
|
+
property tooltipCommand : ""
|
|
10
12
|
property refreshInterval : 10
|
|
11
13
|
|
|
12
14
|
on run argv
|
|
@@ -14,10 +16,18 @@ on run argv
|
|
|
14
16
|
|
|
15
17
|
set titleCommand to item 1 of argv
|
|
16
18
|
set detailCommand to item 2 of argv
|
|
17
|
-
|
|
19
|
+
set tooltipCommand to detailCommand
|
|
20
|
+
if (count of argv) is greater than or equal to 4 then
|
|
21
|
+
set tooltipCommand to item 3 of argv
|
|
18
22
|
try
|
|
19
|
-
set refreshInterval to (item
|
|
23
|
+
set refreshInterval to (item 4 of argv) as real
|
|
20
24
|
end try
|
|
25
|
+
else
|
|
26
|
+
if (count of argv) is greater than or equal to 3 then
|
|
27
|
+
try
|
|
28
|
+
set refreshInterval to (item 3 of argv) as real
|
|
29
|
+
end try
|
|
30
|
+
end if
|
|
21
31
|
end if
|
|
22
32
|
|
|
23
33
|
current application's NSApplication's sharedApplication()
|
|
@@ -25,6 +35,7 @@ on run argv
|
|
|
25
35
|
|
|
26
36
|
set statusItem to current application's NSStatusBar's systemStatusBar()'s statusItemWithLength:(current application's NSVariableStatusItemLength)
|
|
27
37
|
statusItem's button()'s setTitle:"AI --"
|
|
38
|
+
statusItem's button()'s setImagePosition:(current application's NSNoImage)
|
|
28
39
|
statusItem's button()'s setToolTip:"AI Battery"
|
|
29
40
|
|
|
30
41
|
set statusMenu to current application's NSMenu's alloc()'s initWithTitle:"AI Battery"
|
|
@@ -46,27 +57,72 @@ on run argv
|
|
|
46
57
|
|
|
47
58
|
set refreshTimer to current application's NSTimer's scheduledTimerWithTimeInterval:refreshInterval target:me selector:"refresh:" userInfo:(missing value) repeats:true
|
|
48
59
|
current application's NSRunLoop's currentRunLoop()'s addTimer:refreshTimer forMode:(current application's NSRunLoopCommonModes)
|
|
49
|
-
current application's NSApp's run()
|
|
60
|
+
current application's NSApp's |run|()
|
|
50
61
|
end run
|
|
51
62
|
|
|
52
63
|
on refresh_(sender)
|
|
53
64
|
set titleText to "AI --"
|
|
54
65
|
set detailText to "AI Battery unavailable"
|
|
66
|
+
set tooltipText to "AI Battery unavailable"
|
|
67
|
+
set imagePath to ""
|
|
68
|
+
set menuImage to missing value
|
|
69
|
+
set detailImage to missing value
|
|
55
70
|
|
|
56
71
|
try
|
|
57
|
-
set
|
|
72
|
+
set imagePath to (do shell script (titleCommand as text))
|
|
58
73
|
end try
|
|
59
74
|
|
|
60
75
|
try
|
|
61
|
-
set detailText to do shell script detailCommand
|
|
76
|
+
set detailText to (do shell script (detailCommand as text))
|
|
62
77
|
end try
|
|
63
78
|
|
|
64
|
-
|
|
79
|
+
try
|
|
80
|
+
set tooltipText to (do shell script (tooltipCommand as text))
|
|
81
|
+
end try
|
|
82
|
+
|
|
83
|
+
if imagePath is "" then set imagePath to "AI --"
|
|
65
84
|
if detailText is "" then set detailText to "AI Battery unavailable"
|
|
85
|
+
if tooltipText is "" then set tooltipText to detailText
|
|
86
|
+
|
|
87
|
+
try
|
|
88
|
+
set menuImage to current application's NSImage's alloc()'s initWithContentsOfFile:imagePath
|
|
89
|
+
end try
|
|
90
|
+
|
|
91
|
+
try
|
|
92
|
+
set detailImage to current application's NSImage's alloc()'s initWithContentsOfFile:detailText
|
|
93
|
+
end try
|
|
94
|
+
|
|
95
|
+
if menuImage is not missing value then
|
|
96
|
+
set imageSize to menuImage's |size|()
|
|
97
|
+
statusItem's setLength:(width of imageSize)
|
|
98
|
+
menuImage's setTemplate:false
|
|
99
|
+
statusItem's button()'s setImage:menuImage
|
|
100
|
+
statusItem's button()'s setImageScaling:(current application's NSImageScaleProportionallyDown)
|
|
101
|
+
statusItem's button()'s setImagePosition:(current application's NSImageOnly)
|
|
102
|
+
statusItem's button()'s setTitle:""
|
|
103
|
+
else
|
|
104
|
+
set titleText to imagePath
|
|
105
|
+
if titleText is "" then set titleText to "AI --"
|
|
106
|
+
statusItem's setLength:(current application's NSVariableStatusItemLength)
|
|
107
|
+
statusItem's button()'s setImage:(missing value)
|
|
108
|
+
statusItem's button()'s setImagePosition:(current application's NSNoImage)
|
|
109
|
+
statusItem's button()'s setTitle:titleText
|
|
110
|
+
end if
|
|
111
|
+
|
|
112
|
+
if detailImage is not missing value then
|
|
113
|
+
set detailSize to detailImage's |size|()
|
|
114
|
+
set detailFrame to current application's NSMakeRect(0, 0, (width of detailSize), (height of detailSize))
|
|
115
|
+
set detailView to current application's NSImageView's alloc()'s initWithFrame:detailFrame
|
|
116
|
+
detailView's setImage:detailImage
|
|
117
|
+
detailView's setImageScaling:(current application's NSImageScaleNone)
|
|
118
|
+
detailItem's setView:detailView
|
|
119
|
+
detailItem's setTitle:""
|
|
120
|
+
else
|
|
121
|
+
detailItem's setView:(missing value)
|
|
122
|
+
detailItem's setTitle:detailText
|
|
123
|
+
end if
|
|
66
124
|
|
|
67
|
-
statusItem's button()'s
|
|
68
|
-
statusItem's button()'s setToolTip:detailText
|
|
69
|
-
detailItem's setTitle:detailText
|
|
125
|
+
statusItem's button()'s setToolTip:tooltipText
|
|
70
126
|
end refresh_
|
|
71
127
|
|
|
72
128
|
on quit_(sender)
|