diff --git a/packages/agent-chat/src/ComputerManagePanel.tsx b/packages/agent-chat/src/ComputerManagePanel.tsx index bad8601..034064b 100644 --- a/packages/agent-chat/src/ComputerManagePanel.tsx +++ b/packages/agent-chat/src/ComputerManagePanel.tsx @@ -4,7 +4,7 @@ import { useCallback, useEffect, useState } from "react"; import type { RanchLocale } from "./ranch-shell/i18n"; import { colors } from "./ranch-shell/styles"; import { computerName, type ComputerNameRow } from "./computerLabel"; -import { computerManageCta, extraAsksBeforeCreate, pauseAsksBeforeCharge } from "./computerManageCta"; +import { computerManageCta, computerStatusLine, extraAsksBeforeCreate, pauseAsksBeforeCharge } from "./computerManageCta"; import type { ScreenChargeHint } from "./computerSendBoot"; import { ConfirmDialog } from "./ranch-shell/ConfirmDialog"; @@ -39,6 +39,7 @@ const copy = { emptyDisk: "No files yet", idle: "Idle", paused: "Paused", + pausedAt: "Paused · {rate}/hr", running: "On", screen: "Open computer screen", pause: "Pause", @@ -71,6 +72,7 @@ const copy = { emptyDisk: "还没有文件", idle: "空闲", paused: "暂停", + pausedAt: "暂停 {rate} 星币/小时", running: "开着", screen: "打开电脑屏幕", pause: "暂停", @@ -101,13 +103,6 @@ async function authed( return fetch(`${base}${path}`, { ...init, headers }); } -function statusLabel(status: string, t: (typeof copy)[RanchLocale]): string { - if (status === "idle") return t.idle; - if (status === "paused") return t.paused; - if (status === "running") return t.running; - return status; -} - function withRate(template: string, rate: number | undefined): string { return template.replace("{rate}", rate == null ? "—" : String(rate)); } @@ -375,7 +370,7 @@ export function ComputerManagePanel({ {busy ? t.resuming : t.resume} ) : null} - {statusLabel(row.status, t)} · {row.has_disk ? t.files : t.emptyDisk} + {computerStatusLine(row, t)} ); diff --git a/packages/agent-chat/src/computerManageCta.ts b/packages/agent-chat/src/computerManageCta.ts index a70a73d..885ae8f 100644 --- a/packages/agent-chat/src/computerManageCta.ts +++ b/packages/agent-chat/src/computerManageCta.ts @@ -12,3 +12,31 @@ export function extraAsksBeforeCreate(count: number): boolean { export function pauseAsksBeforeCharge(pausedCreditsPerHour: number | undefined): boolean { return (pausedCreditsPerHour ?? 0) > 0; } + +/** Status line. A billed pause prints the hourly disk price. */ +export function computerStatusLine( + row: { status: string; has_disk: boolean; paused_credits_per_hour?: number }, + labels: { + idle: string; + paused: string; + running: string; + files: string; + emptyDisk: string; + pausedAt: string; + }, +): string { + const status = + row.status === "idle" + ? labels.idle + : row.status === "paused" + ? labels.paused + : row.status === "running" + ? labels.running + : row.status; + const disk = row.has_disk ? labels.files : labels.emptyDisk; + const rate = row.paused_credits_per_hour ?? 0; + if (row.status === "paused" && rate > 0) { + return `${labels.pausedAt.replace("{rate}", String(rate))} · ${disk}`; + } + return `${status} · ${disk}`; +} diff --git a/tests/computer-manage-cta.test.cjs b/tests/computer-manage-cta.test.cjs index 8dfcb31..a47608a 100644 --- a/tests/computer-manage-cta.test.cjs +++ b/tests/computer-manage-cta.test.cjs @@ -13,7 +13,7 @@ const js = ts.transpileModule(source, { }).outputText; const moduleExports = {}; new Function("exports", js)(moduleExports); -const { computerManageCta, extraAsksBeforeCreate, pauseAsksBeforeCharge } = moduleExports; +const { computerManageCta, extraAsksBeforeCreate, pauseAsksBeforeCharge, computerStatusLine } = moduleExports; test("an empty list creates the default computer", () => { assert.equal(computerManageCta(0), "create"); @@ -56,7 +56,31 @@ test("a charged pause shows as 暂停 instead of 空闲", () => { "utf8", ); assert.match(panel, /paused: "暂停"/); - assert.match(panel, /status === "paused"/); + assert.match(panel, /pausedAt: "暂停 \{rate\} 星币\/小时"/); + assert.match(panel, /computerStatusLine\(row, t\)/); +}); + +test("a billed pause prints the hourly disk price on the status line", () => { + const labels = { + idle: "空闲", + paused: "暂停", + running: "开着", + files: "已有文件", + emptyDisk: "还没有文件", + pausedAt: "暂停 {rate} 星币/小时", + }; + assert.equal( + computerStatusLine({ status: "paused", has_disk: true, paused_credits_per_hour: 11 }, labels), + "暂停 11 星币/小时 · 已有文件", + ); + assert.equal( + computerStatusLine({ status: "idle", has_disk: true, paused_credits_per_hour: 0 }, labels), + "空闲 · 已有文件", + ); + assert.equal( + computerStatusLine({ status: "running", has_disk: true, paused_credits_per_hour: 11 }, labels), + "开着 · 已有文件", + ); }); test("a running computer can be paused from the panel", () => {