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
2 changes: 2 additions & 0 deletions webview-ui/playwright/vscode-theme-light.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
--vscode-focusBorder: #0090f1;
--vscode-editor-foreground: #333333;
--vscode-editor-background: #ffffff;
--vscode-editorGroup-border: #cecece;
--vscode-button-foreground: #ffffff;
--vscode-button-background: #007acc;
--vscode-button-hoverBackground: #0062a3;
Expand All @@ -25,6 +26,7 @@
--vscode-panel-border: #cecece;
--vscode-widget-border: #d4d4d4;
--vscode-widget-shadow: #00000029;
--vscode-textLink-foreground: #006ab1;
--vscode-menu-foreground: #3b3b3b;
--vscode-menu-background: #ffffff;
--vscode-editorHoverWidget-foreground: #3b3b3b;
Expand Down
37 changes: 14 additions & 23 deletions webview-ui/src/components/chat/ChatTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import ContextMenu from "./ContextMenu"
import { IndexingStatusBadge } from "./IndexingStatusBadge"
import { ZooCodeAuthBadge } from "./ZooCodeAuthBadge"
import { usePromptHistory } from "./hooks/usePromptHistory"
import { disabledChatControlClassName, enabledChatControlClassName } from "./chatControlStyles"

interface ChatTextAreaProps {
inputValue: string
Expand Down Expand Up @@ -1136,14 +1137,11 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
"transition-all duration-1000",
"cursor-pointer",
!shouldDisableImages
? "opacity-50 hover:opacity-100 delay-750 pointer-events-auto"
? "opacity-50 delay-750 pointer-events-auto"
: "opacity-0 pointer-events-none duration-200 delay-0",
!shouldDisableImages &&
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
!shouldDisableImages && enabledChatControlClassName,
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
!shouldDisableImages && "active:bg-[rgba(255,255,255,0.1)]",
shouldDisableImages &&
"opacity-40 cursor-not-allowed grayscale-[30%] hover:bg-transparent hover:border-[rgba(255,255,255,0.08)] active:bg-transparent",
shouldDisableImages && disabledChatControlClassName,
)}>
<Image className="w-4 h-4" />
</button>
Expand All @@ -1158,11 +1156,10 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
"relative inline-flex items-center justify-center",
"bg-transparent border-none p-1.5",
"rounded-md min-w-[28px] min-h-[28px]",
"opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground",
"opacity-60 text-vscode-descriptionForeground hover:text-vscode-foreground",
"transition-all duration-150",
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
enabledChatControlClassName,
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
"cursor-pointer",
)}>
<X className="w-4 h-4" />
Expand All @@ -1182,12 +1179,10 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
"transition-all duration-1000",
"cursor-pointer",
hasInputContent
? "opacity-50 hover:opacity-100 delay-750 pointer-events-auto"
? "opacity-50 delay-750 pointer-events-auto"
: "opacity-0 pointer-events-none duration-200 delay-0",
hasInputContent &&
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
hasInputContent && enabledChatControlClassName,
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
hasInputContent && "active:bg-[rgba(255,255,255,0.1)]",
)}>
<WandSparkles
className={cn("w-4 h-4", isEnhancingPrompt && "animate-spin")}
Expand All @@ -1208,10 +1203,9 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
"rounded-md min-w-[28px] min-h-[28px]",
"text-vscode-descriptionForeground hover:text-vscode-foreground",
"transition-all duration-200",
"opacity-100 hover:opacity-100 pointer-events-auto",
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
"opacity-100 pointer-events-auto",
enabledChatControlClassName,
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
"cursor-pointer",
)}>
<ListEnd className="w-4 h-4" />
Expand Down Expand Up @@ -1244,16 +1238,14 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
"text-vscode-descriptionForeground hover:text-vscode-foreground",
"transition-all duration-200",
isEditMode || isStreaming || hasInputContent
? "opacity-100 hover:opacity-100 pointer-events-auto"
? "opacity-100 pointer-events-auto"
: "opacity-0 pointer-events-none",
(isEditMode || isStreaming || hasInputContent) &&
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
enabledChatControlClassName,
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
(isEditMode || isStreaming || hasInputContent) &&
"active:bg-[rgba(255,255,255,0.1)]",
(isEditMode || isStreaming || hasInputContent) && "cursor-pointer",
isStreaming &&
"bg-vscode-button-background hover:bg-vscode-button-background",
"bg-vscode-button-background hover:bg-vscode-button-background active:bg-vscode-button-background",
)}>
{isStreaming ? (
<Square className="size-4 stroke-none fill-vscode-button-foreground" />
Expand Down Expand Up @@ -1333,9 +1325,8 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
"rounded-md min-w-[28px] min-h-[28px]",
"text-vscode-foreground opacity-85",
"transition-all duration-150",
"hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
enabledChatControlClassName,
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
"cursor-pointer",
)}>
<VolumeX className="w-4 h-4" />
Expand Down
8 changes: 4 additions & 4 deletions webview-ui/src/components/chat/CodebaseSearchResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ const CodebaseSearchResult: React.FC<CodebaseSearchResultProps> = ({ filePath, s
<StandardTooltip content={t("codebaseSearch.resultTooltip", { score: score.toFixed(3) })}>
<div
onClick={handleClick}
className="p-2 border border-[var(--vscode-editorGroup-border)] cursor-pointer hover:bg-secondary hover:text-white">
className="group p-2 border border-vscode-editorGroup-border cursor-pointer hover:bg-vscode-list-hoverBackground">
<div className="flex gap-2 items-center overflow-hidden">
<span className="text-primary-300 whitespace-nowrap flex-shrink-0">
<span className="text-vscode-textLink-foreground group-hover:text-vscode-list-hoverForeground whitespace-nowrap flex-shrink-0">
{filePath.split("/").at(-1)}:{startLine === endLine ? startLine : `${startLine}-${endLine}`}
</span>
<span className="text-gray-500 truncate min-w-0 flex-1">
<span className="text-vscode-descriptionForeground group-hover:text-vscode-list-hoverForeground truncate min-w-0 flex-1">
{filePath.split("/").slice(0, -1).join("/")}
</span>
<span className="text-xs text-vscode-descriptionForeground whitespace-nowrap ml-auto opacity-60">
<span className="text-xs text-vscode-descriptionForeground group-hover:text-vscode-list-hoverForeground whitespace-nowrap ml-auto opacity-60">
{score.toFixed(3)}
</span>
</div>
Expand Down
8 changes: 3 additions & 5 deletions webview-ui/src/components/chat/IconButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { cn } from "@src/lib/utils"
import { Button, StandardTooltip } from "@src/components/ui"
import { disabledChatControlClassName, enabledChatControlClassName } from "./chatControlStyles"

interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
iconClass: string
Expand Down Expand Up @@ -30,12 +31,9 @@ export const IconButton: React.FC<IconButtonProps> = ({
"rounded-md min-w-[28px] min-h-[28px]",
"text-vscode-foreground opacity-85",
"transition-all duration-150",
"hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
!disabled && "cursor-pointer",
disabled &&
"opacity-40 cursor-not-allowed grayscale-[30%] hover:bg-transparent hover:border-[rgba(255,255,255,0.08)] active:bg-transparent",
!disabled && cn("cursor-pointer", enabledChatControlClassName),
disabled && disabledChatControlClassName,
className,
)}
disabled={disabled}
Expand Down
3 changes: 2 additions & 1 deletion webview-ui/src/components/chat/IndexingStatusBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useExtensionState } from "@src/context/ExtensionStateContext"
import { PopoverTrigger, StandardTooltip, Button } from "@src/components/ui"

import { CodeIndexPopover } from "./CodeIndexPopover"
import { enabledChatControlClassName } from "./chatControlStyles"

interface IndexingStatusBadgeProps {
className?: string
Expand Down Expand Up @@ -96,7 +97,7 @@ export const IndexingStatusBadge: React.FC<IndexingStatusBadgeProps> = ({ classN
className={cn(
"relative h-5 w-5 p-0",
"text-vscode-foreground opacity-85",
"hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)]",
enabledChatControlClassName,
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
className,
)}>
Expand Down
6 changes: 3 additions & 3 deletions webview-ui/src/components/chat/LucideIconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { forwardRef } from "react"
import { cn } from "@src/lib/utils"
import { Button, StandardTooltip } from "@src/components/ui"
import { Loader2, LucideIcon } from "lucide-react"
import { disabledChatControlClassName, enabledChatControlClassName } from "./chatControlStyles"

interface LucideIconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
icon: LucideIcon
Expand All @@ -27,9 +28,8 @@ export const LucideIconButton = forwardRef<HTMLButtonElement, LucideIconButtonPr
"text-vscode-foreground opacity-85",
"transition-all duration-150",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
!disabled && "cursor-pointer hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)]",
disabled && "cursor-not-allowed opacity-40 hover:bg-transparent active:bg-transparent",
!disabled && cn("cursor-pointer", enabledChatControlClassName),
disabled && disabledChatControlClassName,
className,
)}
disabled={disabled}
Expand Down
22 changes: 5 additions & 17 deletions webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,9 @@ interface UpdateTodoListToolBlockProps {
}

const STATUS_OPTIONS = [
{ value: "", label: "Not Started", color: "var(--vscode-foreground)", border: "#bbb", bg: "transparent" },
{
value: "in_progress",
label: "In Progress",
color: "var(--vscode-charts-yellow)",
border: "var(--vscode-charts-yellow)",
bg: "rgba(255, 221, 51, 0.15)",
},
{
value: "completed",
label: "Completed",
color: "var(--vscode-charts-green)",
border: "var(--vscode-charts-green)",
bg: "var(--vscode-charts-green)",
},
{ value: "", label: "Not Started" },
{ value: "in_progress", label: "In Progress" },
{ value: "completed", label: "Completed" },
]

const genId = () => Math.random().toString(36).slice(2, 10)
Expand Down Expand Up @@ -345,7 +333,7 @@ const UpdateTodoListToolBlock: React.FC<UpdateTodoListToolBlockProps> = ({
style={{
border: "none",
background: "transparent",
color: "#f14c4c",
color: "var(--vscode-errorForeground)",
cursor: "pointer",
fontSize: 14,
marginLeft: 2,
Expand Down Expand Up @@ -380,7 +368,7 @@ const UpdateTodoListToolBlock: React.FC<UpdateTodoListToolBlockProps> = ({
fontSize: 13,
marginRight: 6,
padding: "1px 3px",
borderBottom: "1px solid #eee",
borderBottom: "1px solid var(--vscode-input-border)",
}}
/>
<button
Expand Down
3 changes: 2 additions & 1 deletion webview-ui/src/components/chat/WorktreeSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { vscode } from "@/utils/vscode"

import { CreateWorktreeModal } from "../worktrees/CreateWorktreeModal"
import { IconButton } from "./IconButton"
import { enabledChatControlClassName } from "./chatControlStyles"

interface WorktreeSelectorProps {
disabled?: boolean
Expand Down Expand Up @@ -95,7 +96,7 @@ export const WorktreeSelector = ({ disabled = false }: WorktreeSelectorProps) =>
"transition-all duration-150 focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder focus-visible:ring-inset",
disabled
? "opacity-50 cursor-not-allowed"
: "opacity-90 hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)] cursor-pointer",
: cn("opacity-90 cursor-pointer", enabledChatControlClassName),
)}>
<span className="font-semibold mr-2">{t("worktrees:selector.worktree")}:</span>
<GitBranch className="w-3 h-3" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { fireEvent, render, screen } from "@/utils/test-utils"
import { CircleHelp } from "lucide-react"
import { describe, expect, test, vi } from "vitest"

import { IconButton } from "../IconButton"
import { LucideIconButton } from "../LucideIconButton"

describe("chat control buttons", () => {
test("invokes enabled codicon controls", () => {
const onClick = vi.fn()
render(<IconButton iconClass="codicon-settings-gear" title="Settings" tooltip={false} onClick={onClick} />)

fireEvent.click(screen.getByRole("button", { name: "Settings" }))
expect(onClick).toHaveBeenCalledOnce()
})

test("keeps disabled codicon controls inert", () => {
const onClick = vi.fn()
render(
<IconButton
iconClass="codicon-settings-gear"
title="Settings"
tooltip={false}
disabled
onClick={onClick}
/>,
)

const button = screen.getByRole("button", { name: "Settings" })
expect(button).toBeDisabled()
fireEvent.click(button)
expect(onClick).not.toHaveBeenCalled()
})

test("renders enabled and disabled Lucide controls", () => {
const { rerender } = render(<LucideIconButton icon={CircleHelp} title="Help" tooltip={false} />)
expect(screen.getByRole("button", { name: "Help" })).toBeEnabled()

rerender(<LucideIconButton icon={CircleHelp} title="Help" tooltip={false} disabled />)
expect(screen.getByRole("button", { name: "Help" })).toBeDisabled()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { fireEvent, render, screen } from "@/utils/test-utils"
import { beforeEach, describe, expect, test, vi } from "vitest"

import CodebaseSearchResult from "../CodebaseSearchResult"
import { vscode } from "@/utils/vscode"

vi.mock("@/utils/vscode", () => ({ vscode: { postMessage: vi.fn() } }))

vi.mock("react-i18next", () => ({
useTranslation: () => ({ t: (key: string) => key }),
}))

describe("CodebaseSearchResult", () => {
beforeEach(() => vi.clearAllMocks())

test("opens the selected file at the result start line", () => {
render(
<CodebaseSearchResult
filePath="src/example.ts"
score={0.95}
startLine={12}
endLine={18}
snippet="const example = true"
language="typescript"
/>,
)

const fileName = screen.getByText("example.ts:12-18")
expect(fileName).toHaveClass("group-hover:text-vscode-list-hoverForeground")
expect(screen.getByText("src")).toHaveClass("group-hover:text-vscode-list-hoverForeground")
fireEvent.click(fileName)
expect(vscode.postMessage).toHaveBeenCalledWith({
type: "openFile",
text: "./src/example.ts",
values: { line: 12 },
})
})
})
Loading
Loading