Files
zmo-cli/packages/opencode/src/cli/cmd/tui/component/error-component.tsx
吴中洋 324549475b
Some checks failed
compliance-close / close-non-compliant (push) Has been cancelled
beta / sync (push) Has been cancelled
docs-update / update-docs (push) Has been cancelled
stats / stats (push) Has been cancelled
feat: 清除终端标题/provider显示名/TUI 中残留的 opencode
- app.tsx: 终端标题 setTerminalTitle 改 zmo(之前改错了 attention 声音通知)
- provider.ts: opencode provider 显示名映射为 "Zmo"(修复底栏 OpenCode Zen,源自远程 models.dev 数据)
- 删除 dialog-provider 里 OpenCode Zen/Go 付费服务介绍段
- footer/attention声音包名/更新提示/dialog-status/error上报URL/docs链接 → zmo

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 18:44:47 +08:00

82 lines
2.8 KiB
TypeScript

import { TextAttributes } from "@opentui/core"
import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
import * as Clipboard from "@tui/util/clipboard"
import { createSignal } from "solid-js"
import { InstallationVersion } from "@opencode-ai/core/installation/version"
import { getScrollAcceleration } from "../util/scroll"
export function ErrorComponent(props: {
error: Error
reset: () => void
exit: () => Promise<void>
mode?: "dark" | "light"
}) {
const term = useTerminalDimensions()
useKeyboard((evt) => {
if (evt.ctrl && evt.name === "c") {
void props.exit()
}
})
const [copied, setCopied] = createSignal(false)
const issueURL = new URL("https://github.com/helpLogin/zmo-cli/issues/new")
// Choose safe fallback colors per mode since theme context may not be available
const isLight = props.mode === "light"
const colors = {
bg: isLight ? "#ffffff" : "#0a0a0a",
text: isLight ? "#1a1a1a" : "#eeeeee",
muted: isLight ? "#8a8a8a" : "#808080",
primary: isLight ? "#3b7dd8" : "#fab283",
}
if (props.error.message) {
issueURL.searchParams.set("title", `opentui: fatal: ${props.error.message}`)
}
if (props.error.stack) {
issueURL.searchParams.set(
"description",
"```\n" + props.error.stack.substring(0, 6000 - issueURL.toString().length) + "...\n```",
)
}
issueURL.searchParams.set("opencode-version", InstallationVersion)
const copyIssueURL = () => {
void Clipboard.copy(issueURL.toString()).then(() => {
setCopied(true)
})
}
return (
<box flexDirection="column" gap={1} backgroundColor={colors.bg}>
<box flexDirection="row" gap={1} alignItems="center">
<text attributes={TextAttributes.BOLD} fg={colors.text}>
Please report an issue.
</text>
<box onMouseUp={copyIssueURL} backgroundColor={colors.primary} padding={1}>
<text attributes={TextAttributes.BOLD} fg={colors.bg}>
Copy issue URL (exception info pre-filled)
</text>
</box>
{copied() && <text fg={colors.muted}>Successfully copied</text>}
</box>
<box flexDirection="row" gap={2} alignItems="center">
<text fg={colors.text}>A fatal error occurred!</text>
<box onMouseUp={props.reset} backgroundColor={colors.primary} padding={1}>
<text fg={colors.bg}>Reset TUI</text>
</box>
<box onMouseUp={() => void props.exit()} backgroundColor={colors.primary} padding={1}>
<text fg={colors.bg}>Exit</text>
</box>
</box>
<scrollbox height={Math.floor(term().height * 0.7)} scrollAcceleration={getScrollAcceleration()}>
<text fg={colors.muted}>{props.error.stack}</text>
</scrollbox>
<text fg={colors.text}>{props.error.message}</text>
</box>
)
}