diff --git a/package.json b/package.json
index 7ce06896ad..ff07c3ad29 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "opencode",
- "description": "AI-powered development tool",
+ "description": "AI-powered coding assistant that generates perfect code most of the time",
"private": true,
"type": "module",
"packageManager": "bun@1.3.10",
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 249a48d358..9a1796a2f9 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
@@ -814,208 +814,204 @@ export function Prompt(props: PromptProps) {
agentStyleId={agentStyleId}
promptPartTypeId={() => promptPartTypeId}
/>
- (anchor = r)} visible={props.visible !== false}>
-
-
-
-
-
-
-
- }>
-
-
-
- [⋯]}>
-
-
-
-
- {(() => {
- 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 is way too hot right now"
- if (r.message.length > 80) return r.message.slice(0, 80) + "..."
- return r.message
- })
- const isTruncated = createMemo(() => {
- const r = retry()
- if (!r) return false
- return r.message.length > 120
- })
- 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)
- })
- })
- const handleMessageClick = () => {
- const r = retry()
- if (!r) return
- if (isTruncated()) {
- DialogAlert.show(dialog, "Retry Error", r.message)
- }
- }
-
- const retryText = () => {
- const r = retry()
- if (!r) return ""
- const baseMessage = message()
- const truncatedHint = isTruncated() ? " (click to expand)" : ""
- const duration = formatDuration(seconds())
- const retryInfo = ` [retrying ${duration ? `in ${duration} ` : ""}attempt #${r.attempt}]`
- return baseMessage + truncatedHint + retryInfo
- }
-
- return (
-
-
- {retryText()}
-
-
- )
- })()}
-
-
- 0 ? theme.primary : theme.text}>
- esc{" "}
- 0 ? theme.primary : theme.textMuted }}>
- {store.interrupt > 0 ? "again to interrupt" : "interrupt"}
-
-
-
-
-
-
-
-
- 0}>
+
+
+ 0}>
+
+ {keybind.print("variant_cycle")} variants
+
+
- {keybind.print("variant_cycle")} variants
+ {keybind.print("agent_cycle")} agents
-
-
- {keybind.print("agent_cycle")} agents
-
-
- {keybind.print("command_list")} commands
-
-
-
-
- esc exit shell mode
-
-
-
-
-
+
+ {keybind.print("command_list")} commands
+
+
+
+
+
+
+ {store.mode === "shell" ? "Shell" : Locale.titlecase(local.agent.current().name)}{" "}
+
+
+
+
>
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx
index f64dbe533a..62c0e8694e 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx
@@ -93,9 +93,6 @@ export function Header() {
paddingBottom={1}
paddingLeft={2}
paddingRight={1}
- {...SplitBorder}
- border={["left"]}
- borderColor={theme.border}
flexShrink={0}
backgroundColor={theme.backgroundPanel}
>
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
index 4682c50df1..d9e0c4e8d4 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
@@ -16,7 +16,7 @@ import { Dynamic } from "solid-js/web"
import path from "path"
import { useRoute, useRouteData } from "@tui/context/route"
import { useSync } from "@tui/context/sync"
-import { SplitBorder } from "@tui/component/border"
+import { EmptyBorder, SplitBorder } from "@tui/component/border"
import { Spinner } from "@tui/component/spinner"
import { selectedForeground, useTheme } from "@tui/context/theme"
import {
@@ -1046,7 +1046,12 @@ export function Session() {
}}
>
-
+
@@ -1057,7 +1062,7 @@ export function Session() {
paddingRight: showScrollbar() ? 1 : 0,
}}
verticalScrollbarOptions={{
- paddingLeft: 1,
+ paddingLeft: 0,
visible: showScrollbar(),
trackOptions: {
backgroundColor: theme.backgroundElement,
@@ -1251,13 +1256,7 @@ function UserMessage(props: {
return (
<>
-
+
{
setHover(true)
@@ -1268,9 +1267,15 @@ function UserMessage(props: {
onMouseUp={props.onMouseUp}
paddingTop={1}
paddingBottom={1}
- paddingLeft={2}
- backgroundColor={hover() ? theme.backgroundElement : theme.backgroundPanel}
+ backgroundColor={hover() ? theme.backgroundMenu : theme.backgroundMenu}
+ paddingLeft={1}
flexShrink={0}
+ border={["left"]}
+ borderColor={color()}
+ customBorderChars={{
+ ...EmptyBorder,
+ vertical: "▎",
+ }}
>
{text()?.text}
@@ -1331,6 +1336,7 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
const messages = createMemo(() => sync.data.message[props.message.sessionID] ?? [])
const final = createMemo(() => {
+ if (props.message.error) return true
return props.message.finish && !["tool-calls", "unknown"].includes(props.message.finish)
})
@@ -1342,6 +1348,8 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
return props.message.time.completed - user.time.created
})
+ const interrupted = createMemo(() => props.message.error?.name === "MessageAbortedError")
+
const keybind = useKeybind()
return (
@@ -1362,14 +1370,14 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
}}
x.type === "tool" && x.tool === "task")}>
-
+
{keybind.print("session_child_first")}
view subagents
-
+
-
-
-
-
- ▣{" "}
- {" "}
- {Locale.titlecase(props.message.mode)}
- · {props.message.modelID}
-
- · {Locale.duration(duration())}
-
-
- · interrupted
-
-
+
+
+
+
+ {Locale.titlecase(props.message.mode)}
+ · {props.message.modelID}
+
+
+
+
+ {Locale.titlecase(props.message.mode)}
+ · {props.message.modelID}
+
+ · {Locale.duration(duration())}
+
+
+ · interrupted
+
+
+
@@ -1457,7 +1463,7 @@ function TextPart(props: { last: boolean; part: TextPart; message: AssistantMess
const { theme, syntax } = useTheme()
return (
-
+
) {
function ToolTitle(props: { fallback: string; when: any; icon: string; children: JSX.Element }) {
const { theme } = useTheme()
return (
-
+
~ {props.fallback}>} when={props.when}>
{props.icon} {props.children}
@@ -1676,7 +1682,7 @@ function InlineTool(props: {
return (
props.onClick && setHover(true)}
onMouseOut={() => setHover(false)}
onMouseUp={() => {
@@ -1711,7 +1717,7 @@ function InlineTool(props: {
-
+
~ {props.pending}>} when={props.complete}>
{props.icon} {props.children}
@@ -1738,15 +1744,13 @@ function BlockTool(props: {
const error = createMemo(() => (props.part?.state.status === "error" ? props.part.state.error : undefined))
return (
props.onClick && setHover(true)}
onMouseOut={() => setHover(false)}
onMouseUp={() => {
@@ -1757,7 +1761,7 @@ function BlockTool(props: {
+
{props.title}
}
@@ -1905,8 +1909,8 @@ function Read(props: ToolProps) {
{(filepath) => (
-
-
+
+
↳ Loaded {normalizePath(filepath)}
@@ -2056,7 +2060,7 @@ function Edit(props: ToolProps) {
-
+
) {
width="100%"
wrapMode={ctx.diffWrapMode()}
fg={theme.text}
+ bg={theme.background}
addedBg={theme.diffAddedBg}
removedBg={theme.diffRemovedBg}
contextBg={theme.diffContextBg}