diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index 66ef937..02755a9 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -1,10 +1,20 @@ "use client"; +import type { UseStream } from "@langchain/langgraph-sdk/react"; import { useParams, useRouter } from "next/navigation"; import { useCallback, useEffect, useMemo, useState } from "react"; import { BreadcrumbItem } from "@/components/ui/breadcrumb"; -import { ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable"; +import { + ResizableHandle, + ResizablePanel, + ResizablePanelGroup, +} from "@/components/ui/resizable"; +import { ArtifactFileDetail } from "@/components/workspace/artifacts"; +import { + ArtifactsProvider, + useArtifacts, +} from "@/components/workspace/artifacts/context"; import { InputBox } from "@/components/workspace/input-box"; import { MessageList } from "@/components/workspace/message-list/message-list"; import { @@ -13,20 +23,18 @@ import { WorkspaceHeader, } from "@/components/workspace/workspace-container"; import { useLocalSettings } from "@/core/settings"; -import { type AgentThread } from "@/core/threads"; +import { type AgentThread, type AgentThreadState } from "@/core/threads"; import { useSubmitThread, useThreadStream } from "@/core/threads/hooks"; import { pathOfThread, titleOfThread } from "@/core/threads/utils"; import { uuid } from "@/core/utils/uuid"; export default function ChatPage() { - const router = useRouter(); const { thread_id: threadIdFromPath } = useParams<{ thread_id: string }>(); const isNewThread = useMemo( () => threadIdFromPath === "new", [threadIdFromPath], ); const [threadId, setThreadId] = useState(null); - const [settings, setSettings] = useLocalSettings(); useEffect(() => { if (threadIdFromPath !== "new") { @@ -39,6 +47,40 @@ export default function ChatPage() { isNewThread, threadId, }); + return ( + + + + {isNewThread + ? "New" + : titleOfThread(thread as unknown as AgentThread)} + + + + + + + + + ); +} + +function ThreadDetail({ + threadId, + thread, + isNewThread, +}: { + threadId?: string | null; + thread: UseStream; + isNewThread: boolean; +}) { + const router = useRouter(); + const [settings, setSettings] = useLocalSettings(); + const { open, selectedArtifact } = useArtifacts(); const handleSubmit = useSubmitThread({ isNewThread, threadId, @@ -52,36 +94,33 @@ export default function ChatPage() { await thread.stop(); }, [thread]); return ( - - - - {isNewThread - ? "New" - : titleOfThread(thread as unknown as AgentThread)} - - - - - -
- -
-
- setSettings("context", context)} - onSubmit={handleSubmit} - onStop={handleStop} - /> -
+ + +
+ +
+
+ setSettings("context", context)} + onSubmit={handleSubmit} + onStop={handleStop} + /> +
+
+ {open && ( + <> + + + {selectedArtifact && ( + + )} - {/* - */} -
-
-
+ + )} + ); } diff --git a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx new file mode 100644 index 0000000..6ae28ff --- /dev/null +++ b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx @@ -0,0 +1,14 @@ +import { FileIcon } from "lucide-react"; + +export function ArtifactFileDetail({ filepath }: { filepath: string }) { + return ( +
+
+
+ +
+
{filepath}
+
+
+ ); +} diff --git a/frontend/src/components/workspace/message-list/present-file-list.tsx b/frontend/src/components/workspace/artifacts/artifact-file-list.tsx similarity index 55% rename from frontend/src/components/workspace/message-list/present-file-list.tsx rename to frontend/src/components/workspace/artifacts/artifact-file-list.tsx index 314c4df..44309cb 100644 --- a/frontend/src/components/workspace/message-list/present-file-list.tsx +++ b/frontend/src/components/workspace/artifacts/artifact-file-list.tsx @@ -1,4 +1,5 @@ import { DownloadIcon } from "lucide-react"; +import { useCallback } from "react"; import { Button } from "@/components/ui/button"; import { @@ -9,12 +10,32 @@ import { CardTitle, } from "@/components/ui/card"; import { getFileExtension, getFileName } from "@/core/utils/files"; +import { cn } from "@/lib/utils"; -export function PresentFileList({ files }: { files: string[] }) { +import { useArtifacts } from "./context"; + +export function ArtifactFileList({ + className, + files, +}: { + className?: string; + files: string[]; +}) { + const { openArtifact } = useArtifacts(); + const handleClick = useCallback( + (filepath: string) => { + openArtifact(filepath); + }, + [openArtifact], + ); return ( -