kimi-code / apps /vscode /webview-ui /src /components /ConfigErrorScreen.tsx
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
f59fbe2 verified
Raw
History Blame Contribute Delete
6.32 kB
import { useState } from "react";
import {
IconAlertTriangle,
IconArrowLeft,
IconCheck,
IconCopy,
IconFileSettings,
IconFolderOpen,
IconLoader2,
IconRefresh,
IconTerminal2,
} from "@tabler/icons-react";
import { bridge } from "@/services";
import { Button } from "@/components/ui/button";
import { KimiMascot } from "./KimiMascot";
interface Props {
type: "loading" | "runtime-error" | "no-models" | "no-workspace";
errorMessage?: string | null;
onRefresh?: () => void;
onBackToLogin?: () => void;
}
function ErrorDetails({ message }: { message?: string | null }) {
const [copied, setCopied] = useState(false);
if (!message) return null;
const copyError = async () => {
await navigator.clipboard.writeText(message);
setCopied(true);
window.setTimeout(() => setCopied(false), 2_000);
};
return (
<div className="bg-muted/50 rounded-lg p-4 text-left space-y-2">
<div className="flex items-center justify-between gap-2 text-xs text-muted-foreground">
<div className="flex items-center gap-2 min-w-0">
<IconTerminal2 className="size-4" />
<span>Error details</span>
</div>
<Button
onClick={() => {
void copyError();
}}
variant="ghost"
size="xs"
className="h-6 px-1.5 gap-1 shrink-0"
>
{copied ? <IconCheck className="size-3" /> : <IconCopy className="size-3" />}
{copied ? "Copied" : "Copy"}
</Button>
</div>
<pre className="max-h-36 overflow-auto whitespace-pre-wrap break-words text-xs bg-background rounded px-3 py-2 font-mono text-foreground">{message}</pre>
</div>
);
}
function NoModelsContent({ onRefresh, onBackToLogin }: Pick<Props, "onRefresh" | "onBackToLogin">) {
return (
<>
<div className="space-y-2">
<div className="inline-flex items-center gap-2 text-amber-500">
<IconAlertTriangle className="size-5" />
<span className="text-sm font-medium">Model setup required</span>
</div>
<p className="text-xs text-muted-foreground">
Sign in with a Kimi account, or configure a provider and model in your shared Kimi Code <code className="bg-muted px-1 rounded">config.toml</code>.
</p>
</div>
<div className="bg-muted/50 rounded-lg p-4 text-left space-y-2">
<div className="flex items-center gap-2 text-xs font-medium">
<IconFileSettings className="size-4" />
Shared Kimi Code configuration
</div>
<p className="text-xs text-muted-foreground">
VS Code and the terminal UI use the same Kimi Code home, configuration, credentials, and sessions.
</p>
</div>
<div className="flex flex-col min-[400px]:flex-row min-[400px]:justify-between gap-2 w-full">
{onBackToLogin && (
<Button onClick={onBackToLogin} variant="ghost" size="sm" className="gap-1 text-muted-foreground">
<IconArrowLeft className="size-3" />
Back to sign in
</Button>
)}
{onRefresh && (
<Button onClick={onRefresh} variant="ghost" size="sm" className="gap-1 text-muted-foreground">
<IconRefresh className="size-3" />
Reload
</Button>
)}
</div>
</>
);
}
export function ConfigErrorScreen({ type, errorMessage, onRefresh, onBackToLogin }: Props) {
if (type === "loading") {
return (
<div className="h-full flex items-center justify-center p-6">
<div className="text-center space-y-4">
<KimiMascot className="h-10 mx-auto opacity-50" />
<div className="inline-flex items-center gap-2 text-muted-foreground">
<IconLoader2 className="size-4 animate-spin" />
<span className="text-sm">Starting Kimi Code…</span>
</div>
</div>
</div>
);
}
if (type === "no-workspace") {
return (
<div className="h-full flex items-center justify-center p-6">
<div className="max-w-sm text-center space-y-6">
<KimiMascot className="h-10 mx-auto opacity-50" />
<div className="space-y-2">
<div className="inline-flex items-center gap-2 text-amber-500">
<IconFolderOpen className="size-5" />
<span className="text-sm font-medium">No workspace open</span>
</div>
<p className="text-xs text-muted-foreground">Open a folder to start using Kimi Code.</p>
</div>
<Button
onClick={() => {
void bridge.openFolder();
}}
className="gap-2"
>
<IconFolderOpen className="size-4" />
Open Folder
</Button>
</div>
</div>
);
}
if (type === "no-models") {
return (
<div className="h-full flex items-center justify-center p-6">
<div className="max-w-sm text-center space-y-6">
<KimiMascot className="h-10 mx-auto opacity-50" />
<NoModelsContent onRefresh={onRefresh} onBackToLogin={onBackToLogin} />
</div>
</div>
);
}
return (
<div className="flex-1 min-h-0 overflow-y-auto p-6">
<div className="max-w-sm mx-auto text-center space-y-6">
<KimiMascot className="h-10 mx-auto opacity-50" />
<div className="space-y-2">
<div className="inline-flex items-center gap-2 text-red-500">
<IconAlertTriangle className="size-5" />
<span className="text-sm font-medium">Kimi Code could not start</span>
</div>
<p className="text-xs text-muted-foreground">Check the error below. Full diagnostics are available in the Kimi Code output channel.</p>
</div>
<ErrorDetails message={errorMessage} />
<div className="flex gap-2 justify-center">
<Button
onClick={() => {
void bridge.showLogs();
}}
variant="outline"
size="sm"
>
Show Logs
</Button>
{onRefresh && (
<Button onClick={onRefresh} size="sm" className="gap-1">
<IconRefresh className="size-3" />
Retry
</Button>
)}
</div>
</div>
</div>
);
}