picoclaw/web/frontend/src/components/chat/chat-empty-state.tsx
wenjie c513ad22d7
fix(web): refactor pico chat flow and fix proxied websocket URLs (#1639)
- move chat controller, state, protocol, history, and websocket logic into a dedicated chat feature module
- improve chat reconnection, session hydration, and send gating based on actual websocket state
- preserve gateway status during transient SSE disconnects and update stop state immediately
- generate wss websocket URLs behind HTTPS proxies and add backend tests for forwarded proto handling
2026-03-16 16:25:16 +08:00

87 lines
2.8 KiB
TypeScript

import {
IconPlugConnectedX,
IconRobot,
IconRobotOff,
IconStar,
} from "@tabler/icons-react"
import { Link } from "@tanstack/react-router"
import { useTranslation } from "react-i18next"
import { Button } from "@/components/ui/button"
interface ChatEmptyStateProps {
hasConfiguredModels: boolean
defaultModelName: string
isConnected: boolean
}
export function ChatEmptyState({
hasConfiguredModels,
defaultModelName,
isConnected,
}: ChatEmptyStateProps) {
const { t } = useTranslation()
if (!hasConfiguredModels) {
return (
<div className="flex flex-col items-center justify-center py-20 opacity-70">
<div className="mb-6 flex h-16 w-16 items-center justify-center rounded-2xl bg-amber-500/10 text-amber-500">
<IconRobotOff className="h-8 w-8" />
</div>
<h3 className="mb-2 text-xl font-medium">
{t("chat.empty.noConfiguredModel")}
</h3>
<p className="text-muted-foreground mb-4 text-center text-sm">
{t("chat.empty.noConfiguredModelDescription")}
</p>
<Button asChild variant="outline" size="sm" className="px-4">
<Link to="/models">{t("chat.empty.goToModels")}</Link>
</Button>
</div>
)
}
if (!defaultModelName) {
return (
<div className="flex flex-col items-center justify-center py-20 opacity-70">
<div className="mb-6 flex h-16 w-16 items-center justify-center rounded-2xl bg-amber-500/10 text-amber-500">
<IconStar className="h-8 w-8" />
</div>
<h3 className="mb-2 text-xl font-medium">
{t("chat.empty.noSelectedModel")}
</h3>
<p className="text-muted-foreground mb-4 text-center text-sm">
{t("chat.empty.noSelectedModelDescription")}
</p>
</div>
)
}
if (!isConnected) {
return (
<div className="flex flex-col items-center justify-center py-20 opacity-70">
<div className="mb-6 flex h-16 w-16 items-center justify-center rounded-2xl bg-amber-500/10 text-amber-500">
<IconPlugConnectedX className="h-8 w-8" />
</div>
<h3 className="mb-2 text-xl font-medium">
{t("chat.empty.notRunning")}
</h3>
<p className="text-muted-foreground mb-4 text-center text-sm">
{t("chat.empty.notRunningDescription")}
</p>
</div>
)
}
return (
<div className="flex flex-col items-center justify-center py-20 opacity-70">
<div className="mb-6 flex h-16 w-16 items-center justify-center rounded-2xl bg-violet-500/10 text-violet-500">
<IconRobot className="h-8 w-8" />
</div>
<h3 className="mb-2 text-xl font-medium">{t("chat.welcome")}</h3>
<p className="text-muted-foreground text-center text-sm">
{t("chat.welcomeDesc")}
</p>
</div>
)
}