Dark muted surfaces missed WCAG AA; action and tertiary tokens plus a 32-pair contract close that. Hit targets, remaining-count, and Enter-to-send follow the interaction audit without changing visual sizes. Co-authored-by: Cursor <cursoragent@cursor.com>
148 lines
4.2 KiB
TypeScript
148 lines
4.2 KiB
TypeScript
"use client";
|
|
|
|
// These pages sit in the shared root layout segment and must not import the
|
|
// site stylesheet — that would drag the chat styles onto every admin route — so
|
|
// they cannot read a single design token. They restate the handful they need,
|
|
// in both themes, and those values are kept in step with the palette by hand.
|
|
|
|
import { useEffect } from "react";
|
|
|
|
import { TriangleAlert } from "lucide-react";
|
|
import Link from "next/link";
|
|
|
|
const canvas = "var(--bp-canvas)";
|
|
const ink = "var(--bp-ink)";
|
|
const inkSecondary = "var(--bp-ink-soft)";
|
|
const danger = "var(--bp-danger)";
|
|
const border = "var(--bp-border)";
|
|
const action = "var(--bp-action)";
|
|
const focus = "var(--bp-action)";
|
|
|
|
const actionStyle = {
|
|
alignItems: "center",
|
|
borderRadius: "12px",
|
|
display: "inline-flex",
|
|
fontSize: "14px",
|
|
fontWeight: 500,
|
|
justifyContent: "center",
|
|
minHeight: "44px",
|
|
minWidth: "88px",
|
|
padding: "0 20px",
|
|
textDecoration: "none",
|
|
} as const;
|
|
|
|
export default function RootError({
|
|
error,
|
|
reset,
|
|
}: {
|
|
error: Error & { digest?: string };
|
|
reset: () => void;
|
|
}) {
|
|
useEffect(() => {
|
|
console.error("app root error", error.digest ?? error.message);
|
|
}, [error]);
|
|
|
|
return (
|
|
<main
|
|
style={{
|
|
alignItems: "center",
|
|
background: canvas,
|
|
color: ink,
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
fontFamily:
|
|
'-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif',
|
|
gap: "16px",
|
|
justifyContent: "center",
|
|
minHeight: "100vh",
|
|
padding: "48px 24px",
|
|
textAlign: "center",
|
|
}}
|
|
>
|
|
<style>{`
|
|
:root {
|
|
color-scheme: light;
|
|
--bp-canvas: #fbfaf7;
|
|
--bp-ink: #1d1d1f;
|
|
--bp-ink-soft: #5f5f59;
|
|
--bp-border: #d8d6cf;
|
|
--bp-action: #85432f;
|
|
--bp-danger: #9a2f2f;
|
|
}
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not([data-theme="light"]) {
|
|
color-scheme: dark;
|
|
--bp-canvas: #262624;
|
|
--bp-ink: #f2f0ea;
|
|
--bp-ink-soft: #b3afa4;
|
|
--bp-border: #3c3b37;
|
|
--bp-action: #d78064;
|
|
--bp-danger: #e08573;
|
|
}
|
|
}
|
|
:root[data-theme="dark"] {
|
|
color-scheme: dark;
|
|
--bp-canvas: #262624;
|
|
--bp-ink: #f2f0ea;
|
|
--bp-ink-soft: #b3afa4;
|
|
--bp-border: #3c3b37;
|
|
--bp-action: #d78064;
|
|
--bp-danger: #e08573;
|
|
}
|
|
.root-error-action:focus-visible { outline: 3px solid ${focus}; outline-offset: 2px; }
|
|
`}</style>
|
|
<TriangleAlert aria-hidden="true" color={danger} size={32} />
|
|
<div role="alert">
|
|
<h1 style={{ color: ink, fontSize: "20px", fontWeight: 600, margin: "0 0 12px" }}>
|
|
页面出错了
|
|
</h1>
|
|
<p style={{ color: inkSecondary, fontSize: "14px", lineHeight: 1.7, margin: 0, maxWidth: "28rem" }}>
|
|
页面渲染时发生异常,已保存的对话不受影响。可以重试,或返回对话首页。
|
|
</p>
|
|
</div>
|
|
{error.digest ? (
|
|
<p style={{ color: inkSecondary, fontSize: "12px", lineHeight: 1.7, margin: 0, maxWidth: "28rem" }}>
|
|
错误编号 <code style={{ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }}>{error.digest}</code>
|
|
,反馈问题时请一并提供。
|
|
</p>
|
|
) : null}
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
flexWrap: "wrap",
|
|
gap: "12px",
|
|
justifyContent: "center",
|
|
marginTop: "8px",
|
|
}}
|
|
>
|
|
<button
|
|
className="root-error-action"
|
|
onClick={() => reset()}
|
|
style={{
|
|
...actionStyle,
|
|
background: action,
|
|
border: "1px solid transparent",
|
|
color: canvas,
|
|
cursor: "pointer",
|
|
}}
|
|
type="button"
|
|
>
|
|
重试
|
|
</button>
|
|
<Link
|
|
className="root-error-action"
|
|
href="/"
|
|
style={{
|
|
...actionStyle,
|
|
background: canvas,
|
|
border: `1px solid ${border}`,
|
|
color: ink,
|
|
}}
|
|
>
|
|
返回对话
|
|
</Link>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|