summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoropencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>2026-01-15 11:50:18 -0600
committerGitHub <[email protected]>2026-01-15 11:50:18 -0600
commitebc194ca9a8725721da13db5e2024a411a99aa8c (patch)
treefad6f2d38f4663b6d4f30281ef9e289cca59fcc8
parent4edb4fa4fa8735a205bd0750513e7c50adf35390 (diff)
downloadopencode-ebc194ca9a8725721da13db5e2024a411a99aa8c.tar.gz
opencode-ebc194ca9a8725721da13db5e2024a411a99aa8c.zip
Prettify retry duration display in TUI (#8608)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: rekram1-node <[email protected]>
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx4
-rw-r--r--packages/opencode/src/util/format.ts20
-rw-r--r--packages/opencode/test/util/format.test.ts59
3 files changed, 82 insertions, 1 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
index 9ad85d08f..96b9e8ffd 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
@@ -23,6 +23,7 @@ import type { FilePart } from "@opencode-ai/sdk/v2"
import { TuiEvent } from "../../event"
import { iife } from "@/util/iife"
import { Locale } from "@/util/locale"
+import { formatDuration } from "@/util/format"
import { createColors, createFrames } from "../../ui/spinner.ts"
import { useDialog } from "@tui/ui/dialog"
import { DialogProvider as DialogProviderConnect } from "../dialog-provider"
@@ -1037,7 +1038,8 @@ export function Prompt(props: PromptProps) {
if (!r) return ""
const baseMessage = message()
const truncatedHint = isTruncated() ? " (click to expand)" : ""
- const retryInfo = ` [retrying ${seconds() > 0 ? `in ${seconds()}s ` : ""}attempt #${r.attempt}]`
+ const duration = formatDuration(seconds())
+ const retryInfo = ` [retrying ${duration ? `in ${duration} ` : ""}attempt #${r.attempt}]`
return baseMessage + truncatedHint + retryInfo
}
diff --git a/packages/opencode/src/util/format.ts b/packages/opencode/src/util/format.ts
new file mode 100644
index 000000000..4ae62eac4
--- /dev/null
+++ b/packages/opencode/src/util/format.ts
@@ -0,0 +1,20 @@
+export function formatDuration(secs: number) {
+ if (secs <= 0) return ""
+ if (secs < 60) return `${secs}s`
+ if (secs < 3600) {
+ const mins = Math.floor(secs / 60)
+ const remaining = secs % 60
+ return remaining > 0 ? `${mins}m ${remaining}s` : `${mins}m`
+ }
+ if (secs < 86400) {
+ const hours = Math.floor(secs / 3600)
+ const remaining = Math.floor((secs % 3600) / 60)
+ return remaining > 0 ? `${hours}h ${remaining}m` : `${hours}h`
+ }
+ if (secs < 604800) {
+ const days = Math.floor(secs / 86400)
+ return days === 1 ? "~1 day" : `~${days} days`
+ }
+ const weeks = Math.floor(secs / 604800)
+ return weeks === 1 ? "~1 week" : `~${weeks} weeks`
+}
diff --git a/packages/opencode/test/util/format.test.ts b/packages/opencode/test/util/format.test.ts
new file mode 100644
index 000000000..5b346e7f6
--- /dev/null
+++ b/packages/opencode/test/util/format.test.ts
@@ -0,0 +1,59 @@
+import { describe, expect, test } from "bun:test"
+import { formatDuration } from "../../src/util/format"
+
+describe("util.format", () => {
+ describe("formatDuration", () => {
+ test("returns empty string for zero or negative values", () => {
+ expect(formatDuration(0)).toBe("")
+ expect(formatDuration(-1)).toBe("")
+ expect(formatDuration(-100)).toBe("")
+ })
+
+ test("formats seconds under a minute", () => {
+ expect(formatDuration(1)).toBe("1s")
+ expect(formatDuration(30)).toBe("30s")
+ expect(formatDuration(59)).toBe("59s")
+ })
+
+ test("formats minutes under an hour", () => {
+ expect(formatDuration(60)).toBe("1m")
+ expect(formatDuration(61)).toBe("1m 1s")
+ expect(formatDuration(90)).toBe("1m 30s")
+ expect(formatDuration(120)).toBe("2m")
+ expect(formatDuration(330)).toBe("5m 30s")
+ expect(formatDuration(3599)).toBe("59m 59s")
+ })
+
+ test("formats hours under a day", () => {
+ expect(formatDuration(3600)).toBe("1h")
+ expect(formatDuration(3660)).toBe("1h 1m")
+ expect(formatDuration(7200)).toBe("2h")
+ expect(formatDuration(8100)).toBe("2h 15m")
+ expect(formatDuration(86399)).toBe("23h 59m")
+ })
+
+ test("formats days under a week", () => {
+ expect(formatDuration(86400)).toBe("~1 day")
+ expect(formatDuration(172800)).toBe("~2 days")
+ expect(formatDuration(259200)).toBe("~3 days")
+ expect(formatDuration(604799)).toBe("~6 days")
+ })
+
+ test("formats weeks", () => {
+ expect(formatDuration(604800)).toBe("~1 week")
+ expect(formatDuration(1209600)).toBe("~2 weeks")
+ expect(formatDuration(1609200)).toBe("~2 weeks")
+ })
+
+ test("handles boundary values correctly", () => {
+ expect(formatDuration(59)).toBe("59s")
+ expect(formatDuration(60)).toBe("1m")
+ expect(formatDuration(3599)).toBe("59m 59s")
+ expect(formatDuration(3600)).toBe("1h")
+ expect(formatDuration(86399)).toBe("23h 59m")
+ expect(formatDuration(86400)).toBe("~1 day")
+ expect(formatDuration(604799)).toBe("~6 days")
+ expect(formatDuration(604800)).toBe("~1 week")
+ })
+ })
+})