93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
import { TextAttributes } from "@opentui/core"
|
|
import { useKeyboard, useRenderer, useTerminalDimensions } from "@opentui/solid"
|
|
import * as Clipboard from "@tui/util/clipboard"
|
|
import { createSignal } from "solid-js"
|
|
import { InstallationVersion } from "@/installation/version"
|
|
import { win32FlushInputBuffer } from "../win32"
|
|
import { getScrollAcceleration } from "../util/scroll"
|
|
|
|
export function ErrorComponent(props: {
|
|
error: Error
|
|
reset: () => void
|
|
onBeforeExit?: () => Promise<void>
|
|
onExit: () => Promise<void>
|
|
mode?: "dark" | "light"
|
|
}) {
|
|
const term = useTerminalDimensions()
|
|
const renderer = useRenderer()
|
|
|
|
const handleExit = async () => {
|
|
await props.onBeforeExit?.()
|
|
renderer.setTerminalTitle("")
|
|
renderer.destroy()
|
|
win32FlushInputBuffer()
|
|
await props.onExit()
|
|
}
|
|
|
|
useKeyboard((evt) => {
|
|
if (evt.ctrl && evt.name === "c") {
|
|
void handleExit()
|
|
}
|
|
})
|
|
const [copied, setCopied] = createSignal(false)
|
|
|
|
const issueURL = new URL("https://github.com/anomalyco/opencode/issues/new?template=bug-report.yml")
|
|
|
|
// 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={handleExit} 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>
|
|
)
|
|
}
|