diff --git a/tui_gateway/server.py b/tui_gateway/server.py index ccc39d34cb55..e13f34910477 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -11319,31 +11319,6 @@ def _(rid, params: dict) -> dict: # ── Methods: config ────────────────────────────────────────────────── -@method("system.battery") -def _(rid, params: dict) -> dict: - """Return the host battery status for the status-bar read-out. - - Always resolves with a payload; ``available: false`` means there is no - battery (desktop/server/VM) or the read failed. The TUI only polls this - while the battery indicator is enabled. - """ - try: - from agent.battery import battery_category, read_battery - - batt = read_battery() - return _ok( - rid, - { - "available": batt.available, - "percent": batt.percent, - "plugged": batt.plugged, - "category": battery_category(batt), - }, - ) - except Exception: - return _ok(rid, {"available": False, "percent": None, "plugged": None, "category": "dim"}) - - @method("config.set") def _(rid, params: dict) -> dict: key, value = params.get("key", ""), params.get("value", "") @@ -12773,6 +12748,31 @@ def _(rid, params: dict) -> dict: # ── Methods: tools & system ────────────────────────────────────────── +@method("system.battery") +def _(rid, params: dict) -> dict: + """Return the host battery status for the status-bar read-out. + + Always resolves with a payload; ``available: false`` means there is no + battery (desktop/server/VM) or the read failed. The TUI only polls this + while the battery indicator is enabled. + """ + try: + from agent.battery import battery_category, read_battery + + batt = read_battery() + return _ok( + rid, + { + "available": batt.available, + "percent": batt.percent, + "plugged": batt.plugged, + "category": battery_category(batt), + }, + ) + except Exception: + return _ok(rid, {"available": False, "percent": None, "plugged": None, "category": "dim"}) + + @method("process.stop") def _(rid, params: dict) -> dict: try: diff --git a/ui-tui/src/app/slash/commands/core.ts b/ui-tui/src/app/slash/commands/core.ts index 14380ec6c1c6..3a952c399d5e 100644 --- a/ui-tui/src/app/slash/commands/core.ts +++ b/ui-tui/src/app/slash/commands/core.ts @@ -11,7 +11,8 @@ import type { SessionStatusResponse, SessionSteerResponse, SessionTitleResponse, - SessionUndoResponse + SessionUndoResponse, + SystemBatteryResponse } from '../../../gatewayTypes.js' import { writeClipboardText } from '../../../lib/clipboard.js' import { writeOsc52Clipboard } from '../../../lib/osc52.js' @@ -580,13 +581,35 @@ export const coreCommands: SlashCommand[] = [ }, { - help: 'toggle a color-coded battery indicator in the status bar [on|off]', + help: 'toggle a color-coded battery indicator in the status bar [on|off|status]', name: 'battery', run: (arg, ctx) => { + const mode = arg.trim().toLowerCase() + + // `/battery status` reports the current setting plus a live reading, + // matching the CLI surface. Fetch on demand so it works even while the + // indicator (and its poller) is off. + if (mode === 'status' || mode === 'show') { + const state = ctx.ui.battery ? 'on' : 'off' + + ctx.gateway + .rpc('system.battery', {}) + .then(r => { + if (r?.available && typeof r.percent === 'number') { + ctx.transcript.sys(`battery indicator ${state} — currently ${r.plugged ? '⚡' : '🔋'} ${r.percent}%`) + } else { + ctx.transcript.sys(`battery indicator ${state} — no battery detected on this machine`) + } + }) + .catch(() => ctx.transcript.sys(`battery indicator ${state}`)) + + return + } + const next = flagFromArg(arg, ctx.ui.battery) if (next === null) { - return ctx.transcript.sys('usage: /battery [on|off|toggle]') + return ctx.transcript.sys('usage: /battery [on|off|status]') } patchUiState({ battery: next, ...(next ? {} : { batteryStatus: null }) }) diff --git a/ui-tui/src/components/appChrome.tsx b/ui-tui/src/components/appChrome.tsx index 3698aa99bc85..cd2d4a9ba9de 100644 --- a/ui-tui/src/components/appChrome.tsx +++ b/ui-tui/src/components/appChrome.tsx @@ -209,8 +209,9 @@ function batteryColor(info: BatteryInfo, t: Theme): string { } // Compact battery label: a bolt while charging, else a battery glyph. +// Renders `--` for an unknown percent so a null can never surface as "null%". function batteryLabel(info: BatteryInfo): string { - return `${info.plugged ? '⚡' : '🔋'} ${info.percent}%` + return `${info.plugged ? '⚡' : '🔋'} ${info.percent ?? '--'}%` } // Colour a credits notice by its level. The notice TEXT already carries its