diff --git a/package-lock.json b/package-lock.json index e47aeef..6d84a1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "ai": "^4.3.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "nuqs": "^2.4.3", "react": "^19.1.0", "react-dom": "^19.1.0", "tailwind-merge": "^3.2.0", @@ -4893,6 +4894,12 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://a0us.jfrog.io/artifactory/api/npm/npm/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4950,6 +4957,39 @@ "dev": true, "license": "MIT" }, + "node_modules/nuqs": { + "version": "2.4.3", + "resolved": "https://a0us.jfrog.io/artifactory/api/npm/npm/nuqs/-/nuqs-2.4.3.tgz", + "integrity": "sha512-BgtlYpvRwLYiJuWzxt34q2bXu/AIS66sLU1QePIMr2LWkb+XH0vKXdbLSgn9t6p7QKzwI7f38rX3Wl9llTXQ8Q==", + "license": "MIT", + "dependencies": { + "mitt": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/franky47" + }, + "peerDependencies": { + "@remix-run/react": ">=2", + "next": ">=14.2.0", + "react": ">=18.2.0 || ^19.0.0-0", + "react-router": "^6 || ^7", + "react-router-dom": "^6 || ^7" + }, + "peerDependenciesMeta": { + "@remix-run/react": { + "optional": true + }, + "next": { + "optional": true + }, + "react-router": { + "optional": true + }, + "react-router-dom": { + "optional": true + } + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/package.json b/package.json index f63bcd7..7c2f2a5 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "ai": "^4.3.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "nuqs": "^2.4.3", "react": "^19.1.0", "react-dom": "^19.1.0", "tailwind-merge": "^3.2.0", diff --git a/src/app.tsx b/src/app.tsx index f56e9d6..b0be804 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -4,6 +4,7 @@ import { useAgentChat } from "agents/ai-react"; import type { Message } from "@ai-sdk/react"; import { APPROVAL } from "./shared"; import type { tools } from "./tools"; +import { useQueryState } from "nuqs"; // Component imports import { Button } from "@/components/button/Button"; @@ -21,7 +22,9 @@ import { Robot, Sun, Trash, + FilePlus, } from "@phosphor-icons/react"; +import { generateShortId } from "./lib/utils"; // List of tools that require human confirmation const toolsRequiringConfirmation: (keyof typeof tools)[] = [ @@ -29,6 +32,7 @@ const toolsRequiringConfirmation: (keyof typeof tools)[] = [ ]; export default function Chat() { + const [ thread, setThread ] = useQueryState("thread"); const [theme, setTheme] = useState<"dark" | "light">(() => { // Check localStorage first, default to dark if not found const savedTheme = localStorage.getItem("theme"); @@ -60,13 +64,25 @@ export default function Chat() { scrollToBottom(); }, [scrollToBottom]); + useEffect(() => { + if (thread) return; + createNewThread(); + }, [thread]); + const toggleTheme = () => { const newTheme = theme === "dark" ? "light" : "dark"; setTheme(newTheme); }; + const createNewThread = () => { + const newThread = generateShortId(); + setThread(newThread); + setMessages([]); + }; + const agent = useAgent({ agent: "chat", + name: thread ?? undefined, }); const { @@ -76,6 +92,7 @@ export default function Chat() { handleSubmit: handleAgentSubmit, addToolResult, clearHistory, + setMessages, } = useAgentChat({ agent, maxSteps: 5, @@ -101,6 +118,10 @@ export default function Chat() { return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); }; + if (!thread) { + return <>>; + } + return (