Add AI chat to dashboard, refactor stream module, snapshot --urls, batch argument mode (#1160)

* chat

* refactor

* fixes

* fixes

* fixes

* fixes

* improvements

* download chat

* batch

* fixes

* fixes

* fixes

* fmt

* fixes

* fixes

* fixes

* fmt
This commit is contained in:
Chris Tate
2026-04-06 08:10:43 -05:00
committed by GitHub
parent fcb6615f5a
commit 317e6869b6
44 changed files with 4933 additions and 1957 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+19
View File
@@ -186,3 +186,22 @@ button {
:is(.dark *).json-punct {
color: #a1a1a1;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.shimmer-text {
background: linear-gradient(
90deg,
currentColor 25%,
color-mix(in srgb, currentColor 40%, transparent) 50%,
currentColor 75%
);
background-size: 200% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer 2s ease-in-out infinite;
}
+7 -4
View File
@@ -4,6 +4,7 @@ import { Geist } from "next/font/google";
import { cn } from "@/lib/utils";
import { TooltipProvider } from "@/components/ui/tooltip";
import { JotaiProvider } from "@/store/provider";
import { ThemeProvider } from "@/components/theme-provider";
const geist = Geist({ subsets: ["latin"], variable: "--font-sans" });
@@ -18,11 +19,13 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
<html lang="en" className={cn("dark font-sans antialiased", geist.variable)}>
<html lang="en" className={cn("font-sans antialiased", geist.variable)} suppressHydrationWarning>
<body>
<JotaiProvider>
<TooltipProvider>{children}</TooltipProvider>
</JotaiProvider>
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
<JotaiProvider>
<TooltipProvider>{children}</TooltipProvider>
</JotaiProvider>
</ThemeProvider>
</body>
</html>
);
+48 -3
View File
@@ -1,14 +1,16 @@
"use client";
import { useAtomValue } from "jotai/react";
import { activePortAtom } from "@/store/sessions";
import { useAtomValue, useSetAtom } from "jotai/react";
import { activePortAtom, sessionsAtom, newSessionDialogAtom } from "@/store/sessions";
import { useSessionsSync } from "@/store/sessions";
import { useStreamSync, hasConsoleErrorsAtom, consoleLogsAtom } from "@/store/stream";
import { useActivitySync } from "@/store/activity";
import { activeExtensionsAtom } from "@/store/sessions";
import { useChatStatusSync } from "@/store/chat";
import { useMediaQuery } from "@/hooks/use-media-query";
import { Viewport } from "@/components/viewport";
import { ActivityFeed } from "@/components/activity-feed";
import { ChatPanel } from "@/components/chat-panel";
import { ConsolePanel } from "@/components/console-panel";
import { StoragePanel } from "@/components/storage-panel";
import { ExtensionsPanel } from "@/components/extensions-panel";
@@ -20,21 +22,28 @@ import {
ResizableHandle,
} from "@/components/ui/resizable";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import { Plus } from "lucide-react";
export default function DashboardPage() {
const activePort = useAtomValue(activePortAtom);
useStreamSync(activePort);
useSessionsSync();
useActivitySync();
useChatStatusSync();
const sessions = useAtomValue(sessionsAtom);
const hasSessions = sessions.length > 0;
const setNewSessionDialog = useSetAtom(newSessionDialogAtom);
const isDesktop = useMediaQuery("(min-width: 768px)");
const hasConsoleErrors = useAtomValue(hasConsoleErrorsAtom);
const activeExtensions = useAtomValue(activeExtensionsAtom);
const sidePanel = (
<Tabs defaultValue="activity" className="flex h-full flex-col">
<Tabs defaultValue="chat" className="flex h-full flex-col">
<div className="shrink-0 px-2 pt-1">
<TabsList variant="line" className="h-7 w-full">
<TabsTrigger value="chat" className="text-[11px]">Chat</TabsTrigger>
<TabsTrigger value="activity" className="text-[11px]">Activity</TabsTrigger>
<TabsTrigger value="console" className="text-[11px]">
Console
@@ -67,10 +76,46 @@ export default function DashboardPage() {
<TabsContent value="extensions" className="min-h-0 flex-1 overflow-hidden">
<ExtensionsPanel />
</TabsContent>
<TabsContent value="chat" className="min-h-0 flex-1 overflow-hidden">
<ChatPanel />
</TabsContent>
</Tabs>
);
if (isDesktop) {
if (!hasSessions) {
return (
<div className="flex h-screen flex-col bg-background">
<ResizablePanelGroup
orientation="horizontal"
className="min-h-0 flex-1"
>
<ResizablePanel id="sessions" defaultSize="15%" minSize="10%" maxSize="30%">
<SessionTree />
</ResizablePanel>
<ResizableHandle />
<ResizablePanel id="empty" defaultSize="85%">
<div className="flex h-full items-center justify-center">
<div className="text-center space-y-4">
<div className="space-y-2">
<p className="text-sm text-muted-foreground">No active sessions</p>
<p className="text-xs text-muted-foreground/60">Create a session to get started</p>
</div>
<Button
size="sm"
onClick={() => setNewSessionDialog(true)}
>
<Plus className="size-3.5" />
New session
</Button>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
);
}
return (
<div className="flex h-screen flex-col bg-background">
<ResizablePanelGroup