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 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 | 미지원 | 지원 | 지원 | 지원 | `ai-battery-run`이 POSIX PTY와 `python3`를 사용합니다. |
54
- | `ai-battery setup codex` | 미지원 | 지원 | 지원 | 지원 | `codex` wrapper POSIX shell/PTY 기반입니다. |
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에는 `codex` wrapper를 설치해서 이후에는 추가 명령 없이 원래처럼 실행하게 합니다.
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`을 PATH 앞쪽으로 추가합니다. 새 터미널부터 `codex`가 자동으로 AI Battery 하단 행과 함께 실행됩니다. 현재 터미널에서 바로 쓰려면 `setup` 출력에 표시되는 `source ...` 명령을 실행하세요.
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에서는 메뉴바 두께에 맞춰 `Cx 75% | Cl 4%`처럼 요약만 표시하고, 클릭하면 자세한 상태를 볼 수 있습니다.
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에서 실행됩니다. `ai-battery-run`과 Codex terminal row는 Python 3와 POSIX PTY가 필요해서 WSL/Linux/macOS에서 지원됩니다. HUD는 Windows/WSL에서는 PowerShell/WinForms, macOS에서는 내장 `osascript`와 AppleScriptObjC를 사용합니다.
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
 
@@ -1,89 +1,21 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- SCRIPT_PATH="$(readlink -f "${BASH_SOURCE[0]}")"
5
- SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
6
- HUD_PS1="$SCRIPT_DIR/ai-battery-hud.ps1"
7
-
8
- if ! command -v powershell.exe >/dev/null 2>&1; then
9
- echo "ai-battery-hud: powershell.exe is required for the Windows always-on-top HUD." >&2
10
- exit 1
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
- HUD_PS1_WIN="$(wslpath -w "$HUD_PS1")"
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 [[ -z "${AI_BATTERY_COMMAND:-}" ]]; then
46
- NODE_BIN="$(command -v node || true)"
47
- if [[ -z "$NODE_BIN" ]]; then
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
- ps_quote() {
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" "$@"
@@ -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
- if (macHudPids().length) {
317
- console.log("AI Battery menu bar is already running.");
318
- process.exit(0);
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 \"ai-battery hud stop\" to close it.");
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
- console.log("AI Battery HUD started or already running. Drag it to place it; right-click and choose Exit to close.");
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 });
@@ -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
- if (count of argv) is greater than or equal to 3 then
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 3 of argv) as real
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 titleText to do shell script titleCommand
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
- if titleText is "" then set titleText to "AI --"
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 setTitle:titleText
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)