Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions webview-ui/src/components/chat/ChatTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
"flex-col-reverse",
"min-h-0",
"overflow-hidden",
"rounded",
"rounded-lg",
)}>
<div
ref={highlightLayerRef}
Expand All @@ -1005,7 +1005,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
isEditMode ? "pr-20" : "pr-9",
"z-10",
"forced-color-adjust-none",
"rounded",
"rounded-lg",
)}
style={{
color: "transparent",
Expand Down
82 changes: 24 additions & 58 deletions webview-ui/src/components/chat/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import { CloudUpsellDialog } from "@src/components/cloud/CloudUpsellDialog"

import TelemetryBanner from "../common/TelemetryBanner"
import VersionIndicator from "../common/VersionIndicator"
import { useTaskSearch } from "../history/useTaskSearch"
import HistoryPreview from "../history/HistoryPreview"
import Announcement from "./Announcement"
import BrowserSessionRow from "./BrowserSessionRow"
Expand Down Expand Up @@ -118,7 +117,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
customModes,
telemetrySetting,
hasSystemPromptOverride,
historyPreviewCollapsed, // Added historyPreviewCollapsed
soundEnabled,
soundVolume,
cloudIsAuthenticated,
Expand All @@ -131,20 +129,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
messagesRef.current = messages
}, [messages])

const { tasks } = useTaskSearch()

// Initialize expanded state based on the persisted setting (default to expanded if undefined)
const [isExpanded, setIsExpanded] = useState(
historyPreviewCollapsed === undefined ? true : !historyPreviewCollapsed,
)

const toggleExpanded = useCallback(() => {
const newState = !isExpanded
setIsExpanded(newState)
// Send message to extension to persist the new collapsed state
vscode.postMessage({ type: "setHistoryPreviewCollapsed", bool: !newState })
}, [isExpanded])

// Leaving this less safe version here since if the first message is not a
// task, then the extension is in a bad state and needs to be debugged (see
// Cline.abort).
Expand Down Expand Up @@ -1810,53 +1794,35 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
)}
</>
) : (
<div className="flex-1 min-h-0 overflow-y-auto flex flex-col gap-4 relative">
{/* Moved Task Bar Header Here */}
{tasks.length !== 0 && (
<div className="flex text-vscode-descriptionForeground w-full mx-auto px-5 pt-3">
<div className="flex items-center gap-1 cursor-pointer" onClick={toggleExpanded}>
{tasks.length < 10 && (
<span className={`font-medium text-xs `}>{t("history:recentTasks")}</span>
)}
<span
className={`codicon ${isExpanded ? "codicon-eye" : "codicon-eye-closed"} scale-90`}
/>
</div>
</div>
)}
<div
className={` w-full flex flex-col gap-4 m-auto ${isExpanded && tasks.length > 0 ? "mt-0" : ""} px-3.5 min-[370px]:px-10 pt-5 transition-all duration-300`}>
{/* Version indicator in top-right corner - only on welcome screen */}
<div className="flex flex-col h-full justify-center p-6 min-h-0 overflow-y-auto gap-4 relative">
<div className="flex flex-col items-start gap-2 justify-center h-full min-[400px]:px-6">
<VersionIndicator
onClick={() => setShowAnnouncementModal(true)}
className="absolute top-2 right-3 z-10"
/>

<RooHero />

<div className="mb-2.5">
{cloudIsAuthenticated || taskHistory.length < 4 ? (
<RooTips />
) : (
<>
<DismissibleUpsell
upsellId="taskList"
icon={<Cloud className="size-4 mt-0.5 shrink-0" />}
onClick={() => openUpsell()}
dismissOnClick={false}
className="bg-vscode-editor-background p-4 !text-base">
<Trans
i18nKey="cloud:upsell.taskList"
components={{
learnMoreLink: <VSCodeLink href="#" />,
}}
/>
</DismissibleUpsell>
</>
)}
<div className="flex flex-col gap-4 w-full">
<RooHero />
{/* Show RooTips when authenticated or when user is new */}
{taskHistory.length < 6 && <RooTips />}
{/* Everyone should see their task history if any */}
{taskHistory.length > 0 && <HistoryPreview />}
</div>
{/* Show the task history preview if expanded and tasks exist */}
{taskHistory.length > 0 && isExpanded && <HistoryPreview />}
{/* Logged out users should see a one-time upsell, but not for brand new users */}
{!cloudIsAuthenticated && taskHistory.length >= 6 && (
<DismissibleUpsell
upsellId="taskList2"
icon={<Cloud className="size-5 mt-0.5 shrink-0" />}
onClick={() => openUpsell()}
dismissOnClick={false}
className="!bg-vscode-editor-background mt-6 border-border rounded-xl pl-4 pr-3 py-3 !text-base">
<Trans
i18nKey="cloud:upsell.taskList"
components={{
learnMoreLink: <VSCodeLink href="#" />,
}}
/>
</DismissibleUpsell>
)}
</div>
</div>
)}
Expand Down
8 changes: 5 additions & 3 deletions webview-ui/src/components/chat/TaskHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ const TaskHeader = ({
)}
<div
className={cn(
"px-2.5 pt-2.5 pb-2 flex flex-col gap-1.5 relative z-1 cursor-pointer",
"px-3 pt-2.5 pb-2 flex flex-col gap-1.5 relative z-1 cursor-pointer",
"bg-vscode-input-background hover:bg-vscode-input-background/90",
"text-vscode-foreground/80 hover:text-vscode-foreground",
"shadow-sm shadow-black/30 rounded-md",
"shadow-sm shadow-black/30 rounded-xl",
hasTodos && "border-b-0",
)}
onClick={(e) => {
Expand Down Expand Up @@ -163,7 +163,9 @@ const TaskHeader = ({
</div>
</div>
{!isTaskExpanded && contextWindow > 0 && (
<div className="flex items-center gap-2 text-sm" onClick={(e) => e.stopPropagation()}>
<div
className="flex items-center gap-2 text-sm text-muted-foreground/70"
onClick={(e) => e.stopPropagation()}>
<StandardTooltip
content={
<div className="space-y-1">
Expand Down
37 changes: 9 additions & 28 deletions webview-ui/src/components/chat/__tests__/ChatView.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1319,39 +1319,20 @@ describe("ChatView - DismissibleUpsell Display Tests", () => {
expect(queryByTestId("dismissible-upsell")).not.toBeInTheDocument()
})

it("shows DismissibleUpsell when user is not authenticated and has run 4 or more tasks", async () => {
it("shows DismissibleUpsell when user is not authenticated and has run 6 or more tasks", async () => {
const { getByTestId } = renderChatView()

// Hydrate state with user not authenticated and 4 tasks
mockPostMessage({
cloudIsAuthenticated: false,
taskHistory: [
{ id: "1", ts: Date.now() - 3000 },
{ id: "2", ts: Date.now() - 2000 },
{ id: "3", ts: Date.now() - 1000 },
{ id: "4", ts: Date.now() },
],
clineMessages: [], // No active task
})

// Wait for component to render and show DismissibleUpsell
await waitFor(() => {
expect(getByTestId("dismissible-upsell")).toBeInTheDocument()
})
})

it("shows DismissibleUpsell when user is not authenticated and has run 5 tasks", async () => {
const { getByTestId } = renderChatView()

// Hydrate state with user not authenticated and 5 tasks
mockPostMessage({
cloudIsAuthenticated: false,
taskHistory: [
{ id: "1", ts: Date.now() - 4000 },
{ id: "2", ts: Date.now() - 3000 },
{ id: "3", ts: Date.now() - 2000 },
{ id: "4", ts: Date.now() - 1000 },
{ id: "5", ts: Date.now() },
{ id: "1", ts: Date.now() - 6000 },
{ id: "2", ts: Date.now() - 5000 },
{ id: "3", ts: Date.now() - 4000 },
{ id: "4", ts: Date.now() - 3000 },
{ id: "5", ts: Date.now() - 2000 },
{ id: "6", ts: Date.now() - 1000 },
{ id: "7", ts: Date.now() },
],
clineMessages: [], // No active task
})
Expand Down Expand Up @@ -1415,7 +1396,7 @@ describe("ChatView - DismissibleUpsell Display Tests", () => {
expect(getByTestId("roo-tips")).toBeInTheDocument()
})

it("shows RooTips when user has fewer than 4 tasks (instead of DismissibleUpsell)", () => {
it("shows RooTips when user has fewer than 6 tasks (instead of DismissibleUpsell)", () => {
const { queryByTestId, getByTestId } = renderChatView()

// Hydrate state with user not authenticated but fewer than 4 tasks
Expand Down
19 changes: 11 additions & 8 deletions webview-ui/src/components/history/HistoryPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,21 @@ const HistoryPreview = () => {
}

return (
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-1">
<div className="flex flex-wrap items-center justify-between mt-4 mb-2">
<h2 className="font-semibold text-lg grow m-0">{t("history:recentTasks")}</h2>
<button
onClick={handleViewAllHistory}
className="text-base text-vscode-descriptionForeground hover:text-vscode-textLink-foreground transition-colors cursor-pointer"
aria-label={t("history:viewAllHistory")}>
{t("history:viewAllHistory")}
</button>
</div>
{tasks.length !== 0 && (
<>
{tasks.slice(0, 3).map((item) => (
{tasks.slice(0, 4).map((item) => (
<TaskItem key={item.id} item={item} variant="compact" />
))}
<button
onClick={handleViewAllHistory}
className="text-base text-vscode-descriptionForeground hover:text-vscode-textLink-foreground transition-colors cursor-pointer text-center w-full"
aria-label={t("history:viewAllHistory")}>
{t("history:viewAllHistory")}
</button>
</>
)}
</div>
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/components/history/TaskItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const TaskItem = ({
key={item.id}
data-testid={`task-item-${item.id}`}
className={cn(
"cursor-pointer group bg-vscode-editor-background rounded relative overflow-hidden border border-transparent hover:bg-vscode-list-hoverBackground transition-colors",
"cursor-pointer group bg-vscode-editor-background rounded-xl relative overflow-hidden border border-transparent hover:bg-vscode-editor-foreground/10 transition-colors",
className,
)}
onClick={handleClick}>
Expand All @@ -70,7 +70,7 @@ const TaskItem = ({
<div className="flex-1 min-w-0">
<div
className={cn(
"overflow-hidden whitespace-pre-wrap text-vscode-foreground text-ellipsis line-clamp-2",
"overflow-hidden whitespace-pre-wrap font-light text-vscode-foreground text-ellipsis line-clamp-3",
{
"text-base": !isCompact,
},
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/components/history/TaskItemFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface TaskItemFooterProps {
const TaskItemFooter: React.FC<TaskItemFooterProps> = ({ item, variant, isSelectionMode = false, onDelete }) => {
return (
<div className="text-xs text-vscode-descriptionForeground flex justify-between items-center">
<div className="flex gap-2 items-center text-vscode-descriptionForeground/60">
<div className="flex gap-1 items-center text-vscode-descriptionForeground/60">
{/* Datetime with time-ago format */}
<StandardTooltip content={new Date(item.ts).toLocaleString()}>
<span className="first-letter:uppercase">{formatTimeAgo(item.ts)}</span>
Expand All @@ -32,7 +32,7 @@ const TaskItemFooter: React.FC<TaskItemFooterProps> = ({ item, variant, isSelect

{/* Action Buttons for non-compact view */}
{!isSelectionMode && (
<div className="flex flex-row gap-0 items-center text-vscode-descriptionForeground/60 hover:text-vscode-descriptionForeground">
<div className="flex flex-row gap-0 -mx-2 items-center text-vscode-descriptionForeground/60 hover:text-vscode-descriptionForeground">
<CopyButton itemTask={item.task} />
{variant === "full" && <ExportButton itemId={item.id} />}
{onDelete && <DeleteButton itemId={item.id} onDelete={onDelete} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ describe("HistoryPreview", () => {
const { container } = render(<HistoryPreview />)

// Should render the container but no task items
expect(container.firstChild).toHaveClass("flex", "flex-col", "gap-3")
expect(container.firstChild).toHaveClass("flex", "flex-col", "gap-1")
expect(screen.queryByTestId(/task-item-/)).not.toBeInTheDocument()
})

it("renders up to 3 tasks when tasks are available", () => {
it("renders up to 4 tasks when tasks are available", () => {
mockUseTaskSearch.mockReturnValue({
tasks: mockTasks,
searchQuery: "",
Expand All @@ -123,7 +123,7 @@ describe("HistoryPreview", () => {
expect(screen.getByTestId("task-item-task-1")).toBeInTheDocument()
expect(screen.getByTestId("task-item-task-2")).toBeInTheDocument()
expect(screen.getByTestId("task-item-task-3")).toBeInTheDocument()
expect(screen.queryByTestId("task-item-task-4")).not.toBeInTheDocument()
expect(screen.getByTestId("task-item-task-4")).toBeInTheDocument()
expect(screen.queryByTestId("task-item-task-5")).not.toBeInTheDocument()
expect(screen.queryByTestId("task-item-task-6")).not.toBeInTheDocument()
})
Expand Down Expand Up @@ -226,6 +226,6 @@ describe("HistoryPreview", () => {

const { container } = render(<HistoryPreview />)

expect(container.firstChild).toHaveClass("flex", "flex-col", "gap-3")
expect(container.firstChild).toHaveClass("flex", "flex-col", "gap-1")
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,6 @@ describe("TaskItem", () => {
)

const taskItem = screen.getByTestId("task-item-1")
expect(taskItem).toHaveClass("hover:bg-vscode-list-hoverBackground")
expect(taskItem).toHaveClass("hover:bg-vscode-editor-foreground/10")
})
})
4 changes: 2 additions & 2 deletions webview-ui/src/components/welcome/RooHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const RooHero = () => {
})

return (
<div className="flex flex-col items-center justify-center pb-4 forced-color-adjust-none">
<div className="pb-4 forced-color-adjust-none">
<div
style={{
backgroundColor: "var(--vscode-foreground)",
Expand All @@ -18,7 +18,7 @@ const RooHero = () => {
maskRepeat: "no-repeat",
maskSize: "contain",
}}
className="mx-auto">
className="mx-auto hover:animate-bounce">
<img src={imagesBaseUri + "/roo-logo.svg"} alt="Roo logo" className="h-8 opacity-0" />
</div>
</div>
Expand Down
Loading