Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/app/chat/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use client";

import ChatComponent from "@/components/ChatComponent";
import { Button } from "@/components/ui/button";
import { ArrowLeft, Maximize2 } from "lucide-react";
import Link from "next/link";

export default function ChatPage() {
return (
<div className="flex h-screen w-full bg-zinc-950 overflow-hidden flex-col">
{/* Header for full-page chat */}
<header className="flex items-center justify-between px-6 py-4 border-b border-zinc-800 bg-black">
<div className="flex items-center gap-4">
<Link href="/k8s-dashboard">
<Button variant="ghost" size="icon" className="text-zinc-400 hover:text-white">
<ArrowLeft className="w-5 h-5" />
</Button>
</Link>
<div className="flex items-center gap-2">
<Maximize2 className="w-5 h-5 text-[#368dab]" />
<h1 className="text-xl font-bold text-white tracking-tight">Full-Page Chat</h1>
</div>
</div>
<div className="text-xs text-zinc-500 font-mono hidden sm:block">
Distraction-free Kubernetes Assistant
</div>
</header>

{/* Main Chat Area */}
<main className="flex-1 relative overflow-hidden flex justify-center p-4">
<div className="w-full h-full">
<ChatComponent isFullPage={true} />
</div>
</main>
</div>
);
}
16 changes: 15 additions & 1 deletion src/app/k8s-dashboard/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import {
Clock,
Play,
Lock,
Zap
Zap,
MessageSquareShare
} from "lucide-react";
import Image from "next/image";
import { useState, useEffect } from "react";
import Link from "next/link";

export type ToolType =
| "pod-resources"
Expand Down Expand Up @@ -122,6 +124,18 @@ export default function Sidebar({
</Button>
))}
</div>

<div className="mt-6 pt-6 border-t border-zinc-800">
<Link href="/chat" target="_blank" className="w-full">
<Button
variant="outline"
className="w-full justify-start gap-2 border-zinc-800 hover:bg-zinc-800 text-zinc-400 hover:text-white"
>
<MessageSquareShare className="w-4 h-4" />
<span>Full-Page Chat</span>
</Button>
</Link>
</div>
</div>
</aside>
);
Expand Down
62 changes: 44 additions & 18 deletions src/components/ChatComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useState, useRef, useEffect, FormEvent } from "react";
import { Button } from "./ui/button";
import { Input } from "./ui/input";
import { Card } from "./ui/card";
import { cn } from "@/lib/utils";
import {
Send,
X,
Expand All @@ -19,6 +20,7 @@ import {
Trash2,
RotateCcw,
MessageSquare,
ExternalLink,
} from "lucide-react";
import {
DropdownMenu,
Expand All @@ -30,10 +32,12 @@ import { useChat } from "./ChatContext";
import type { ChatSession } from "./ChatContext";
import { WebLLMProvider, useWebLLM } from "./WebLLMProvider";
import * as webllm from "@mlc-ai/web-llm";
import Link from "next/link";

interface ChatComponentProps {
isOpen?: boolean;
onClose?: () => void;
isFullPage?: boolean;
}

export default function ChatComponent(props: ChatComponentProps) {
Expand All @@ -47,8 +51,9 @@ export default function ChatComponent(props: ChatComponentProps) {
function ChatComponentInner({
isOpen: controlledIsOpen,
onClose,
isFullPage = false,
}: ChatComponentProps) {
const [isOpen, setIsOpen] = useState<boolean>(!!controlledIsOpen);
const [isOpen, setIsOpen] = useState<boolean>(isFullPage || !!controlledIsOpen);
const [model, setModel] = useState("gpt-4o");
const [availableModels, setAvailableModels] = useState<{ id: string; name: string; isLocal?: boolean }[]>([]);
const [input, setInput] = useState("");
Expand Down Expand Up @@ -331,15 +336,28 @@ function ChatComponentInner({

return (
<Card
className="fixed bottom-0 right-0 z-[60] w-full max-w-sm sm:max-w-md md:max-w-md h-[70vh] sm:h-[80vh] flex flex-col bg-zinc-900 border border-zinc-800 shadow-2xl rounded-tl-xl p-1 gap-0"
style={{ boxShadow: "0 8px 32px 0 rgba(0,0,0,0.45)" }}
className={cn(
"z-[60] flex flex-col bg-zinc-900 border-zinc-800 shadow-2xl p-1 gap-0",
isFullPage
? "w-full h-full rounded-xl border-zinc-700/50"
: "fixed bottom-0 right-0 w-full max-w-sm sm:max-w-md md:max-w-md h-[70vh] sm:h-[80vh] rounded-tl-xl"
)}
style={!isFullPage ? { boxShadow: "0 8px 32px 0 rgba(0,0,0,0.45)" } : {}}
>
{/* Header */}
<div className="flex items-center justify-between px-4 py-3 border-b border-zinc-800 bg-zinc-950 rounded-tl-xl">
<div className={cn(
"flex items-center justify-between px-4 py-3 border-b border-zinc-800 bg-zinc-950",
isFullPage ? "rounded-t-xl" : "rounded-tl-xl"
)}>
<div className="flex flex-col">
<div className="flex items-center gap-2">
<MessageCircle className="w-5 h-5 text-blue-400" />
<span className="font-semibold text-white text-lg">ST-K8s Chat</span>
{!isFullPage && (
<Link href="/chat" target="_blank" className="ml-1 text-zinc-500 hover:text-blue-400 transition-colors">
<ExternalLink className="w-3.5 h-3.5" />
</Link>
)}
</div>
<div className="flex items-center gap-2 mt-0.5">
<button
Expand Down Expand Up @@ -444,15 +462,17 @@ function ChatComponentInner({
)}
</DropdownMenuContent>
</DropdownMenu>
<Button
variant="ghost"
size="icon"
className="text-zinc-400 hover:text-white w-8 h-8"
onClick={handleToggle}
aria-label="Close chat"
>
<X className="w-5 h-5" />
</Button>
{!isFullPage && (
<Button
variant="ghost"
size="icon"
className="text-zinc-400 hover:text-white w-8 h-8"
onClick={handleToggle}
aria-label="Close chat"
>
<X className="w-5 h-5" />
</Button>
)}
</div>
</div>

Expand Down Expand Up @@ -556,10 +576,13 @@ function ChatComponentInner({
className={`flex \${msg.role === "user" ? "justify-end" : "justify-start"}`}
>
<div
className={`max-w-[80%] px-4 py-2 rounded-lg text-sm whitespace-pre-line \${msg.role === "user"
? "bg-blue-600 text-white rounded-br-none"
: "bg-zinc-800 text-zinc-100 rounded-bl-none border border-zinc-700"
}`}
className={cn(
"px-4 py-2 rounded-lg text-sm whitespace-pre-line",
isFullPage ? "max-w-[90%]" : "max-w-[80%]",
msg.role === "user"
? "bg-blue-600 text-white rounded-br-none"
: "bg-zinc-800 text-zinc-100 rounded-bl-none border border-zinc-700"
)}
>
{msg.content}
</div>
Expand All @@ -575,7 +598,10 @@ function ChatComponentInner({
)}
{webllmLoading && webllmProgress && (
<div className="flex justify-start">
<div className="flex flex-col gap-1 bg-zinc-800 text-zinc-300 px-4 py-2 rounded-lg border border-zinc-700 w-full max-w-[80%]">
<div className={cn(
"flex flex-col gap-1 bg-zinc-800 text-zinc-300 px-4 py-2 rounded-lg border border-zinc-700 w-full",
isFullPage ? "max-w-[90%]" : "max-w-[80%]"
)}>
<div className="flex justify-between text-xs mb-1">
<span>Downloading Model Array into Browser</span>
</div>
Expand Down
Loading