69 lines
2.5 KiB
TypeScript
69 lines
2.5 KiB
TypeScript
import { useState } from 'react';
|
|
import { ChevronDown, FolderTree, RefreshCw, TriangleAlert, X } from 'lucide-react';
|
|
import type { AppDiagnostic } from '../../stores/useAppStore';
|
|
import { Button, CopyButton, IconButton } from '../../components/ui';
|
|
export function ErrorRecoveryPanel({
|
|
value,
|
|
onClose,
|
|
onRetry,
|
|
onOpenProject,
|
|
}: {
|
|
value: AppDiagnostic;
|
|
onClose: () => void;
|
|
onRetry?: () => void;
|
|
onOpenProject: () => void;
|
|
}) {
|
|
const [expanded, setExpanded] = useState(false);
|
|
return (
|
|
<section
|
|
role="alert"
|
|
className="absolute bottom-4 left-1/2 z-30 w-[min(42rem,calc(100%-2rem))] -translate-x-1/2 overflow-hidden rounded-xl border border-danger-border bg-panel shadow-2xl"
|
|
>
|
|
<div className="flex items-start gap-3 p-3">
|
|
<span className="mt-0.5 grid h-7 w-7 shrink-0 place-items-center rounded-full bg-danger-soft text-danger">
|
|
<TriangleAlert className="h-4 w-4" />
|
|
</span>
|
|
<div className="min-w-0 flex-1">
|
|
<h2 className="text-sm font-semibold">{value.summary}</h2>
|
|
{value.path && <p className="truncate text-xs text-text-tertiary">路径:{value.path}</p>}
|
|
<div className="mt-2 flex flex-wrap gap-2">
|
|
{onRetry && (
|
|
<Button
|
|
variant="danger"
|
|
onClick={onRetry}
|
|
icon={<RefreshCw className="h-3.5 w-3.5" />}
|
|
>
|
|
重试当前入口
|
|
</Button>
|
|
)}
|
|
<Button onClick={onOpenProject} icon={<FolderTree className="h-3.5 w-3.5" />}>
|
|
返回工程树
|
|
</Button>
|
|
<CopyButton
|
|
value={`${value.summary}\n${value.path ?? ''}\n${value.detail}`}
|
|
label="复制错误详情"
|
|
/>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
aria-expanded={expanded}
|
|
className="mt-2 flex items-center gap-1 text-xs text-danger"
|
|
onClick={() => setExpanded((v) => !v)}
|
|
>
|
|
技术详情
|
|
<ChevronDown className={`h-3 w-3 ${expanded ? 'rotate-180' : ''}`} />
|
|
</button>
|
|
</div>
|
|
<IconButton aria-label="关闭错误" tooltip="关闭" onClick={onClose}>
|
|
<X className="h-4 w-4" />
|
|
</IconButton>
|
|
</div>
|
|
{expanded && (
|
|
<pre className="max-h-36 overflow-auto border-t border-danger-border bg-danger-soft p-3 text-xs text-danger">
|
|
{value.detail}
|
|
</pre>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|