Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions packages/agent-chat/src/ComputerManagePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -71,6 +72,7 @@ const copy = {
emptyDisk: "还没有文件",
idle: "空闲",
paused: "暂停",
pausedAt: "暂停 {rate} 星币/小时",
running: "开着",
screen: "打开电脑屏幕",
pause: "暂停",
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -375,7 +370,7 @@ export function ComputerManagePanel({
{busy ? t.resuming : t.resume}
</button>
) : null}
{statusLabel(row.status, t)} · {row.has_disk ? t.files : t.emptyDisk}
{computerStatusLine(row, t)}
</span>
</div>
);
Expand Down
28 changes: 28 additions & 0 deletions packages/agent-chat/src/computerManageCta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
}
28 changes: 26 additions & 2 deletions tests/computer-manage-cta.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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", () => {
Expand Down
Loading