mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-24 06:33:01 -04:00
fix(app): refine mobile error page (#44353)
This commit is contained in:
committed by
GitHub
parent
0a5349c51c
commit
708e4d8867
@@ -570,10 +570,13 @@ export const dict = {
|
||||
"toast.update.action.notYet": "Not yet",
|
||||
|
||||
"error.page.title": "Something went wrong",
|
||||
"error.page.title.status": "Server request failed",
|
||||
"error.page.description": "An error occurred while loading the application.",
|
||||
"error.page.description.status": "The server returned an HTTP {{status}} response.",
|
||||
"error.page.description.localServerStartup": "An error occurred while starting the local server.",
|
||||
"error.page.details.label": "Error Details",
|
||||
"error.page.action.restart": "Restart",
|
||||
"error.page.action.reload": "Reload",
|
||||
"error.page.action.report": "Report Error",
|
||||
"error.page.action.reported": "Error Reported",
|
||||
"error.page.action.exportLogs": "Export Logs",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { errorDescriptionKey } from "./description"
|
||||
import { errorDescriptionKey, errorStatus } from "./description"
|
||||
|
||||
describe("error description", () => {
|
||||
test("describes local server startup errors", () => {
|
||||
@@ -15,3 +15,19 @@ describe("error description", () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("error status", () => {
|
||||
test("finds status codes in an error cause", () => {
|
||||
expect(errorStatus(new Error("UnexpectedStatus", { cause: { status: 502 } }))).toBe(502)
|
||||
})
|
||||
|
||||
test("finds status codes in structured error data", () => {
|
||||
expect(errorStatus({ name: "APIError", data: { statusCode: 401 } })).toBe(401)
|
||||
})
|
||||
|
||||
test("ignores invalid and circular status values", () => {
|
||||
const error: { status: number; cause?: unknown } = { status: 99 }
|
||||
error.cause = error
|
||||
expect(errorStatus(error)).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,3 +9,21 @@ export function errorDescriptionKey(error: unknown) {
|
||||
}
|
||||
return "error.page.description" as const
|
||||
}
|
||||
|
||||
export function errorStatus(error: unknown) {
|
||||
const seen = new Set<object>()
|
||||
const visit = (value: unknown): number | undefined => {
|
||||
if (typeof value !== "object" || value === null || seen.has(value)) return
|
||||
seen.add(value)
|
||||
const item = value as Record<string, unknown>
|
||||
|
||||
for (const key of ["status", "statusCode"] as const) {
|
||||
const status = item[key]
|
||||
if (typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599) return status
|
||||
}
|
||||
|
||||
return visit(item.cause) ?? visit(item.data)
|
||||
}
|
||||
|
||||
return visit(error)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { createStore } from "solid-js/store"
|
||||
import { usePlatform } from "@/runtime/platform/platform"
|
||||
import { useLanguage } from "@/runtime/i18n/language"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { errorDescriptionKey } from "./description"
|
||||
import { errorDescriptionKey, errorStatus } from "./description"
|
||||
|
||||
export type InitError = {
|
||||
name: string
|
||||
@@ -223,6 +223,7 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
||||
const platform = usePlatform()
|
||||
const language = useLanguage()
|
||||
const formattedError = () => formatError(props.error, language.t)
|
||||
const status = () => errorStatus(props.error)
|
||||
let recordedFatalError: Promise<void> | undefined
|
||||
const [store, setStore] = createStore({
|
||||
actionError: undefined as string | undefined,
|
||||
@@ -277,14 +278,20 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
class="relative flex-1 h-screen w-screen min-h-0 flex flex-col items-center justify-center font-sans"
|
||||
class="relative flex-1 h-screen w-screen min-h-0 overflow-y-auto flex flex-col items-center justify-start sm:justify-center p-4 sm:p-8 font-sans"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<div class="w-2/3 max-w-3xl flex flex-col items-center justify-center gap-8">
|
||||
<Logo class="w-58.5 opacity-12 shrink-0" />
|
||||
<div class="w-full max-w-3xl flex flex-col items-center justify-center gap-6 sm:gap-8 my-auto">
|
||||
<Logo class="w-48 sm:w-58.5 opacity-12 shrink-0" />
|
||||
<div class="flex flex-col items-center gap-2 text-center">
|
||||
<h1 class="text-lg font-medium text-text-strong">{language.t("error.page.title")}</h1>
|
||||
<p class="text-sm text-text-weak">{language.t(errorDescriptionKey(props.error))}</p>
|
||||
<h1 class="text-lg font-medium text-text-strong">
|
||||
{language.t(status() ? "error.page.title.status" : "error.page.title")}
|
||||
</h1>
|
||||
<p class="text-sm text-text-weak">
|
||||
{status()
|
||||
? language.t("error.page.description.status", { status: status()! })
|
||||
: language.t(errorDescriptionKey(props.error))}
|
||||
</p>
|
||||
</div>
|
||||
<TextField
|
||||
value={formattedError()}
|
||||
@@ -297,7 +304,7 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
||||
/>
|
||||
<div class="flex flex-row items-center justify-center gap-3 flex-wrap max-w-64">
|
||||
<Button size="large" onClick={platform.restart}>
|
||||
{language.t("error.page.action.restart")}
|
||||
{language.t(platform.platform === "web" ? "error.page.action.reload" : "error.page.action.restart")}
|
||||
</Button>
|
||||
<Show when={platform.platform === "desktop" && platform.exportDebugLogs}>
|
||||
<Button size="large" variant="ghost" onClick={exportDebugLogs}>
|
||||
@@ -348,8 +355,8 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
||||
<Show when={store.actionError}>
|
||||
{(message) => <p class="text-xs text-text-danger-base text-center max-w-2xl">{message()}</p>}
|
||||
</Show>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div class="flex items-center justify-center gap-1">
|
||||
<div class="flex flex-col items-center gap-2 text-xs text-center">
|
||||
<div class="flex flex-wrap items-center justify-center gap-1">
|
||||
{language.t("error.page.report.prefix")}
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user