ai-battery 0.1.4 → 0.1.6
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 +7 -5
- 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 +339 -0
- package/bin/ai-battery.js +644 -68
- 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 프로세스 목록을 사용합니다.
|
|
@@ -154,7 +154,7 @@ npm uninstall -g ai-battery
|
|
|
154
154
|
|
|
155
155
|
## Setup
|
|
156
156
|
|
|
157
|
-
`setup`은 한 번만 실행합니다. Claude Code에는 statusLine hook을 설치하고, Codex에는
|
|
157
|
+
`setup`은 한 번만 실행합니다. Claude Code에는 statusLine hook을 설치하고, Codex에는 platform wrapper를 설치해서 이후에는 추가 명령 없이 원래처럼 실행하게 합니다.
|
|
158
158
|
|
|
159
159
|
```bash
|
|
160
160
|
ai-battery setup
|
|
@@ -169,6 +169,8 @@ ai-battery setup codex
|
|
|
169
169
|
|
|
170
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
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 안에서만 표시됩니다.
|
|
173
|
+
|
|
172
174
|
Codex 하단 행이 보이지 않으면 진단을 실행합니다.
|
|
173
175
|
|
|
174
176
|
```bash
|
|
@@ -237,7 +239,7 @@ Claude가 한 번 이상 statusLine payload를 전달해야 Claude의 사용량
|
|
|
237
239
|
|
|
238
240
|
## Desktop HUD
|
|
239
241
|
|
|
240
|
-
일반 터미널 위에 외부 프로세스가 안전하게 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 로고, 짧은 미터, 퍼센트만 표시하고, 클릭하면 자세한 상태를 볼 수 있습니다.
|
|
241
243
|
|
|
242
244
|
```bash
|
|
243
245
|
ai-battery hud
|
|
@@ -303,7 +305,7 @@ Codex는 최근 세션 로그에서 `rate_limits` 이벤트를 찾습니다. Cla
|
|
|
303
305
|
|
|
304
306
|
## Source Environment
|
|
305
307
|
|
|
306
|
-
기본 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를 사용합니다.
|
|
307
309
|
|
|
308
310
|
Codex 데이터는 기본적으로 `~/.codex/sessions`를 읽습니다. 다른 위치를 쓰고 있다면 `CODEX_HOME`을 설정하세요.
|
|
309
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)
|