diff --git a/apps/web/src/app/(v2)/chat/[thread_id]/page.tsx b/apps/web/src/app/(v2)/chat/[thread_id]/page.tsx index 64cbfc9e..1751df3b 100644 --- a/apps/web/src/app/(v2)/chat/[thread_id]/page.tsx +++ b/apps/web/src/app/(v2)/chat/[thread_id]/page.tsx @@ -29,7 +29,9 @@ export default function ThreadPage({ reconnectOnMount: true, }); - const { threads, threadsLoading } = useThreads(); + const { threads, threadsLoading } = useThreads( + process.env.NEXT_PUBLIC_MANAGER_ASSISTANT_ID, + ); // Find the thread by ID const thread = threads?.find((t) => t.thread_id === thread_id); @@ -37,7 +39,7 @@ export default function ThreadPage({ router.push("/chat"); }; - if (!thread || threadsLoading || !threadsLoading) { + if (!thread || threadsLoading) { return ; } @@ -46,17 +48,12 @@ export default function ThreadPage({ threads?.map(threadToDisplayInfo) ?? []; const currentDisplayThread = threadToDisplayInfo(thread); - const handleThreadSelect = (selectedThread: ThreadDisplayInfo) => { - router.push(`/chat/${selectedThread.id}`); - }; - return ( -
+
diff --git a/apps/web/src/app/(v2)/chat/page.tsx b/apps/web/src/app/(v2)/chat/page.tsx index 089b33ae..4d04aefc 100644 --- a/apps/web/src/app/(v2)/chat/page.tsx +++ b/apps/web/src/app/(v2)/chat/page.tsx @@ -18,7 +18,7 @@ export default function ChatPage() { threads?.map(threadToDisplayInfo) ?? []; return ( -
+
diff --git a/apps/web/src/app/(v2)/chat/threads/page.tsx b/apps/web/src/app/(v2)/chat/threads/page.tsx index 2ed0e6ac..fefbb930 100644 --- a/apps/web/src/app/(v2)/chat/threads/page.tsx +++ b/apps/web/src/app/(v2)/chat/threads/page.tsx @@ -3,32 +3,23 @@ import type React from "react"; import { useState } from "react"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; -import { - ArrowLeft, - Search, - Filter, - CheckCircle, - XCircle, - Loader2, - GitBranch, - GitPullRequest, - Bug, - Calendar, - Clock, -} from "lucide-react"; +import { ArrowLeft, Search, Filter } from "lucide-react"; import { useRouter } from "next/navigation"; import { ThreadDisplayInfo, threadToDisplayInfo } from "@/components/v2/types"; import { useThreads } from "@/hooks/useThreads"; import { GraphState } from "@open-swe/shared/open-swe/types"; +import { ThreadCard } from "@/components/v2/thread-card"; +import { ThemeToggle } from "@/components/theme-toggle"; type FilterStatus = "all" | "running" | "completed" | "failed" | "pending"; export default function AllThreadsPage() { const router = useRouter(); - const { threads } = useThreads(); + const { threads } = useThreads( + process.env.NEXT_PUBLIC_MANAGER_ASSISTANT_ID, + ); const [searchQuery, setSearchQuery] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); @@ -36,51 +27,6 @@ export default function AllThreadsPage() { const displayThreads: ThreadDisplayInfo[] = threads?.map(threadToDisplayInfo) ?? []; - const getStatusColor = (status: ThreadDisplayInfo["status"]) => { - switch (status) { - case "running": - return "bg-blue-950 text-blue-400"; - case "completed": - return "bg-green-950 text-green-400"; - case "failed": - return "bg-red-950 text-red-400"; - case "pending": - return "bg-yellow-950 text-yellow-400"; - default: - return "bg-gray-800 text-gray-400"; - } - }; - - const getStatusIcon = (status: ThreadDisplayInfo["status"]) => { - switch (status) { - case "running": - return ; - case "completed": - return ; - case "failed": - return ; - case "pending": - return ; - default: - return null; - } - }; - - const getPRStatusColor = (status: string) => { - switch (status) { - case "merged": - return "text-purple-400"; - case "open": - return "text-green-400"; - case "draft": - return "text-gray-400"; - case "closed": - return "text-red-400"; - default: - return "text-gray-400"; - } - }; - // Filter and search threads const filteredThreads = displayThreads.filter((thread) => { const matchesSearch = @@ -107,50 +53,51 @@ export default function AllThreadsPage() { pending: displayThreads.filter((t) => t.status === "pending").length, }; - const handleThreadClick = (thread: ThreadDisplayInfo) => { - router.push(`/chat/${thread.id}`); - }; - return ( -
+
{/* Header */} -
+
- All Threads -
-
- - {filteredThreads.length} threads + + All Threads
+
+
+ + {filteredThreads.length} threads + +
+ +
{/* Search and Filters */} -
+
- + setSearchQuery(e.target.value)} - className="border-gray-700 bg-gray-900 pl-10 text-gray-300 placeholder:text-gray-600" + className="border-border bg-background text-foreground placeholder:text-muted-foreground pl-10 dark:bg-gray-900" />
- - Filter: + + Filter: {( [ "all", @@ -166,8 +113,8 @@ export default function AllThreadsPage() { size="sm" className={`h-7 text-xs ${ statusFilter === status - ? "bg-gray-700 text-gray-200" - : "text-gray-500 hover:bg-gray-800 hover:text-gray-300" + ? "bg-muted text-foreground dark:bg-gray-700" + : "text-muted-foreground hover:bg-muted hover:text-foreground" }`} onClick={() => setStatusFilter(status)} > @@ -176,7 +123,7 @@ export default function AllThreadsPage() { : status.charAt(0).toUpperCase() + status.slice(1)} {statusCounts[status]} @@ -197,12 +144,12 @@ export default function AllThreadsPage() { return (
-

+

{status} Threads

{threads.length} @@ -212,10 +159,6 @@ export default function AllThreadsPage() { handleThreadClick(thread)} - getStatusColor={getStatusColor} - getStatusIcon={getStatusIcon} - getPRStatusColor={getPRStatusColor} /> ))}
@@ -230,10 +173,6 @@ export default function AllThreadsPage() { handleThreadClick(thread)} - getStatusColor={getStatusColor} - getStatusIcon={getStatusIcon} - getPRStatusColor={getPRStatusColor} /> ))}
@@ -241,8 +180,8 @@ export default function AllThreadsPage() { {filteredThreads.length === 0 && (
-
No threads found
-
+
No threads found
+
{searchQuery ? "Try adjusting your search query" : "No threads match the selected filter"} @@ -254,95 +193,3 @@ export default function AllThreadsPage() {
); } - -interface ThreadCardProps { - thread: ThreadDisplayInfo; - onClick: () => void; - getStatusColor: (status: ThreadDisplayInfo["status"]) => string; - getStatusIcon: (status: ThreadDisplayInfo["status"]) => React.ReactNode; - getPRStatusColor: (status: string) => string; -} - -function ThreadCard({ - thread, - onClick, - getStatusColor, - getStatusIcon, - getPRStatusColor, -}: ThreadCardProps) { - return ( - - -
-
- - {thread.title} - -
- - - {thread.repository} - -
-
- -
- {getStatusIcon(thread.status)} - {thread.status} -
-
-
-
- -
-
- - {thread.taskCount} tasks - - • -
- - - {thread.lastActivity} - -
-
-
- {thread.githubIssue && ( - - )} - {thread.pullRequest && ( - - )} -
-
-
-
- ); -} diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 60afd2ed..db5adfa1 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -3,6 +3,7 @@ import "./globals.css"; import { Inter } from "next/font/google"; import React from "react"; import { NuqsAdapter } from "nuqs/adapters/next/app"; +import { ThemeProvider } from "@/components/theme-provider"; const inter = Inter({ subsets: ["latin"], @@ -26,9 +27,17 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + - {children} + + {children} + ); diff --git a/apps/web/src/components/gen-ui/action-step.tsx b/apps/web/src/components/gen-ui/action-step.tsx index 8c0164ba..6597ee6d 100644 --- a/apps/web/src/components/gen-ui/action-step.tsx +++ b/apps/web/src/components/gen-ui/action-step.tsx @@ -54,10 +54,12 @@ export function ActionStep(props: ActionStepProps) { switch (props.status) { case "loading": return ( -
+
); case "generating": - return ; + return ( + + ); case "done": return props.success ? ( @@ -106,13 +108,13 @@ export function ActionStep(props: ActionStepProps) { const renderHeaderIcon = () => { if (props.status === "loading" || !("actionType" in props)) { // In loading state, we don't know the type yet, use a generic icon - return ; + return ; } return props.actionType === "shell" ? ( - + ) : ( - + ); }; @@ -120,7 +122,7 @@ export function ActionStep(props: ActionStepProps) { const renderHeaderContent = () => { if (props.status === "loading" || !("actionType" in props)) { return ( - + Preparing action... ); @@ -130,18 +132,18 @@ export function ActionStep(props: ActionStepProps) { return (
{props.workdir && ( -
+
{props.workdir}
)} - + {props.command.join(" ")}
); } else { return ( - + {props.file} ); @@ -156,12 +158,12 @@ export function ActionStep(props: ActionStepProps) { if (props.actionType === "shell" && props.output) { return ( -
+
             {props.output}
           
{props.errorCode !== undefined && !props.success && ( -
+
Exit code: {props.errorCode}
)} @@ -169,23 +171,25 @@ export function ActionStep(props: ActionStepProps) { ); } else if (props.actionType === "apply-patch" && props.diff) { return ( -
+
 
           {!props.success && props.errorMessage && (
-            
+
{props.errorMessage}
)} {!props.success && props.fixedDiff && (
-
Fixed diff:
+
+ Fixed diff: +
+    
{props.reasoningText && ( -
+
{showReasoning && ( -

+

{props.reasoningText}

)}
)} -
+
{renderHeaderIcon()} {renderHeaderContent()}
- + {getStatusText()} {getStatusIcon()} {shouldShowToggle() && ( {showSummary && ( -

+

{props.summaryText}

)} @@ -268,11 +272,11 @@ function formatDiff(diff: string) { .split("\n") .map((line) => { if (line.startsWith("+")) { - return `${line}`; + return `${line}`; } else if (line.startsWith("-")) { - return `${line}`; + return `${line}`; } else if (line.startsWith("@")) { - return `${line}`; + return `${line}`; } return line; }) diff --git a/apps/web/src/components/plan/plan-viewer.tsx b/apps/web/src/components/plan/plan-viewer.tsx index e030d708..49313f51 100644 --- a/apps/web/src/components/plan/plan-viewer.tsx +++ b/apps/web/src/components/plan/plan-viewer.tsx @@ -38,11 +38,11 @@ export function PlanViewer({ return (
-

+

{isProposedPlan ? "Proposed" : "Execution"} Plan

{!isProposedPlan && ( - + {planItems.filter((item) => item.completed).length} of{" "} {planItems.length} completed @@ -61,47 +61,47 @@ export function PlanViewer({ className={cn( "flex items-start gap-3 rounded-lg border p-3 transition-colors", { - "border-green-800 bg-green-900/30": status === "completed", - "border-blue-800 bg-blue-900/30": status === "current", - "border-gray-800 bg-gray-900/30": [ - "remaining", - "proposed", - ].includes(status), + "border-green-300 bg-green-100/30 dark:border-green-800 dark:bg-green-900/30": + status === "completed", + "border-blue-300 bg-blue-100/30 dark:border-blue-800 dark:bg-blue-900/30": + status === "current", + "border-border bg-muted/30 dark:border-gray-800 dark:bg-gray-900/30": + ["remaining", "proposed"].includes(status), }, )} > {/* Status Icon */}
{status === "completed" && ( - + )} {status === "current" && ( - + )} {["remaining", "proposed"].includes(status) && ( - + )}
{/* Task Content */}
-
+
Step {item.index + 1} {status === "current" && ( - + In Progress )} {status === "completed" && ( - + Done )} {status === "proposed" && ( - + Proposed )} @@ -162,13 +162,13 @@ export function PlanViewer({ }); }); }} - className="text-gray-200" + className="text-foreground/90 dark:text-gray-200" /> ) : (

-

+

+

Summary:

-

{item.summary}

+

+ {item.summary} +

)}
@@ -193,7 +195,7 @@ export function PlanViewer({ {isProposedPlan && (