Skip to content

Commit f49d394

Browse files
committed
Add bento grid dashboard component and fix ESLint errors
1 parent d2a1fbd commit f49d394

File tree

4 files changed

+419
-114
lines changed

4 files changed

+419
-114
lines changed
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
2+
import { useState, useEffect } from "react"
3+
import { Trans } from "react-i18next"
4+
5+
import { useAppTranslation } from "@src/i18n/TranslationContext"
6+
import { useCopyToClipboard } from "@src/utils/clipboard"
7+
import RooHero from "@src/components/welcome/RooHero"
8+
// Unused import but needed for UI rendering
9+
import TelemetryBanner from "../common/TelemetryBanner"
10+
11+
interface TaskItem {
12+
id: string
13+
title: string
14+
date: string
15+
tokensIn: string
16+
tokensOut: string
17+
cost: string
18+
}
19+
20+
interface BentoGridProps {
21+
tasks: any[]
22+
isExpanded: boolean
23+
toggleExpanded: () => void
24+
telemetrySetting: string
25+
}
26+
27+
const BentoGrid = ({ telemetrySetting }: BentoGridProps) => {
28+
const _t = useAppTranslation()
29+
30+
// Dummy tasks for demonstration
31+
const dummyTasks: TaskItem[] = [
32+
{
33+
title: "Please create a red fish game.",
34+
date: "Apr 26, 6:09 PM",
35+
tokensIn: "3.4k",
36+
tokensOut: "33.7k",
37+
cost: "$0.54",
38+
id: "dummy1",
39+
},
40+
{
41+
title: "Refactor the authentication module.",
42+
date: "Apr 26, 5:30 PM",
43+
tokensIn: "10.2k",
44+
tokensOut: "55.1k",
45+
cost: "$1.15",
46+
id: "dummy2",
47+
},
48+
{
49+
title: "Write unit tests for the API client.",
50+
date: "Apr 25, 11:15 AM",
51+
tokensIn: "5.8k",
52+
tokensOut: "21.9k",
53+
cost: "$0.38",
54+
id: "dummy3",
55+
},
56+
]
57+
58+
// Feature cards with title and subtitle
59+
const featureCards = [
60+
{
61+
title: "Customizable Modes",
62+
subtitle: "Specialized personas with their own behaviors and assigned models",
63+
id: "feature1",
64+
},
65+
{
66+
title: "Smart Context",
67+
subtitle: "Automatically includes relevant files and code for better assistance",
68+
id: "feature2",
69+
},
70+
{
71+
title: "Integrated Tools",
72+
subtitle: "Access to file operations, terminal commands, and browser interactions",
73+
id: "feature3",
74+
},
75+
]
76+
77+
// Agent quick start options
78+
const agents = [
79+
{
80+
name: "Code",
81+
emoji: "💻",
82+
description: "Write, edit, and improve your code",
83+
id: "agent1",
84+
},
85+
{
86+
name: "Debug",
87+
emoji: "🪲",
88+
description: "Find and fix issues in your code",
89+
id: "agent2",
90+
},
91+
{
92+
name: "Architect",
93+
emoji: "🏗️",
94+
description: "Design systems and plan implementations",
95+
id: "agent3",
96+
},
97+
{
98+
name: "Ask",
99+
emoji: "❓",
100+
description: "Get answers to your technical questions",
101+
id: "agent4",
102+
},
103+
{
104+
name: "Orchestrator",
105+
emoji: "🪃",
106+
description: "Coordinate complex tasks across modes",
107+
id: "agent5",
108+
},
109+
]
110+
111+
return (
112+
<div className="flex-1 min-h-0 overflow-y-auto p-5">
113+
{/* Modern Bento Grid Layout */}
114+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
115+
{/* Box 1: Logo Card */}
116+
<div className="col-span-full md:col-span-1 row-span-1 bg-[var(--vscode-editorWidget-background)] rounded-2xl overflow-hidden">
117+
<div className="p-5 flex flex-col items-center justify-center h-full">
118+
<h2 className="text-lg font-bold mb-3 text-vscode-editor-foreground">Roo</h2>
119+
<div className="flex items-center justify-center w-full">
120+
<RooHero />
121+
</div>
122+
</div>
123+
</div>
124+
125+
{/* Box 2: Intro Text Card */}
126+
<div className="col-span-full md:col-span-2 row-span-1 bg-[var(--vscode-editorWidget-background)] rounded-2xl overflow-hidden">
127+
<div className="p-5 flex flex-col items-start text-left h-full">
128+
<h2 className="text-lg font-bold mb-3 text-vscode-editor-foreground">About</h2>
129+
<p className="text-vscode-editor-foreground leading-relaxed text-sm">
130+
<Trans
131+
i18nKey="chat:about"
132+
components={{
133+
DocsLink: (
134+
<a
135+
href="https://docs.roocode.com/"
136+
target="_blank"
137+
rel="noopener noreferrer"
138+
className="text-vscode-textLink-foreground hover:underline font-medium">
139+
the docs
140+
</a>
141+
),
142+
}}
143+
/>
144+
</p>
145+
</div>
146+
</div>
147+
148+
{/* Box 3: Agents Quick Start Card */}
149+
<div className="col-span-full md:col-span-2 row-span-2 bg-[var(--vscode-editorWidget-background)] rounded-2xl overflow-hidden">
150+
<div className="p-5 flex flex-col h-full">
151+
<h2 className="text-xs font-bold mb-4 text-vscode-descriptionForeground tracking-widest uppercase">
152+
Agents
153+
</h2>
154+
<p className="text-base mb-4 text-vscode-editor-foreground">Start a conversation with:</p>
155+
156+
<div className="flex-1 overflow-y-auto">
157+
<div className="grid grid-cols-2 gap-2">
158+
{agents.map((agent) => (
159+
<div
160+
key={agent.id}
161+
className="flex items-center p-2 hover:bg-[var(--vscode-list-hoverBackground)] rounded cursor-pointer transition-colors duration-200">
162+
<span className="text-lg mr-2">{agent.emoji}</span>
163+
<div className="overflow-hidden">
164+
<h3 className="text-sm font-bold text-vscode-editor-foreground truncate">
165+
{agent.name}
166+
</h3>
167+
<p className="text-xs text-vscode-descriptionForeground truncate">
168+
{agent.description}
169+
</p>
170+
</div>
171+
</div>
172+
))}
173+
</div>
174+
</div>
175+
</div>
176+
</div>
177+
178+
{/* Box 4: Feature Carousel Card */}
179+
<div className="col-span-full md:col-span-1 row-span-1 bg-[var(--vscode-editorWidget-background)] rounded-2xl overflow-hidden">
180+
<FeatureCarousel features={featureCards} />
181+
</div>
182+
183+
{/* Box 6: Telemetry Banner (Conditional) */}
184+
{telemetrySetting === "unset" && (
185+
<div className="col-span-full bg-[var(--vscode-editorWidget-background)] rounded-2xl overflow-hidden">
186+
<TelemetryBanner />
187+
</div>
188+
)}
189+
190+
{/* Task Cards */}
191+
{dummyTasks.map((task) => (
192+
<TaskCard key={task.id} task={task} />
193+
))}
194+
</div>
195+
</div>
196+
)
197+
}
198+
199+
// Helper component for task cards
200+
const TaskCard = ({ task }: { task: TaskItem }) => {
201+
const [showCopySuccess, setShowCopySuccess] = useState(false)
202+
const { copyWithFeedback } = useCopyToClipboard(1000)
203+
204+
const handleCopy = (e: React.MouseEvent) => {
205+
e.stopPropagation()
206+
copyWithFeedback(task.title).then((success: boolean) => {
207+
if (success) {
208+
setShowCopySuccess(true)
209+
setTimeout(() => setShowCopySuccess(false), 1000)
210+
}
211+
})
212+
}
213+
214+
return (
215+
<div className="col-span-full md:col-span-1 bg-[var(--vscode-editorWidget-background)] rounded-2xl overflow-hidden">
216+
<div className="p-5 relative flex flex-col justify-between h-full min-h-[140px]">
217+
{/* Copy Button */}
218+
<VSCodeButton
219+
appearance="icon"
220+
onClick={handleCopy}
221+
title="Copy Task"
222+
className="absolute top-3 right-3 text-vscode-descriptionForeground hover:text-vscode-foreground transition-colors duration-200">
223+
<span className={`codicon ${showCopySuccess ? "codicon-check" : "codicon-copy"}`}></span>
224+
</VSCodeButton>
225+
226+
{/* Content */}
227+
<div>
228+
<h3 className="text-xs font-bold mb-3 text-vscode-descriptionForeground tracking-widest uppercase">
229+
Recent Task
230+
</h3>
231+
<p className="text-base font-bold mb-2 text-vscode-editor-foreground leading-tight">{task.title}</p>
232+
</div>
233+
234+
{/* Footer */}
235+
<div className="flex justify-between items-center text-xs text-vscode-descriptionForeground mt-auto pt-3 border-t border-[var(--vscode-panel-border)]">
236+
<span>{task.date}</span>
237+
<div className="flex gap-3">
238+
<span className="flex items-center">
239+
<span className="codicon codicon-arrow-up text-xs mr-1"></span>
240+
{task.tokensIn}
241+
</span>
242+
<span className="flex items-center">
243+
<span className="codicon codicon-arrow-down text-xs mr-1"></span>
244+
{task.tokensOut}
245+
</span>
246+
<span className="font-medium">{task.cost}</span>
247+
</div>
248+
</div>
249+
</div>
250+
</div>
251+
)
252+
}
253+
254+
// Carousel component for features
255+
const FeatureCarousel = ({ features }: { features: { title: string; subtitle: string; id: string }[] }) => {
256+
const [currentIndex, setCurrentIndex] = useState(0)
257+
258+
// Auto-advance the carousel every 5 seconds
259+
useEffect(() => {
260+
const interval = setInterval(() => {
261+
setCurrentIndex((prevIndex) => (prevIndex + 1) % features.length)
262+
}, 5000)
263+
264+
return () => clearInterval(interval)
265+
}, [features.length])
266+
267+
const nextSlide = () => {
268+
setCurrentIndex((prevIndex) => (prevIndex + 1) % features.length)
269+
}
270+
271+
const prevSlide = () => {
272+
setCurrentIndex((prevIndex) => (prevIndex - 1 + features.length) % features.length)
273+
}
274+
275+
return (
276+
<div className="p-5 flex flex-col h-full relative">
277+
<h2 className="text-xs font-bold mb-4 text-vscode-descriptionForeground tracking-widest uppercase">
278+
Features
279+
</h2>
280+
281+
<div className="flex-1 flex flex-col justify-center">
282+
<div className="transition-opacity duration-300 min-h-[100px]">
283+
<h3 className="text-lg font-bold mb-2 text-vscode-editor-foreground">
284+
{features[currentIndex].title}
285+
</h3>
286+
<p className="text-sm text-vscode-descriptionForeground">{features[currentIndex].subtitle}</p>
287+
</div>
288+
</div>
289+
290+
<div className="flex justify-between mt-4">
291+
<button
292+
onClick={prevSlide}
293+
className="text-vscode-descriptionForeground hover:text-vscode-foreground transition-colors duration-200">
294+
<span className="codicon codicon-chevron-left"></span>
295+
</button>
296+
297+
<div className="flex gap-2">
298+
{features.map((_, index) => (
299+
<span
300+
key={index}
301+
className={`h-1.5 w-1.5 rounded-full ${index === currentIndex ? "bg-vscode-foreground" : "bg-vscode-descriptionForeground opacity-50"}`}></span>
302+
))}
303+
</div>
304+
305+
<button
306+
onClick={nextSlide}
307+
className="text-vscode-descriptionForeground hover:text-vscode-foreground transition-colors duration-200">
308+
<span className="codicon codicon-chevron-right"></span>
309+
</button>
310+
</div>
311+
</div>
312+
)
313+
}
314+
315+
export default BentoGrid

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,11 @@ import { vscode } from "@src/utils/vscode"
2626
import { useSelectedModel } from "@/components/ui/hooks/useSelectedModel"
2727
import { validateCommand } from "@src/utils/command-validation"
2828
import { useAppTranslation } from "@src/i18n/TranslationContext"
29+
import { useCopyToClipboard as _useCopyToClipboard } from "@src/utils/clipboard"
2930

30-
import TelemetryBanner from "../common/TelemetryBanner"
31-
import HistoryPreview from "../history/HistoryPreview"
32-
import RooHero from "@src/components/welcome/RooHero"
33-
import RooTips from "@src/components/welcome/RooTips"
31+
import _TelemetryBanner from "../common/TelemetryBanner"
3432
import Announcement from "./Announcement"
33+
import BentoGrid from "./BentoGrid"
3534
import BrowserSessionRow from "./BrowserSessionRow"
3635
import ChatRow from "./ChatRow"
3736
import ChatTextArea from "./ChatTextArea"
@@ -62,7 +61,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
6261
const modeShortcutText = `${isMac ? "⌘" : "Ctrl"} + . ${t("chat:forNextMode")}`
6362
const {
6463
clineMessages: messages,
65-
taskHistory,
64+
taskHistory: _taskHistory, // Prefix with underscore to indicate it's unused
6665
apiConfiguration,
6766
mcpServers,
6867
alwaysAllowBrowser,
@@ -1241,41 +1240,12 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
12411240
)}
12421241
</>
12431242
) : (
1244-
<div className="flex-1 min-h-0 overflow-y-auto flex flex-col gap-4">
1245-
{/* Moved Task Bar Header Here */}
1246-
{tasks.length !== 0 && (
1247-
<div className="flex text-vscode-descriptionForeground w-full mx-auto px-5 pt-3">
1248-
<div className="flex items-center gap-1 cursor-pointer" onClick={toggleExpanded}>
1249-
{tasks.length < 10 && (
1250-
<span className={`font-medium text-xs `}>{t("history:recentTasks")}</span>
1251-
)}
1252-
<span
1253-
className={`codicon ${isExpanded ? "codicon-eye" : "codicon-eye-closed"} scale-90`}
1254-
/>
1255-
</div>
1256-
</div>
1257-
)}
1258-
<div
1259-
className={` w-full flex flex-col gap-4 m-auto ${isExpanded && tasks.length > 0 ? "mt-0" : ""} p-10 pt-5`}>
1260-
<RooHero />
1261-
{telemetrySetting === "unset" && <TelemetryBanner />}
1262-
{/* Show the task history preview if expanded and tasks exist */}
1263-
{taskHistory.length > 0 && isExpanded && <HistoryPreview />}
1264-
<p className="ext-vscode-editor-foreground leading-tight font-vscode text-center">
1265-
<Trans
1266-
i18nKey="chat:about"
1267-
components={{
1268-
DocsLink: (
1269-
<a href="https://docs.roocode.com/" target="_blank" rel="noopener noreferrer">
1270-
the docs
1271-
</a>
1272-
),
1273-
}}
1274-
/>
1275-
</p>
1276-
<RooTips cycle={false} />
1277-
</div>
1278-
</div>
1243+
<BentoGrid
1244+
tasks={tasks}
1245+
isExpanded={isExpanded}
1246+
toggleExpanded={toggleExpanded}
1247+
telemetrySetting={telemetrySetting}
1248+
/>
12791249
)}
12801250

12811251
{/*

0 commit comments

Comments
 (0)