Skip to content
Closed
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
6 changes: 5 additions & 1 deletion webview-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import ModesView from "./components/modes/ModesView"
import { HumanRelayDialog } from "./components/human-relay/HumanRelayDialog"
import { AccountView } from "./components/account/AccountView"
import { useAddNonInteractiveClickListener } from "./components/ui/hooks/useNonInteractiveClick"
import { TooltipProvider } from "./components/ui/tooltip"
import { STANDARD_TOOLTIP_DELAY } from "./components/ui/standard-tooltip"

type Tab = "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account"

Expand Down Expand Up @@ -215,7 +217,9 @@ const AppWithProviders = () => (
<ExtensionStateContextProvider>
<TranslationProvider>
<QueryClientProvider client={queryClient}>
<App />
<TooltipProvider delayDuration={STANDARD_TOOLTIP_DELAY}>
<App />
</TooltipProvider>
</QueryClientProvider>
</TranslationProvider>
</ExtensionStateContextProvider>
Expand Down
2 changes: 1 addition & 1 deletion webview-ui/src/__tests__/App.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// npx vitest run src/__tests__/App.spec.tsx

import React from "react"
import { render, screen, act, cleanup } from "@testing-library/react"
import { render, screen, act, cleanup } from "@/utils/test-utils"

import AppWithProviders from "../App"

Expand Down
28 changes: 16 additions & 12 deletions webview-ui/src/__tests__/ContextWindowProgress.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// npm run test ContextWindowProgress.spec.tsx

import { render, screen } from "@testing-library/react"
import { render, screen } from "@/utils/test-utils"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"

import TaskHeader from "@src/components/chat/TaskHeader"
Expand Down Expand Up @@ -103,18 +103,22 @@ describe("ContextWindowProgress", () => {
it("calculates percentages correctly", () => {
renderComponent({ contextTokens: 1000, contextWindow: 4000 })

// Instead of checking the title attribute, verify the data-test-id
// which identifies the element containing info about the percentage of tokens used
const tokenUsageDiv = screen.getByTestId("context-tokens-used")
expect(tokenUsageDiv).toBeInTheDocument()
// Verify that the token count and window size are displayed correctly
const tokenCount = screen.getByTestId("context-tokens-count")
const windowSize = screen.getByTestId("context-window-size")

// Just verify that the element has a title attribute (the actual text is translated and may vary)
expect(tokenUsageDiv).toHaveAttribute("title")
expect(tokenCount).toBeInTheDocument()
expect(tokenCount).toHaveTextContent("1000")

// We can't reliably test computed styles in JSDOM, so we'll just check
// that the component appears to be working correctly by checking for expected elements
// The context-window-label is not part of the ContextWindowProgress component
expect(screen.getByTestId("context-tokens-count")).toBeInTheDocument()
expect(screen.getByTestId("context-tokens-count")).toHaveTextContent("1000")
expect(windowSize).toBeInTheDocument()
expect(windowSize).toHaveTextContent("4000")

// The progress bar is now wrapped in tooltips, but we can verify the structure exists
// by checking for the progress bar container
const progressBarContainer = screen.getByTestId("context-tokens-count").parentElement
expect(progressBarContainer).toBeInTheDocument()

// Verify the flex container has the expected structure
expect(progressBarContainer?.querySelector(".flex-1.relative")).toBeInTheDocument()
})
})
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render, screen } from "@testing-library/react"
import { render, screen } from "@/utils/test-utils"
import { describe, it, expect, vi } from "vitest"
import { AccountView } from "../AccountView"

Expand Down
39 changes: 21 additions & 18 deletions webview-ui/src/components/chat/ChatTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
SearchResult,
} from "@src/utils/context-mentions"
import { convertToMentionPath } from "@/utils/path-mentions"
import { SelectDropdown, DropdownOptionType, Button } from "@/components/ui"
import { SelectDropdown, DropdownOptionType, Button, StandardTooltip } from "@/components/ui"

import Thumbnails from "../common/Thumbnails"
import ModeSelector from "./ModeSelector"
Expand Down Expand Up @@ -1094,8 +1094,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
<div
className={cn("truncate min-w-0 overflow-hidden", {
"font-medium": isCurrentConfig,
})}
title={label}>
})}>
{label}
</div>
<div className="flex justify-end w-10 flex-shrink-0">
Expand All @@ -1106,21 +1105,25 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
})}>
<Check className="size-3" />
</div>
<Button
variant="ghost"
size="icon"
title={pinned ? t("chat:unpin") : t("chat:pin")}
onClick={(e) => {
e.stopPropagation()
togglePinnedApiConfig(value)
vscode.postMessage({ type: "toggleApiConfigPin", text: value })
}}
className={cn("size-5", {
"hidden group-hover:flex": !pinned,
"bg-accent": pinned,
})}>
<Pin className="size-3 p-0.5 opacity-50" />
</Button>
<StandardTooltip content={pinned ? t("chat:unpin") : t("chat:pin")}>
<Button
variant="ghost"
size="icon"
onClick={(e) => {
e.stopPropagation()
togglePinnedApiConfig(value)
vscode.postMessage({
type: "toggleApiConfigPin",
text: value,
})
}}
className={cn("size-5", {
"hidden group-hover:flex": !pinned,
"bg-accent": pinned,
})}>
<Pin className="size-3 p-0.5 opacity-50" />
</Button>
</StandardTooltip>
</div>
</div>
)
Expand Down
66 changes: 36 additions & 30 deletions webview-ui/src/components/chat/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { useExtensionState } from "@src/context/ExtensionStateContext"
import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel"
import RooHero from "@src/components/welcome/RooHero"
import RooTips from "@src/components/welcome/RooTips"
import { StandardTooltip } from "@src/components/ui"

import TelemetryBanner from "../common/TelemetryBanner"
import { useTaskSearch } from "../history/useTaskSearch"
Expand Down Expand Up @@ -730,7 +731,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
}
},
50,
[isHidden, sendingDisabled, enableButtons]
[isHidden, sendingDisabled, enableButtons],
)

const visibleMessages = useMemo(() => {
Expand Down Expand Up @@ -1095,8 +1096,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro

useEffect(() => {
return () => {
if (scrollToBottomSmooth && typeof (scrollToBottomSmooth as any).cancel === 'function') {
(scrollToBottomSmooth as any).cancel()
if (scrollToBottomSmooth && typeof (scrollToBottomSmooth as any).cancel === "function") {
;(scrollToBottomSmooth as any).cancel()
}
}
}, [scrollToBottomSmooth])
Expand Down Expand Up @@ -1477,15 +1478,16 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
<AutoApproveMenu />
{showScrollToBottom ? (
<div className="flex px-[15px] pt-[10px]">
<div
className="bg-[color-mix(in_srgb,_var(--vscode-toolbar-hoverBackground)_55%,_transparent)] rounded-[3px] overflow-hidden cursor-pointer flex justify-center items-center flex-1 h-[25px] hover:bg-[color-mix(in_srgb,_var(--vscode-toolbar-hoverBackground)_90%,_transparent)] active:bg-[color-mix(in_srgb,_var(--vscode-toolbar-hoverBackground)_70%,_transparent)]"
onClick={() => {
scrollToBottomSmooth()
disableAutoScrollRef.current = false
}}
title={t("chat:scrollToBottom")}>
<span className="codicon codicon-chevron-down text-[18px]"></span>
</div>
<StandardTooltip content={t("chat:scrollToBottom")}>
<div
className="bg-[color-mix(in_srgb,_var(--vscode-toolbar-hoverBackground)_55%,_transparent)] rounded-[3px] overflow-hidden cursor-pointer flex justify-center items-center flex-1 h-[25px] hover:bg-[color-mix(in_srgb,_var(--vscode-toolbar-hoverBackground)_90%,_transparent)] active:bg-[color-mix(in_srgb,_var(--vscode-toolbar-hoverBackground)_70%,_transparent)]"
onClick={() => {
scrollToBottomSmooth()
disableAutoScrollRef.current = false
}}>
<span className="codicon codicon-chevron-down text-[18px]"></span>
</div>
</StandardTooltip>
</div>
) : (
<div
Expand All @@ -1499,11 +1501,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
: "opacity-0"
}`}>
{primaryButtonText && !isStreaming && (
<VSCodeButton
appearance="primary"
disabled={!enableButtons}
className={secondaryButtonText ? "flex-1 mr-[6px]" : "flex-[2] mr-0"}
title={
<StandardTooltip
content={
primaryButtonText === t("chat:retry.title")
? t("chat:retry.tooltip")
: primaryButtonText === t("chat:save.title")
Expand All @@ -1522,17 +1521,19 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
t("chat:proceedWhileRunning.title")
? t("chat:proceedWhileRunning.tooltip")
: undefined
}
onClick={() => handlePrimaryButtonClick(inputValue, selectedImages)}>
{primaryButtonText}
</VSCodeButton>
}>
<VSCodeButton
appearance="primary"
disabled={!enableButtons}
className={secondaryButtonText ? "flex-1 mr-[6px]" : "flex-[2] mr-0"}
onClick={() => handlePrimaryButtonClick(inputValue, selectedImages)}>
{primaryButtonText}
</VSCodeButton>
</StandardTooltip>
)}
{(secondaryButtonText || isStreaming) && (
<VSCodeButton
appearance="secondary"
disabled={!enableButtons && !(isStreaming && !didClickCancel)}
className={isStreaming ? "flex-[2] ml-0" : "flex-1 ml-[6px]"}
title={
<StandardTooltip
content={
isStreaming
? t("chat:cancel.tooltip")
: secondaryButtonText === t("chat:startNewTask.title")
Expand All @@ -1542,10 +1543,15 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
: secondaryButtonText === t("chat:terminate.title")
? t("chat:terminate.tooltip")
: undefined
}
onClick={() => handleSecondaryButtonClick(inputValue, selectedImages)}>
{isStreaming ? t("chat:cancel.title") : secondaryButtonText}
</VSCodeButton>
}>
<VSCodeButton
appearance="secondary"
disabled={!enableButtons && !(isStreaming && !didClickCancel)}
className={isStreaming ? "flex-[2] ml-0" : "flex-1 ml-[6px]"}
onClick={() => handleSecondaryButtonClick(inputValue, selectedImages)}>
{isStreaming ? t("chat:cancel.title") : secondaryButtonText}
</VSCodeButton>
</StandardTooltip>
)}
</div>
)}
Expand Down
26 changes: 14 additions & 12 deletions webview-ui/src/components/chat/CodebaseSearchResult.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react"
import { vscode } from "@src/utils/vscode"
import { StandardTooltip } from "@/components/ui"

interface CodebaseSearchResultProps {
filePath: string
Expand All @@ -23,19 +24,20 @@ const CodebaseSearchResult: React.FC<CodebaseSearchResultProps> = ({ filePath, s
}

return (
<div
onClick={handleClick}
className="mb-1 p-2 border border-primary rounded cursor-pointer hover:bg-secondary hover:text-white"
title={`Score: ${score.toFixed(2)}`}>
<div className="flex gap-2 items-center overflow-hidden">
<span className="text-primary-300 whitespace-nowrap flex-shrink-0">
{filePath.split("/").at(-1)}:{startLine}-{endLine}
</span>
<span className="text-gray-500 truncate min-w-0 flex-1">
{filePath.split("/").slice(0, -1).join("/")}
</span>
<StandardTooltip content={`Score: ${score.toFixed(2)}`}>
<div
onClick={handleClick}
className="mb-1 p-2 border border-primary rounded cursor-pointer hover:bg-secondary hover:text-white">
<div className="flex gap-2 items-center overflow-hidden">
<span className="text-primary-300 whitespace-nowrap flex-shrink-0">
{filePath.split("/").at(-1)}:{startLine}-{endLine}
</span>
<span className="text-gray-500 truncate min-w-0 flex-1">
{filePath.split("/").slice(0, -1).join("/")}
</span>
</div>
</div>
</div>
</StandardTooltip>
)
}

Expand Down
Loading