diff --git a/packages/opencode/src/cli/cmd/tui/component/border.tsx b/packages/opencode/src/cli/cmd/tui/component/border.tsx
index 9cbb96068..333071020 100644
--- a/packages/opencode/src/cli/cmd/tui/component/border.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/border.tsx
@@ -1,16 +1,21 @@
+export const EmptyBorder = {
+ topLeft: "",
+ bottomLeft: "",
+ vertical: "",
+ topRight: "",
+ bottomRight: "",
+ horizontal: " ",
+ bottomT: "",
+ topT: "",
+ cross: "",
+ leftT: "",
+ rightT: "",
+}
+
export const SplitBorder = {
border: ["left" as const, "right" as const],
customBorderChars: {
- topLeft: "",
- bottomLeft: "",
+ ...EmptyBorder,
vertical: "┃",
- topRight: "",
- bottomRight: "",
- horizontal: "",
- bottomT: "",
- topT: "",
- cross: "",
- leftT: "",
- rightT: "",
},
}
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 8281ab617..c74b3da8b 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
@@ -9,10 +9,10 @@ import {
fg,
type KeyBinding,
} from "@opentui/core"
-import { createEffect, createMemo, Match, Switch, type JSX, onMount } from "solid-js"
+import { createEffect, createMemo, Match, Switch, type JSX, onMount, createSignal, onCleanup, Show } from "solid-js"
import { useLocal } from "@tui/context/local"
import { useTheme } from "@tui/context/theme"
-import { SplitBorder } from "@tui/component/border"
+import { EmptyBorder, SplitBorder } from "@tui/component/border"
import { useSDK } from "@tui/context/sdk"
import { useRoute } from "@tui/context/route"
import { useSync } from "@tui/context/sync"
@@ -29,6 +29,8 @@ import { Clipboard } from "../../util/clipboard"
import type { FilePart } from "@opencode-ai/sdk"
import { TuiEvent } from "../../event"
import { iife } from "@/util/iife"
+import { Locale } from "@/util/locale"
+import { Shimmer } from "../../ui/shimmer"
export type PromptProps = {
sessionID?: string
@@ -57,7 +59,7 @@ export function Prompt(props: PromptProps) {
const sdk = useSDK()
const route = useRoute()
const sync = useSync()
- const status = createMemo(() => (props.sessionID ? sync.session.status(props.sessionID) : "idle"))
+ const status = createMemo(() => sync.data.session_status[props.sessionID ?? ""] ?? { type: "idle" })
const history = usePromptHistory()
const command = useCommandDialog()
const renderer = useRenderer()
@@ -542,6 +544,16 @@ export function Prompt(props: PromptProps) {
return
}
+ const highlight = createMemo(() => {
+ if (keybind.leader) return theme.accent
+ if (store.mode === "shell") return theme.primary
+ return local.agent.color(local.agent.current().name)
+ })
+
+ createEffect(() => {
+ renderer.setCursorColor(highlight())
+ })
+
return (
<>
(anchor = r)}>
-
-
- {store.mode === "normal" ? ">" : "!"}
-
-
-
+
-
-
-
-
-
- {local.model.parsed().provider}{" "}
- {local.model.parsed().model}
-
-
-
- compacting...
-
-
+
+
+ {keybind.leader
+ ? "Leader"
+ : store.mode === "shell"
+ ? "Shell"
+ : Locale.titlecase(local.agent.current().name)}{" "}
+
- 0 ? theme.primary : theme.text}>
- esc{" "}
- 0 ? theme.primary : theme.textMuted }}>
- {store.interrupt > 0 ? "again to interrupt" : "interrupt"}
-
+ {local.model.parsed().provider}
+
+ {local.model.parsed().model}
-
- {props.hint!}
-
+
+
+
+
+
+
+
+ }>
+
+
+ {(() => {
+ const retry = createMemo(() => {
+ const s = status()
+ if (s.type !== "retry") return
+ return s
+ })
+ const message = createMemo(() => {
+ const r = retry()
+ if (!r) return
+ if (r.message.includes("exceeded your current quota") && r.message.includes("gemini"))
+ return "gemini 3 way too hot right now"
+ if (r.message.length > 50) return r.message.slice(0, 50) + "..."
+ return r.message
+ })
+ const [seconds, setSeconds] = createSignal(0)
+ onMount(() => {
+ const timer = setInterval(() => {
+ const next = retry()?.next
+ if (next) setSeconds(Math.round((next - Date.now()) / 1000))
+ }, 1000)
+
+ onCleanup(() => {
+ clearInterval(timer)
+ })
+ })
+ return (
+
+
+ {message()} [retrying {seconds() > 0 ? `in ${seconds()}s ` : ""}
+ attempt #{retry()!.attempt}]
+
+
+ )
+ })()}
+
+ 0 ? theme.primary : theme.text}>
+ esc{" "}
+ 0 ? theme.primary : theme.textMuted }}>
+ {store.interrupt > 0 ? "again to interrupt" : "interrupt"}
+
+
+
+
+
+
+
+ {keybind.print("agent_cycle")} switch agent
+
{keybind.print("command_list")} commands
-
-
+
+
>
diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx
index c2db85442..f9963fae8 100644
--- a/packages/opencode/src/cli/cmd/tui/context/local.tsx
+++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx
@@ -10,6 +10,7 @@ import { createSimpleContext } from "./helper"
import { useToast } from "../ui/toast"
import { Provider } from "@/provider/provider"
import { useArgs } from "./args"
+import { RGBA } from "@opentui/core"
export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
name: "Local",
@@ -91,7 +92,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
},
color(name: string) {
const agent = agents().find((x) => x.name === name)
- if (agent?.color) return agent.color
+ if (agent?.color) return RGBA.fromHex(agent.color)
const index = agents().findIndex((x) => x.name === name)
return colors()[index % colors().length]
},