fix: UI improvements to the approved plan component (#611)

* fix: UI improvements to the approved plan component

* cr
This commit is contained in:
Brace Sproul 2025-07-30 12:12:38 -07:00 • committed by GitHub
parent 997b7fc278
commit 6440542b23
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 21 additions and 7 deletions

View file

@ -12,6 +12,9 @@ import { cn } from "@/lib/utils";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { Badge } from "../ui/badge"; import { Badge } from "../ui/badge";
import { HumanResponse } from "@langchain/langgraph/prebuilt"; import { HumanResponse } from "@langchain/langgraph/prebuilt";
import { TaskPlan } from "@open-swe/shared/open-swe/types";
import { getActivePlanItems } from "@open-swe/shared/open-swe/tasks";
import { InlineMarkdownText } from "../thread/markdown-text";
type PlanItem = { type PlanItem = {
index: number; index: number;
@ -20,6 +23,7 @@ type PlanItem = {
}; };
type AcceptedPlanStepProps = { type AcceptedPlanStepProps = {
taskPlan?: TaskPlan;
planTitle?: string; planTitle?: string;
planItems?: PlanItem[]; planItems?: PlanItem[];
interruptType?: HumanResponse["type"]; interruptType?: HumanResponse["type"];
@ -27,12 +31,14 @@ type AcceptedPlanStepProps = {
}; };
export function AcceptedPlanStep({ export function AcceptedPlanStep({
taskPlan,
planTitle, planTitle,
planItems = [], planItems = [],
interruptType, interruptType,
collapse: collapseProp = true, collapse: collapseProp = true,
}: AcceptedPlanStepProps) { }: AcceptedPlanStepProps) {
const [collapsed, setCollapsed] = useState(collapseProp); const [collapsed, setCollapsed] = useState(collapseProp);
const activeTaskPlan = taskPlan ? getActivePlanItems(taskPlan) : planItems;
const getStatusText = () => { const getStatusText = () => {
if (interruptType === "edit") { if (interruptType === "edit") {
@ -68,7 +74,7 @@ export function AcceptedPlanStep({
); );
}; };
const totalCount = planItems.length; const totalCount = activeTaskPlan.length;
return ( return (
<div <div
@ -97,7 +103,7 @@ export function AcceptedPlanStep({
</div> </div>
{totalCount > 0 && ( {totalCount > 0 && (
<p className="text-muted-foreground mt-1 text-xs"> <p className="text-muted-foreground mt-1 text-xs">
{totalCount} steps {totalCount} step{totalCount === 1 ? "" : "s"}
</p> </p>
)} )}
</div> </div>
@ -139,7 +145,7 @@ export function AcceptedPlanStep({
</div> </div>
)} )}
{planItems.length > 0 && ( {activeTaskPlan.length > 0 && (
<div className="space-y-3"> <div className="space-y-3">
<h4 className="text-foreground flex items-center gap-2 text-sm font-medium"> <h4 className="text-foreground flex items-center gap-2 text-sm font-medium">
Execution Steps Execution Steps
@ -152,7 +158,7 @@ export function AcceptedPlanStep({
</h4> </h4>
<div className="space-y-3"> <div className="space-y-3">
{planItems {activeTaskPlan
.sort((a, b) => a.index - b.index) .sort((a, b) => a.index - b.index)
.map((item, idx) => ( .map((item, idx) => (
<div <div
@ -170,7 +176,7 @@ export function AcceptedPlanStep({
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<div className="flex items-start justify-between gap-2"> <div className="flex items-start justify-between gap-2">
<p <InlineMarkdownText
className={cn( className={cn(
"text-sm leading-relaxed", "text-sm leading-relaxed",
item.completed item.completed
@ -179,7 +185,7 @@ export function AcceptedPlanStep({
)} )}
> >
{item.plan} {item.plan}
</p> </InlineMarkdownText>
<Badge <Badge
variant="outline" variant="outline"
className={cn( className={cn(

View file

@ -13,7 +13,11 @@ import { Message } from "@langchain/langgraph-sdk";
import { InitializeStep } from "../gen-ui/initialize-step"; import { InitializeStep } from "../gen-ui/initialize-step";
import { AcceptedPlanStep } from "../gen-ui/accepted-plan-step"; import { AcceptedPlanStep } from "../gen-ui/accepted-plan-step";
import { PlannerGraphState } from "@open-swe/shared/open-swe/planner/types"; import { PlannerGraphState } from "@open-swe/shared/open-swe/planner/types";
import { GraphState, PlanItem } from "@open-swe/shared/open-swe/types"; import {
GraphState,
PlanItem,
TaskPlan,
} from "@open-swe/shared/open-swe/types";
import { HumanResponse } from "@langchain/langgraph/prebuilt"; import { HumanResponse } from "@langchain/langgraph/prebuilt";
import { LoadingActionsCardContent } from "./thread-view-loading"; import { LoadingActionsCardContent } from "./thread-view-loading";
import { Interrupt } from "../thread/messages/interrupt"; import { Interrupt } from "../thread/messages/interrupt";
@ -65,6 +69,7 @@ interface ActionsRendererProps<
customNodeEvents: CustomNodeEvent[]; customNodeEvents: CustomNodeEvent[];
setCustomNodeEvents: Dispatch<SetStateAction<CustomNodeEvent[]>>; setCustomNodeEvents: Dispatch<SetStateAction<CustomNodeEvent[]>>;
stream: ReturnType<typeof useStream<StateType>>; stream: ReturnType<typeof useStream<StateType>>;
taskPlan?: TaskPlan;
} }
const getCustomNodeEventsFromMessages = ( const getCustomNodeEventsFromMessages = (
@ -92,6 +97,7 @@ const getCustomNodeEventsFromMessages = (
export function ActionsRenderer< export function ActionsRenderer<
StateType extends PlannerGraphState | GraphState, StateType extends PlannerGraphState | GraphState,
>({ >({
taskPlan,
runId, runId,
customNodeEvents, customNodeEvents,
setCustomNodeEvents, setCustomNodeEvents,
@ -224,6 +230,7 @@ export function ActionsRenderer<
{acceptedPlanEvents.length > 0 && {acceptedPlanEvents.length > 0 &&
isAcceptedPlanEvents(acceptedPlanEvents) && ( isAcceptedPlanEvents(acceptedPlanEvents) && (
<AcceptedPlanStep <AcceptedPlanStep
taskPlan={taskPlan}
planTitle={ planTitle={
acceptedPlanEvents[acceptedPlanEvents.length - 1].data.planTitle acceptedPlanEvents[acceptedPlanEvents.length - 1].data.planTitle
} }

View file

@ -403,6 +403,7 @@ export function ThreadView({
{plannerSession ? ( {plannerSession ? (
<div className="scrollbar-pretty-auto overflow-y-auto px-2"> <div className="scrollbar-pretty-auto overflow-y-auto px-2">
<ActionsRenderer<PlannerGraphState> <ActionsRenderer<PlannerGraphState>
taskPlan={programmerTaskPlan}
runId={plannerSession.runId} runId={plannerSession.runId}
customNodeEvents={customPlannerNodeEvents} customNodeEvents={customPlannerNodeEvents}
setCustomNodeEvents={ setCustomNodeEvents={