- 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>
82 lines
2.8 KiB
TypeScript
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>
|
|
)
|
|
}
|