diff --git a/frontend/src/components/ai-elements/chain-of-thought.tsx b/frontend/src/components/ai-elements/chain-of-thought.tsx
index 1a8550a..b0fde74 100644
--- a/frontend/src/components/ai-elements/chain-of-thought.tsx
+++ b/frontend/src/components/ai-elements/chain-of-thought.tsx
@@ -15,7 +15,13 @@ import {
type LucideIcon,
} from "lucide-react";
import type { ComponentProps, ReactNode } from "react";
-import { createContext, memo, useContext, useMemo } from "react";
+import {
+ createContext,
+ isValidElement,
+ memo,
+ useContext,
+ useMemo,
+} from "react";
type ChainOfThoughtContextValue = {
isOpen: boolean;
@@ -108,7 +114,7 @@ export const ChainOfThoughtHeader = memo(
);
export type ChainOfThoughtStepProps = ComponentProps<"div"> & {
- icon?: LucideIcon;
+ icon?: LucideIcon | React.ReactElement;
label: ReactNode;
description?: ReactNode;
status?: "complete" | "active" | "pending";
@@ -141,7 +147,7 @@ export const ChainOfThoughtStep = memo(
{...props}
>
-
+ {isValidElement(Icon) ? Icon :
}
diff --git a/frontend/src/components/ai-elements/code-block.tsx b/frontend/src/components/ai-elements/code-block.tsx
index a3d0d09..68d562a 100644
--- a/frontend/src/components/ai-elements/code-block.tsx
+++ b/frontend/src/components/ai-elements/code-block.tsx
@@ -114,7 +114,7 @@ export const CodeBlock = ({
dangerouslySetInnerHTML={{ __html: html }}
/>
diff --git a/frontend/src/components/workspace/flip-display.tsx b/frontend/src/components/workspace/flip-display.tsx
index 6c3a2ca..19a4229 100644
--- a/frontend/src/components/workspace/flip-display.tsx
+++ b/frontend/src/components/workspace/flip-display.tsx
@@ -12,7 +12,7 @@ export function FlipDisplay({
className?: string;
}) {
return (
-
+
convertToSteps(messages), [messages]);
- const stepCount = useMemo(
- () => steps.filter((step) => step.type !== "reasoning").length,
- [steps],
- );
+ const lastToolCallStep = useMemo(() => {
+ const filteredSteps = steps.filter((step) => step.type === "toolCall");
+ return filteredSteps[filteredSteps.length - 1];
+ }, [steps]);
+ const aboveLastToolCallSteps = useMemo(() => {
+ if (lastToolCallStep) {
+ const index = steps.indexOf(lastToolCallStep);
+ return steps.slice(0, index);
+ }
+ return [];
+ }, [lastToolCallStep, steps]);
+ const lastReasoningStep = useMemo(() => {
+ if (lastToolCallStep) {
+ const index = steps.indexOf(lastToolCallStep);
+ return steps.slice(index + 1).find((step) => step.type === "reasoning");
+ } else {
+ const filteredSteps = steps.filter((step) => step.type === "reasoning");
+ return filteredSteps[filteredSteps.length - 1];
+ }
+ }, [lastToolCallStep, steps]);
const rehypePlugins = useRehypeSplitWordsIntoSpans(isLoading);
- const [open, setOpen] = useState(false);
- const lastStep = steps[steps.length - 1];
- const { label, icon } = describeStep(lastStep);
return (
- 1 ? : icon
- }
- >
-
-
-
- {open && stepCount > 1 ? (
-
{stepCount} steps
+ {aboveLastToolCallSteps.length > 0 && (
+
+ )}
+ {aboveLastToolCallSteps.length > 0 && (
+
+ {showAbove &&
+ aboveLastToolCallSteps.map((step) =>
+ step.type === "reasoning" ? (
+
+ {step.reasoning ?? ""}
+
+ }
+ >
) : (
-
-
- {label}
-
-
- )}
-
-
-
- {!open && stepCount > 1 && (
-
- {stepCount > 1 ? `${stepCount} steps` : `${stepCount} step`}
-
+
+ ),
)}
-
-
-
-
- {steps.map((step) =>
- step.type === "reasoning" ? (
-
- {step.reasoning ?? ""}
-
- }
- />
- ) : (
-
- ),
- )}
-
+ {lastToolCallStep && (
+
+
+
+ )}
+
+ )}
+ {lastReasoningStep && (
+ <>
+
+ {showLastThinking && (
+
+
+ {lastReasoningStep.reasoning ?? ""}
+
+ }
+ >
+
+ )}
+ >
+ )}
);
}
@@ -156,7 +212,15 @@ function ToolCall({
}
}
return (
-
+ {
+ window.open(url, "_blank");
+ }}
+ >
{url && (
@@ -331,47 +395,3 @@ function convertToSteps(messages: Message[]): CoTStep[] {
}
return steps;
}
-
-function describeStep(step: CoTStep | undefined): {
- label: string;
- icon: React.ReactElement;
-} {
- if (!step) {
- return { label: "Thinking", icon: };
- }
- if (step.type === "reasoning") {
- return { label: "Thinking", icon: };
- } else {
- let label: string;
- let icon: React.ReactElement = ;
- if (step.name === "web_search") {
- label = `Search "${(step.args as { query: string }).query}" on web`;
- icon = ;
- } else if (step.name === "web_fetch") {
- label = "View web page";
- icon = ;
- } else if (step.name === "ls") {
- label = "List folder";
- icon = ;
- } else if (step.name === "read_file") {
- label = "Read file";
- icon = ;
- } else if (step.name === "write_file" || step.name === "str_replace") {
- label = "Write file";
- icon = ;
- } else if (step.name === "bash") {
- label = "Execute command";
- icon = ;
- } else if (step.name === "present_files") {
- label = "Present files";
- icon = ;
- } else {
- label = `Call tool "${step.name}"`;
- icon = ;
- }
- if (typeof step.args.description === "string") {
- label = step.args.description;
- }
- return { label, icon };
- }
-}