diff --git a/apps/open-swe/package.json b/apps/open-swe/package.json index 2ddfb041..f5436a50 100644 --- a/apps/open-swe/package.json +++ b/apps/open-swe/package.json @@ -27,7 +27,7 @@ "@langchain/core": "^0.3.56", "@langchain/google-genai": "^0.2.9", "@langchain/langgraph": "^0.3.3", - "@langchain/langgraph-sdk": "^0.0.84", + "@langchain/langgraph-sdk": "^0.0.85", "@langchain/openai": "^0.5.10", "@octokit/rest": "^22.0.0", "@open-swe/shared": "*", diff --git a/apps/web/package.json b/apps/web/package.json index e54a74d3..add86a4a 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -21,7 +21,7 @@ "dependencies": { "@langchain/core": "^0.3.57", "@langchain/langgraph": "^0.3.3", - "@langchain/langgraph-sdk": "^0.0.83", + "@langchain/langgraph-sdk": "^0.0.85", "@open-swe/shared": "*", "@radix-ui/react-alert-dialog": "^1.1.14", "@radix-ui/react-avatar": "^1.1.3", diff --git a/apps/web/src/components/gen-ui/initialize-step.tsx b/apps/web/src/components/gen-ui/initialize-step.tsx index 1dec7355..f08361fb 100644 --- a/apps/web/src/components/gen-ui/initialize-step.tsx +++ b/apps/web/src/components/gen-ui/initialize-step.tsx @@ -41,9 +41,15 @@ export function InitializeStep({ const stepStatusIcon = { waiting: ( -
+
+ ), + generating: ( + ), - generating: , success: , error: , }; @@ -53,11 +59,15 @@ export function InitializeStep({ case "loading": return (
); case "generating": - return ; + return ( + + ); case "done": return success ? ( @@ -79,11 +89,11 @@ export function InitializeStep({ }; return ( -
+
{/* Collapse/Expand Icon */} -
- - +
+ + {getStatusText()} {getStatusIcon()} @@ -118,7 +128,7 @@ export function InitializeStep({ ] ?? (
)} @@ -128,7 +138,7 @@ export function InitializeStep({ "font-normal", step.status === "error" ? "text-red-500" - : "text-gray-800", + : "text-gray-800 dark:text-gray-200", )} > {step.name} diff --git a/apps/web/src/components/github/repo-branch-selectors/index.tsx b/apps/web/src/components/github/repo-branch-selectors/index.tsx index e4653cf9..3177247c 100644 --- a/apps/web/src/components/github/repo-branch-selectors/index.tsx +++ b/apps/web/src/components/github/repo-branch-selectors/index.tsx @@ -6,9 +6,9 @@ export function RepositoryBranchSelectors() { const [threadId] = useQueryState("threadId"); const chatStarted = !!threadId; const defaultButtonStyles = - "bg-inherit border-none text-gray-500 hover:text-gray-300 text-xs p-0 h-fit hover:bg-inherit"; + "bg-inherit border-none text-gray-500 hover:text-black dark:hover:text-gray-300 text-xs p-0 h-fit hover:bg-inherit"; const defaultStylesChatStarted = - "hover:bg-inherit cursor-default hover:cursor-default hover:text-gray-300 hover:border-gray-300 hover:ring-inherit"; + "hover:bg-inherit cursor-default hover:cursor-default hover:text-black dark:hover:text-gray-300 hover:border-gray-300 hover:ring-inherit"; return (
diff --git a/apps/web/src/components/v2/terminal-input.tsx b/apps/web/src/components/v2/terminal-input.tsx index 77d7c144..1d8e3fa7 100644 --- a/apps/web/src/components/v2/terminal-input.tsx +++ b/apps/web/src/components/v2/terminal-input.tsx @@ -155,13 +155,13 @@ export function TerminalInput({
diff --git a/apps/web/src/components/v2/thread-view.tsx b/apps/web/src/components/v2/thread-view.tsx index 4cc8f573..d2516c5c 100644 --- a/apps/web/src/components/v2/thread-view.tsx +++ b/apps/web/src/components/v2/thread-view.tsx @@ -1,14 +1,15 @@ "use client"; - import { v4 as uuidv4 } from "uuid"; -import { useState } from "react"; +import { useState, useEffect, useRef } from "react"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Card, CardContent } from "@/components/ui/card"; import { Textarea } from "@/components/ui/textarea"; import { ArrowLeft, GitBranch, Send, + Terminal, + Clock, User, Bot, Copy, @@ -23,10 +24,16 @@ import { ThreadDisplayInfo } from "./types"; import { useStream } from "@langchain/langgraph-sdk/react"; import { ManagerGraphState } from "@open-swe/shared/open-swe/manager/types"; import { PlannerGraphState } from "@open-swe/shared/open-swe/planner/types"; +import { GraphState } from "@open-swe/shared/open-swe/types"; import { ActionsRenderer } from "./actions-renderer"; import { ThemeToggle } from "../theme-toggle"; import { HumanMessage } from "@langchain/core/messages"; import { DO_NOT_RENDER_ID_PREFIX } from "@open-swe/shared/constants"; +import { StickToBottom } from "use-stick-to-bottom"; +import { + StickyToBottomContent, + ScrollToBottom, +} from "../../utils/scroll-utils"; const PROGRAMMER_ASSISTANT_ID = process.env.NEXT_PUBLIC_PROGRAMMER_ASSISTANT_ID; const PLANNER_ASSISTANT_ID = process.env.NEXT_PUBLIC_PLANNER_ASSISTANT_ID; @@ -100,9 +107,23 @@ export function ThreadView({ const [programmerSession, setProgrammerSession] = useState(); - if (!stream.messages?.length) { - return null; - } + const prevMessageLength = useRef(0); + useEffect(() => { + if ( + stream.messages && + stream.messages.length !== prevMessageLength.current + ) { + prevMessageLength.current = stream.messages.length; + } + }, [stream.messages]); + + useEffect(() => { + // Auto-scroll to bottom when tab switches (requirement fulfilled) + // StickToBottom handles the actual scrolling via scrollToBottom() + }, [selectedTab]); + + // Don't early return for empty messages - show loading/empty state instead + // This prevents blank pages when navigating to existing threads const handleSendMessage = () => { if (chatInput.trim()) { @@ -179,40 +200,58 @@ export function ThreadView({ {/* Left Side - Chat Interface */}
{/* Chat Messages */} -
- {filteredMessages.map((message) => ( -
-
- {message.type === "human" ? ( -
- -
- ) : ( -
- -
- )} -
-
-
- - {message.type === "human" ? "You" : "Agent"} - +
+ + + {filteredMessages.map((message) => ( +
+
+ {message.type === "human" ? ( +
+ +
+ ) : ( +
+ +
+ )} +
+
+
+ + {message.type === "human" ? "You" : "Agent"} + +
+
+ {getMessageContentString(message.content)} +
+
+ +
+
+
+ ))} + + } + footer={ +
+
-
- {getMessageContentString(message.content)} -
-
- -
-
-
- ))} + } + /> +
{/* Chat Input - Fixed at bottom */} @@ -233,10 +272,10 @@ export function ThreadView({
@@ -247,56 +286,87 @@ export function ThreadView({ {/* Right Side - Actions & Plan */}
-
- - setSelectedTab(value as "planner" | "programmer") - } +
+ - - Planner - Programmer - - - - - {plannerThreadId && - plannerRunId && - PLANNER_ASSISTANT_ID && ( - - graphId={PLANNER_ASSISTANT_ID} - threadId={plannerThreadId} - runId={plannerRunId} - setProgrammerSession={setProgrammerSession} - programmerSession={programmerSession} - setSelectedTab={setSelectedTab} - /> - )} - - - - - - - {programmerSession && PROGRAMMER_ASSISTANT_ID && ( - - graphId={PROGRAMMER_ASSISTANT_ID} - threadId={programmerSession.threadId} - runId={programmerSession.runId} - /> - )} - {!programmerSession && ( -
- No programmer session -
- )} -
-
-
- + + setSelectedTab(value as "planner" | "programmer") + } + > + + Planner + Programmer + + + + + {plannerThreadId && + plannerRunId && + PLANNER_ASSISTANT_ID && ( + + graphId={PLANNER_ASSISTANT_ID} + threadId={plannerThreadId} + runId={plannerRunId} + setProgrammerSession={setProgrammerSession} + programmerSession={programmerSession} + setSelectedTab={setSelectedTab} + /> + )} + {!( + plannerThreadId && + plannerRunId && + PLANNER_ASSISTANT_ID + ) && ( +
+ + + No planner session + +
+ )} +
+
+
+ + + + {programmerSession && PROGRAMMER_ASSISTANT_ID && ( + + graphId={PROGRAMMER_ASSISTANT_ID} + threadId={programmerSession.threadId} + runId={programmerSession.runId} + /> + )} + {!programmerSession && ( +
+ + + No programmer session + +
+ )} +
+
+
+ + } + footer={ +
+ +
+ } + /> +
diff --git a/apps/web/src/utils/scroll-utils.tsx b/apps/web/src/utils/scroll-utils.tsx new file mode 100644 index 00000000..91ccce4a --- /dev/null +++ b/apps/web/src/utils/scroll-utils.tsx @@ -0,0 +1,45 @@ +import { ReactNode } from "react"; +import { Button } from "@/components/ui/button"; +import { ArrowDown } from "lucide-react"; +import { useStickToBottomContext } from "use-stick-to-bottom"; + +export function StickyToBottomContent(props: { + content: ReactNode; + footer?: ReactNode; + className?: string; + contentClassName?: string; +}) { + const context = useStickToBottomContext(); + return ( +
+
+ {props.content} +
+ {props.footer} +
+ ); +} + +export function ScrollToBottom(props: { className?: string }) { + const { isAtBottom, scrollToBottom } = useStickToBottomContext(); + + if (isAtBottom) return null; + return ( + + ); +} diff --git a/package.json b/package.json index b9edb4c3..7ade16b3 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "typescript": "^5" }, "resolutions": { - "@langchain/langgraph-sdk": "^0.0.83", + "@langchain/langgraph-sdk": "^0.0.85", "@langchain/core": "^0.3.58" }, "packageManager": "yarn@3.5.1" diff --git a/packages/shared/package.json b/packages/shared/package.json index 1f16e400..7df1e825 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -19,7 +19,7 @@ "dependencies": { "@langchain/core": "^0.3.56", "@langchain/langgraph": "^0.3.3", - "@langchain/langgraph-sdk": "^0.0.83", + "@langchain/langgraph-sdk": "^0.0.85", "@octokit/rest": "^22.0.0", "zod": "^3.25.32" }, diff --git a/yarn.lock b/yarn.lock index ab991d5c..ccefd315 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1803,9 +1803,9 @@ __metadata: languageName: node linkType: hard -"@langchain/langgraph-sdk@npm:^0.0.83": - version: 0.0.83 - resolution: "@langchain/langgraph-sdk@npm:0.0.83" +"@langchain/langgraph-sdk@npm:^0.0.85": + version: 0.0.85 + resolution: "@langchain/langgraph-sdk@npm:0.0.85" dependencies: "@types/json-schema": ^7.0.15 p-queue: ^6.6.2 @@ -1819,7 +1819,7 @@ __metadata: optional: true react: optional: true - checksum: c6d037b8b9486456a8180d892e8b05ad3c2332456d7d237597720ebe42dd6b126f1aed963a0d2a681405724b7b8e0eac0607994b8e527d2f0e756e4c9e30c19e + checksum: f6d206c718cf4cb5d69dd677f2afa38325fdd69b485310a6beb61f06f24b2b4a8c0446eacbe7911c02cedc8bd4b719e22894c6f9b93572e6665c6a609cc9f511 languageName: node linkType: hard @@ -2264,7 +2264,7 @@ __metadata: "@langchain/google-genai": ^0.2.9 "@langchain/langgraph": ^0.3.3 "@langchain/langgraph-cli": latest - "@langchain/langgraph-sdk": ^0.0.84 + "@langchain/langgraph-sdk": ^0.0.85 "@langchain/openai": ^0.5.10 "@octokit/rest": ^22.0.0 "@open-swe/shared": "*" @@ -2299,7 +2299,7 @@ __metadata: "@eslint/js": ^9.19.0 "@langchain/core": ^0.3.56 "@langchain/langgraph": ^0.3.3 - "@langchain/langgraph-sdk": ^0.0.83 + "@langchain/langgraph-sdk": ^0.0.85 "@octokit/rest": ^22.0.0 "@octokit/types": ^12.0.0 "@tsconfig/recommended": ^1.0.8 @@ -2324,7 +2324,7 @@ __metadata: "@eslint/js": ^9.19.0 "@langchain/core": ^0.3.57 "@langchain/langgraph": ^0.3.3 - "@langchain/langgraph-sdk": ^0.0.83 + "@langchain/langgraph-sdk": ^0.0.85 "@open-swe/shared": "*" "@radix-ui/react-alert-dialog": ^1.1.14 "@radix-ui/react-avatar": ^1.1.3