sparkecoder 0.1.35 → 0.1.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.js +4 -4
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +92 -4
- package/dist/cli.js.map +1 -1
- package/dist/{index-BVgU6Fmb.d.ts → index-D2eLXP9V.d.ts} +5 -5
- package/dist/index.d.ts +3 -3
- package/dist/index.js +92 -4
- package/dist/index.js.map +1 -1
- package/dist/{search-bazwc4Vi.d.ts → search-D3Wtf7TJ.d.ts} +2 -2
- package/dist/server/index.js +92 -4
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js +4 -4
- package/dist/tools/index.js.map +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_41a27541._.js → 2374f_147b1db0._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_fb82ac0d._.js → 2374f_36dd81aa._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_4bf2df9d._.js → 2374f_3eca1553._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_c33b095a._.js → 2374f_4d1accac._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_663d1038._.js → 2374f_4e2c9d06._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_244589df._.js → 2374f_b30bed04._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_fa61fbb2._.js → 2374f_c99180da._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_47c9e2d5._.js → 2374f_f5998fca._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__2dbf511a._.js → [root-of-the-server]__a7aab2da._.js} +5 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_76ccf09f._.js +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_76f53fe7._.js +7 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_layout_tsx_453f6492._.js +1 -1
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/standalone/web/.next/static/{static/chunks/6407c045dfc908fe.js → chunks/1e9fdac31c7fd474.js} +3 -3
- package/web/.next/standalone/web/.next/static/chunks/{74595088419436d9.js → 6b8642759b62770c.js} +6 -4
- package/web/.next/standalone/web/.next/static/chunks/9c5414ca72e69e79.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/9e4b05251574c9ab.js +5 -0
- package/web/.next/standalone/web/.next/static/chunks/f81a19eca278424b.css +1 -0
- package/web/.next/standalone/web/.next/static/{chunks/6407c045dfc908fe.js → static/chunks/1e9fdac31c7fd474.js} +3 -3
- package/web/.next/standalone/web/.next/static/static/chunks/{74595088419436d9.js → 6b8642759b62770c.js} +6 -4
- package/web/.next/standalone/web/.next/static/static/chunks/9c5414ca72e69e79.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/9e4b05251574c9ab.js +5 -0
- package/web/.next/standalone/web/.next/static/static/chunks/f81a19eca278424b.css +1 -0
- package/web/.next/standalone/web/src/components/ai-elements/search-tool.tsx +222 -240
- package/web/.next/standalone/web/src/components/ai-elements/subagent-modal.tsx +116 -172
- package/web/.next/standalone/web/src/components/chat-interface.tsx +68 -3
- package/web/.next/standalone/web/src/lib/api.ts +33 -0
- package/web/.next/static/chunks/{6407c045dfc908fe.js → 1e9fdac31c7fd474.js} +3 -3
- package/web/.next/static/chunks/{74595088419436d9.js → 6b8642759b62770c.js} +6 -4
- package/web/.next/static/chunks/9c5414ca72e69e79.js +1 -0
- package/web/.next/static/chunks/9e4b05251574c9ab.js +5 -0
- package/web/.next/static/chunks/f81a19eca278424b.css +1 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_656c1e45._.js +0 -7
- package/web/.next/standalone/web/.next/static/chunks/89bc21c0443670f4.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/af22745850132107.css +0 -1
- package/web/.next/standalone/web/.next/static/chunks/db9b22c844a35e20.js +0 -5
- package/web/.next/standalone/web/.next/static/static/chunks/89bc21c0443670f4.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/af22745850132107.css +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/db9b22c844a35e20.js +0 -5
- package/web/.next/static/chunks/89bc21c0443670f4.js +0 -1
- package/web/.next/static/chunks/af22745850132107.css +0 -1
- package/web/.next/static/chunks/db9b22c844a35e20.js +0 -5
- /package/web/.next/standalone/web/.next/static/{rUoKuERGJU0zgIZXVv4LR → Px4gKJACBDiCG6uNwuICG}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{rUoKuERGJU0zgIZXVv4LR → Px4gKJACBDiCG6uNwuICG}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{rUoKuERGJU0zgIZXVv4LR → Px4gKJACBDiCG6uNwuICG}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{rUoKuERGJU0zgIZXVv4LR → Px4gKJACBDiCG6uNwuICG}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{rUoKuERGJU0zgIZXVv4LR → Px4gKJACBDiCG6uNwuICG}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{rUoKuERGJU0zgIZXVv4LR → Px4gKJACBDiCG6uNwuICG}/_ssgManifest.js +0 -0
- /package/web/.next/static/{rUoKuERGJU0zgIZXVv4LR → Px4gKJACBDiCG6uNwuICG}/_buildManifest.js +0 -0
- /package/web/.next/static/{rUoKuERGJU0zgIZXVv4LR → Px4gKJACBDiCG6uNwuICG}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{rUoKuERGJU0zgIZXVv4LR → Px4gKJACBDiCG6uNwuICG}/_ssgManifest.js +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { Badge } from "@/components/ui/badge";
|
|
4
3
|
import { Button } from "@/components/ui/button";
|
|
5
4
|
import { cn } from "@/lib/utils";
|
|
6
5
|
import {
|
|
@@ -9,17 +8,13 @@ import {
|
|
|
9
8
|
ChevronRightIcon,
|
|
10
9
|
SearchIcon,
|
|
11
10
|
XCircleIcon,
|
|
12
|
-
Loader2Icon,
|
|
13
|
-
FileIcon,
|
|
14
|
-
CodeIcon,
|
|
15
11
|
FolderIcon,
|
|
16
12
|
TerminalIcon,
|
|
17
13
|
FileTextIcon,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
MaximizeIcon,
|
|
14
|
+
SparklesIcon,
|
|
15
|
+
EyeIcon,
|
|
21
16
|
} from "lucide-react";
|
|
22
|
-
import { useState, useEffect, useRef } from "react";
|
|
17
|
+
import { useState, useEffect, useRef, useMemo } from "react";
|
|
23
18
|
import { SubagentModal } from "./subagent-modal";
|
|
24
19
|
|
|
25
20
|
export interface SearchInput {
|
|
@@ -47,6 +42,8 @@ export interface SearchOutput {
|
|
|
47
42
|
formattedResult?: string;
|
|
48
43
|
executionId?: string;
|
|
49
44
|
stepsCount?: number;
|
|
45
|
+
// Raw result text from agent
|
|
46
|
+
result?: string;
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
export interface SubagentStep {
|
|
@@ -83,6 +80,53 @@ const getToolIcon = (toolName?: string) => {
|
|
|
83
80
|
}
|
|
84
81
|
};
|
|
85
82
|
|
|
83
|
+
/** Truncate text with ellipsis */
|
|
84
|
+
const truncate = (text: string, maxLen: number) => {
|
|
85
|
+
if (!text) return "";
|
|
86
|
+
return text.length > maxLen ? text.slice(0, maxLen) + "..." : text;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** Get the latest meaningful step for display */
|
|
90
|
+
const getLatestStep = (steps: SubagentStep[]) => {
|
|
91
|
+
if (steps.length === 0) return null;
|
|
92
|
+
const latest = steps[steps.length - 1];
|
|
93
|
+
if (latest.type === "tool_call" && latest.toolName) {
|
|
94
|
+
return { type: "tool", name: latest.toolName };
|
|
95
|
+
}
|
|
96
|
+
if (latest.type === "thought" || latest.type === "text") {
|
|
97
|
+
return { type: "text", content: truncate(latest.content, 40) };
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/** Extract stats from steps */
|
|
103
|
+
const getStatsFromSteps = (steps: SubagentStep[]) => {
|
|
104
|
+
const toolCalls = steps.filter(s => s.type === "tool_call");
|
|
105
|
+
const filesRead = new Set<string>();
|
|
106
|
+
|
|
107
|
+
for (const step of toolCalls) {
|
|
108
|
+
if (step.toolName === "read_file" && step.toolInput) {
|
|
109
|
+
const input = step.toolInput as { path?: string };
|
|
110
|
+
if (input.path) filesRead.add(input.path);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
toolCallCount: toolCalls.length,
|
|
116
|
+
filesExplored: filesRead.size,
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/** Get first line or sentence as summary */
|
|
121
|
+
const getFirstSentence = (text: string): string => {
|
|
122
|
+
if (!text) return "";
|
|
123
|
+
// Get first line
|
|
124
|
+
const firstLine = text.split('\n')[0].trim();
|
|
125
|
+
// Or first sentence
|
|
126
|
+
const match = firstLine.match(/^[^.!?]+[.!?]/);
|
|
127
|
+
return match ? match[0] : truncate(firstLine, 100);
|
|
128
|
+
};
|
|
129
|
+
|
|
86
130
|
export function SearchTool({
|
|
87
131
|
input,
|
|
88
132
|
output,
|
|
@@ -90,8 +134,8 @@ export function SearchTool({
|
|
|
90
134
|
steps = [],
|
|
91
135
|
className,
|
|
92
136
|
}: SearchToolProps) {
|
|
93
|
-
const [isExpanded, setIsExpanded] = useState(
|
|
94
|
-
const [
|
|
137
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
138
|
+
const [isResultExpanded, setIsResultExpanded] = useState(false);
|
|
95
139
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
96
140
|
const stepsContainerRef = useRef<HTMLDivElement>(null);
|
|
97
141
|
|
|
@@ -99,100 +143,113 @@ export function SearchTool({
|
|
|
99
143
|
const isCompleted = status === "completed";
|
|
100
144
|
const isError = status === "error" || output?.success === false;
|
|
101
145
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
if (isLoading && steps.length === 1) {
|
|
105
|
-
setIsModalOpen(true);
|
|
106
|
-
}
|
|
107
|
-
}, [isLoading, steps.length]);
|
|
146
|
+
// Calculate stats from steps
|
|
147
|
+
const stats = useMemo(() => getStatsFromSteps(steps), [steps]);
|
|
108
148
|
|
|
109
|
-
// Auto-scroll steps
|
|
149
|
+
// Auto-scroll steps when expanded
|
|
110
150
|
useEffect(() => {
|
|
111
|
-
if (stepsContainerRef.current && isLoading) {
|
|
151
|
+
if (stepsContainerRef.current && isLoading && isExpanded) {
|
|
112
152
|
stepsContainerRef.current.scrollTop = stepsContainerRef.current.scrollHeight;
|
|
113
153
|
}
|
|
114
|
-
}, [steps, isLoading]);
|
|
154
|
+
}, [steps, isLoading, isExpanded]);
|
|
115
155
|
|
|
116
156
|
const query = input?.query || "";
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
label: `Found ${output.matchCount || 0} matches`,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
if (isError) {
|
|
137
|
-
return {
|
|
138
|
-
Icon: XCircleIcon,
|
|
139
|
-
color: "text-red-600 dark:text-red-500",
|
|
140
|
-
iconClass: "",
|
|
141
|
-
label: "Explore failed",
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
Icon: SearchIcon,
|
|
146
|
-
color: "text-muted-foreground",
|
|
147
|
-
iconClass: "",
|
|
148
|
-
label: "",
|
|
149
|
-
};
|
|
150
|
-
};
|
|
157
|
+
const latestStep = getLatestStep(steps);
|
|
158
|
+
|
|
159
|
+
// Get the result text - could be in different fields
|
|
160
|
+
const resultText = useMemo(() => {
|
|
161
|
+
if (!output) return "";
|
|
162
|
+
// Try different possible fields
|
|
163
|
+
if (typeof output === "string") return output;
|
|
164
|
+
if (output.result) return output.result;
|
|
165
|
+
if (output.formattedResult) return output.formattedResult;
|
|
166
|
+
if (output.summary) return output.summary;
|
|
167
|
+
// Check if output has a nested structure
|
|
168
|
+
const anyOutput = output as Record<string, unknown>;
|
|
169
|
+
if (anyOutput.text) return String(anyOutput.text);
|
|
170
|
+
if (anyOutput.content) return String(anyOutput.content);
|
|
171
|
+
return "";
|
|
172
|
+
}, [output]);
|
|
151
173
|
|
|
152
|
-
const
|
|
174
|
+
const summaryText = getFirstSentence(resultText);
|
|
175
|
+
const hasLongResult = resultText.length > 200;
|
|
153
176
|
|
|
154
177
|
return (
|
|
155
178
|
<div className={cn("rounded-lg border overflow-hidden bg-background", className)}>
|
|
156
|
-
{/* Header */}
|
|
157
|
-
<div className="flex items-center
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
"
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
179
|
+
{/* Compact Header */}
|
|
180
|
+
<div className="flex items-center gap-3 px-3 py-2.5">
|
|
181
|
+
{/* Status Icon */}
|
|
182
|
+
<div className="shrink-0">
|
|
183
|
+
{isLoading ? (
|
|
184
|
+
<div className="relative">
|
|
185
|
+
<SparklesIcon className="size-4 text-blue-500" />
|
|
186
|
+
<span className="absolute -top-0.5 -right-0.5 size-2 bg-blue-500 rounded-full animate-pulse" />
|
|
187
|
+
</div>
|
|
188
|
+
) : isCompleted && !isError ? (
|
|
189
|
+
<CheckCircleIcon className="size-4 text-green-500" />
|
|
190
|
+
) : isError ? (
|
|
191
|
+
<XCircleIcon className="size-4 text-red-500" />
|
|
192
|
+
) : (
|
|
193
|
+
<SearchIcon className="size-4 text-muted-foreground" />
|
|
194
|
+
)}
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
{/* Main Text */}
|
|
198
|
+
<div className="flex-1 min-w-0">
|
|
199
|
+
<div className="text-sm">
|
|
200
|
+
{isLoading ? (
|
|
201
|
+
<span className="text-foreground">
|
|
202
|
+
SparkeCoder is exploring the codebase
|
|
203
|
+
</span>
|
|
204
|
+
) : isCompleted && !isError ? (
|
|
205
|
+
<span className="text-foreground">
|
|
206
|
+
Exploration complete
|
|
207
|
+
</span>
|
|
208
|
+
) : isError ? (
|
|
209
|
+
<span className="text-red-500">
|
|
210
|
+
Exploration failed
|
|
211
|
+
</span>
|
|
212
|
+
) : (
|
|
213
|
+
<span className="text-muted-foreground">
|
|
214
|
+
Exploring...
|
|
215
|
+
</span>
|
|
216
|
+
)}
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
{/* Tiny log line while loading */}
|
|
220
|
+
{isLoading && latestStep && (
|
|
221
|
+
<div className="text-xs text-muted-foreground truncate mt-0.5">
|
|
222
|
+
{latestStep.type === "tool" ? (
|
|
223
|
+
<span className="font-mono">{latestStep.name}</span>
|
|
224
|
+
) : (
|
|
225
|
+
<span className="italic">{latestStep.content}</span>
|
|
178
226
|
)}
|
|
179
|
-
>
|
|
180
|
-
|
|
181
|
-
|
|
227
|
+
</div>
|
|
228
|
+
)}
|
|
229
|
+
|
|
230
|
+
{/* Completed stats - derived from actual steps */}
|
|
231
|
+
{isCompleted && !isError && (
|
|
232
|
+
<div className="text-xs text-muted-foreground mt-0.5">
|
|
233
|
+
{stats.toolCallCount} actions · {stats.filesExplored} files explored
|
|
234
|
+
</div>
|
|
182
235
|
)}
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
{/* Actions */}
|
|
239
|
+
<div className="flex items-center gap-1.5 shrink-0">
|
|
183
240
|
<Button
|
|
184
241
|
variant="ghost"
|
|
185
|
-
size="
|
|
186
|
-
className="
|
|
187
|
-
onClick={(
|
|
188
|
-
e.stopPropagation();
|
|
189
|
-
setIsModalOpen(true);
|
|
190
|
-
}}
|
|
191
|
-
title="View in fullscreen"
|
|
242
|
+
size="sm"
|
|
243
|
+
className="h-7 px-2.5 text-xs gap-1.5"
|
|
244
|
+
onClick={() => setIsModalOpen(true)}
|
|
192
245
|
>
|
|
193
|
-
<
|
|
246
|
+
<EyeIcon className="size-3" />
|
|
247
|
+
See How
|
|
194
248
|
</Button>
|
|
195
|
-
<button
|
|
249
|
+
<button
|
|
250
|
+
onClick={() => setIsExpanded(!isExpanded)}
|
|
251
|
+
className="p-1 hover:bg-muted rounded transition-colors"
|
|
252
|
+
>
|
|
196
253
|
{isExpanded ? (
|
|
197
254
|
<ChevronDownIcon className="size-4 text-muted-foreground" />
|
|
198
255
|
) : (
|
|
@@ -202,108 +259,75 @@ export function SearchTool({
|
|
|
202
259
|
</div>
|
|
203
260
|
</div>
|
|
204
261
|
|
|
205
|
-
{/* Content */}
|
|
262
|
+
{/* Expanded Content */}
|
|
206
263
|
{isExpanded && (
|
|
207
264
|
<div className="border-t">
|
|
208
265
|
{/* Query */}
|
|
209
266
|
<div className="px-3 py-2 bg-muted/30 border-b">
|
|
210
267
|
<div className="flex items-start gap-2">
|
|
211
|
-
<SearchIcon className="size-3
|
|
212
|
-
<p className="text-xs text-muted-foreground"
|
|
268
|
+
<SearchIcon className="size-3 text-muted-foreground mt-0.5 shrink-0" />
|
|
269
|
+
<p className="text-xs text-muted-foreground truncate" title={query}>
|
|
270
|
+
{truncate(query, 100)}
|
|
271
|
+
</p>
|
|
213
272
|
</div>
|
|
214
|
-
{input.context && (
|
|
215
|
-
<div className="mt-1.5 pl-5 text-xs text-muted-foreground/70 italic">
|
|
216
|
-
Context: {input.context}
|
|
217
|
-
</div>
|
|
218
|
-
)}
|
|
219
273
|
</div>
|
|
220
274
|
|
|
221
|
-
{/*
|
|
222
|
-
{
|
|
275
|
+
{/* Result - clean text with expand */}
|
|
276
|
+
{resultText && (
|
|
223
277
|
<div className="border-b">
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:bg-muted/50"
|
|
230
|
-
>
|
|
231
|
-
{isStepsExpanded ? (
|
|
232
|
-
<ChevronDownIcon className="size-3" />
|
|
278
|
+
<div className="px-3 py-2">
|
|
279
|
+
{isResultExpanded ? (
|
|
280
|
+
<div className="text-sm text-foreground whitespace-pre-wrap break-words max-h-[300px] overflow-auto">
|
|
281
|
+
{resultText}
|
|
282
|
+
</div>
|
|
233
283
|
) : (
|
|
234
|
-
<
|
|
284
|
+
<p className="text-sm text-muted-foreground">
|
|
285
|
+
{summaryText}
|
|
286
|
+
</p>
|
|
235
287
|
)}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
{isStepsExpanded && (
|
|
243
|
-
<div
|
|
244
|
-
ref={stepsContainerRef}
|
|
245
|
-
className="max-h-[200px] overflow-auto bg-zinc-950"
|
|
288
|
+
</div>
|
|
289
|
+
{hasLongResult && (
|
|
290
|
+
<button
|
|
291
|
+
onClick={() => setIsResultExpanded(!isResultExpanded)}
|
|
292
|
+
className="w-full px-3 py-1.5 text-xs text-muted-foreground hover:bg-muted/50 flex items-center justify-center gap-1 border-t"
|
|
246
293
|
>
|
|
247
|
-
{
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
294
|
+
{isResultExpanded ? (
|
|
295
|
+
<>
|
|
296
|
+
<ChevronDownIcon className="size-3" />
|
|
297
|
+
Show less
|
|
298
|
+
</>
|
|
299
|
+
) : (
|
|
300
|
+
<>
|
|
301
|
+
<ChevronRightIcon className="size-3" />
|
|
302
|
+
Show full result
|
|
303
|
+
</>
|
|
304
|
+
)}
|
|
305
|
+
</button>
|
|
251
306
|
)}
|
|
252
307
|
</div>
|
|
253
308
|
)}
|
|
254
309
|
|
|
255
|
-
{/*
|
|
256
|
-
{output && (
|
|
257
|
-
<div className="
|
|
258
|
-
{output.
|
|
259
|
-
<div className="space-y-3">
|
|
260
|
-
{/* Summary */}
|
|
261
|
-
{output.summary && (
|
|
262
|
-
<div className="text-sm text-foreground">
|
|
263
|
-
{output.summary}
|
|
264
|
-
</div>
|
|
265
|
-
)}
|
|
266
|
-
|
|
267
|
-
{/* Findings */}
|
|
268
|
-
{output.findings && output.findings.length > 0 && (
|
|
269
|
-
<div className="space-y-1">
|
|
270
|
-
<div className="text-xs font-medium text-muted-foreground mb-2">
|
|
271
|
-
Key Findings ({output.findings.length})
|
|
272
|
-
</div>
|
|
273
|
-
<div className="space-y-1 max-h-[200px] overflow-auto">
|
|
274
|
-
{output.findings.slice(0, 10).map((finding, index) => (
|
|
275
|
-
<FindingItem key={index} finding={finding} />
|
|
276
|
-
))}
|
|
277
|
-
{output.findings.length > 10 && (
|
|
278
|
-
<div className="text-xs text-muted-foreground pl-5">
|
|
279
|
-
... and {output.findings.length - 10} more
|
|
280
|
-
</div>
|
|
281
|
-
)}
|
|
282
|
-
</div>
|
|
283
|
-
</div>
|
|
284
|
-
)}
|
|
285
|
-
|
|
286
|
-
{/* Stats */}
|
|
287
|
-
<div className="flex items-center gap-4 text-xs text-muted-foreground pt-2 border-t">
|
|
288
|
-
<span>{output.matchCount || 0} matches</span>
|
|
289
|
-
<span>{output.filesSearched || 0} files explored</span>
|
|
290
|
-
<span>{output.stepsCount || 0} steps</span>
|
|
291
|
-
</div>
|
|
292
|
-
</div>
|
|
293
|
-
) : (
|
|
294
|
-
<div className="flex items-start gap-2 text-red-600 dark:text-red-400 text-sm">
|
|
295
|
-
<XCircleIcon className="size-4 mt-0.5 shrink-0" />
|
|
296
|
-
<span>{output.error || "Search failed"}</span>
|
|
297
|
-
</div>
|
|
298
|
-
)}
|
|
310
|
+
{/* Error */}
|
|
311
|
+
{isError && output && (
|
|
312
|
+
<div className="px-3 py-2 text-xs text-red-400 border-b">
|
|
313
|
+
{(output as SearchOutput).error || "Exploration failed"}
|
|
299
314
|
</div>
|
|
300
315
|
)}
|
|
301
316
|
|
|
302
|
-
{/*
|
|
303
|
-
{
|
|
304
|
-
<div
|
|
305
|
-
|
|
306
|
-
|
|
317
|
+
{/* Steps - compact log view */}
|
|
318
|
+
{steps.length > 0 && (
|
|
319
|
+
<div
|
|
320
|
+
ref={stepsContainerRef}
|
|
321
|
+
className="max-h-[100px] overflow-auto bg-zinc-950/50"
|
|
322
|
+
>
|
|
323
|
+
{steps.slice(-6).map((step, index) => (
|
|
324
|
+
<MiniStepItem key={step.id || index} step={step} />
|
|
325
|
+
))}
|
|
326
|
+
{steps.length > 6 && (
|
|
327
|
+
<div className="px-3 py-1 text-xs text-zinc-500">
|
|
328
|
+
+{steps.length - 6} more steps
|
|
329
|
+
</div>
|
|
330
|
+
)}
|
|
307
331
|
</div>
|
|
308
332
|
)}
|
|
309
333
|
</div>
|
|
@@ -323,78 +347,36 @@ export function SearchTool({
|
|
|
323
347
|
);
|
|
324
348
|
}
|
|
325
349
|
|
|
326
|
-
|
|
350
|
+
/** Minimal step item for compact log view */
|
|
351
|
+
function MiniStepItem({ step }: { step: SubagentStep }) {
|
|
327
352
|
const ToolIcon = getToolIcon(step.toolName);
|
|
328
353
|
|
|
329
354
|
return (
|
|
330
|
-
<div className="px-3 py-1
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
</>
|
|
356
|
-
)}
|
|
357
|
-
{step.type === "text" && (
|
|
358
|
-
<>
|
|
359
|
-
<CodeIcon className="size-3 text-purple-400 mt-0.5 shrink-0" />
|
|
360
|
-
<span className="text-zinc-300 truncate">{step.content}</span>
|
|
361
|
-
</>
|
|
362
|
-
)}
|
|
363
|
-
{step.type === "thought" && (
|
|
364
|
-
<>
|
|
365
|
-
<BotIcon className="size-3 text-amber-400 mt-0.5 shrink-0" />
|
|
366
|
-
<span className="text-zinc-400 italic truncate">{step.content}</span>
|
|
367
|
-
</>
|
|
368
|
-
)}
|
|
369
|
-
</div>
|
|
370
|
-
</div>
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
function FindingItem({ finding }: { finding: SearchFinding }) {
|
|
375
|
-
const Icon = finding.type === "match" ? CodeIcon : FileIcon;
|
|
376
|
-
const relevanceColor = {
|
|
377
|
-
high: "text-green-500",
|
|
378
|
-
medium: "text-amber-500",
|
|
379
|
-
low: "text-muted-foreground",
|
|
380
|
-
}[finding.relevance];
|
|
381
|
-
|
|
382
|
-
return (
|
|
383
|
-
<div className="flex items-start gap-2 text-xs py-1 px-2 rounded hover:bg-muted/50">
|
|
384
|
-
<Icon className={cn("size-3 mt-0.5 shrink-0", relevanceColor)} />
|
|
385
|
-
<div className="min-w-0 flex-1">
|
|
386
|
-
<div className="flex items-center gap-2">
|
|
387
|
-
<span className="font-mono text-foreground truncate">{finding.path}</span>
|
|
388
|
-
{finding.lineNumber && (
|
|
389
|
-
<span className="text-muted-foreground">:{finding.lineNumber}</span>
|
|
390
|
-
)}
|
|
391
|
-
</div>
|
|
392
|
-
{finding.content && (
|
|
393
|
-
<code className="text-muted-foreground block truncate mt-0.5">
|
|
394
|
-
{finding.content}
|
|
395
|
-
</code>
|
|
396
|
-
)}
|
|
397
|
-
</div>
|
|
355
|
+
<div className="px-3 py-1 text-xs font-mono border-b border-zinc-800/50 last:border-0 flex items-center gap-2">
|
|
356
|
+
{step.type === "tool_call" && (
|
|
357
|
+
<>
|
|
358
|
+
<ToolIcon className="size-2.5 text-blue-400 shrink-0" />
|
|
359
|
+
<span className="text-blue-400 truncate">{step.toolName}</span>
|
|
360
|
+
</>
|
|
361
|
+
)}
|
|
362
|
+
{step.type === "tool_result" && (
|
|
363
|
+
<>
|
|
364
|
+
<CheckCircleIcon className="size-2.5 text-green-400 shrink-0" />
|
|
365
|
+
<span className="text-zinc-500 truncate">{truncate(step.content, 50)}</span>
|
|
366
|
+
</>
|
|
367
|
+
)}
|
|
368
|
+
{step.type === "text" && (
|
|
369
|
+
<>
|
|
370
|
+
<SparklesIcon className="size-2.5 text-purple-400 shrink-0" />
|
|
371
|
+
<span className="text-zinc-400 truncate">{truncate(step.content, 50)}</span>
|
|
372
|
+
</>
|
|
373
|
+
)}
|
|
374
|
+
{step.type === "thought" && (
|
|
375
|
+
<>
|
|
376
|
+
<SparklesIcon className="size-2.5 text-amber-400 shrink-0" />
|
|
377
|
+
<span className="text-zinc-500 italic truncate">{truncate(step.content, 50)}</span>
|
|
378
|
+
</>
|
|
379
|
+
)}
|
|
398
380
|
</div>
|
|
399
381
|
);
|
|
400
382
|
}
|