Sanitized mirror from private repository - 2026-04-18 11:19:59 UTC
This commit is contained in:
90
dashboard/ui/components/ollama-chat.tsx
Normal file
90
dashboard/ui/components/ollama-chat.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
"use client";
|
||||
import { useState, useRef } from "react";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
interface Message { role: "user" | "assistant"; content: string }
|
||||
|
||||
export function OllamaChat() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [input, setInput] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
async function send() {
|
||||
if (!input.trim() || loading) return;
|
||||
const userMsg = input.trim();
|
||||
setInput("");
|
||||
setMessages(prev => [...prev, { role: "user", content: userMsg }]);
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await fetch("/api/chat", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ message: userMsg }),
|
||||
});
|
||||
const data = await res.json();
|
||||
setMessages(prev => [...prev, { role: "assistant", content: data.response || data.error || "No response" }]);
|
||||
} catch (e) {
|
||||
setMessages(prev => [...prev, { role: "assistant", content: `Error: ${e}` }]);
|
||||
}
|
||||
setLoading(false);
|
||||
setTimeout(() => scrollRef.current?.scrollTo(0, scrollRef.current.scrollHeight), 100);
|
||||
}
|
||||
|
||||
if (!open) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="fixed bottom-4 left-4 z-50 w-10 h-10 rounded-full bg-gradient-to-br from-violet-500 to-blue-500 text-white flex items-center justify-center shadow-lg shadow-violet-500/20 hover:shadow-violet-500/40 hover:scale-110 transition-all duration-200"
|
||||
title="Chat with Ollama"
|
||||
>
|
||||
<span className="text-sm font-bold">AI</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 left-4 z-50 w-80">
|
||||
<Card className="shadow-2xl" style={{ background: "rgba(10, 10, 25, 0.95)", backdropFilter: "blur(30px)", border: "1px solid rgba(139, 92, 246, 0.2)" }}>
|
||||
<CardHeader className="pb-2 flex flex-row items-center justify-between">
|
||||
<CardTitle className="text-sm font-medium">Ollama Chat</CardTitle>
|
||||
<Button variant="ghost" size="sm" className="h-6 w-6 p-0 text-xs hover:bg-white/[0.06]" onClick={() => setOpen(false)}>x</Button>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2">
|
||||
<div ref={scrollRef} className="h-48 overflow-y-auto space-y-2 text-xs">
|
||||
{messages.length === 0 && (
|
||||
<div className="text-center py-3 space-y-2">
|
||||
<p className="text-muted-foreground/60">Ask about your homelab...</p>
|
||||
<div className="flex flex-wrap gap-1 justify-center">
|
||||
{["How many containers?", "GPU status?", "What's unhealthy?", "Disk space?"].map(q => (
|
||||
<button key={q} onClick={() => { setInput(q); }} className="text-[10px] px-2 py-1 rounded-md bg-white/[0.04] border border-white/[0.06] hover:bg-white/[0.08] transition-colors text-muted-foreground">
|
||||
{q}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{messages.map((m, i) => (
|
||||
<div key={i} className={`rounded-lg px-3 py-2 ${m.role === "user" ? "bg-blue-500/10 border border-blue-500/10 ml-8" : "bg-white/[0.04] border border-white/[0.06] mr-8"}`}>
|
||||
<p className="whitespace-pre-wrap">{m.content}</p>
|
||||
</div>
|
||||
))}
|
||||
{loading && <div className="bg-white/[0.04] border border-white/[0.06] rounded-lg px-3 py-2 mr-8 animate-pulse"><p className="text-muted-foreground">Thinking...</p></div>}
|
||||
</div>
|
||||
<div className="flex gap-1.5">
|
||||
<input
|
||||
value={input}
|
||||
onChange={e => setInput(e.target.value)}
|
||||
onKeyDown={e => e.key === "Enter" && send()}
|
||||
placeholder="Ask Ollama..."
|
||||
className="flex-1 rounded-lg glass-input px-3 py-1.5 text-xs"
|
||||
/>
|
||||
<Button size="sm" className="h-7 text-xs px-3 bg-violet-500/20 hover:bg-violet-500/30 border border-violet-500/20" onClick={send} disabled={loading}>Send</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user