diff --git a/chat/next.config.mjs b/chat/next.config.mjs deleted file mode 100644 index d9f02b2..0000000 --- a/chat/next.config.mjs +++ /dev/null @@ -1,23 +0,0 @@ -/** @type {import('next').NextConfig} */ -const basePath = process.env.BASE_PATH ?? "/chat"; - -const nextConfig = { - // Enable static exports - output: "export", - - // Disable image optimization since it's not supported in static exports - images: { - unoptimized: true, - }, - - // Configure base path for GitHub Pages (repo/chat) - basePath, - - // Configure asset prefix for GitHub Pages - helps with static asset loading - assetPrefix: `${basePath}/`, - - // Configure trailing slashes (recommended for static exports) - trailingSlash: true, -}; - -export default nextConfig; diff --git a/chat/next.config.ts b/chat/next.config.ts index e9ffa30..baf9e51 100644 --- a/chat/next.config.ts +++ b/chat/next.config.ts @@ -1,7 +1,23 @@ import type { NextConfig } from "next"; +const basePath = process.env.BASE_PATH ?? "/chat"; const nextConfig: NextConfig = { - /* config options here */ + // Enable static exports + output: "export", + + // Disable image optimization since it's not supported in static exports + images: { + unoptimized: true, + }, + + // Configure base path for GitHub Pages (repo/chat) + basePath, + + // Configure asset prefix for GitHub Pages - helps with static asset loading + assetPrefix: `${basePath}/`, + + // Configure trailing slashes (recommended for static exports) + trailingSlash: true, }; export default nextConfig; diff --git a/chat/src/app/embed/page.tsx b/chat/src/app/embed/page.tsx new file mode 100644 index 0000000..768acdd --- /dev/null +++ b/chat/src/app/embed/page.tsx @@ -0,0 +1,19 @@ +import { Chat } from "@/components/chat"; +import { ChatProvider } from "@/components/chat-provider"; +import { Suspense } from "react"; + +export default function EmbedPage() { + return ( + Loading chat interface... + } + > + + + + + + + ); +} diff --git a/chat/src/app/globals.css b/chat/src/app/globals.css index dfdd607..b628ac3 100644 --- a/chat/src/app/globals.css +++ b/chat/src/app/globals.css @@ -79,7 +79,7 @@ } .dark { - --background: oklch(0.129 0.042 264.695); + --background: oklch(0.14 0 0); --foreground: oklch(0.984 0.003 247.858); --card: oklch(0.208 0.042 265.755); --card-foreground: oklch(0.984 0.003 247.858); @@ -90,11 +90,11 @@ --secondary: oklch(0.279 0.041 260.031); --secondary-foreground: oklch(0.984 0.003 247.858); --muted: oklch(0.279 0.041 260.031); - --muted-foreground: oklch(0.704 0.04 256.788); + --muted-foreground: oklch(0.7118 0.0129 286.07); --accent: oklch(0.279 0.041 260.031); --accent-foreground: oklch(0.984 0.003 247.858); --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); + --border: oklch(0.27 0.01 0); --input: oklch(1 0 0 / 15%); --ring: oklch(0.551 0.027 264.364); --chart-1: oklch(0.488 0.243 264.376); diff --git a/chat/src/app/header.tsx b/chat/src/app/header.tsx new file mode 100644 index 0000000..85c2836 --- /dev/null +++ b/chat/src/app/header.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { useChat } from "@/components/chat-provider"; +import { ModeToggle } from "../components/mode-toggle"; + +export function Header() { + const { serverStatus } = useChat(); + + return ( + + AgentAPI Chat + + + {serverStatus !== "unknown" && ( + + + Status: + {serverStatus} + + )} + + + + ); +} diff --git a/chat/src/app/page.tsx b/chat/src/app/page.tsx index c5ce056..1530885 100644 --- a/chat/src/app/page.tsx +++ b/chat/src/app/page.tsx @@ -1,14 +1,21 @@ +import { Chat } from "@/components/chat"; +import { ChatProvider } from "@/components/chat-provider"; +import { Header } from "./header"; import { Suspense } from "react"; -import ChatInterface from "@/components/ChatInterface"; export default function Home() { return ( Loading chat interface... + Loading chat interface... } > - + + + + + + ); } diff --git a/chat/src/components/ChatInterface.tsx b/chat/src/components/chat-provider.tsx similarity index 62% rename from chat/src/components/ChatInterface.tsx rename to chat/src/components/chat-provider.tsx index 0528a98..ba673f7 100644 --- a/chat/src/components/ChatInterface.tsx +++ b/chat/src/components/chat-provider.tsx @@ -1,14 +1,15 @@ "use client"; -import { useState, useEffect, useRef, useCallback } from "react"; -import MessageList from "./MessageList"; -import MessageInput from "./MessageInput"; import { useSearchParams } from "next/navigation"; +import { + useState, + useEffect, + useRef, + createContext, + PropsWithChildren, + useContext, +} from "react"; import { toast } from "sonner"; -import { Button } from "./ui/button"; -import { TriangleAlertIcon } from "lucide-react"; -import { Alert, AlertTitle, AlertDescription } from "./ui/alert"; -import { ModeToggle } from "./mode-toggle"; interface Message { id: number; @@ -33,42 +34,30 @@ interface StatusChangeEvent { status: string; } -const isDraftMessage = (message: Message | DraftMessage): boolean => { +function isDraftMessage(message: Message | DraftMessage): boolean { return message.id === undefined; -}; +} -export default function ChatInterface() { - const [messages, setMessages] = useState<(Message | DraftMessage)[]>([]); - const [loading, setLoading] = useState(false); - const [serverStatus, setServerStatus] = useState("unknown"); - const searchParams = useSearchParams(); +type MessageType = "user" | "raw"; - const getAgentApiUrl = useCallback(() => { - const apiUrlFromParam = searchParams.get("url"); - if (apiUrlFromParam) { - try { - // Validate if it's a proper URL - new URL(apiUrlFromParam); - return apiUrlFromParam; - } catch (e) { - console.warn("Invalid url parameter, defaulting...", e); - // Fallback if parsing fails or it's not a valid URL. - // Ensure window is defined (for SSR/Node.js environments during build) - return typeof window !== "undefined" ? window.location.origin : ""; - } - } - // Ensure window is defined - return typeof window !== "undefined" ? window.location.origin : ""; - }, [searchParams]); +type ServerStatus = "online" | "offline" | "unknown"; - const [agentAPIUrl, setAgentAPIUrl] = useState(getAgentApiUrl()); +interface ChatContextValue { + messages: (Message | DraftMessage)[]; + loading: boolean; + serverStatus: ServerStatus; + sendMessage: (message: string, type?: MessageType) => void; +} - const eventSourceRef = useRef(null); +const ChatContext = createContext(undefined); - // Update agentAPIUrl when searchParams change (e.g. url is added/removed) - useEffect(() => { - setAgentAPIUrl(getAgentApiUrl()); - }, [getAgentApiUrl, searchParams]); +export function ChatProvider({ children }: PropsWithChildren) { + const [messages, setMessages] = useState<(Message | DraftMessage)[]>([]); + const [loading, setLoading] = useState(false); + const [serverStatus, setServerStatus] = useState("unknown"); + const eventSourceRef = useRef(null); + const searchParams = useSearchParams(); + const agentAPIUrl = searchParams.get("url"); // Set up SSE connection to the events endpoint useEffect(() => { @@ -132,7 +121,7 @@ export default function ChatInterface() { // Handle status changes eventSource.addEventListener("status_change", (event) => { const data: StatusChangeEvent = JSON.parse(event.data); - setServerStatus(data.status); + setServerStatus(data.status as ServerStatus); }); // Handle connection open (server is online) @@ -240,55 +229,23 @@ export default function ChatInterface() { }; return ( - - - AgentAPI Chat - - - {serverStatus !== "unknown" && ( - - - Status: - {serverStatus} - - )} - - - - - - {serverStatus === "offline" && ( - - - - - API server is offline - - Please start the AgentAPI server. Attempting to connect to:{" "} - {agentAPIUrl || "N/A"}. - - - window.location.reload()} - > - Retry - - - - )} - - - - - + + {children} + ); } + +export function useChat() { + const context = useContext(ChatContext); + if (!context) { + throw new Error("useChat must be used within a ChatProvider"); + } + return context; +} diff --git a/chat/src/components/chat.tsx b/chat/src/components/chat.tsx new file mode 100644 index 0000000..89cd780 --- /dev/null +++ b/chat/src/components/chat.tsx @@ -0,0 +1,16 @@ +"use client"; + +import { useChat } from "./chat-provider"; +import MessageInput from "./message-input"; +import MessageList from "./message-list"; + +export function Chat() { + const { messages, loading, sendMessage } = useChat(); + + return ( + <> + + + > + ); +} diff --git a/chat/src/components/MessageInput.tsx b/chat/src/components/message-input.tsx similarity index 98% rename from chat/src/components/MessageInput.tsx rename to chat/src/components/message-input.tsx index a9f51ec..aa8c59b 100644 --- a/chat/src/components/MessageInput.tsx +++ b/chat/src/components/message-input.tsx @@ -155,7 +155,7 @@ export default function MessageInput({ onKeyDown={handleKeyDown as any} onFocus={() => setControlAreaFocused(true)} onBlur={() => setControlAreaFocused(false)} - className="cursor-text p-4 h-20 text-muted-foreground flex items-center justify-center w-full outline-none" + className="cursor-text p-4 h-20 text-muted-foreground flex items-center justify-center w-full outline-none text-sm" > {controlAreaFocused ? "Press any key to send to terminal (arrows, Ctrl+C, Ctrl+R, etc.)" @@ -175,7 +175,7 @@ export default function MessageInput({ - + { diff --git a/chat/src/components/MessageList.tsx b/chat/src/components/message-list.tsx similarity index 98% rename from chat/src/components/MessageList.tsx rename to chat/src/components/message-list.tsx index b01c476..75c6ade 100644 --- a/chat/src/components/MessageList.tsx +++ b/chat/src/components/message-list.tsx @@ -120,7 +120,7 @@ export default function MessageList({ messages }: MessageListProps) { } ${message.id === undefined ? "animate-pulse" : ""}`} >