Compare commits

...
Author SHA1 Message Date
abeatrix 35938bd601 Replace HeroUI Tooltip with RadixUI 2025-09-24 18:45:18 -07:00
abeatrix 0e8d2e84b7 wip 2025-09-24 14:33:03 -07:00
abeatrix a6554e0722 Apply upgrade 2025-09-24 09:55:01 -07:00
abeatrix 840685487d Upgrade to tailwind v4 2025-09-24 09:54:30 -07:00
103 changed files with 3346 additions and 1142 deletions
+1 -1
View File
@@ -1,10 +1,10 @@
import { sendAddToInputEventToClient } from "@/components/ui/subscribeToAddToInput"
import { getFileMentionFromPath } from "@/core/mentions"
import { WebviewProvider } from "@/core/webview"
import { singleFileDiagnosticsToProblemsString } from "@/integrations/diagnostics"
import { telemetryService } from "@/services/telemetry"
import { CommandContext, Empty } from "@/shared/proto/index.cline"
import { Controller } from "../index"
import { sendAddToInputEventToClient } from "../ui/subscribeToAddToInput"
// 'Add to Cline' context menu in editor and code action
// Inserts the selected code into the chat.
+1 -1
View File
@@ -2,9 +2,9 @@ import { McpServer } from "@shared/mcp"
import { StringRequest } from "@shared/proto/cline/common"
import { McpDownloadResponse } from "@shared/proto/cline/mcp"
import axios from "axios"
import { sendChatButtonClickedEvent } from "@/components/ui/subscribeToChatButtonClicked"
import { clineEnvConfig } from "@/config"
import { Controller } from ".."
import { sendChatButtonClickedEvent } from "../ui/subscribeToChatButtonClicked"
/**
* Download an MCP server from the marketplace
+1 -1
View File
@@ -1,10 +1,10 @@
import { Empty } from "@shared/proto/cline/common"
import { ResetStateRequest } from "@shared/proto/cline/state"
import { sendChatButtonClickedEvent } from "@/components/ui/subscribeToChatButtonClicked"
import { resetGlobalState, resetWorkspaceState } from "@/core/storage/utils/state-helpers"
import { HostProvider } from "@/hosts/host-provider"
import { ShowMessageType } from "@/shared/proto/host/window"
import { Controller } from ".."
import { sendChatButtonClickedEvent } from "../ui/subscribeToChatButtonClicked"
/**
* Resets the extension state to its defaults
+1 -1
View File
@@ -1,7 +1,7 @@
import { StringRequest } from "@shared/proto/cline/common"
import { TaskResponse } from "@shared/proto/cline/task"
import { sendChatButtonClickedEvent } from "@/components/ui/subscribeToChatButtonClicked"
import { Controller } from ".."
import { sendChatButtonClickedEvent } from "../ui/subscribeToChatButtonClicked"
/**
* Shows a task with the specified ID
+22
View File
@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.mjs",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {}
}
+1141 -26
View File
File diff suppressed because it is too large Load Diff
+24 -4
View File
@@ -19,8 +19,23 @@
"@floating-ui/react": "^0.27.4",
"@fontsource/azeret-mono": "^5.2.9",
"@heroui/react": "^2.8.0-beta.2",
"@hookform/resolvers": "^5.2.2",
"@paper-design/shaders-react": "^0.0.46",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.7",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tooltip": "^1.2.8",
"@vscode/webview-ui-toolkit": "^1.4.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"debounce": "^2.1.1",
"dompurify": "^3.2.4",
"fast-deep-equal": "^3.1.3",
@@ -34,6 +49,7 @@
"pretty-bytes": "^6.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.63.0",
"react-remark": "^2.1.0",
"react-textarea-autosize": "^8.5.7",
"react-use": "^17.6.0",
@@ -43,18 +59,22 @@
"rehype-remark": "^10.0.1",
"remark-stringify": "^11.0.0",
"styled-components": "^6.1.15",
"tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7",
"tw-animate-css": "^1.4.0",
"unified": "^11.0.5",
"uuid": "^9.0.1"
"uuid": "^9.0.1",
"zod": "^4.1.11"
},
"devDependencies": {
"@storybook/react-vite": "^9.1.6",
"@tailwindcss/vite": "^4.1.4",
"@tailwindcss/vite": "^4.1.13",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^14.6.1",
"@types/dompurify": "^3.0.5",
"@types/jest": "^29.5.14",
"@types/node": "^22.13.4",
"@types/node": "^22.18.6",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@types/uuid": "^9.0.8",
@@ -64,7 +84,7 @@
"globals": "^15.14.0",
"jsdom": "^26.0.0",
"storybook": "^9.1.6",
"tailwindcss": "^4.1.5",
"tailwindcss": "^4.1.13",
"typescript": "^5.7.3",
"vite": "^6.3.6",
"vitest": "^3.0.5"
+1 -4
View File
@@ -1,4 +1,3 @@
import { HeroUIProvider } from "@heroui/react"
import { type ReactNode } from "react"
import { CustomPostHogProvider } from "./CustomPostHogProvider"
import { ClineAuthProvider } from "./context/ClineAuthContext"
@@ -10,9 +9,7 @@ export function Providers({ children }: { children: ReactNode }) {
<PlatformProvider>
<ExtensionStateContextProvider>
<CustomPostHogProvider>
<ClineAuthProvider>
<HeroUIProvider>{children}</HeroUIProvider>
</ClineAuthProvider>
<ClineAuthProvider>{children}</ClineAuthProvider>
</CustomPostHogProvider>
</ExtensionStateContextProvider>
</PlatformProvider>
@@ -37,10 +37,10 @@ const AccountView = ({ onDone, clineUser, organizations, activeOrganization }: A
return (
<div className="fixed inset-0 flex flex-col overflow-hidden pt-[10px] pl-[20px]">
<div className="flex justify-between items-center mb-[17px] pr-[17px]">
<h3 className="text-[var(--vscode-foreground)] m-0">Account</h3>
<h3 className="text-(--vscode-foreground) m-0">Account</h3>
<VSCodeButton onClick={onDone}>Done</VSCodeButton>
</div>
<div className="flex-grow overflow-hidden pr-[8px] flex flex-col">
<div className="grow overflow-hidden pr-[8px] flex flex-col">
<div className="h-full mb-1.5">
{clineUser?.uid ? (
<ClineAccountView
@@ -297,17 +297,15 @@ export const ClineAccountView = ({ clineUser, userOrganizations, activeOrganizat
{/* {user.photoUrl ? (
<img src={user.photoUrl} alt="Profile" className="size-16 rounded-full mr-4" />
) : ( */}
<div className="size-16 rounded-full bg-[var(--vscode-button-background)] flex items-center justify-center text-2xl text-[var(--vscode-button-foreground)] mr-4">
<div className="size-16 rounded-full bg-(--vscode-button-background) flex items-center justify-center text-2xl text-(--vscode-button-foreground) mr-4">
{displayName?.[0] || email?.[0] || "?"}
</div>
{/* )} */}
<div className="flex flex-col">
{displayName && (
<h2 className="text-[var(--vscode-foreground)] m-0 text-lg font-medium">{displayName}</h2>
)}
{displayName && <h2 className="text-(--vscode-foreground) m-0 text-lg font-medium">{displayName}</h2>}
{email && <div className="text-sm text-[var(--vscode-descriptionForeground)]">{email}</div>}
{email && <div className="text-sm text-(--vscode-descriptionForeground)">{email}</div>}
<div className="flex gap-2 items-center mt-1">
<VSCodeDropdown
@@ -357,7 +355,7 @@ export const ClineAccountView = ({ clineUser, userOrganizations, activeOrganizat
<VSCodeDivider className="mt-6 mb-3 w-full" />
<div className="flex-grow flex flex-col min-h-0 pb-[0px]">
<div className="grow flex flex-col min-h-0 pb-[0px]">
<CreditsHistoryTable
isLoading={isLoading}
paymentsData={paymentsData}
@@ -15,7 +15,7 @@ export const AccountWelcomeView = () => (
Sign up with Cline
</VSCodeButton>
<p className="text-[var(--vscode-descriptionForeground)] text-xs text-center m-0">
<p className="text-(--vscode-descriptionForeground) text-xs text-center m-0">
By continuing, you agree to the <VSCodeLink href="https://cline.bot/tos">Terms of Service</VSCodeLink> and{" "}
<VSCodeLink href="https://cline.bot/privacy">Privacy Policy.</VSCodeLink>
</p>
@@ -15,11 +15,9 @@ export const CreditBalance = ({ balance, fetchCreditBalance, creditUrl, lastFetc
<div
className="w-full flex flex-col items-center"
title={`Last updated: ${new Date(lastFetchTime).toLocaleTimeString()}`}>
<div className="text-sm text-[var(--vscode-descriptionForeground)] mb-3 font-azeret-mono font-light">
CURRENT BALANCE
</div>
<div className="text-sm text-(--vscode-descriptionForeground) mb-3 font-azeret-mono font-light">CURRENT BALANCE</div>
<div className="text-4xl font-bold text-[var(--vscode-foreground)] mb-6 flex items-center gap-2">
<div className="text-4xl font-bold text-(--vscode-foreground) mb-6 flex items-center gap-2">
{balance === null ? <span>----</span> : <StyledCreditDisplay balance={balance} />}
<VSCodeButton
appearance="icon"
@@ -15,9 +15,9 @@ const CreditsHistoryTable = ({ isLoading, usageData, paymentsData, showPayments
const [activeTab, setActiveTab] = useState<"usage" | "payments">("usage")
return (
<div className="flex flex-col flex-grow h-full">
<div className="flex flex-col grow h-full">
{/* Tabs container */}
<div className="flex border-b border-[var(--vscode-panel-border)]">
<div className="flex border-b border-(--vscode-panel-border)">
<TabButton isActive={activeTab === "usage"} onClick={() => setActiveTab("usage")}>
USAGE HISTORY
</TabButton>
@@ -29,10 +29,10 @@ const CreditsHistoryTable = ({ isLoading, usageData, paymentsData, showPayments
</div>
{/* Content container */}
<div className="mt-[15px] mb-[0px] rounded-md overflow-auto flex-grow">
<div className="mt-[15px] mb-[0px] rounded-md overflow-auto grow">
{isLoading ? (
<div className="flex justify-center items-center p-4">
<div className="text-[var(--vscode-descriptionForeground)]">Loading...</div>
<div className="text-(--vscode-descriptionForeground)">Loading...</div>
</div>
) : (
<>
@@ -67,7 +67,7 @@ const CreditsHistoryTable = ({ isLoading, usageData, paymentsData, showPayments
</VSCodeDataGrid>
) : (
<div className="flex justify-center items-center p-4">
<div className="text-[var(--vscode-descriptionForeground)]">No usage history</div>
<div className="text-(--vscode-descriptionForeground)">No usage history</div>
</div>
))}
@@ -97,7 +97,7 @@ const CreditsHistoryTable = ({ isLoading, usageData, paymentsData, showPayments
</VSCodeDataGrid>
) : (
<div className="flex justify-center items-center p-4">
<div className="text-[var(--vscode-descriptionForeground)]">No payment history</div>
<div className="text-(--vscode-descriptionForeground)">No payment history</div>
</div>
))}
</>
@@ -54,7 +54,7 @@ export const StyledCreditDisplay = ({ balance }: { balance: number }) => {
return (
<span className="font-azeret-mono font-light tabular-nums">
{wholePart}.{firstTwoDecimals}
<span className="text-[var(--vscode-descriptionForeground)]">{lastTwoDecimals}</span>
<span className="text-(--vscode-descriptionForeground)">{lastTwoDecimals}</span>
</span>
)
}
@@ -163,7 +163,7 @@ export const BrowserSettingsMenu = () => {
{showInfoPopover && (
// InfoPopover - Dropdown container with connection details
<div
className="absolute top-[30px] right-0 z-[100] w-[60dvw] max-w-[250px] rounded p-2.5 shadow-lg"
className="absolute top-[30px] right-0 z-100 w-[60dvw] max-w-[250px] rounded p-2.5 shadow-lg"
ref={popoverRef}
style={{
backgroundColor: "var(--vscode-editorWidget-background)",
@@ -141,9 +141,9 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
aria-label="Previous Updates"
classNames={{
trigger: "bg-transparent border-0 pl-0 pb-0 w-fit",
title: "font-bold text-[var(--vscode-foreground)]",
title: "font-bold text-(--vscode-foreground)",
indicator:
"text-[var(--vscode-foreground)] mb-0.5 -rotate-180 data-[open=true]:-rotate-90 rtl:rotate-0 rtl:data-[open=true]:-rotate-90",
"text-(--vscode-foreground) mb-0.5 -rotate-180 data-[open=true]:-rotate-90 rtl:rotate-0 rtl:data-[open=true]:-rotate-90",
}}
key="1"
title="Previous Updates:">
+66 -55
View File
@@ -19,9 +19,9 @@ import { CHAT_CONSTANTS } from "@/components/chat/chat-view/constants"
import SlashCommandMenu from "@/components/chat/SlashCommandMenu"
import { CODE_BLOCK_BG_COLOR } from "@/components/common/CodeBlock"
import Thumbnails from "@/components/common/Thumbnails"
import Tooltip from "@/components/common/Tooltip"
import ApiOptions from "@/components/settings/ApiOptions"
import { getModeSpecificFields, normalizeApiConfiguration } from "@/components/settings/utils/providerUtils"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { useClineAuth } from "@/context/ClineAuthContext"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { usePlatform } from "@/context/PlatformContext"
@@ -319,7 +319,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
const [menuPosition, setMenuPosition] = useState(0)
const [shownTooltipMode, setShownTooltipMode] = useState<Mode | null>(null)
const [pendingInsertions, setPendingInsertions] = useState<string[]>([])
const _shiftHoldTimerRef = useRef<NodeJS.Timeout | null>(null)
const [showUnsupportedFileError, setShowUnsupportedFileError] = useState(false)
const unsupportedFileTimerRef = useRef<NodeJS.Timeout | null>(null)
const [showDimensionError, setShowDimensionError] = useState(false)
@@ -1568,7 +1567,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
style={{
width: "100%",
boxSizing: "border-box",
backgroundColor: "transparent",
backgroundColor: "var(--vscode-input-background)",
color: "var(--vscode-input-foreground)",
//border: "1px solid var(--vscode-input-border)",
borderRadius: 2,
@@ -1605,7 +1604,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
value={inputValue}
/>
{!inputValue && selectedImages.length === 0 && selectedFiles.length === 0 && (
<div className="text-[10px] absolute bottom-5 left-5 right-16 text-[var(--vscode-input-placeholderForeground)]/50 whitespace-nowrap overflow-hidden text-ellipsis pointer-events-none z-1">
<div className="text-[10px] absolute bottom-5 left-5 right-16 text-(--vscode-input-placeholderForeground)/50 whitespace-nowrap overflow-hidden text-ellipsis pointer-events-none z-1">
Type @ for context, / for slash commands & workflows, hold shift to drag in files/images
</div>
)}
@@ -1691,35 +1690,41 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
<div className="relative flex-1 min-w-0 h-7">
{/* ButtonGroup - always in DOM but visibility controlled */}
<ButtonGroup className="absolute top-0 left-0 right-0 transition-opacity duration-300 ease-in-out w-full h-5 z-10 flex items-center">
<Tooltip style={{ left: 0 }} tipText="Add Context">
<VSCodeButton
appearance="icon"
aria-label="Add Context"
className="p-0 m-0 flex items-center mt-0.5"
data-testid="context-button"
onClick={handleContextButtonClick}>
<ButtonContainer>
<AtSignIcon size={12} />
</ButtonContainer>
</VSCodeButton>
<Tooltip>
<TooltipContent>Add Context</TooltipContent>
<TooltipTrigger>
<VSCodeButton
appearance="icon"
aria-label="Add Context"
className="p-0 m-0 flex items-center mt-0.5"
data-testid="context-button"
onClick={handleContextButtonClick}>
<ButtonContainer>
<AtSignIcon size={12} />
</ButtonContainer>
</VSCodeButton>
</TooltipTrigger>
</Tooltip>
<Tooltip tipText="Add Files & Images">
<VSCodeButton
appearance="icon"
aria-label="Add Files & Images"
className="p-0 m-0 flex items-center mt-0.5"
data-testid="files-button"
disabled={shouldDisableFilesAndImages}
onClick={() => {
if (!shouldDisableFilesAndImages) {
onSelectFilesAndImages()
}
}}>
<ButtonContainer>
<PlusIcon size={13} />
</ButtonContainer>
</VSCodeButton>
<Tooltip>
<TooltipContent>Add Files & Images</TooltipContent>
<TooltipTrigger>
<VSCodeButton
appearance="icon"
aria-label="Add Files & Images"
className="p-0 m-0 flex items-center mt-0.5"
data-testid="files-button"
disabled={shouldDisableFilesAndImages}
onClick={() => {
if (!shouldDisableFilesAndImages) {
onSelectFilesAndImages()
}
}}>
<ButtonContainer>
<PlusIcon size={13} />
</ButtonContainer>
</VSCodeButton>
</TooltipTrigger>
</Tooltip>
<ServersToggleModal />
<ClineRulesToggleModal />
@@ -1755,30 +1760,36 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
</ButtonGroup>
</div>
{/* Tooltip for Plan/Act toggle remains outside the conditional rendering */}
<Tooltip
hintText={`Toggle w/ ${togglePlanActKeys}`}
style={{ zIndex: 1000 }}
tipText={`In ${shownTooltipMode === "act" ? "Act" : "Plan"} mode, Cline will ${shownTooltipMode === "act" ? "complete the task immediately" : "gather information to architect a plan"}`}
visible={shownTooltipMode !== null}>
<SwitchContainer data-testid="mode-switch" disabled={false} onClick={onModeToggle}>
<Slider isAct={mode === "act"} isPlan={mode === "plan"} />
<SwitchOption
aria-checked={mode === "plan"}
isActive={mode === "plan"}
onMouseLeave={() => setShownTooltipMode(null)}
onMouseOver={() => setShownTooltipMode("plan")}
role="switch">
Plan
</SwitchOption>
<SwitchOption
aria-checked={mode === "act"}
isActive={mode === "act"}
onMouseLeave={() => setShownTooltipMode(null)}
onMouseOver={() => setShownTooltipMode("act")}
role="switch">
Act
</SwitchOption>
</SwitchContainer>
<Tooltip>
{shownTooltipMode !== null && (
<TooltipContent className="text-xs text-description p-2 flex flex-col gap-1" side="top">
<div>{`In ${shownTooltipMode === "act" ? "Act" : "Plan"} mode, Cline will ${shownTooltipMode === "act" ? "complete the task immediately" : "gather information to architect a plan"}`}</div>
<div className="text-description/80">
Toggle w/ <kbd className="text-xs text-muted-foreground mx-1">{togglePlanActKeys}</kbd>
</div>
</TooltipContent>
)}
<TooltipTrigger>
<SwitchContainer data-testid="mode-switch" disabled={false} onClick={onModeToggle}>
<Slider isAct={mode === "act"} isPlan={mode === "plan"} />
<SwitchOption
aria-checked={mode === "plan"}
isActive={mode === "plan"}
onMouseLeave={() => setShownTooltipMode(null)}
onMouseOver={() => setShownTooltipMode("plan")}
role="switch">
Plan
</SwitchOption>
<SwitchOption
aria-checked={mode === "act"}
isActive={mode === "act"}
onMouseLeave={() => setShownTooltipMode(null)}
onMouseOver={() => setShownTooltipMode("act")}
role="switch">
Act
</SwitchOption>
</SwitchContainer>
</TooltipTrigger>
</Tooltip>
</div>
</div>
+1 -1
View File
@@ -370,7 +370,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
/>
)}
</div>
<footer className="bg-[var(--vscode-sidebar-background)]" style={{ gridRow: "2" }}>
<footer className="bg-(--vscode-sidebar-background)" style={{ gridRow: "2" }}>
<AutoApproveBar />
<ActionButtons
chatState={chatState}
@@ -50,7 +50,7 @@ const CreditLimitError: React.FC<CreditLimitErrorProps> = ({
// We have to divide because the balance is stored in microcredits
return (
<div className="p-2 border-none rounded-md mb-2 bg-[var(--vscode-textBlockQuote-background)]">
<div className="p-2 border-none rounded-md mb-2 bg-(--vscode-textBlockQuote-background)">
<div className="mb-3 font-azeret-mono">
<div className="text-error mb-2">{message}</div>
<div className="mb-3">
@@ -25,7 +25,7 @@ const RetryMessage = React.memo(
}, [seconds])
return (
<span className="font-bold text-[var(--vscode-foreground)]">
<span className="font-bold text-(--vscode-foreground)">
{`API Request (Retrying failed attempt ${attempt}/${retryOperations}`}
{remainingSeconds > 0 && ` in ${remainingSeconds} seconds`}
)...
@@ -61,14 +61,14 @@ export const ErrorBlockTitle = ({
const icon =
apiReqCancelReason != null ? (
apiReqCancelReason === "user_cancelled" ? (
getIconSpan("error", "text-[var(--vscode-descriptionForeground)]")
getIconSpan("error", "text-(--vscode-descriptionForeground)")
) : (
getIconSpan("error", "text-[var(--vscode-errorForeground)]")
getIconSpan("error", "text-(--vscode-errorForeground)")
)
) : cost != null ? (
getIconSpan("check", "text-[var(--vscode-charts-green)]")
getIconSpan("check", "text-(--vscode-charts-green)")
) : apiRequestFailedMessage ? (
getIconSpan("error", "text-[var(--vscode-errorForeground)]")
getIconSpan("error", "text-(--vscode-errorForeground)")
) : (
<ProgressIndicator />
)
@@ -79,20 +79,20 @@ export const ErrorBlockTitle = ({
// Handle cancellation states first
if (apiReqCancelReason === "user_cancelled") {
details.title = "API Request Cancelled"
details.classNames.push("text-[var(--vscode-foreground)]")
details.classNames.push("text-(--vscode-foreground)")
} else if (apiReqCancelReason != null) {
details.title = "API Streaming Failed"
details.classNames.push("text-[var(--vscode-errorForeground)]")
details.classNames.push("text-(--vscode-errorForeground)")
} else if (cost != null) {
// Handle completed request
details.title = "API Request"
details.classNames.push("text-[var(--vscode-foreground)]")
details.classNames.push("text-(--vscode-foreground)")
} else if (apiRequestFailedMessage) {
// Handle failed request
const clineError = ClineError.parse(apiRequestFailedMessage)
const titleText = clineError?.isErrorType(ClineErrorType.Balance) ? "Credit Limit Reached" : "API Request Failed"
details.title = titleText
details.classNames.push("font-bold text-[var(--vscode-errorForeground)]")
details.classNames.push("font-bold text-(--vscode-errorForeground)")
} else if (retryStatus) {
// Handle retry state
const retryOperations = Math.max(0, retryStatus.maxAttempts - 1)
+6 -10
View File
@@ -47,7 +47,7 @@ const ErrorRow = memo(({ message, errorType, apiRequestFailedMessage, apiReqStre
if (clineError?.isErrorType(ClineErrorType.RateLimit)) {
return (
<p className="m-0 whitespace-pre-wrap text-[var(--vscode-errorForeground)] wrap-anywhere">
<p className="m-0 whitespace-pre-wrap text-(--vscode-errorForeground) wrap-anywhere">
{clineErrorMessage}
{requestId && <div>Request ID: {requestId}</div>}
</p>
@@ -56,7 +56,7 @@ const ErrorRow = memo(({ message, errorType, apiRequestFailedMessage, apiReqStre
// Default error display
return (
<p className="m-0 whitespace-pre-wrap text-[var(--vscode-errorForeground)] wrap-anywhere">
<p className="m-0 whitespace-pre-wrap text-(--vscode-errorForeground) wrap-anywhere">
{clineErrorMessage}
{requestId && <div>Request ID: {requestId}</div>}
{clineErrorMessage?.toLowerCase()?.includes("powershell") && (
@@ -78,9 +78,7 @@ const ErrorRow = memo(({ message, errorType, apiRequestFailedMessage, apiReqStre
<br />
{/* The user is signed in or not using cline provider */}
{clineUser && !isClineProvider ? (
<span className="mb-4 text-[var(--vscode-descriptionForeground)]">
(Click "Retry" below)
</span>
<span className="mb-4 text-(--vscode-descriptionForeground)">(Click "Retry" below)</span>
) : (
<VSCodeButton className="w-full mb-4" onClick={handleSignIn}>
Sign in to Cline
@@ -93,20 +91,18 @@ const ErrorRow = memo(({ message, errorType, apiRequestFailedMessage, apiReqStre
}
// Regular error message
return (
<p className="m-0 whitespace-pre-wrap text-[var(--vscode-errorForeground)] wrap-anywhere">{message.text}</p>
)
return <p className="m-0 whitespace-pre-wrap text-(--vscode-errorForeground) wrap-anywhere">{message.text}</p>
case "diff_error":
return (
<div className="flex flex-col p-2 rounded text-xs opacity-80 bg-[var(--vscode-textBlockQuote-background)] text-[var(--vscode-foreground)]">
<div className="flex flex-col p-2 rounded text-xs opacity-80 bg-(--vscode-textBlockQuote-background) text-(--vscode-foreground)">
<div>The model used search patterns that don't match anything in the file. Retrying...</div>
</div>
)
case "clineignore_error":
return (
<div className="flex flex-col p-2 rounded text-xs bg-[var(--vscode-textBlockQuote-background)] text-[var(--vscode-foreground)] opacity-80">
<div className="flex flex-col p-2 rounded text-xs bg-(--vscode-textBlockQuote-background) text-(--vscode-foreground) opacity-80">
<div>
Cline tried to access <code>{message.text}</code> which is blocked by the <code>.clineignore</code>
file.
@@ -7,7 +7,7 @@ interface NewTaskPreviewProps {
const NewTaskPreview: React.FC<NewTaskPreviewProps> = ({ context }) => {
return (
<div className="bg-[var(--vscode-badge-background)] text-[var(--vscode-badge-foreground)] rounded-[3px] p-[14px] pb-[6px]">
<div className="bg-(--vscode-badge-background) text-(--vscode-badge-foreground) rounded-[3px] p-[14px] pb-[6px]">
<span style={{ fontWeight: "bold" }}>Task</span>
<MarkdownBlock markdown={context} />
</div>
@@ -28,7 +28,7 @@ const ContentRow = styled.div`
`
const TextContainer = styled.div`
flex-grow: 1;
grow: 1;
margin: 0 2px; /* Further reduced space around text */
white-space: pre-wrap;
word-break: break-word;
@@ -45,7 +45,7 @@ const TextContainer = styled.div`
const DismissButton = styled(VSCodeButton)`
/* margin-left: auto; */ /* Removed as ContentRow handles spacing */
flex-shrink: 0; /* Prevent button from shrinking */
shrink: 0; /* Prevent button from shrinking */
min-width: 22px;
height: 22px;
padding: 0;
@@ -58,7 +58,7 @@ const DismissButton = styled(VSCodeButton)`
const ReplyIcon = styled.span`
color: var(--vscode-descriptionForeground);
margin-right: 2px; /* Further reduced space between icon and text */
flex-shrink: 0;
shrink: 0;
font-size: 13px; /* Make icon even smaller */
/* transform: translateY(-1px); */ /* Removed vertical transform */
`
@@ -17,7 +17,7 @@ const ReportBugPreview: React.FC<ReportBugPreviewProps> = ({ data }) => {
}, [data])
return (
<div className="bg-[var(--vscode-badge-background)] text-[var(--vscode-badge-foreground)] rounded-[3px] p-[14px]">
<div className="bg-(--vscode-badge-background) text-(--vscode-badge-foreground) rounded-[3px] p-[14px]">
<h3 className="font-bold text-base mb-3 mt-0">{bugData.title || "Bug Report"}</h3>
<div className="space-y-3 text-sm">
@@ -2,39 +2,16 @@ import { EmptyRequest } from "@shared/proto/cline/common"
import { McpServers } from "@shared/proto/cline/mcp"
import { convertProtoMcpServersToMcpServers } from "@shared/proto-conversions/mcp/mcp-server-conversion"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import React, { useEffect, useRef, useState } from "react"
import { useClickAway, useWindowSize } from "react-use"
import { CODE_BLOCK_BG_COLOR } from "@/components/common/CodeBlock"
import Tooltip from "@/components/common/Tooltip"
import React, { useEffect, useState } from "react"
import ServersToggleList from "@/components/mcp/configuration/tabs/installed/ServersToggleList"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { McpServiceClient } from "@/services/grpc-client"
const ServersToggleModal: React.FC = () => {
const { mcpServers, navigateToMcp, setMcpServers } = useExtensionState()
const [isVisible, setIsVisible] = useState(false)
const buttonRef = useRef<HTMLDivElement>(null)
const modalRef = useRef<HTMLDivElement>(null)
const { width: viewportWidth, height: viewportHeight } = useWindowSize()
const [arrowPosition, setArrowPosition] = useState(0)
const [menuPosition, setMenuPosition] = useState(0)
// Close modal when clicking outside
useClickAway(modalRef, () => {
setIsVisible(false)
})
// Calculate positions for modal and arrow
useEffect(() => {
if (isVisible && buttonRef.current) {
const buttonRect = buttonRef.current.getBoundingClientRect()
const buttonCenter = buttonRect.left + buttonRect.width / 2
const rightPosition = document.documentElement.clientWidth - buttonCenter - 5
setArrowPosition(rightPosition)
setMenuPosition(buttonRect.top + 1)
}
}, [isVisible, viewportWidth, viewportHeight])
useEffect(() => {
if (isVisible) {
@@ -52,61 +29,44 @@ const ServersToggleModal: React.FC = () => {
}, [isVisible])
return (
<div ref={modalRef}>
<div className="inline-flex min-w-0 max-w-full" ref={buttonRef}>
<Tooltip tipText="Manage MCP Servers" visible={isVisible ? false : undefined}>
<VSCodeButton
appearance="icon"
aria-label={isVisible ? "Hide MCP Servers" : "Show MCP Servers"}
onClick={() => setIsVisible(!isVisible)}
style={{ padding: "0px 0px", height: "20px" }}>
<div className="flex items-center gap-1 text-xs whitespace-nowrap min-w-0 w-full">
<span
className="codicon codicon-server flex items-center"
style={{ fontSize: "12.5px", marginBottom: 1 }}
/>
</div>
</VSCodeButton>
</Tooltip>
</div>
{isVisible && (
<div
className="fixed left-[15px] right-[15px] border border-[var(--vscode-editorGroup-border)] p-3 rounded z-[1000] overflow-y-auto"
style={{
bottom: `calc(100vh - ${menuPosition}px + 6px)`,
background: CODE_BLOCK_BG_COLOR,
maxHeight: "calc(100vh - 100px)",
overscrollBehavior: "contain",
}}>
<div
className="fixed w-[10px] h-[10px] z-[-1] rotate-45 border-r border-b border-[var(--vscode-editorGroup-border)]"
style={{
bottom: `calc(100vh - ${menuPosition}px)`,
right: arrowPosition,
background: CODE_BLOCK_BG_COLOR,
}}
/>
<div className="flex justify-between items-center mb-2.5">
<div className="m-0 text-base font-semibold">MCP Servers</div>
<Popover onOpenChange={(isModalOpened) => setIsVisible(isModalOpened)}>
<PopoverTrigger>
<Tooltip>
<TooltipContent hidden={isVisible} side="top">
Manage MCP Servers
</TooltipContent>
<TooltipTrigger asChild>
<VSCodeButton
appearance="icon"
aria-label="Go to MCP server settings"
onClick={() => {
setIsVisible(false)
navigateToMcp("installed")
}}>
<span className="codicon codicon-gear text-[10px]"></span>
aria-label={isVisible ? "Hide MCP Servers" : "Show MCP Servers"}
style={{ padding: "0px 0px", height: "20px" }}>
<div className="flex items-center gap-1 text-xs whitespace-nowrap min-w-0 w-full">
<span
className="codicon codicon-server flex items-center"
style={{ fontSize: "12.5px", marginBottom: 1 }}
/>
</div>
</VSCodeButton>
</div>
</TooltipTrigger>
</Tooltip>
</PopoverTrigger>
<div style={{ marginBottom: -10 }}>
<ServersToggleList hasTrashIcon={false} isExpandable={false} listGap="small" servers={mcpServers} />
</div>
<PopoverContent className="mx-3 h-full wrap-break-word text-foreground w-[94vw]" side="top">
<div className="flex justify-between items-center mb-2.5">
<div className="m-0 text-base font-semibold text-foreground">MCP Servers</div>
<VSCodeButton
appearance="icon"
aria-label="Go to MCP server settings"
onClick={() => navigateToMcp("installed")}>
<span className="codicon codicon-gear text-[10px]"></span>
</VSCodeButton>
</div>
)}
</div>
<div style={{ marginBottom: -10 }}>
<ServersToggleList hasTrashIcon={false} isExpandable={false} listGap="small" servers={mcpServers} />
</div>
</PopoverContent>
</Popover>
)
}
@@ -58,18 +58,18 @@ const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({
return (
<>
<div className="text-xs text-[var(--vscode-descriptionForeground)] px-3 py-1 font-bold border-b border-[var(--vscode-editorGroup-border)]">
<div className="text-xs text-(--vscode-descriptionForeground) px-3 py-1 font-bold border-b border-(--vscode-editorGroup-border)">
{title}
</div>
{commands.map((command, index) => {
const itemIndex = index + indexOffset
return (
<div
className={`slash-command-menu-item py-2 px-3 cursor-pointer flex flex-col border-b border-[var(--vscode-editorGroup-border)] ${
className={`slash-command-menu-item py-2 px-3 cursor-pointer flex flex-col border-b border-(--vscode-editorGroup-border) ${
itemIndex === selectedIndex
? "bg-[var(--vscode-quickInputList-focusBackground)] text-[var(--vscode-quickInputList-focusForeground)]"
? "bg-(--vscode-quickInputList-focusBackground) text-(--vscode-quickInputList-focusForeground)"
: ""
} hover:bg-[var(--vscode-list-hoverBackground)]`}
} hover:bg-(--vscode-list-hoverBackground)`}
id={`slash-command-menu-item-${itemIndex}`}
key={command.name}
onClick={() => handleClick(command)}
@@ -78,7 +78,7 @@ const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({
<span className="ph-no-capture">/{command.name}</span>
</div>
{showDescriptions && command.description && (
<div className="text-[0.85em] text-[var(--vscode-descriptionForeground)] whitespace-normal overflow-hidden text-ellipsis">
<div className="text-[0.85em] text-(--vscode-descriptionForeground) whitespace-normal overflow-hidden text-ellipsis">
<span className="ph-no-capture">{command.description}</span>
</div>
)}
@@ -91,10 +91,10 @@ const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({
return (
<div
className="absolute bottom-[calc(100%-10px)] left-[15px] right-[15px] overflow-x-hidden z-[1000]"
className="absolute bottom-[calc(100%-10px)] left-[15px] right-[15px] overflow-x-hidden z-1000"
onMouseDown={onMouseDown}>
<div
className="bg-[var(--vscode-dropdown-background)] border border-[var(--vscode-editorGroup-border)] rounded-[3px] shadow-[0_4px_10px_rgba(0,0,0,0.25)] flex flex-col overflow-y-auto"
className="bg-(--vscode-dropdown-background) border border-(--vscode-editorGroup-border) rounded-[3px] shadow-[0_4px_10px_rgba(0,0,0,0.25)] flex flex-col overflow-y-auto"
ref={menuRef}
style={{ maxHeight: "min(200px, calc(50vh))", overscrollBehavior: "contain" }}>
{filteredCommands.length > 0 ? (
@@ -104,7 +104,7 @@ const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({
</>
) : (
<div className="py-2 px-3 cursor-default flex flex-col">
<div className="text-[0.85em] text-[var(--vscode-descriptionForeground)]">No matching commands found</div>
<div className="text-[0.85em] text-(--vscode-descriptionForeground)">No matching commands found</div>
</div>
)}
</div>
@@ -1,10 +1,12 @@
import { cn } from "@heroui/react"
import { TooltipTrigger } from "@radix-ui/react-tooltip"
import { TranscribeAudioRequest } from "@shared/proto/cline/dictation"
import { EmptyRequest } from "@shared/proto/index.cline"
import { SquareIcon, StopCircleIcon } from "lucide-react"
import React, { useCallback, useEffect, useRef, useState } from "react"
import { Tooltip, TooltipContent } from "@/components/ui/tooltip"
import { DictationServiceClient } from "@/services/grpc-client"
import { formatSeconds } from "@/utils/format"
import HeroTooltip from "../common/HeroTooltip"
interface VoiceRecorderProps {
onTranscription: (text: string) => void
@@ -240,40 +242,50 @@ const VoiceRecorder: React.FC<VoiceRecorderProps> = ({
? "Starting recording..."
: error
? `Error: ${error}`
: null
: "Start recording"
return (
<HeroTooltip content={tooltipContent} placement="top">
<div
className={`input-icon-button mr-1.5 text-base ${iconAdjustment} ${iconAnimation} ${disabled || isProcessing || isStarting ? "disabled" : ""}`}
onClick={handleStartClick}
style={{ color: iconColor }}>
<span className={`codicon ${iconClass}`} />
</div>
</HeroTooltip>
<Tooltip>
<TooltipTrigger asChild>
<div
className={`input-icon-button mr-1.5 text-base ${iconAdjustment} ${iconAnimation} ${disabled || isProcessing || isStarting ? "disabled" : ""}`}
onClick={handleStartClick}
style={{ color: iconColor }}>
<span className={`codicon ${iconClass}`} />
</div>
</TooltipTrigger>
<TooltipContent side="top">{tooltipContent}</TooltipContent>
</Tooltip>
)
}
return (
<div className={`flex items-center ${isRecording ? "mr-0.5" : "mr-1.5"}`}>
<HeroTooltip
content={`Stop Recording (${formatSeconds(recordingDuration)}/${formatSeconds(MAX_DURATION)})`}
placement="top">
<div
className={cn("input-icon-button mr-1.5 text-base", iconAdjustment, iconAnimation, {
disabled: disabled || isProcessing,
})}
onClick={handleStopClick}>
<span className="codicon codicon-stop-circle" />
</div>
</HeroTooltip>
<HeroTooltip content="Cancel Recording" placement="top">
<div
className={`input-icon-button text-base mt-1 text-foreground ${disabled || isProcessing ? "disabled" : ""}`}
onClick={handleCancelClick}>
<span className="codicon codicon-close" />
</div>
</HeroTooltip>
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn("input-icon-button mr-1.5 text-base", iconAdjustment, iconAnimation, {
disabled: disabled || isProcessing,
})}
onClick={handleStopClick}>
<StopCircleIcon />
</div>
</TooltipTrigger>
<TooltipContent side="top">
Stop Recording ({formatSeconds(recordingDuration)}/{formatSeconds(MAX_DURATION)})
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<div
className={`input-icon-button text-base mt-1 text-foreground ${disabled || isProcessing ? "disabled" : ""}`}
onClick={handleCancelClick}>
<SquareIcon />
</div>
</TooltipTrigger>
<TooltipContent side="top">"Cancel Recording</TooltipContent>
</Tooltip>
</div>
)
}
@@ -58,12 +58,12 @@ const AutoApproveBar = ({ style }: AutoApproveBarProps) => {
return [
...favorites.map((favId) => renderFavoritedItem(favId)),
minusFavorites.length > 0 ? (
<span className="text-[color:var(--vscode-foreground-muted)] pl-[10px] opacity-60" key="separator">
<span className="text-(--vscode-foreground-muted) pl-[10px] opacity-60" key="separator">
</span>
) : null,
...minusFavorites.map((action, index) => (
<span className="text-[color:var(--vscode-foreground-muted)] opacity-60" key={action?.id}>
<span className="text-(--vscode-foreground-muted) opacity-60" key={action?.id}>
{action?.shortName}
{index < minusFavorites.length - 1 && ","}
</span>
@@ -1,7 +1,8 @@
import { TooltipTrigger } from "@radix-ui/react-tooltip"
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import React from "react"
import styled from "styled-components"
import HeroTooltip from "@/components/common/HeroTooltip"
import { Tooltip, TooltipContent } from "@/components/ui/tooltip"
import { ActionMetadata } from "./types"
interface AutoApproveMenuItemProps {
@@ -89,34 +90,41 @@ const AutoApproveMenuItem = ({
const content = (
<>
<ActionButtonContainer>
<HeroTooltip content={action.description} delay={500}>
<CheckboxContainer isFavorited={favorited} onClick={onChange}>
<div className="left-content">
{onToggleFavorite && !condensed && (
<HeroTooltip
content={favorited ? "Remove from quick-access menu" : "Add to quick-access menu"}
delay={500}>
<span
className={`p-0.5 codicon codicon-${favorited ? "star-full" : "star-empty"} star`}
onClick={async (e) => {
e.stopPropagation()
if (action.id === "enableAll") {
return
}
await onToggleFavorite?.(action.id)
}}
style={{
cursor: "pointer",
}}
/>
</HeroTooltip>
)}
<VSCodeCheckbox checked={checked} />
{showIcon && <span className={`codicon ${action.icon} icon`}></span>}
<span className="label">{condensed ? action.shortName : action.label}</span>
</div>
</CheckboxContainer>
</HeroTooltip>
<Tooltip>
{" "}
<TooltipContent>{action.description}</TooltipContent>
<TooltipTrigger>
<CheckboxContainer isFavorited={favorited} onClick={onChange}>
<div className="left-content">
{onToggleFavorite && !condensed && (
<Tooltip>
<TooltipContent>
{favorited ? "Remove from quick-access menu" : "Add to quick-access menu"}
</TooltipContent>
<TooltipTrigger>
<span
className={`p-0.5 codicon codicon-${favorited ? "star-full" : "star-empty"} star`}
onClick={async (e) => {
e.stopPropagation()
if (action.id === "enableAll") {
return
}
await onToggleFavorite?.(action.id)
}}
style={{
cursor: "pointer",
}}
/>
</TooltipTrigger>
</Tooltip>
)}
<VSCodeCheckbox checked={checked} />
{showIcon && <span className={`codicon ${action.icon} icon`}></span>}
<span className="label">{condensed ? action.shortName : action.label}</span>
</div>
</CheckboxContainer>
</TooltipTrigger>
</Tooltip>
</ActionButtonContainer>
{action.subAction && !condensed && (
<SubOptionAnimateIn show={checked}>
@@ -1,8 +1,9 @@
import { TooltipContent, TooltipTrigger } from "@radix-ui/react-tooltip"
import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import React, { useEffect, useRef, useState } from "react"
import { useClickAway, useWindowSize } from "react-use"
import { CODE_BLOCK_BG_COLOR } from "@/components/common/CodeBlock"
import HeroTooltip from "@/components/common/HeroTooltip"
import { Tooltip } from "@/components/ui/tooltip"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useAutoApproveActions } from "@/hooks/useAutoApproveActions"
import { getAsVar, VSC_TITLEBAR_INACTIVE_FOREGROUND } from "@/utils/vscStyles"
@@ -125,10 +126,10 @@ const AutoApproveModal: React.FC<AutoApproveModalProps> = ({
return (
<div className="overflow-hidden" ref={modalRef}>
<div
className="fixed left-[15px] right-[15px] border border-[var(--vscode-editorGroup-border)] rounded z-[1000] flex flex-col"
className="fixed left-[15px] right-[15px] border border-(--vscode-editorGroup-border) rounded z-1000 flex flex-col"
style={calculateModalStyle()}>
<div
className="fixed w-[10px] h-[10px] z-[-1] rotate-45 border-r border-b border-[var(--vscode-editorGroup-border)]"
className="fixed w-[10px] h-[10px] z-[-1] rotate-45 border-r border-b border-(--vscode-editorGroup-border)"
style={{
bottom: `calc(100vh - ${menuPosition}px)`,
right: arrowPosition,
@@ -138,18 +139,22 @@ const AutoApproveModal: React.FC<AutoApproveModalProps> = ({
{/* Scrollable content container */}
<div className="overflow-y-auto p-3 flex-1 min-h-0 overscroll-contain">
<div className="flex justify-between items-center mb-3">
<HeroTooltip
content="Auto-approve allows Cline to perform the following actions without asking for permission. Please use with caution and only enable if you understand the risks."
placement="top">
<div className="text-base font-semibold mb-1">Auto-approve Settings</div>
</HeroTooltip>
<Tooltip>
<TooltipContent side="top">
Auto-approve allows Cline to perform the following actions without asking for permission. Please
use with caution and only enable if you understand the risks.
</TooltipContent>
<TooltipTrigger>
<div className="text-base font-semibold mb-1">Auto-approve Settings</div>
</TooltipTrigger>
</Tooltip>
<VSCodeButton appearance="icon" onClick={() => setIsVisible(false)}>
<span className="codicon codicon-close text-[10px]"></span>
</VSCodeButton>
</div>
<div className="mb-2.5">
<span className="text-[color:var(--vscode-foreground)] font-medium">Actions:</span>
<span className="text-(--vscode-foreground) font-medium">Actions:</span>
</div>
<div
@@ -184,7 +189,7 @@ const AutoApproveModal: React.FC<AutoApproveModalProps> = ({
</div>
<div className="mb-2.5">
<span className="text-[color:var(--vscode-foreground)] font-medium">Quick Settings:</span>
<span className="text-(--vscode-foreground) font-medium">Quick Settings:</span>
</div>
<AutoApproveMenuItem
@@ -196,36 +201,40 @@ const AutoApproveModal: React.FC<AutoApproveModalProps> = ({
onToggleFavorite={toggleFavorite}
/>
<HeroTooltip
content="Cline will automatically make this many API requests before asking for approval to proceed with the task."
placement="top">
<div className="flex items-center pl-1.5 my-2">
<span className="codicon codicon-settings text-[#CCCCCC] text-[14px]" />
<span className="text-[#CCCCCC] text-xs font-medium ml-2">Max Requests:</span>
<VSCodeTextField
className="flex-1 w-full pr-[35px] ml-4"
onInput={async (e) => {
const input = e.target as HTMLInputElement
// Remove any non-numeric characters
input.value = input.value.replace(/[^0-9]/g, "")
const value = parseInt(input.value)
if (!Number.isNaN(value) && value > 0) {
await updateMaxRequests(value)
}
}}
onKeyDown={(e) => {
// Prevent non-numeric keys (except for backspace, delete, arrows)
if (
!/^\d$/.test(e.key) &&
!["Backspace", "Delete", "ArrowLeft", "ArrowRight"].includes(e.key)
) {
e.preventDefault()
}
}}
value={autoApprovalSettings.maxRequests.toString()}
/>
</div>
</HeroTooltip>
<Tooltip>
<TooltipContent side="top">
Cline will automatically make this many API requests before asking for approval to proceed with the
task.
</TooltipContent>
<TooltipTrigger>
<div className="flex items-center pl-1.5 my-2">
<span className="codicon codicon-settings text-[#CCCCCC] text-[14px]" />
<span className="text-[#CCCCCC] text-xs font-medium ml-2">Max Requests:</span>
<VSCodeTextField
className="flex-1 w-full pr-[35px] ml-4"
onInput={async (e) => {
const input = e.target as HTMLInputElement
// Remove any non-numeric characters
input.value = input.value.replace(/[^0-9]/g, "")
const value = parseInt(input.value)
if (!Number.isNaN(value) && value > 0) {
await updateMaxRequests(value)
}
}}
onKeyDown={(e) => {
// Prevent non-numeric keys (except for backspace, delete, arrows)
if (
!/^\d$/.test(e.key) &&
!["Backspace", "Delete", "ArrowLeft", "ArrowRight"].includes(e.key)
) {
e.preventDefault()
}
}}
value={autoApprovalSettings.maxRequests.toString()}
/>
</div>
</TooltipTrigger>
</Tooltip>
</div>
</div>
</div>
@@ -106,7 +106,7 @@ export const ActionButtons: React.FC<ActionButtonsProps> = ({
<VSCodeButton
appearance="icon"
aria-label="Scroll to bottom"
className="text-lg text-[var(--vscode-primaryButton-foreground)] 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)] border-0"
className="text-lg text-(--vscode-primaryButton-foreground) 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)] border-0"
onClick={handleScrollToBottom}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
@@ -136,7 +136,7 @@ export const ActionButtons: React.FC<ActionButtonsProps> = ({
{primaryText && primaryAction && (
<VSCodeButton
appearance="primary"
className={secondaryText ? "flex-1 mr-[6px]" : "flex-[2]"}
className={secondaryText ? "flex-1 mr-[6px]" : "flex-2"}
disabled={!canInteract}
onClick={() => handleActionClick(primaryAction, inputValue, selectedImages, selectedFiles)}>
{primaryText}
@@ -145,7 +145,7 @@ export const ActionButtons: React.FC<ActionButtonsProps> = ({
{secondaryText && secondaryAction && (
<VSCodeButton
appearance="secondary"
className={primaryText ? "flex-1 mr-[6px]" : "flex-[2]"}
className={primaryText ? "flex-1 mr-[6px]" : "flex-2"}
disabled={!canInteract}
onClick={() => handleActionClick(secondaryAction, inputValue, selectedImages, selectedFiles)}>
{secondaryText}
@@ -62,7 +62,7 @@ export const MessagesArea: React.FC<MessagesAreaProps> = ({
return (
<div className="overflow-hidden flex flex-col h-full">
<div className="flex-grow flex" ref={scrollContainerRef}>
<div className="grow flex" ref={scrollContainerRef}>
<Virtuoso
atBottomStateChange={(isAtBottom) => {
setIsAtBottom(isAtBottom)
@@ -1,7 +1,7 @@
import { Alert } from "@heroui/react"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { XIcon } from "lucide-react"
import { AlertCircleIcon, XIcon } from "lucide-react"
import { useMemo, useState } from "react"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { Button } from "@/components/ui/button"
interface CheckpointErrorProps {
checkpointManagerErrorMessage?: string
@@ -20,15 +20,17 @@ export const CheckpointError: React.FC<CheckpointErrorProps> = ({
return { message, showDisableButton, showGitInstructions }
}, [checkpointManagerErrorMessage])
if (!checkpointManagerErrorMessage || dismissed) {
if (dismissed || !messages?.message) {
return null
}
return (
<div className="flex items-center justify-center w-full opacity-80 hover:opacity-100 transition-opacity duration-200">
<Alert
className="rounded-sm border-0 bg-[var(--vscode-inputValidation-errorBackground)] text-[var(--vscode-inputValidation-errorForeground)] p-1.5"
color="warning"
description={
<Alert
className="relative w-full h-fit flex items-center opacity-80 hover:opacity-100 transition-opacity duration-200 justify-between"
variant="error">
<div className="flex items-center">
<AlertCircleIcon size={12} />
<AlertTitle className="flex text-xs items-end">{messages.message}</AlertTitle>
<AlertDescription className="text-xs">
<div className="flex gap-2">
{messages.showDisableButton && (
<button
@@ -45,22 +47,11 @@ export const CheckpointError: React.FC<CheckpointErrorProps> = ({
</a>
)}
</div>
}
endContent={
<VSCodeButton
appearance="icon"
aria-label="Dismiss"
className="inline-flex opacity-100 hover:bg-transparent hover:opacity-60 p-0"
onClick={() => setDismissed(true)}
title="Dismiss Checkpoint Error">
<XIcon size={12} />
</VSCodeButton>
}
hideIconWrapper={true}
isVisible={!dismissed}
title={messages.message}
variant="faded"
/>
</div>
</AlertDescription>
</div>
<Button aria-label="Dismiss" onClick={() => setDismissed(true)} title="Dismiss Checkpoint Error">
<XIcon size={10} />
</Button>
</Alert>
)
}
@@ -1,8 +1,9 @@
import { cn, Progress, Tooltip } from "@heroui/react"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import debounce from "debounce"
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
import { updateSetting } from "@/components/settings/utils/settingsHandlers"
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"
import { Progress } from "@/components/ui/progress"
import { formatLargeNumber as formatTokenNumber } from "@/utils/format"
import { AutoCondenseMarker } from "./AutoCondenseMarker"
import CompactTaskButton from "./buttons/CompactTaskButton"
@@ -210,9 +211,8 @@ const ContextWindow: React.FC<ContextWindowProgressProps> = ({
{tokenData.used}
</span>
<div className="flex relative items-center gap-1 flex-1 w-full h-full" onMouseEnter={() => setIsOpened(true)}>
<Tooltip
closeDelay={0}
content={
<HoverCard>
<HoverCardContent className="bg-menu rounded-xs shadow-sm border border-menu-border">
<ContextWindowSummary
autoCompactThreshold={useAutoCondense ? threshold : undefined}
cacheReads={cacheReads}
@@ -223,52 +223,38 @@ const ContextWindow: React.FC<ContextWindowProgressProps> = ({
tokensOut={tokensOut}
tokenUsed={tokenData.used}
/>
}
disableAnimation={true}
isOpen={isOpened}
offset={-2}
placement="bottom"
shouldCloseOnBlur={false}
shouldCloseOnInteractOutside={() => false}
showArrow={true}>
<div
aria-label="Auto condense threshold"
aria-valuemax={100}
aria-valuemin={0}
aria-valuenow={Math.round(threshold * 100)}
aria-valuetext={`${Math.round(threshold * 100)}% threshold`}
className="relative w-full text-badge-foreground context-window-progress brightness-100"
onFocus={handleFocus}
onKeyDown={handleKeyDown}
ref={progressBarRef}
role="slider"
tabIndex={useAutoCondense ? 0 : -1}>
<Progress
aria-label="Context window usage progress"
classNames={{
base: "drop-shadow-md w-full cursor-pointer",
track: cn("rounded max-h-2 h-3 bg-foreground/10"),
indicator: "bg-foreground rounded-r",
label: "tracking-wider font-medium text-foreground/80",
value: "text-description",
}}
color="success"
disableAnimation={true}
onClick={handleContextWindowBarClick}
size="md"
value={tokenData.percentage}
/>
{useAutoCondense && (
<AutoCondenseMarker
isContextWindowHoverOpen={isOpened}
shouldAnimate={shouldAnimateMarker}
threshold={threshold}
usage={tokenData.percentage}
</HoverCardContent>
<HoverCardTrigger asChild>
<div
aria-label="Auto condense threshold"
aria-valuemax={100}
aria-valuemin={0}
aria-valuenow={Math.round(threshold * 100)}
aria-valuetext={`${Math.round(threshold * 100)}% threshold`}
className="relative w-full text-badge-foreground context-window-progress brightness-100"
onFocus={handleFocus}
onKeyDown={handleKeyDown}
ref={progressBarRef}
role="slider"
tabIndex={useAutoCondense ? 0 : -1}>
<Progress
aria-label="Context window usage progress"
color="success"
onClick={handleContextWindowBarClick}
value={tokenData.percentage}
/>
)}
{isOpened}
</div>
</Tooltip>
{useAutoCondense && (
<AutoCondenseMarker
isContextWindowHoverOpen={isOpened}
shouldAnimate={shouldAnimateMarker}
threshold={threshold}
usage={tokenData.percentage}
/>
)}
{isOpened}
</div>
</HoverCardTrigger>
</HoverCard>
</div>
<span className="cursor-pointer" title="Maximum context window size for this model">
{tokenData.max}
@@ -1,5 +1,4 @@
import { ChevronDownIcon, ChevronRightIcon } from "lucide-react"
import React, { memo, useCallback, useMemo, useState } from "react"
import React, { memo, useMemo } from "react"
import { formatLargeNumber as formatTokenNumber } from "@/utils/format"
interface TokenUsageInfoProps {
@@ -28,31 +27,17 @@ interface TaskContextWindowButtonsProps extends TokenUsageInfoProps {
const AccordionItem = memo<{
title: string
value: React.ReactNode
isExpanded: boolean
onToggle: (event?: React.MouseEvent) => void
children?: React.ReactNode
}>(({ title, value, isExpanded, onToggle, children }) => {
const handleClick = useCallback(
(event: React.MouseEvent) => {
event.preventDefault()
event.stopPropagation()
onToggle(event)
},
[onToggle],
)
}>(({ title, value, children }) => {
return (
<div className="flex flex-col">
<div
className="flex justify-between items-center gap-3 cursor-pointer hover:bg-foreground/5 rounded px-1 py-0.5 transition-colors"
onClick={handleClick}>
<div className="flex flex-col text-xs">
<div className="flex justify-between items-center gap-3 rounded px-1 transition-colors text-foreground">
<div className="flex items-center gap-1">
{isExpanded ? <ChevronDownIcon size={12} /> : <ChevronRightIcon size={12} />}
<div className="font-semibold text-sm">{title}</div>
<div className="font-semibold text-xs">{title}</div>
</div>
<div className="text-muted-foreground text-sm">{value}</div>
<div className="text-muted-foreground text-xs">{value}</div>
</div>
{isExpanded && children && <div className="ml-4 mt-2 mb-1 text-xs text-muted-foreground">{children}</div>}
{children && <div className="mt-2 mb-1 text-xs text-muted-foreground px-1.5">{children}</div>}
</div>
)
})
@@ -60,10 +45,10 @@ AccordionItem.displayName = "AccordionItem"
// Constants
const TOKEN_DETAILS_CONFIG: Omit<TokenDetail, "value">[] = [
{ title: "Prompt Tokens", icon: "codicon-arrow-up" },
{ title: "Completion Tokens", icon: "codicon-arrow-down" },
{ title: "Cache Writes", icon: "codicon-arrow-left" },
{ title: "Cache Reads", icon: "codicon-arrow-right" },
{ title: "Prompt:", icon: "codicon-arrow-up" },
{ title: "Completion:", icon: "codicon-arrow-down" },
{ title: "Cache Writes:", icon: "codicon-arrow-left" },
{ title: "Cache Reads:", icon: "codicon-arrow-right" },
]
const TokenUsageDetails = memo<TokenUsageInfoProps>(({ tokensIn, tokensOut, cacheWrites, cacheReads }) => {
@@ -77,13 +62,10 @@ const TokenUsageDetails = memo<TokenUsageInfoProps>(({ tokensIn, tokensOut, cach
}
return (
<div className="space-y-2">
<div className="space-y-1">
{contextTokenDetails.map((item) => (
<div className="flex items-center justify-between" key={item.icon}>
<div className="flex items-center gap-1">
<i className={`codicon ${item.icon} text-xs`} />
<span>{item.title}</span>
</div>
<div className="flex justify-between">
<span>{item.title}</span>
<span className="font-mono">{formatTokenNumber(item.value || 0)}</span>
</div>
))}
@@ -102,33 +84,12 @@ export const ContextWindowSummary: React.FC<TaskContextWindowButtonsProps> = ({
percentage,
autoCompactThreshold = 0,
}) => {
// Accordion state
const [expandedSections, setExpandedSections] = useState<Set<string>>(new Set())
const toggleSection = useCallback((section: string, event?: React.MouseEvent) => {
if (event) {
event.preventDefault()
event.stopPropagation()
}
setExpandedSections((prev) => {
const newSet = new Set(prev)
if (newSet.has(section)) {
newSet.delete(section)
} else {
newSet.add(section)
}
return newSet
})
}, [])
const totalTokens = (tokensIn || 0) + (tokensOut || 0)
return (
<div className="context-window-tooltip-content flex flex-col gap-2 bg-menu rounded shadow-sm border border-menu-border z-100 w-60 p-4">
<div className="context-window-tooltip-content flex flex-col gap-2 p-1">
{autoCompactThreshold > 0 && (
<AccordionItem
isExpanded={expandedSections.has("threshold")}
onToggle={(event) => toggleSection("threshold", event)}
title="Auto Condense Threshold"
value={<span className="text-muted-foreground">{`${(autoCompactThreshold * 100).toFixed(0)}%`}</span>}>
<div className="space-y-1">
@@ -142,11 +103,7 @@ export const ContextWindowSummary: React.FC<TaskContextWindowButtonsProps> = ({
</AccordionItem>
)}
<AccordionItem
isExpanded={expandedSections.has("context")}
onToggle={(event) => toggleSection("context", event)}
title="Context Window"
value={percentage ? `${percentage.toFixed(1)}% used` : contextWindow}>
<AccordionItem title="Context Window" value={percentage ? `${percentage.toFixed(1)}% used` : contextWindow}>
<div className="space-y-1">
<div className="flex justify-between">
<span>Used:</span>
@@ -166,11 +123,7 @@ export const ContextWindowSummary: React.FC<TaskContextWindowButtonsProps> = ({
</AccordionItem>
{totalTokens > 0 && (
<AccordionItem
isExpanded={expandedSections.has("tokens")}
onToggle={(event) => toggleSection("tokens", event)}
title="Token Usage"
value={`${formatTokenNumber(totalTokens)} total`}>
<AccordionItem title="Token Usage" value={`${formatTokenNumber(totalTokens)} total`}>
<TokenUsageDetails
cacheReads={cacheReads}
cacheWrites={cacheWrites}
@@ -54,7 +54,6 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
checkpointManagerErrorMessage,
clineMessages,
navigateToSettings,
useAutoCondense,
mode,
expandTaskHeader: isTaskExpanded,
setExpandTaskHeader: setIsTaskExpanded,
@@ -97,19 +96,18 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
{/* Task Header */}
<div
className={cn(
"relative overflow-hidden cursor-pointer text-badge-foreground rounded-sm flex flex-col gap-1.5 z-10 pt-2 pb-2 px-2 hover:opacity-100 bg-[var(--vscode-toolbar-hoverBackground)]/65",
"relative overflow-hidden cursor-pointer text-badge-foreground rounded-sm flex flex-col gap-1 z-10 pt-2 pb-2 px-2 hover:opacity-100 bg-(--vscode-toolbar-hoverBackground)/65",
{
"opacity-100 border-1 border-muted-foreground": isTaskExpanded, // No hover effects when expanded, add border
"opacity-80 transition-opacity duration-200 hover:bg-[var(--vscode-toolbar-hoverBackground)]":
!isTaskExpanded, // Hover effects only when collapsed
"opacity-100 border border-muted-foreground": isTaskExpanded, // No hover effects when expanded, add border
"opacity-80 transition-opacity duration-200 hover:bg-(--vscode-toolbar-hoverBackground)": !isTaskExpanded, // Hover effects only when collapsed
},
)}>
{/* Task Title */}
<div className="flex justify-between items-center cursor-pointer" onClick={toggleTaskExpanded}>
<div className="flex justify-between items-center">
<div className="flex justify-between items-center gap-1">
{isTaskExpanded ? <ChevronDownIcon size="16" /> : <ChevronRightIcon size="16" />}
{isTaskExpanded && (
<div className="mt-1 max-h-3 flex justify-end flex-wrap cursor-pointer opacity-80">
<div className="ml-1 mt-1 max-h-3 flex justify-end flex-wrap cursor-pointer opacity-80 gap-2">
<CopyTaskButton className={BUTTON_CLASS} taskText={task.text} />
<DeleteTaskButton
className={BUTTON_CLASS}
@@ -117,18 +115,22 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
taskSize={currentTaskItem?.size}
/>
{/* Only visible in development mode */}
{IS_DEV && <OpenDiskTaskHistoryButton className={BUTTON_CLASS} taskId={currentTaskItem?.id} />}
<OpenDiskTaskHistoryButton
className={BUTTON_CLASS}
taskId={currentTaskItem?.id}
visible={IS_DEV}
/>
</div>
)}
</div>
<div className="flex items-center select-none flex-grow min-w-0 gap-1 justify-between">
<div className="flex items-center select-none grow min-w-0 gap-1 justify-between">
{!isTaskExpanded && (
<div className="text-sm whitespace-nowrap overflow-hidden text-ellipsis flex-grow min-w-0">
<div className="text-sm whitespace-nowrap overflow-hidden text-ellipsis grow min-w-0">
<span className="ph-no-capture">{highlightText(task.text, false)}</span>
</div>
)}
</div>
<div className="inline-flex items-center justify-end select-none flex-shrink-0">
<div className="inline-flex items-center justify-end select-none shrink-0">
{isCostAvailable && (
<div
className="mr-1 px-1 py-0.25 rounded-full inline-flex shrink-0 text-badge-background bg-badge-foreground/80 items-center"
@@ -143,7 +145,7 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
{/* Expand/Collapse Task Details */}
{isTaskExpanded && (
<div className="flex flex-col break-words" key={`task-details-${currentTaskItem?.id}`}>
<div className="whitespace-nowrap overflow-hidden text-ellipsis flex-grow min-w-0 max-h-20 overflow-y-auto scroll-smooth">
<div className="whitespace-nowrap overflow-hidden text-ellipsis grow min-w-0 max-h-20 overflow-y-auto scroll-smooth">
<div
className={
"ph-no-capture overflow-hidden whitespace-pre-wrap break-words px-0.5 text-sm cursor-pointer opacity-80 hover:opacity-100 transition-opacity duration-200 mt-1"
@@ -154,7 +154,7 @@ const TaskTimelineTooltip = ({ message, children }: TaskTimelineTooltipProps) =>
return (
<Tooltip
classNames={{
base: "bg-[var(--vscode-editor-background)] text-[var(--vscode-editor-foreground)] border-[var(--vscode-widget-border)] py-1 rounded-[3px] max-w-[calc(100dvw-2rem)] text-xs",
base: "bg-(--vscode-editor-background) text-(--vscode-editor-foreground) border-(--vscode-widget-border) py-1 rounded-[3px] max-w-[calc(100dvw-2rem)] text-xs",
}}
closeDelay={100}
content={
@@ -1,33 +1,28 @@
import { cn, Tooltip } from "@heroui/react"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { FoldVerticalIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
const CompactTaskButton: React.FC<{
className?: string
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
}> = ({ onClick, className }) => {
return (
<Tooltip
content={
<div className="flex flex-col gap-1.5 bg-menu rounded shadow-sm border border-menu-border z-100 max-w-xs p-4">
<Tooltip>
<TooltipContent>
<div className="flex flex-col gap-1.5 bg-menu rounded shadow-sm border border-menu-border z-100 max-w-xs ">
<div className="text-sm font-medium">Compact Task</div>
<div className="text-sm text-muted-foreground">
Reduces the number of tokens used by summarizing the task. To enable automatic condensing, turn on{" "}
<kbd>Auto Compact</kbd> in the settings and set the threshold by clicking on the context window usage bar.
</div>
</div>
}
placement="bottom">
<VSCodeButton
appearance="icon"
className={cn(
"text-badge-foreground flex items-center text-sm font-bold hover:bg-transparent hover:opacity-80",
className,
)}
onClick={onClick}
type="button">
<FoldVerticalIcon size={12} />
</VSCodeButton>
</TooltipContent>
<TooltipTrigger className={cn("flex items-center", className)}>
<Button aria-label="Delete Task" onClick={onClick} size="icon" variant="icon">
<FoldVerticalIcon strokeWidth={1.5} />
</Button>
</TooltipTrigger>
</Tooltip>
)
}
@@ -1,8 +1,8 @@
import { Button } from "@heroui/button"
import { cn } from "@heroui/react"
import { CheckIcon, CopyIcon } from "lucide-react"
import { useCallback, useState } from "react"
import HeroTooltip from "@/components/common/HeroTooltip"
import { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { cn } from "@/lib/utils"
const CopyTaskButton: React.FC<{
taskText?: string
@@ -22,17 +22,14 @@ const CopyTaskButton: React.FC<{
}, [taskText])
return (
<HeroTooltip content="Copy Text" placement="right">
<Button
aria-label="Copy"
className={cn("bg-transparent hover:opacity-100", className)}
isIconOnly={true}
onPress={() => handleCopy()}
radius="sm"
size="sm">
{copied ? <CheckIcon size="14" /> : <CopyIcon size="14" />}
</Button>
</HeroTooltip>
<Tooltip>
<TooltipContent side="bottom">Copy Text</TooltipContent>
<TooltipTrigger className={cn("flex items-center", className)}>
<Button aria-label="Copy" onClick={() => handleCopy()} size="icon" variant="icon">
{copied ? <CheckIcon strokeWidth={1.5} /> : <CopyIcon strokeWidth={1.5} />}
</Button>
</TooltipTrigger>
</Tooltip>
)
}
@@ -1,7 +1,8 @@
import { Button, cn } from "@heroui/react"
import { StringArrayRequest } from "@shared/proto/cline/common"
import { TrashIcon } from "lucide-react"
import HeroTooltip from "@/components/common/HeroTooltip"
import { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { cn } from "@/lib/utils"
import { TaskServiceClient } from "@/services/grpc-client"
import { formatSize } from "@/utils/format"
@@ -10,19 +11,19 @@ const DeleteTaskButton: React.FC<{
taskSize?: number
className?: string
}> = ({ taskId, className, taskSize }) => (
<HeroTooltip content={`Delete Task (size: ${taskSize ? formatSize(taskSize) : "--"})`} placement="right">
<Button
aria-label="Delete Task"
className={cn("flex items-center border-0 text-sm font-bold bg-transparent hover:opacity-100 p-0", className)}
isIconOnly={true}
onPress={() => {
taskId && TaskServiceClient.deleteTasksWithIds(StringArrayRequest.create({ value: [taskId] }))
}}
radius="sm"
size="sm">
<TrashIcon size="14" />
</Button>
</HeroTooltip>
<Tooltip>
<TooltipContent>{`Delete Task (size: ${taskSize ? formatSize(taskSize) : "--"})`}</TooltipContent>
<TooltipTrigger className={cn("flex items-center", className)}>
<Button
aria-label="Delete Task"
disabled={!taskId}
onClick={() => taskId && TaskServiceClient.deleteTasksWithIds(StringArrayRequest.create({ value: [taskId] }))}
size="xs"
variant="icon">
<TrashIcon strokeWidth={1.5} />
</Button>
</TooltipTrigger>
</Tooltip>
)
DeleteTaskButton.displayName = "DeleteTaskButton"
@@ -1,29 +1,29 @@
import { cn } from "@heroui/react"
import { XIcon } from "lucide-react"
import HeroTooltip from "@/components/common/HeroTooltip"
import { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { cn } from "@/lib/utils"
const NewTaskButton: React.FC<{
onClick: () => void
className?: string
}> = ({ className, onClick }) => {
return (
<HeroTooltip content="Close current task to start new one" delay={1000} placement="bottom">
<button
aria-label="Start a New Task"
className={cn(
"flex items-center border-0 text-sm font-bold bg-transparent opacity-70 hover:opacity-100",
className,
"hover:bg-muted/10 px-0 cursor-pointer",
)}
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
onClick()
}}
type="button">
<XIcon size="14" />
</button>
</HeroTooltip>
<Tooltip>
<TooltipContent side="bottom">Start a New Task</TooltipContent>
<TooltipTrigger className={cn("flex items-center", className)}>
<Button
aria-label="Start a New Task"
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
onClick()
}}
size="icon"
variant="icon">
<XIcon strokeWidth={1.5} />
</Button>
</TooltipTrigger>
</Tooltip>
)
}
@@ -1,33 +1,38 @@
import { Button, cn } from "@heroui/react"
import { StringRequest } from "@shared/proto/cline/common"
import { ArrowDownToLineIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { cn } from "@/lib/utils"
import { FileServiceClient } from "@/services/grpc-client"
const OpenDiskTaskHistoryButton: React.FC<{
taskId?: string
visible?: boolean
className?: string
}> = ({ taskId, className }) => {
const handleOpenDiskTaskHistory = () => {
if (!taskId) {
return
}
FileServiceClient.openTaskHistory(StringRequest.create({ value: taskId })).catch((err) => {
console.error(err)
})
}> = ({ taskId, className, visible }) => {
if (!visible) {
return null
}
return (
<Button
aria-label="Open Disk Task History"
className={cn("flex items-center border-0 text-sm font-bold bg-transparent hover:opacity-100", className)}
isIconOnly={true}
onPress={() => handleOpenDiskTaskHistory()}
radius="sm"
size="sm"
title="Export Task">
<ArrowDownToLineIcon size="14" />
</Button>
<Tooltip>
<TooltipContent>Export Task</TooltipContent>
<TooltipTrigger className={cn("flex items-center", className)}>
<Button
aria-label="Delete Task"
onClick={() =>
taskId &&
FileServiceClient.openTaskHistory(StringRequest.create({ value: taskId })).catch((err) => {
console.error(err)
})
}
size="icon"
title="Export Task"
variant="icon">
<ArrowDownToLineIcon strokeWidth={1.5} />
</Button>
</TooltipTrigger>
</Tooltip>
)
}
@@ -12,7 +12,7 @@ import React, { useEffect, useRef, useState } from "react"
import { useClickAway, useWindowSize } from "react-use"
import styled from "styled-components"
import { CODE_BLOCK_BG_COLOR } from "@/components/common/CodeBlock"
import Tooltip from "@/components/common/Tooltip"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { FileServiceClient } from "@/services/grpc-client"
import RulesToggleList from "./RulesToggleList"
@@ -196,25 +196,28 @@ const ClineRulesToggleModal: React.FC = () => {
return (
<div ref={modalRef}>
<div className="inline-flex min-w-0 max-w-full" ref={buttonRef}>
<Tooltip tipText="Manage Cline Rules & Workflows" visible={isVisible ? false : undefined}>
<VSCodeButton
appearance="icon"
aria-label={isVisible ? "Hide Cline Rules & Workflows" : "Show Cline Rules & Workflows"}
onClick={() => setIsVisible(!isVisible)}
style={{ padding: "0px 0px", height: "20px" }}>
<div className="flex items-center gap-1 text-xs whitespace-nowrap min-w-0 w-full">
<span
className="codicon codicon-law flex items-center"
style={{ fontSize: "12.5px", marginBottom: 1 }}
/>
</div>
</VSCodeButton>
<Tooltip>
{!isVisible && <TooltipContent>Manage Cline Rules & Workflows</TooltipContent>}
<TooltipTrigger>
<VSCodeButton
appearance="icon"
aria-label={isVisible ? "Hide Cline Rules & Workflows" : "Show Cline Rules & Workflows"}
onClick={() => setIsVisible(!isVisible)}
style={{ padding: "0px 0px", height: "20px" }}>
<div className="flex items-center gap-1 text-xs whitespace-nowrap min-w-0 w-full">
<span
className="codicon codicon-law flex items-center"
style={{ fontSize: "12.5px", marginBottom: 1 }}
/>
</div>
</VSCodeButton>
</TooltipTrigger>
</Tooltip>
</div>
{isVisible && (
<div
className="fixed left-[15px] right-[15px] border border-[var(--vscode-editorGroup-border)] p-3 rounded z-[1000] overflow-y-auto"
className="fixed left-[15px] right-[15px] border border-(--vscode-editorGroup-border) p-3 rounded z-1000 overflow-y-auto"
style={{
bottom: `calc(100vh - ${menuPosition}px + 6px)`,
background: CODE_BLOCK_BG_COLOR,
@@ -222,7 +225,7 @@ const ClineRulesToggleModal: React.FC = () => {
overscrollBehavior: "contain",
}}>
<div
className="fixed w-[10px] h-[10px] z-[-1] rotate-45 border-r border-b border-[var(--vscode-editorGroup-border)]"
className="fixed w-[10px] h-[10px] z-[-1] rotate-45 border-r border-b border-(--vscode-editorGroup-border)"
style={{
bottom: `calc(100vh - ${menuPosition}px)`,
right: arrowPosition,
@@ -253,7 +256,7 @@ const ClineRulesToggleModal: React.FC = () => {
</div>
{/* Description text */}
<div className="text-xs text-[var(--vscode-descriptionForeground)] mb-4">
<div className="text-xs text-(--vscode-descriptionForeground) mb-4">
{currentView === "rules" ? (
<p>
Rules allow you to provide Cline with system-level guidance. Think of them as a persistent way to
@@ -271,7 +274,7 @@ const ClineRulesToggleModal: React.FC = () => {
such as deploying a service or submitting a PR. To invoke a workflow, type{" "}
<span
className="
text-[var(--vscode-foreground)] font-bold">
text-(--vscode-foreground) font-bold">
/workflow-name
</span>{" "}
in the chat.{" "}
@@ -92,13 +92,13 @@ const NewRuleRow: React.FC<NewRuleRowProps> = ({ isGlobal, ruleType }) => {
onClick={() => !isExpanded && setIsExpanded(true)}
ref={componentRef}>
<div
className={`flex items-center p-2 rounded bg-[var(--vscode-input-background)] transition-all duration-300 ease-in-out h-[18px] ${
className={`flex items-center px-2 py-3 rounded bg-(--vscode-input-background) transition-all duration-300 ease-in-out h-[18px] ${
isExpanded ? "shadow-sm" : ""
}`}>
{isExpanded ? (
<form className="flex flex-1 items-center" onSubmit={handleSubmit}>
<input
className="flex-1 bg-[var(--vscode-input-background)] text-[var(--vscode-input-foreground)] border-0 outline-0 rounded focus:outline-none focus:ring-0 focus:border-transparent"
className="flex-1 bg-(--vscode-input-background) text-(--vscode-input-foreground) border-0 outline-0 rounded focus:outline-none focus:ring-0 focus:border-transparent"
onChange={(e) => setFilename(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={
@@ -127,7 +127,7 @@ const NewRuleRow: React.FC<NewRuleRowProps> = ({ isGlobal, ruleType }) => {
</form>
) : (
<>
<span className="flex-1 text-[var(--vscode-descriptionForeground)] bg-[var(--vscode-input-background)] italic text-xs">
<span className="flex-1 text-(--vscode-descriptionForeground) bg-(--vscode-input-background) italic text-xs">
{ruleType === "workflow" ? "New workflow file..." : "New rule file..."}
</span>
<div className="flex items-center ml-2 space-x-2">
@@ -146,7 +146,7 @@ const NewRuleRow: React.FC<NewRuleRowProps> = ({ isGlobal, ruleType }) => {
</>
)}
</div>
{isExpanded && error && <div className="text-[var(--vscode-errorForeground)] text-xs mt-1 ml-2">{error}</div>}
{isExpanded && error && <div className="text-(--vscode-errorForeground) text-xs mt-1 ml-2">{error}</div>}
</div>
)
}
@@ -1,6 +1,7 @@
import { StringRequest } from "@shared/proto/cline/common"
import { RuleFileRequest } from "@shared/proto/index.cline"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { Switch } from "@/components/ui/switch"
import { FileServiceClient } from "@/services/grpc-client"
const RuleRow: React.FC<{
@@ -75,7 +76,7 @@ const RuleRow: React.FC<{
return (
<div className="mb-2.5">
<div
className={`flex items-center p-2 rounded bg-[var(--vscode-textCodeBlock-background)] h-[18px] ${
className={`flex items-center px-2 py-3 rounded-xs bg-(--vscode-textCodeBlock-background) h-4 ${
enabled ? "opacity-100" : "opacity-60"
}`}>
<span className="flex-1 overflow-hidden break-all whitespace-normal flex items-center mr-1" title={rulePath}>
@@ -85,28 +86,7 @@ const RuleRow: React.FC<{
{/* Toggle Switch */}
<div className="flex items-center ml-2 space-x-2">
<div
aria-checked={enabled}
className={`w-[20px] h-[10px] rounded-[5px] relative cursor-pointer transition-colors duration-200 ${
enabled
? "bg-[var(--vscode-testing-iconPassed)] opacity-90"
: "bg-[var(--vscode-titleBar-inactiveForeground)] opacity-50"
}`}
onClick={() => toggleRule(rulePath, !enabled)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault()
toggleRule(rulePath, !enabled)
}
}}
role="switch"
tabIndex={0}>
<div
className={`w-[6px] h-[6px] bg-white border border-[#66666699] rounded-full absolute top-[1px] transition-all duration-200 ${
enabled ? "left-[12px]" : "left-[2px]"
}`}
/>
</div>
<Switch aria-readonly checked={enabled} onCheckedChange={() => toggleRule(rulePath, !enabled)} />
<VSCodeButton
appearance="icon"
aria-label="Edit rule file"
@@ -1,3 +1,4 @@
import { cn } from "@/lib/utils"
import NewRuleRow from "./NewRuleRow"
import RuleRow from "./RuleRow"
@@ -27,9 +28,12 @@ const RulesToggleList = ({
const gapClass = gapClasses[listGap]
return (
<div className={`flex flex-col ${gapClass}`}>
<div
className={cn("flex flex-col", {
[gapClass]: gapClass,
})}>
{rules.length > 0 ? (
<>
<div>
{rules.map(([rulePath, enabled]) => (
<RuleRow
enabled={enabled}
@@ -41,16 +45,16 @@ const RulesToggleList = ({
/>
))}
{showNewRule && <NewRuleRow isGlobal={isGlobal} ruleType={ruleType} />}
</>
</div>
) : (
<>
<div>
{showNoRules && (
<div className="flex flex-col items-center gap-3 my-3 text-[var(--vscode-descriptionForeground)]">
<div className="flex flex-col items-center gap-3 my-3 text-(--vscode-descriptionForeground)">
{ruleType === "workflow" ? "No workflows found" : "No rules found"}
</div>
)}
{showNewRule && <NewRuleRow isGlobal={isGlobal} ruleType={ruleType} />}
</>
</div>
)}
</div>
)
@@ -37,7 +37,7 @@ export function AlertDialogContent({ className, children, ...props }: React.HTML
className={`fixed top-[50%] left-[50%] grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] ${className}`}
onClick={(e) => e.stopPropagation()}
{...props}>
<div className="bg-[var(--vscode-editor-background)] rounded-sm gap-3 border border-[var(--vscode-panel-border)] p-6 shadow-lg sm:max-w-lg">
<div className="bg-(--vscode-editor-background) rounded-sm gap-3 border border-(--vscode-panel-border) p-6 shadow-lg sm:max-w-lg">
{children}
</div>
</div>
@@ -55,14 +55,14 @@ export function AlertDialogFooter({ className, ...props }: React.HTMLAttributes<
export function AlertDialogTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
return (
<h2
className={`text-base font-medium text-[var(--vscode-editor-foreground)] flex items-center gap-2 text-left ${className}`}
className={`text-base font-medium text-(--vscode-editor-foreground) flex items-center gap-2 text-left ${className}`}
{...props}
/>
)
}
export function AlertDialogDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
return <p className={`text-[var(--vscode-descriptionForeground)] text-sm text-left ${className}`} {...props} />
return <p className={`text-(--vscode-descriptionForeground) text-sm text-left ${className}`} {...props} />
}
export function AlertDialogAction({ className, ...props }: React.ComponentProps<typeof VSCodeButton>) {
@@ -101,7 +101,7 @@ export function UnsavedChangesDialog({
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
<AlertTriangle className="w-5 h-5 text-[var(--vscode-errorForeground)]" />
<AlertTriangle className="w-5 h-5 text-(--vscode-errorForeground)" />
{title}
</AlertDialogTitle>
<AlertDialogDescription>{description}</AlertDialogDescription>
@@ -289,7 +289,7 @@ const Container = styled.div<{ isMenuOpen?: boolean; $isCheckedOut?: boolean }>`
const Label = styled.span<{ $isCheckedOut?: boolean }>`
color: ${(props) => (props.$isCheckedOut ? "var(--vscode-textLink-foreground)" : "var(--vscode-descriptionForeground)")};
font-size: 9px;
flex-shrink: 0;
shrink: 0;
`
const DottedLine = styled.div<{ small?: boolean; $isCheckedOut?: boolean }>`
@@ -309,7 +309,7 @@ const ButtonGroup = styled.div`
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
shrink: 0;
`
const CustomButton = styled.button<{ disabled?: boolean; isActive?: boolean; $isCheckedOut?: boolean }>`
@@ -7,13 +7,13 @@ const DangerButton: React.FC<DangerButtonProps> = (props) => {
<VSCodeButton
{...props}
className={`
!bg-[#c42b2b]
!border-[#c42b2b]
!text-white
hover:!bg-[#a82424]
hover:!border-[#a82424]
active:!bg-[#8f1f1f]
active:!border-[#8f1f1f]
bg-[#c42b2b]!
border-[#c42b2b]!
text-white!
hover:bg-[#a82424]!
hover:border-[#a82424]!
active:bg-[#8f1f1f]!
active:border-[#8f1f1f]!
${props.className || ""}
`}
/>
@@ -1,63 +0,0 @@
import { cn, Tooltip } from "@heroui/react"
import React, { useMemo } from "react"
interface HeroTooltipProps {
content: React.ReactNode
children: React.ReactNode
className?: string
delay?: number
closeDelay?: number
placement?: "top" | "bottom" | "left" | "right"
showArrow?: boolean
disabled?: boolean
}
/**
* HeroTooltip component that wraps the HeroUI tooltip with styling
* similar to TaskTimelineTooltip
*/
const HeroTooltip: React.FC<HeroTooltipProps> = ({
content,
children,
className,
showArrow = false,
delay = 0,
closeDelay = 500,
placement = "top",
disabled = false,
}) => {
// If content is a simple string, wrap it in the tailwind styled divs
const formattedContent = useMemo(() => {
return typeof content === "string" ? (
<div
className={cn(
"bg-code-background text-code-foreground border border-code-foreground/20 rounded shadow-md max-w-[250px] text-sm",
className,
"p-2",
)}>
<span className="whitespace-pre-wrap break-words overflow-y-auto">{content}</span>
</div>
) : (
// If content is already a React node, assume it's pre-formatted
content
)
}, [content, className])
return (
<Tooltip
classNames={{
content: "hero-tooltip-content pointer-events-none", // Prevent hovering over tooltip
}}
closeDelay={closeDelay}
content={formattedContent} // Immediate close when cursor moves away
delay={delay}
disableAnimation={true}
isDisabled={disabled}
placement={placement} // Disable animation for immediate appearance/disappearance
showArrow={showArrow}>
{children}
</Tooltip>
)
}
export default HeroTooltip
@@ -19,7 +19,7 @@ const ActModeHighlight: React.FC = () => {
return (
<span
className={`text-[var(--vscode-textLink-foreground)] inline-flex items-center gap-1 ${
className={`text-(--vscode-textLink-foreground) inline-flex items-center gap-1 ${
mode === "plan" ? "hover:opacity-90 cursor-pointer" : "cursor-default opacity-60"
}`}
onClick={() => {
@@ -33,8 +33,8 @@ const ActModeHighlight: React.FC = () => {
}
}}
title={mode === "plan" ? "Click to toggle to Act Mode" : "Already in Act Mode"}>
<div className="p-1 rounded-[12px] bg-[var(--vscode-editor-background)] flex items-center justify-end w-4 border-[1px] border-[var(--vscode-input-border)]">
<div className="rounded-full bg-[var(--vscode-textLink-foreground)] w-2 h-2" />
<div className="p-1 rounded-[12px] bg-(--vscode-editor-background) flex items-center justify-end w-4 border border-(--vscode-input-border)">
<div className="rounded-full bg-(--vscode-textLink-foreground) w-2 h-2" />
</div>
Act Mode (A)
</span>
@@ -383,7 +383,7 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => {
<>
<code {...props} />
<button
className="codicon codicon-link-external bg-transparent border-0 appearance-none p-0 ml-0.5 leading-none align-middle opacity-70 hover:opacity-100 transition-opacity text-[1em] relative top-[1px] text-[var(--vscode-textPreformat-foreground)] translate-y-[-2px]"
className="codicon codicon-link-external bg-transparent border-0 appearance-none p-0 ml-0.5 leading-none align-middle opacity-70 hover:opacity-100 transition-opacity text-[1em] relative top-px text-(--vscode-textPreformat-foreground) translate-y-[-2px]"
onClick={() => FileServiceClient.openFileRelativePath({ value: filePath })}
title={`Open ${filePath} in editor`}
type="button"
@@ -22,7 +22,7 @@ const StyledButton = styled(VSCodeButton)`
i.codicon {
margin-right: 6px;
flex-shrink: 0;
shrink: 0;
font-size: 16px !important;
}
`
@@ -7,13 +7,13 @@ const SuccessButtonTW: React.FC<SuccessButtonTWProps> = (props) => {
<VSCodeButton
{...props}
className={`
!bg-[#176f2c]
!border-[#176f2c]
!text-white
hover:!bg-[#197f31]
hover:!border-[#197f31]
active:!bg-[#156528]
active:!border-[#156528]
bg-[#176f2c]!
border-[#176f2c]!
text-white!
hover:bg-[#197f31]!
hover:border-[#197f31]!
active:bg-[#156528]!
active:border-[#156528]!
${props.className || ""}
`
.replace(/\s+/g, " ")
+1 -1
View File
@@ -9,7 +9,7 @@ export const Tab = ({ className, children, ...props }: TabProps) => (
)
export const TabHeader = ({ className, children, ...props }: TabProps) => (
<div className={`px-5 py-2.5 border-b border-[var(--vscode-panel-border)] ${className}`} {...props}>
<div className={`px-5 py-2.5 border-b border-(--vscode-panel-border) ${className}`} {...props}>
{children}
</div>
)
@@ -1,65 +0,0 @@
import React, { useState } from "react"
import styled from "styled-components"
import {
getAsVar,
VSC_DESCRIPTION_FOREGROUND,
VSC_INPUT_BORDER,
VSC_INPUT_PLACEHOLDER_FOREGROUND,
VSC_SIDEBAR_BACKGROUND,
} from "@/utils/vscStyles"
interface TooltipProps {
visible?: boolean
hintText?: string
tipText: string
children: React.ReactNode
style?: React.CSSProperties
}
// add styled component for tooltip
const TooltipBody = styled.div<Pick<TooltipProps, "style">>`
position: absolute;
background-color: ${getAsVar(VSC_SIDEBAR_BACKGROUND)};
color: ${getAsVar(VSC_DESCRIPTION_FOREGROUND)};
padding: 5px;
border-radius: 5px;
bottom: 100%;
left: ${(props) => props.style?.left ?? -180}%;
z-index: ${(props) => props.style?.zIndex ?? 1000};
white-space: wrap;
max-width: 200px;
border: 1px solid ${getAsVar(VSC_INPUT_BORDER)};
pointer-events: none;
font-size: 0.9em;
`
const Hint = styled.div`
font-size: 0.8em;
color: ${getAsVar(VSC_INPUT_PLACEHOLDER_FOREGROUND)};
opacity: 0.8;
margin-top: 2px;
`
const Tooltip: React.FC<TooltipProps> = ({ visible, tipText, hintText, children, style }) => {
const [isHovered, setIsHovered] = useState(false)
// Determine final visibility based on prop or internal state
const shouldShow = visible !== undefined ? visible : isHovered
return (
<div
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
style={{ position: "relative", display: "inline-block" }}>
{children}
{shouldShow && (
<TooltipBody style={style}>
{tipText}
{hintText && <Hint>{hintText}</Hint>}
</TooltipBody>
)}
</div>
)
}
export default Tooltip
@@ -28,16 +28,16 @@ interface CustomFilterRadioProps {
const CustomFilterRadio = ({ checked, onChange, icon, label }: CustomFilterRadioProps) => {
return (
<div
className="flex items-center cursor-pointer py-[0.3em] px-0 mr-[10px] text-[var(--vscode-font-size)] select-none"
className="flex items-center cursor-pointer py-[0.3em] px-0 mr-[10px] text-(--vscode-font-size) select-none"
onClick={onChange}>
<div
className={`w-[14px] h-[14px] rounded-full border border-[var(--vscode-checkbox-border)] relative flex justify-center items-center mr-[6px] ${
checked ? "bg-[var(--vscode-checkbox-background)]" : "bg-transparent"
className={`w-[14px] h-[14px] rounded-full border border-(--vscode-checkbox-border) relative flex justify-center items-center mr-[6px] ${
checked ? "bg-(--vscode-checkbox-background)" : "bg-transparent"
}`}>
{checked && <div className="w-[6px] h-[6px] rounded-full bg-[var(--vscode-checkbox-foreground)]" />}
{checked && <div className="w-[6px] h-[6px] rounded-full bg-(--vscode-checkbox-foreground)" />}
</div>
<span className="flex items-center gap-[3px]">
<div className={`codicon codicon-${icon} text-[var(--vscode-button-background)] text-base`} />
<div className={`codicon codicon-${icon} text-(--vscode-button-background) text-base`} />
{label}
</span>
</div>
@@ -11,7 +11,7 @@ type AddLocalServerFormProps = {
const AddLocalServerForm = ({ onServerAdded }: AddLocalServerFormProps) => {
return (
<FormContainer>
<div className="text-[var(--vscode-foreground)]">
<div className="text-(--vscode-foreground)">
Add a local MCP server by configuring it in <code>cline_mcp_settings.json</code>. You'll need to specify the
server name, command, arguments, and any required environment variables in the JSON configuration. Learn more
<VSCodeLink href={LINKS.DOCUMENTATION.LOCAL_MCP_SERVER_DOCS} style={{ display: "inline" }}>
@@ -65,7 +65,7 @@ const AddRemoteServerForm = ({ onServerAdded }: { onServerAdded: () => void }) =
return (
<div className="p-4 px-5">
<div className="text-[var(--vscode-foreground)] mb-2">
<div className="text-(--vscode-foreground) mb-2">
Add a remote MCP server by providing a name and its URL endpoint. Learn more{" "}
<VSCodeLink href={LINKS.DOCUMENTATION.REMOTE_MCP_SERVER_DOCS} style={{ display: "inline" }}>
here.
@@ -101,7 +101,7 @@ const AddRemoteServerForm = ({ onServerAdded }: { onServerAdded: () => void }) =
</VSCodeTextField>
</div>
{error && <div className="mb-3 text-[var(--vscode-errorForeground)]">{error}</div>}
{error && <div className="mb-3 text-(--vscode-errorForeground)">{error}</div>}
<div className="flex items-center mt-3 w-full">
<VSCodeButton className="w-full" disabled={isSubmitting} type="submit">
@@ -109,7 +109,7 @@ const AddRemoteServerForm = ({ onServerAdded }: { onServerAdded: () => void }) =
</VSCodeButton>
{showConnectingMessage && (
<div className="ml-3 text-[var(--vscode-notificationsInfoIcon-foreground)] text-sm">
<div className="ml-3 text-(--vscode-notificationsInfoIcon-foreground) text-sm">
Connecting to server... This may take a few seconds.
</div>
)}
@@ -27,7 +27,7 @@ const ServersToggleList = ({
))}
</div>
) : (
<div className="flex flex-col items-center gap-3 my-5 text-[var(--vscode-descriptionForeground)]">
<div className="flex flex-col items-center gap-3 my-5 text-(--vscode-descriptionForeground)">
No MCP servers installed
</div>
)
@@ -18,6 +18,7 @@ import {
} from "@vscode/webview-ui-toolkit/react"
import { useCallback, useState } from "react"
import DangerButton from "@/components/common/DangerButton"
import { Switch } from "@/components/ui/switch"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { McpServiceClient } from "@/services/grpc-client"
import { getMcpServerDisplayName } from "@/utils/mcp"
@@ -203,7 +204,7 @@ const ServerRow = ({
</span>
{/* Collapsed view controls */}
{!server.error && (
<div style={{ display: "flex", alignItems: "center", gap: "4px", marginLeft: "8px" }}>
<div style={{ display: "flex", alignItems: "center", gap: "4px", marginLeft: "8px", marginRight: "4px" }}>
<VSCodeButton
appearance="icon"
disabled={server.status === "connecting" || isRestarting}
@@ -229,47 +230,7 @@ const ServerRow = ({
</div>
)}
{/* Toggle Switch */}
<div onClick={(e) => e.stopPropagation()} style={{ display: "flex", alignItems: "center", marginLeft: "8px" }}>
<div
aria-checked={!server.disabled}
onClick={() => {
handleToggleMcpServer()
}}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault()
handleToggleMcpServer()
}
}}
role="switch"
style={{
width: "20px",
height: "10px",
backgroundColor: server.disabled
? "var(--vscode-titleBar-inactiveForeground)"
: "var(--vscode-testing-iconPassed)",
borderRadius: "5px",
position: "relative",
cursor: "pointer",
transition: "background-color 0.2s",
opacity: server.disabled ? 0.5 : 0.9,
}}
tabIndex={0}>
<div
style={{
width: "6px",
height: "6px",
backgroundColor: "white",
border: "1px solid color-mix(in srgb, #666666 65%, transparent)",
borderRadius: "50%",
position: "absolute",
top: "1px",
left: server.disabled ? "2px" : "12px",
transition: "left 0.2s",
}}
/>
</div>
</div>
<Switch aria-readonly checked={!server.disabled} onCheckedChange={() => handleToggleMcpServer()} />
<div
style={{
width: "8px",
+20 -17
View File
@@ -1,9 +1,10 @@
import { TooltipContent } from "@radix-ui/react-tooltip"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { HistoryIcon, PlusIcon, SettingsIcon, UserCircleIcon } from "lucide-react"
import { useMemo } from "react"
import { Tooltip } from "@/components/ui/tooltip"
import { TaskServiceClient } from "@/services/grpc-client"
import { useExtensionState } from "../../context/ExtensionStateContext"
import HeroTooltip from "../common/HeroTooltip"
// Custom MCP Server Icon component using VSCode codicon
const McpServerIcon = ({ className, size }: { className?: string; size?: number }) => (
@@ -66,23 +67,25 @@ export const Navbar = () => {
return (
<nav
className="flex-none inline-flex justify-end bg-transparent gap-2 mb-1 z-10 border-none items-center mr-4!"
id="cline-navbar-container"
style={{ gap: "4px" }}>
className="flex-none inline-flex justify-end bg-transparent gap-1 mb-1 z-10 border-none items-center mr-4!"
id="cline-navbar-container">
{SETTINGS_TABS.map((tab) => (
<HeroTooltip content={tab.tooltip} key={`navbar-tooltip-${tab.id}`} placement="bottom">
<VSCodeButton
appearance="icon"
aria-label={tab.tooltip}
data-testid={`tab-${tab.id}`}
key={`navbar-button-${tab.id}`}
onClick={() => tab.navigate()}
style={{ padding: "0px", height: "20px" }}>
<div className="flex items-center gap-1 text-xs whitespace-nowrap min-w-0 w-full">
<tab.icon className="text-[var(--vscode-foreground)]" size={18} strokeWidth={1} />
</div>
</VSCodeButton>
</HeroTooltip>
<Tooltip key={`navbar-tooltip-${tab.id}`}>
<TooltipContent side="bottom">{tab.tooltip}</TooltipContent>
<TooltipContent>
<VSCodeButton
appearance="icon"
aria-label={tab.tooltip}
data-testid={`tab-${tab.id}`}
key={`navbar-button-${tab.id}`}
onClick={() => tab.navigate()}
style={{ padding: "0px", height: "20px" }}>
<div className="flex items-center gap-1 text-xs whitespace-nowrap min-w-0 w-full">
<tab.icon className="text-(--vscode-foreground)" size={18} strokeWidth={1} />
</div>
</VSCodeButton>
</TooltipContent>
</Tooltip>
))}
</nav>
)
@@ -241,7 +241,7 @@ const BasetenModelPicker: React.FC<BasetenModelPickerProps> = ({ isPopup, curren
</VSCodeTextField>
{isDropdownVisible && (
<div
className="absolute top-[calc(100%-3px)] left-0 w-[calc(100%-2px)] max-h-[200px] overflow-y-auto border border-[var(--vscode-list-activeSelectionBackground)] rounded-b-[3px]"
className="absolute top-[calc(100%-3px)] left-0 w-[calc(100%-2px)] max-h-[200px] overflow-y-auto border border-(--vscode-list-activeSelectionBackground) rounded-b-[3px]"
ref={dropdownListRef}
style={{
backgroundColor: "var(--vscode-dropdown-background)",
@@ -249,8 +249,8 @@ const BasetenModelPicker: React.FC<BasetenModelPickerProps> = ({ isPopup, curren
}}>
{modelSearchResults.map((item, index) => (
<div
className={`px-2.5 py-1.5 cursor-pointer break-all whitespace-normal hover:bg-[var(--vscode-list-activeSelectionBackground)] ${
index === selectedIndex ? "bg-[var(--vscode-list-activeSelectionBackground)]" : ""
className={`px-2.5 py-1.5 cursor-pointer break-all whitespace-normal hover:bg-(--vscode-list-activeSelectionBackground) ${
index === selectedIndex ? "bg-(--vscode-list-activeSelectionBackground)" : ""
}`}
key={item.id}
onClick={() => {
@@ -272,7 +272,7 @@ const BasetenModelPicker: React.FC<BasetenModelPickerProps> = ({ isPopup, curren
{hasInfo ? (
<ModelInfoView isPopup={isPopup} modelInfo={selectedModelInfo} selectedModelId={selectedModelId} />
) : (
<p className="text-xs mt-0 text-[var(--vscode-descriptionForeground)]">
<p className="text-xs mt-0 text-(--vscode-descriptionForeground)">
The extension automatically fetches the latest list of models available on{" "}
<VSCodeLink className="inline text-inherit" href="https://www.baseten.co/products/model-apis/">
Baseten.
@@ -21,7 +21,7 @@ export const ClineAccountInfoCard = () => {
}
return (
<div className="max-w-[600px]">
<div className="max-w-[600px] justify-start">
{user ? (
<VSCodeButton appearance="secondary" onClick={handleShowAccount}>
View Billing & Usage
@@ -223,7 +223,7 @@ const GroqModelPicker: React.FC<GroqModelPickerProps> = ({ isPopup, currentMode
</VSCodeTextField>
{isDropdownVisible && (
<div
className="absolute top-[calc(100%-3px)] left-0 w-[calc(100%-2px)] max-h-[200px] overflow-y-auto border border-[var(--vscode-list-activeSelectionBackground)] rounded-b-[3px]"
className="absolute top-[calc(100%-3px)] left-0 w-[calc(100%-2px)] max-h-[200px] overflow-y-auto border border-(--vscode-list-activeSelectionBackground) rounded-b-[3px]"
ref={dropdownListRef}
style={{
backgroundColor: "var(--vscode-dropdown-background)",
@@ -231,8 +231,8 @@ const GroqModelPicker: React.FC<GroqModelPickerProps> = ({ isPopup, currentMode
}}>
{modelSearchResults.map((item, index) => (
<div
className={`px-2.5 py-1.5 cursor-pointer break-all whitespace-normal hover:bg-[var(--vscode-list-activeSelectionBackground)] ${
index === selectedIndex ? "bg-[var(--vscode-list-activeSelectionBackground)]" : ""
className={`px-2.5 py-1.5 cursor-pointer break-all whitespace-normal hover:bg-(--vscode-list-activeSelectionBackground) ${
index === selectedIndex ? "bg-(--vscode-list-activeSelectionBackground)" : ""
}`}
dangerouslySetInnerHTML={{
__html: item.html,
@@ -254,7 +254,7 @@ const GroqModelPicker: React.FC<GroqModelPickerProps> = ({ isPopup, currentMode
{hasInfo ? (
<ModelInfoView isPopup={isPopup} modelInfo={selectedModelInfo} selectedModelId={selectedModelId} />
) : (
<p className="text-xs mt-0 text-[var(--vscode-descriptionForeground)]">
<p className="text-xs mt-0 text-(--vscode-descriptionForeground)">
The extension automatically fetches the latest list of models available on{" "}
<VSCodeLink className="inline text-inherit" href="https://console.groq.com/docs/models">
Groq.
@@ -171,7 +171,7 @@ const HuggingFaceModelPicker: React.FC<HuggingFaceModelPickerProps> = ({ isPopup
<div className="relative w-full" ref={dropdownRef}>
<VSCodeTextField
className="w-full relative z-[1000]"
className="w-full relative z-1000"
id="hf-model-search"
onFocus={() => setIsDropdownVisible(true)}
onInput={(e: any) => {
@@ -204,13 +204,13 @@ const HuggingFaceModelPicker: React.FC<HuggingFaceModelPickerProps> = ({ isPopup
<div
className={`absolute top-[calc(100%-3px)] left-0 w-[calc(100%-2px)] ${
isPopup ? "max-h-[90px]" : "max-h-[200px]"
} overflow-y-auto bg-[var(--vscode-dropdown-background)] border border-[var(--vscode-list-activeSelectionBackground)] z-[999] rounded-b-[3px]`}
} overflow-y-auto bg-(--vscode-dropdown-background) border border-(--vscode-list-activeSelectionBackground) z-999 rounded-b-[3px]`}
ref={dropdownListRef}>
{modelSearchResults.map((result, index) => (
<div
className={`p-[5px_10px] cursor-pointer break-all whitespace-normal ${
index === selectedIndex ? "bg-[var(--vscode-list-activeSelectionBackground)]" : ""
} hover:bg-[var(--vscode-list-activeSelectionBackground)]`}
index === selectedIndex ? "bg-(--vscode-list-activeSelectionBackground)" : ""
} hover:bg-(--vscode-list-activeSelectionBackground)`}
key={result.id}
onClick={() => {
handleModelChange(result.id)
@@ -219,7 +219,7 @@ const HuggingFaceModelPicker: React.FC<HuggingFaceModelPickerProps> = ({ isPopup
onMouseEnter={() => setSelectedIndex(index)}
ref={(el: HTMLDivElement | null) => (itemRefs.current[index] = el)}>
<div
className="[&_.model-item-highlight]:bg-[var(--vscode-editor-findMatchHighlightBackground)] [&_.model-item-highlight]:text-inherit"
className="[&_.model-item-highlight]:bg-(--vscode-editor-findMatchHighlightBackground) [&_.model-item-highlight]:text-inherit"
dangerouslySetInnerHTML={{ __html: result.html }}
/>
</div>
@@ -41,7 +41,7 @@ const PreferredLanguageSetting: React.FC = () => {
<VSCodeOption value="Traditional Chinese - 繁體中文">Traditional Chinese - </VSCodeOption>
<VSCodeOption value="Turkish - Türkçe">Turkish - Türkçe</VSCodeOption>
</VSCodeDropdown>
<p className="text-xs text-[var(--vscode-descriptionForeground)] mt-1">
<p className="text-xs text-(--vscode-descriptionForeground) mt-1">
The language that Cline should use for communication.
</p>
</div>
@@ -1,3 +1,4 @@
import { TooltipTrigger } from "@radix-ui/react-tooltip"
import { ExtensionMessage } from "@shared/ExtensionMessage"
import { ResetStateRequest } from "@shared/proto/cline/state"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
@@ -5,7 +6,7 @@ import debounce from "debounce"
import { CheckCheck, FlaskConical, Info, LucideIcon, Settings, SquareMousePointer, SquareTerminal, Webhook } from "lucide-react"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { useEvent } from "react-use"
import HeroTooltip from "@/components/common/HeroTooltip"
import { Tooltip, TooltipContent } from "@/components/ui/tooltip"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { StateServiceClient } from "@/services/grpc-client"
import { Tab, TabContent, TabHeader, TabList, TabTrigger } from "../common/Tab"
@@ -23,11 +24,11 @@ const IS_DEV = process.env.IS_DEV
// Styles for the tab system
const settingsTabsContainer = "flex flex-1 overflow-hidden [&.narrow_.tab-label]:hidden"
const settingsTabList =
"w-48 data-[compact=true]:w-12 flex-shrink-0 flex flex-col overflow-y-auto overflow-x-hidden border-r border-[var(--vscode-sideBar-background)]"
"w-48 data-[compact=true]:w-12 shrink-0 flex flex-col overflow-y-auto overflow-x-hidden border-r border-(--vscode-sideBar-background)"
const settingsTabTrigger =
"whitespace-nowrap overflow-hidden min-w-0 h-12 px-4 py-3 box-border flex items-center border-l-2 border-transparent text-[var(--vscode-foreground)] opacity-70 bg-transparent hover:bg-[var(--vscode-list-hoverBackground)] data-[compact=true]:w-12 data-[compact=true]:p-4 cursor-pointer"
"whitespace-nowrap overflow-hidden min-w-0 h-12 px-4 py-3 box-border flex items-center border-l-2 border-transparent text-(--vscode-foreground) opacity-70 bg-transparent hover:bg-(--vscode-list-hoverBackground) data-[compact=true]:w-12 data-[compact=true]:p-4 cursor-pointer"
const settingsTabTriggerActive =
"opacity-100 border-l-2 border-l-[var(--vscode-focusBorder)] border-t-0 border-r-0 border-b-0 bg-[var(--vscode-list-activeSelectionBackground)]"
"opacity-100 border-l-2 border-l-(--vscode-focusBorder) border-t-0 border-r-0 border-b-0 bg-(--vscode-list-activeSelectionBackground)"
// Tab definitions
interface SettingsTab {
@@ -244,16 +245,19 @@ const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
if (isCompactMode) {
return (
<HeroTooltip content={tab.tooltipText} key={tab.id} placement="right">
<div
className={tabClassName}
data-compact={isCompactMode}
data-testid={`tab-${tab.id}`}
data-value={tab.id}
onClick={() => handleTabChange(tab.id)}>
{tabContent}
</div>
</HeroTooltip>
<Tooltip key={tab.id}>
<TooltipTrigger>
<div
className={tabClassName}
data-compact={isCompactMode}
data-testid={`tab-${tab.id}`}
data-value={tab.id}
onClick={() => handleTabChange(tab.id)}>
{tabContent}
</div>
</TooltipTrigger>
<TooltipContent side="right">{tab.tooltipText}</TooltipContent>
</Tooltip>
)
}
@@ -110,7 +110,7 @@ const OcaModelPicker: React.FC<OcaModelPickerProps> = ({
}
`}</style>
<label className="font-medium text-[12px] mt-[10px] mb-[2px]">Model</label>
<div className="relative z-[100] flex items-center gap-2 mb-1">
<div className="relative z-100 flex items-center gap-2 mb-1">
<VSCodeDropdown
className="flex-1 text-[12px] min-h-[24px]"
id="model-id"
@@ -151,7 +151,7 @@ const OcaModelPicker: React.FC<OcaModelPickerProps> = ({
</VSCodeButton>
</div>
{lastRefreshedText ? (
<div className="text-[11px] text-[var(--vscode-descriptionForeground)] mt-0 mb-2">
<div className="text-[11px] text-(--vscode-descriptionForeground) mt-0 mb-2">
Last refreshed at {lastRefreshedText}
</div>
) : null}
@@ -171,7 +171,7 @@ const OcaRestrictivePopup: React.FC<{
onAcknowledge: () => void
bannerText?: string | null
}> = React.memo(({ onAcknowledge, bannerText }) => (
<div className="fixed top-0 left-0 w-screen h-screen z-[2000] [background:rgba(0,0,0,0.25)] flex items-center justify-center">
<div className="fixed top-0 left-0 w-screen h-screen z-2000 [background:rgba(0,0,0,0.25)] flex items-center justify-center">
<div
aria-labelledby="oca-popup-title"
aria-modal="true"
@@ -183,7 +183,7 @@ const OcaRestrictivePopup: React.FC<{
<h4 className={`mb-2 [color:var(${VSC_DESCRIPTION_FOREGROUND},#b3b3b3)] font-semibold`}>
Disclaimer: Prohibited Data Submission
</h4>
<div className="overflow-y-auto flex-1 pr-2 mb-4 text-[13px] leading-[1.5] [color:var(--vscode-foreground,#222)] [mask-image:linear-gradient(to_bottom,black_96%,transparent_100%)]">
<div className="overflow-y-auto flex-1 pr-2 mb-4 text-[13px] leading-normal text-(--vscode-foreground,#222) mask-[linear-gradient(to_bottom,black_96%,transparent_100%)]">
{bannerText && <div dangerouslySetInnerHTML={{ __html: bannerText }} />}
</div>
<div className="text-right">
@@ -287,7 +287,7 @@ export const OcaProvider = ({ isPopup, currentMode }: OcaProviderProps) => {
}}>
Sign in with Oracle Code Assist
</VSCodeButton>
<p className="text-xs mt-0 text-[var(--vscode-descriptionForeground)]">
<p className="text-xs mt-0 text-(--vscode-descriptionForeground)">
Please ask your IT administrator to set up Oracle Code Assist as a model provider. Oracle Employees,
please see the{" "}
<VSCodeLink
@@ -312,7 +312,7 @@ export const OcaProvider = ({ isPopup, currentMode }: OcaProviderProps) => {
<span className="font-semibold opacity-95 mt-2">Unknown User</span>
)}
{isOracle && (
<p className="text-xs mt-0 font-normal text-[var(--vscode-descriptionForeground)]">
<p className="text-xs mt-0 font-normal text-(--vscode-descriptionForeground)">
Oracle Employees, please see the{" "}
<VSCodeLink
href="https://confluence.oraclecorp.com/confluence/display/AICODE/Oracle+Code+Assist+via+Cline"
@@ -2,9 +2,9 @@ import { EmptyRequest } from "@shared/proto/cline/common"
import { Mode } from "@shared/storage/types"
import { VSCodeButton, VSCodeCheckbox, VSCodeDropdown, VSCodeLink, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { useState } from "react"
import { useOpenRouterKeyInfo } from "@/components/ui/hooks/useOpenRouterKeyInfo"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { AccountServiceClient } from "@/services/grpc-client"
import { useOpenRouterKeyInfo } from "../../ui/hooks/useOpenRouterKeyInfo"
import { DebouncedTextField } from "../common/DebouncedTextField"
import { DropdownContainer } from "../common/ModelSelector"
import OpenRouterModelPicker, { OPENROUTER_MODEL_PICKER_Z_INDEX } from "../OpenRouterModelPicker"
@@ -137,7 +137,7 @@ export const SapAiCoreProvider = ({ showModelOptions, isPopup, currentMode }: Sa
<span className="font-medium">AI Core Client Id</span>
</DebouncedTextField>
{apiConfiguration?.sapAiCoreClientId && (
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
<p className="text-xs text-(--vscode-descriptionForeground)">
Client Id is set. To change it, please re-enter the value.
</p>
)}
@@ -151,7 +151,7 @@ export const SapAiCoreProvider = ({ showModelOptions, isPopup, currentMode }: Sa
<span className="font-medium">AI Core Client Secret</span>
</DebouncedTextField>
{apiConfiguration?.sapAiCoreClientSecret && (
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
<p className="text-xs text-(--vscode-descriptionForeground)">
Client Secret is set. To change it, please re-enter the value.
</p>
)}
@@ -180,7 +180,7 @@ export const SapAiCoreProvider = ({ showModelOptions, isPopup, currentMode }: Sa
<span className="font-medium">AI Core Resource Group</span>
</DebouncedTextField>
<p className="text-xs mt-1.5 text-[var(--vscode-descriptionForeground)]">
<p className="text-xs mt-1.5 text-(--vscode-descriptionForeground)">
These credentials are stored locally and only used to make API requests from this extension.
<VSCodeLink
className="inline"
@@ -200,7 +200,7 @@ export const SapAiCoreProvider = ({ showModelOptions, isPopup, currentMode }: Sa
<span className="font-medium">Orchestration Mode</span>
</div>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
<p className="text-xs text-(--vscode-descriptionForeground)">
When enabled, provides access to all available models without requiring individual deployments.
<br />
<br />
@@ -213,12 +213,12 @@ export const SapAiCoreProvider = ({ showModelOptions, isPopup, currentMode }: Sa
<>
<div className="flex flex-col gap-1.5">
{isLoadingModels ? (
<div className="text-xs text-[var(--vscode-descriptionForeground)]">Loading models...</div>
<div className="text-xs text-(--vscode-descriptionForeground)">Loading models...</div>
) : modelError ? (
<div className="text-xs text-[var(--vscode-errorForeground)]">
<div className="text-xs text-(--vscode-errorForeground)">
{modelError}
<button
className="ml-2 text-[11px] px-1.5 py-0.5 bg-[var(--vscode-button-background)] text-[var(--vscode-button-foreground)] border-none rounded-sm cursor-pointer"
className="ml-2 text-[11px] px-1.5 py-0.5 bg-(--vscode-button-background) text-(--vscode-button-foreground) border-none rounded-sm cursor-pointer"
onClick={fetchSapAiCoreModels}>
Retry
</button>
@@ -226,7 +226,7 @@ export const SapAiCoreProvider = ({ showModelOptions, isPopup, currentMode }: Sa
) : hasRequiredCredentials ? (
<>
{sapAiCoreModelDeployments.length === 0 && (
<div className="text-xs text-[var(--vscode-errorForeground)] mb-2">
<div className="text-xs text-(--vscode-errorForeground) mb-2">
Unable to fetch models from SAP AI Core service instance. Please check your SAP AI Core
configuration or ensure your deployments are deployed and running in the service instance
</div>
@@ -247,7 +247,7 @@ export const SapAiCoreProvider = ({ showModelOptions, isPopup, currentMode }: Sa
/>
</>
) : (
<div className="text-xs text-[var(--vscode-errorForeground)]">
<div className="text-xs text-(--vscode-errorForeground)">
Please configure your SAP AI Core credentials to see available models.
</div>
)}
@@ -11,7 +11,7 @@ const AboutSection = ({ version, renderSectionHeader }: AboutSectionProps) => {
<div>
{renderSectionHeader("about")}
<Section>
<div className="text-center text-[var(--vscode-descriptionForeground)] text-xs leading-[1.2] px-0 py-0 pr-2 pb-[15px] mt-auto">
<div className="text-center text-(--vscode-descriptionForeground) text-xs leading-[1.2] px-0 py-0 pr-2 pb-[15px] mt-auto">
<p className="break-words m-0 p-0">
If you have any questions or feedback, feel free to open an issue at{" "}
<VSCodeLink className="inline" href="https://github.com/cline/cline">
@@ -24,8 +24,8 @@ const ApiConfigurationSection = ({ renderSectionHeader }: ApiConfigurationSectio
<Section>
{/* Tabs container */}
{planActSeparateModelsSetting ? (
<div className="rounded-md mb-5 bg-[var(--vscode-panel-background)]">
<div className="flex gap-[1px] mb-[10px] -mt-2 border-0 border-b border-solid border-[var(--vscode-panel-border)]">
<div className="rounded-md mb-5 bg-(--vscode-panel-background)">
<div className="flex gap-px mb-[10px] -mt-2 border-0 border-b border-solid border-(--vscode-panel-border)">
<TabButton
disabled={currentTab === "plan"}
isActive={currentTab === "plan"}
@@ -79,7 +79,7 @@ const ApiConfigurationSection = ({ renderSectionHeader }: ApiConfigurationSectio
}}>
Use different models for Plan and Act modes
</VSCodeCheckbox>
<p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
<p className="text-xs mt-[5px] text-(--vscode-descriptionForeground)">
Switching between Plan and Act mode will persist the API and model used in the previous mode. This may be
helpful e.g. when using a strong reasoning model to architect a plan for a cheaper coding model to act on.
</p>
@@ -23,7 +23,7 @@ const DebugSection = ({ onResetState, renderSectionHeader }: DebugSectionProps)
style={{ backgroundColor: "var(--vscode-errorForeground)", color: "black" }}>
Reset Global State
</VSCodeButton>
<p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
<p className="text-xs mt-[5px] text-(--vscode-descriptionForeground)">
This will reset all global state and secret storage in the extension.
</p>
</Section>
@@ -45,7 +45,7 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
}}>
Enable Checkpoints
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
<p className="text-xs text-(--vscode-descriptionForeground)">
Enables extension to save checkpoints of workspace throughout the task. Uses git under the hood which
may not work well with large workspaces.
</p>
@@ -59,13 +59,13 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
}}>
Enable MCP Marketplace
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
<p className="text-xs text-(--vscode-descriptionForeground)">
Enables the MCP Marketplace tab for discovering and installing MCP servers.
</p>
</div>
<div style={{ marginTop: 10 }}>
<label
className="block text-sm font-medium text-[var(--vscode-foreground)] mb-1"
className="block text-sm font-medium text-(--vscode-foreground) mb-1"
htmlFor="mcp-display-mode-dropdown">
MCP Display Mode
</label>
@@ -75,7 +75,7 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
onChange={(newMode: McpDisplayMode) => updateSetting("mcpDisplayMode", newMode)}
value={mcpDisplayMode}
/>
<p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
<p className="text-xs mt-[5px] text-(--vscode-descriptionForeground)">
Controls how MCP responses are displayed: plain text, rich formatting with links/images, or markdown
rendering.
</p>
@@ -89,13 +89,13 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
}}>
Collapse MCP Responses
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
<p className="text-xs text-(--vscode-descriptionForeground)">
Sets the default display mode for MCP response panels
</p>
</div>
<div style={{ marginTop: 10 }}>
<label
className="block text-sm font-medium text-[var(--vscode-foreground)] mb-1"
className="block text-sm font-medium text-(--vscode-foreground) mb-1"
htmlFor="openai-reasoning-effort-dropdown">
OpenAI Reasoning Effort
</label>
@@ -112,7 +112,7 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
<VSCodeOption value="medium">Medium</VSCodeOption>
<VSCodeOption value="high">High</VSCodeOption>
</VSCodeDropdown>
<p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
<p className="text-xs mt-[5px] text-(--vscode-descriptionForeground)">
Reasoning effort for the OpenAI family of models(applies to all OpenAI model providers)
</p>
</div>
@@ -125,7 +125,7 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
}}>
Enable strict plan mode
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
<p className="text-xs text-(--vscode-descriptionForeground)">
Enforces strict tool use while in plan mode, preventing file edits.
</p>
</div>
@@ -139,7 +139,7 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
}}>
Enable Focus Chain
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
<p className="text-xs text-(--vscode-descriptionForeground)">
Enables enhanced task progress tracking and automatic focus chain list management throughout
tasks.
</p>
@@ -148,7 +148,7 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
{focusChainSettings?.enabled && (
<div style={{ marginTop: 10, marginLeft: 20 }}>
<label
className="block text-sm font-medium text-[var(--vscode-foreground)] mb-1"
className="block text-sm font-medium text-(--vscode-foreground) mb-1"
htmlFor="focus-chain-remind-interval">
Focus Chain Reminder Interval
</label>
@@ -166,7 +166,7 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
}}
value={String(focusChainSettings?.remindClineInterval || 6)}
/>
<p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
<p className="text-xs mt-[5px] text-(--vscode-descriptionForeground)">
Interval (in messages) to remind Cline about its focus chain checklist (1-100). Lower values
provide more frequent reminders.
</p>
@@ -237,10 +237,10 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
}}>
Enable Auto Compact
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
<p className="text-xs text-(--vscode-descriptionForeground)">
Enables advanced context management system which uses LLM based condensing for next-gen models.{" "}
<a
className="text-[var(--vscode-textLink-foreground)] hover:text-[var(--vscode-textLink-activeForeground)]"
className="text-(--vscode-textLink-foreground) hover:text-(--vscode-textLink-activeForeground)"
href="https://docs.cline.bot/features/auto-compact"
rel="noopener noreferrer"
target="_blank">
@@ -257,7 +257,7 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
}}>
Enable Yolo Mode
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-errorForeground)]">
<p className="text-xs text-(--vscode-errorForeground)">
EXPERIMENTAL & DANGEROUS: This mode disables safety checks and user confirmations. Cline will
automatically approve all actions without asking. Use with extreme caution.
</p>
@@ -27,7 +27,7 @@ const GeneralSettingsSection = ({ renderSectionHeader }: GeneralSettingsSectionP
}}>
Allow error and usage reporting
</VSCodeCheckbox>
<p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
<p className="text-xs mt-[5px] text-(--vscode-descriptionForeground)">
Help improve Cline by sending usage data and error reports. No code, prompts, or personal information are
ever sent. See our{" "}
<VSCodeLink className="text-inherit" href="https://docs.cline.bot/more-info/telemetry">
@@ -91,7 +91,7 @@ export const TerminalSettingsSection: React.FC<TerminalSettingsSectionProps> = (
</VSCodeOption>
))}
</VSCodeDropdown>
<p className="text-xs text-[var(--vscode-descriptionForeground)] mt-1">
<p className="text-xs text-(--vscode-descriptionForeground) mt-1">
Select the default terminal Cline will use. 'Default' uses your VSCode global setting.
</p>
</div>
@@ -108,9 +108,9 @@ export const TerminalSettingsSection: React.FC<TerminalSettingsSectionProps> = (
value={inputValue}
/>
</div>
{inputError && <div className="text-[var(--vscode-errorForeground)] text-xs mt-1">{inputError}</div>}
{inputError && <div className="text-(--vscode-errorForeground) text-xs mt-1">{inputError}</div>}
</div>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
<p className="text-xs text-(--vscode-descriptionForeground)">
Set how long Cline waits for shell integration to activate before executing commands. Increase this
value if you experience terminal connection timeouts.
</p>
@@ -124,17 +124,17 @@ export const TerminalSettingsSection: React.FC<TerminalSettingsSectionProps> = (
Enable aggressive terminal reuse
</VSCodeCheckbox>
</div>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
<p className="text-xs text-(--vscode-descriptionForeground)">
When enabled, Cline will reuse existing terminal windows that aren't in the current working directory.
Disable this if you experience issues with task lockout after a terminal command.
</p>
</div>
<TerminalOutputLineLimitSlider />
<div className="mt-5 p-3 bg-[var(--vscode-textBlockQuote-background)] rounded border border-[var(--vscode-textBlockQuote-border)]">
<div className="mt-5 p-3 bg-(--vscode-textBlockQuote-background) rounded border border-(--vscode-textBlockQuote-border)">
<p className="text-[13px] m-0">
<strong>Having terminal issues?</strong> Check our{" "}
<a
className="text-[var(--vscode-textLink-foreground)] underline hover:no-underline"
className="text-(--vscode-textLink-foreground) underline hover:no-underline"
href="https://docs.cline.bot/troubleshooting/terminal-quick-fixes"
rel="noopener noreferrer"
target="_blank">
@@ -142,7 +142,7 @@ export const TerminalSettingsSection: React.FC<TerminalSettingsSectionProps> = (
</a>{" "}
or the{" "}
<a
className="text-[var(--vscode-textLink-foreground)] underline hover:no-underline"
className="text-(--vscode-textLink-foreground) underline hover:no-underline"
href="https://docs.cline.bot/troubleshooting/terminal-integration-guide"
rel="noopener noreferrer"
target="_blank">
+45
View File
@@ -0,0 +1,45 @@
import { cva, type VariantProps } from "class-variance-authority"
import * as React from "react"
import { cn } from "@/lib/utils"
const alertVariants = cva(
"relative w-full rounded-sm border px-1 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
{
variants: {
variant: {
default: "bg-code-background text-code-foreground",
error: "text-foreground bg-error/70 [&>svg]:text-current *:data-[slot=alert-foreground]:text-foreground/90 border border-error",
},
},
defaultVariants: {
variant: "default",
},
},
)
function Alert({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
return <div className={cn(alertVariants({ variant }), className)} data-slot="alert" role="alert" {...props} />
}
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
className={cn("pl-1 col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", className)}
data-slot="alert-title"
{...props}
/>
)
}
function AlertDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
className={cn("text-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed", className)}
data-slot="alert-description"
{...props}
/>
)
}
export { Alert, AlertTitle, AlertDescription }
+29
View File
@@ -0,0 +1,29 @@
import { cva, type VariantProps } from "class-variance-authority"
import * as React from "react"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
},
)
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return <div className={cn(badgeVariants({ variant }), className)} {...props} />
}
export { Badge, badgeVariants }
+48
View File
@@ -0,0 +1,48 @@
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import * as React from "react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-3 [&_svg]:shrink-0 cursor-pointer",
{
variants: {
variant: {
default: "bg-button-background text-primary-foreground shadow hover:bg-button-background-hover",
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-button-secondary-background text-button-secondary-foreground shadow-sm hover:bg-button-secondary-background-hover",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
icon: "bg-transparent hover:transparent p-0 h-auto m-0 border-0 pointer-cu",
},
size: {
default: "h-5 px-4 py-2",
sm: "h-3 rounded-md px-3 text-xs",
xs: "h-1 rounded-xs px-1 text-xs",
lg: "h-8 rounded-md px-8",
icon: "p-0 m-0",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
)
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
},
)
Button.displayName = "Button"
export { Button, buttonVariants }
+35
View File
@@ -0,0 +1,35 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
<div className={cn("rounded-xl border bg-card text-card-foreground shadow", className)} ref={ref} {...props} />
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
<div className={cn("flex flex-col space-y-1.5 p-6", className)} ref={ref} {...props} />
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
<div className={cn("font-semibold leading-none tracking-tight", className)} ref={ref} {...props} />
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
<div className={cn("text-sm text-muted-foreground", className)} ref={ref} {...props} />
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
<div className={cn("p-6 pt-0", className)} ref={ref} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
<div className={cn("flex items-center p-6 pt-0", className)} ref={ref} {...props} />
))
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
+124
View File
@@ -0,0 +1,124 @@
"use client"
import { type DialogProps } from "@radix-ui/react-dialog"
import { Command as CommandPrimitive } from "cmdk"
import { Search } from "lucide-react"
import * as React from "react"
import { Dialog, DialogContent } from "@/components/ui/dialog"
import { cn } from "@/lib/utils"
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
>(({ className, ...props }, ref) => (
<CommandPrimitive
className={cn("flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", className)}
ref={ref}
{...props}
/>
))
Command.displayName = CommandPrimitive.displayName
const CommandDialog = ({ children, ...props }: DialogProps) => {
return (
<Dialog {...props}>
<DialogContent className="overflow-hidden p-0">
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
{children}
</Command>
</DialogContent>
</Dialog>
)
}
const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<CommandPrimitive.Input
className={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
ref={ref}
{...props}
/>
</div>
))
CommandInput.displayName = CommandPrimitive.Input.displayName
const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => (
<CommandPrimitive.List className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)} ref={ref} {...props} />
))
CommandList.displayName = CommandPrimitive.List.displayName
const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => <CommandPrimitive.Empty className="py-6 text-center text-sm" ref={ref} {...props} />)
CommandEmpty.displayName = CommandPrimitive.Empty.displayName
const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Group
className={cn(
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
className,
)}
ref={ref}
{...props}
/>
))
CommandGroup.displayName = CommandPrimitive.Group.displayName
const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Separator className={cn("-mx-1 h-px bg-border", className)} ref={ref} {...props} />
))
CommandSeparator.displayName = CommandPrimitive.Separator.displayName
const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Item
className={cn(
"relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
className,
)}
ref={ref}
{...props}
/>
))
CommandItem.displayName = CommandPrimitive.Item.displayName
const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return <span className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)} {...props} />
}
CommandShortcut.displayName = "CommandShortcut"
export {
Command,
CommandDialog,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandShortcut,
CommandSeparator,
}
+113
View File
@@ -0,0 +1,113 @@
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react"
import * as React from "react"
import { cn } from "@/lib/utils"
function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />
}
function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
}
function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
}
function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
}
function DialogOverlay({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
return (
<DialogPrimitive.Overlay
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className,
)}
data-slot="dialog-overlay"
{...props}
/>
)
}
function DialogContent({
className,
children,
showCloseButton = true,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean
}) {
return (
<DialogPortal data-slot="dialog-portal">
<DialogOverlay />
<DialogPrimitive.Content
className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
className,
)}
data-slot="dialog-content"
{...props}>
{children}
{showCloseButton && (
<DialogPrimitive.Close
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
data-slot="dialog-close">
<XIcon />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
)
}
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return <div className={cn("flex flex-col gap-2 text-center sm:text-left", className)} data-slot="dialog-header" {...props} />
}
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
data-slot="dialog-footer"
{...props}
/>
)
}
function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
return (
<DialogPrimitive.Title
className={cn("text-lg leading-none font-semibold", className)}
data-slot="dialog-title"
{...props}
/>
)
}
function DialogDescription({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Description>) {
return (
<DialogPrimitive.Description
className={cn("text-muted-foreground text-sm", className)}
data-slot="dialog-description"
{...props}
/>
)
}
export {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
}
@@ -0,0 +1,177 @@
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { Check, ChevronRight, Circle } from "lucide-react"
import * as React from "react"
import { cn } from "@/lib/utils"
const DropdownMenu = DropdownMenuPrimitive.Root
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
const DropdownMenuGroup = DropdownMenuPrimitive.Group
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
const DropdownMenuSub = DropdownMenuPrimitive.Sub
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
className={cn(
"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
inset && "pl-8",
className,
)}
ref={ref}
{...props}>
{children}
<ChevronRight className="ml-auto" />
</DropdownMenuPrimitive.SubTrigger>
))
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName
const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.SubContent
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
className,
)}
ref={ref}
{...props}
/>
))
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName
const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
className={cn(
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
className,
)}
ref={ref}
sideOffset={sideOffset}
{...props}
/>
</DropdownMenuPrimitive.Portal>
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
className={cn(
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
inset && "pl-8",
className,
)}
ref={ref}
{...props}
/>
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem
checked={checked}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
)}
ref={ref}
{...props}>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
))
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
)}
ref={ref}
{...props}>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
className={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
ref={ref}
{...props}
/>
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator className={cn("-mx-1 my-1 h-px bg-muted", className)} ref={ref} {...props} />
))
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return <span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />
}
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
}
+128
View File
@@ -0,0 +1,128 @@
"use client"
import * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot"
import * as React from "react"
import { Controller, type ControllerProps, type FieldPath, type FieldValues, FormProvider, useFormContext } from "react-hook-form"
import { Label } from "@/components/ui/label"
import { cn } from "@/lib/utils"
const Form = FormProvider
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName
}
const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue)
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
)
}
const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext)
const itemContext = React.useContext(FormItemContext)
const { getFieldState, formState } = useFormContext()
const fieldState = getFieldState(fieldContext.name, formState)
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>")
}
const { id } = itemContext
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
}
}
type FormItemContextValue = {
id: string
}
const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue)
const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => {
const id = React.useId()
return (
<FormItemContext.Provider value={{ id }}>
<div className={cn("space-y-2", className)} ref={ref} {...props} />
</FormItemContext.Provider>
)
})
FormItem.displayName = "FormItem"
const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField()
return <Label className={cn(error && "text-destructive", className)} htmlFor={formItemId} ref={ref} {...props} />
})
FormLabel.displayName = "FormLabel"
const FormControl = React.forwardRef<React.ElementRef<typeof Slot>, React.ComponentPropsWithoutRef<typeof Slot>>(
({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
return (
<Slot
aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
aria-invalid={!!error}
id={formItemId}
ref={ref}
{...props}
/>
)
},
)
FormControl.displayName = "FormControl"
const FormDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField()
return <p className={cn("text-[0.8rem] text-muted-foreground", className)} id={formDescriptionId} ref={ref} {...props} />
},
)
FormDescription.displayName = "FormDescription"
const FormMessage = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message ?? "") : children
if (!body) {
return null
}
return (
<p className={cn("text-[0.8rem] font-medium text-destructive", className)} id={formMessageId} ref={ref} {...props}>
{body}
</p>
)
},
)
FormMessage.displayName = "FormMessage"
export { useFormField, Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField }
@@ -0,0 +1,37 @@
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
import * as React from "react"
import { cn } from "@/lib/utils"
const HoverCard = HoverCardPrimitive.Root
const HoverCardTrigger = HoverCardPrimitive.Trigger
function HoverCardContent({
className,
align = "center",
sideOffset = 4,
children,
...props
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
return (
<HoverCardPrimitive.Portal data-slot="hover-card-portal">
<HoverCardPrimitive.Content
align={align}
className={cn(
"bg-code text-code-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-xs p-2 shadow-md outline-hidden border border-muted-foreground/30",
className,
)}
data-slot="hover-card-content"
sideOffset={sideOffset}
{...props}>
{children}
<HoverCardPrimitive.Arrow className="bg-code fill-background z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] border-b border-r border-muted-foreground/30" />
</HoverCardPrimitive.Content>
</HoverCardPrimitive.Portal>
)
}
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
export { HoverCard, HoverCardTrigger, HoverCardContent }
+19
View File
@@ -0,0 +1,19 @@
import * as LabelPrimitive from "@radix-ui/react-label"
import * as React from "react"
import { cn } from "@/lib/utils"
function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
return (
<LabelPrimitive.Root
className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className,
)}
data-slot="label"
{...props}
/>
)
}
export { Label }
+39
View File
@@ -0,0 +1,39 @@
import * as PopoverPrimitive from "@radix-ui/react-popover"
import * as React from "react"
import { cn } from "@/lib/utils"
const Popover = PopoverPrimitive.Root
const PopoverTrigger = PopoverPrimitive.Trigger
const PopoverAnchor = PopoverPrimitive.Anchor
function PopoverContent({
className,
align = "center",
sideOffset = 4,
children,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
align={align}
className={cn(
"bg-menu text-base text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-xs border p-2 shadow-md outline-hidden border-menu-foreground/10",
className,
)}
data-slot="popover-content"
sideOffset={sideOffset}
{...props}>
{children}
<PopoverPrimitive.Arrow className="bg-menu fill-background z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-xs border-b border-r border-menu-foreground/10" />
</PopoverPrimitive.Content>
</PopoverPrimitive.Portal>
)
}
PopoverContent.displayName = PopoverPrimitive.Content.displayName
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
+22
View File
@@ -0,0 +1,22 @@
import * as ProgressPrimitive from "@radix-ui/react-progress"
import * as React from "react"
import { cn } from "@/lib/utils"
const Progress = React.forwardRef<
React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
>(({ className, value, ...props }, ref) => (
<ProgressPrimitive.Root
className={cn("relative h-2 w-full overflow-hidden rounded-full bg-code-foreground/20", className)}
ref={ref}
{...props}>
<ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-code-foreground transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
))
Progress.displayName = ProgressPrimitive.Root.displayName
export { Progress }
+136
View File
@@ -0,0 +1,136 @@
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown, ChevronUp } from "lucide-react"
import * as React from "react"
import { cn } from "@/lib/utils"
const Select = SelectPrimitive.Root
const SelectGroup = SelectPrimitive.Group
const SelectValue = SelectPrimitive.Value
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
className={cn(
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className,
)}
ref={ref}
{...props}>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
className={cn("flex cursor-default items-center justify-center py-1", className)}
ref={ref}
{...props}>
<ChevronUp className="h-4 w-4" />
</SelectPrimitive.ScrollUpButton>
))
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
className={cn("flex cursor-default items-center justify-center py-1", className)}
ref={ref}
{...props}>
<ChevronDown className="h-4 w-4" />
</SelectPrimitive.ScrollDownButton>
))
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
className={cn(
"relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className,
)}
position={position}
ref={ref}
{...props}>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
)}>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label className={cn("px-2 py-1.5 text-sm font-semibold", className)} ref={ref} {...props} />
))
SelectLabel.displayName = SelectPrimitive.Label.displayName
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
)}
ref={ref}
{...props}>
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator className={cn("-mx-1 my-1 h-px bg-muted", className)} ref={ref} {...props} />
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
}
+22
View File
@@ -0,0 +1,22 @@
import * as SliderPrimitive from "@radix-ui/react-slider"
import * as React from "react"
import { cn } from "@/lib/utils"
const Slider = React.forwardRef<
React.ElementRef<typeof SliderPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => (
<SliderPrimitive.Root
className={cn("relative flex w-full touch-none select-none items-center", className)}
ref={ref}
{...props}>
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
<SliderPrimitive.Range className="absolute h-full bg-primary" />
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
</SliderPrimitive.Root>
))
Slider.displayName = SliderPrimitive.Root.displayName
export { Slider }
+26
View File
@@ -0,0 +1,26 @@
import * as SwitchPrimitives from "@radix-ui/react-switch"
import * as React from "react"
import { cn } from "@/lib/utils"
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-3 w-7 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-success/80 data-[state=unchecked]:bg-description",
className,
)}
{...props}
ref={ref}>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-2 w-2 rounded-full bg-foreground shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0 data-[state=unchecked]:bg-code/80",
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName
export { Switch }
+72
View File
@@ -0,0 +1,72 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table className={cn("w-full caption-bottom text-sm", className)} ref={ref} {...props} />
</div>
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => <thead className={cn("[&_tr]:border-b", className)} ref={ref} {...props} />,
)
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => <tbody className={cn("[&_tr:last-child]:border-0", className)} ref={ref} {...props} />,
)
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => (
<tfoot className={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className)} ref={ref} {...props} />
),
)
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
({ className, ...props }, ref) => (
<tr
className={cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className)}
ref={ref}
{...props}
/>
),
)
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(
({ className, ...props }, ref) => (
<th
className={cn(
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className,
)}
ref={ref}
{...props}
/>
),
)
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(
({ className, ...props }, ref) => (
<td
className={cn("p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className)}
ref={ref}
{...props}
/>
),
)
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<HTMLTableCaptionElement, React.HTMLAttributes<HTMLTableCaptionElement>>(
({ className, ...props }, ref) => (
<caption className={cn("mt-4 text-sm text-muted-foreground", className)} ref={ref} {...props} />
),
)
TableCaption.displayName = "TableCaption"
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }
+47
View File
@@ -0,0 +1,47 @@
"use client"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
import * as React from "react"
import { cn } from "@/lib/utils"
function TooltipProvider({ delayDuration = 0, ...props }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
return <TooltipPrimitive.Provider data-slot="tooltip-provider" delayDuration={delayDuration} {...props} />
}
function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
return (
<TooltipProvider>
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
</TooltipProvider>
)
}
function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
}
function TooltipContent({
className,
sideOffset = 0,
children,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
className={cn(
"bg-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 origin-(--radix-tooltip-content-transform-origin) rounded-xs px-1 py-1.5 text-xs text-balance border border-muted-foreground/30 shadow-2xs mx-1",
className,
)}
data-slot="tooltip-content"
sideOffset={sideOffset}
{...props}>
{children}
<TooltipPrimitive.Arrow className="bg-background fill-background z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] border-b border-r border-muted-foreground/30" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
)
}
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -1,6 +1,7 @@
import { EmptyRequest } from "@shared/proto/cline/common"
import { InfoIcon } from "lucide-react"
import ClineLogoVariable from "@/assets/ClineLogoVariable"
import HeroTooltip from "@/components/common/HeroTooltip"
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"
import { UiServiceClient } from "@/services/grpc-client"
interface HomeHeaderProps {
@@ -22,20 +23,23 @@ const HomeHeader = ({ shouldShowQuickWins = false }: HomeHeaderProps) => {
<ClineLogoVariable className="size-16" />
</div>
<div className="text-center flex items-center justify-center">
<h2 className="m-0 text-lg">{"What can I do for you?"}</h2>
<HeroTooltip
className="max-w-[300px]"
content={
"I can develop software step-by-step by editing files, exploring projects, running commands, and using browsers. I can even extend my capabilities with MCP tools to assist beyond basic code completion."
}
placement="bottom">
<span className="codicon codicon-info ml-2 cursor-pointer text-link text-sm" />
</HeroTooltip>
<h2 className="m-0 text-md font-bold">What can I do for you?</h2>
<HoverCard>
<HoverCardContent className="h-full wrap-break-word">
<div className="space-y-1 text-description text-sm break-words">
I can develop software step-by-step by editing files, exploring projects, running commands, and using
browsers. I can even extend my capabilities with MCP tools to assist beyond basic code completion.
</div>
</HoverCardContent>
<HoverCardTrigger asChild>
<InfoIcon className="ml-2 cursor-pointer text-link text-sm size-3" />
</HoverCardTrigger>
</HoverCard>
</div>
{shouldShowQuickWins && (
<div className="mt-4">
<button
className="flex items-center gap-2 px-4 py-2 rounded-full border border-border-panel bg-white/[0.02] hover:bg-list-background-hover transition-colors duration-150 ease-in-out text-code-foreground text-sm font-medium cursor-pointer"
className="flex items-center gap-2 px-4 py-2 rounded-full border border-border-panel bg-white/2 hover:bg-list-background-hover transition-colors duration-150 ease-in-out text-code-foreground text-sm font-medium cursor-pointer"
onClick={handleTakeATour}
type="button">
Take a Tour
@@ -8,7 +8,7 @@ interface QuickWinCardProps {
const renderIcon = (iconName?: string) => {
if (!iconName) {
return <span className="codicon codicon-rocket !text-[28px] !leading-[1]"></span>
return <span className="codicon codicon-rocket text-[28px]! leading-none!"></span>
}
let iconClass = "codicon-rocket"
@@ -25,25 +25,23 @@ const renderIcon = (iconName?: string) => {
default:
break
}
return <span className={`codicon ${iconClass} !text-[28px] !leading-[1]`}></span>
return <span className={`codicon ${iconClass} text-[28px]! leading-none!`}></span>
}
const QuickWinCard: React.FC<QuickWinCardProps> = ({ task, onExecute }) => {
return (
<div
className="flex items-center mb-2 py-0 px-5 space-x-3 rounded-full cursor-pointer group transition-colors duration-150 ease-in-out bg-white/[0.02] border border-[var(--vscode-panel-border)] hover:bg-[var(--vscode-list-hoverBackground)]"
className="flex items-center mb-2 py-0 px-5 space-x-3 rounded-full cursor-pointer group transition-colors duration-150 ease-in-out bg-white/2 border border-(--vscode-panel-border) hover:bg-(--vscode-list-hoverBackground)"
onClick={() => onExecute()}>
<div className="flex-shrink-0 flex items-center justify-center w-6 h-6 text-[var(--vscode-icon-foreground)]">
<div className="shrink-0 flex items-center justify-center w-6 h-6 text-(--vscode-icon-foreground)">
{renderIcon(task.icon)}
</div>
<div className="flex-grow min-w-0">
<h3 className="text-sm font-medium truncate text-[var(--vscode-editor-foreground)] leading-tight mb-0 mt-0 pt-3">
<div className="grow min-w-0">
<h3 className="text-sm font-medium truncate text-(--vscode-editor-foreground) leading-tight mb-0 mt-0 pt-3">
{task.title}
</h3>
<p className="text-xs truncate text-[var(--vscode-descriptionForeground)] leading-tight mt-[1px]">
{task.description}
</p>
<p className="text-xs truncate text-(--vscode-descriptionForeground) leading-tight mt-px">{task.description}</p>
</div>
</div>
)
@@ -49,7 +49,7 @@ const WelcomeView = memo(() => {
create new tools and extend my own capabilities.
</p>
<p className="text-[var(--vscode-descriptionForeground)]">
<p className="text-(--vscode-descriptionForeground)">
Sign up for an account to get started for free, or use an API key that provides access to models like Claude
Sonnet.
</p>
+166 -181
View File
@@ -1,195 +1,180 @@
@layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
/* Disable Tailwind's CSS reset to preserve existing styles */
/* @import "tailwindcss/preflight.css" layer(base); */
@import "tailwindcss/utilities.css" layer(utilities);
@config "../tailwind.config.mjs";
@import "tailwindcss";
@import "tw-animate-css";
/* Import external CSS files */
@import url("../../node_modules/@vscode/codicons/dist/codicon.css");
@import url("../node_modules/@fontsource/azeret-mono/index.css");
textarea:focus {
outline: 1.5px solid var(--vscode-focusBorder, #007fd4);
@plugin "tailwindcss-animate";
@custom-variant dark (&:is(.dark *));
/* Editor theme mapping */
@theme {
--color-background: var(--vscode-sideBar-background);
--color-border: var(--vscode-focusBorder);
--color-border-panel: var(--vscode-panel-border);
--color-foreground: var(--vscode-foreground);
--color-shadow: var(--vscode-widget-shadow);
--color-code: var(--vscode-editor-background);
--color-code-foreground: var(--vscode-editor-foreground);
--color-code-border: var(--vscode-editor-border);
--color-sidebar-background: var(--vscode-sideBar-background);
--color-sidebar-foreground: var(--vscode-sideBar-foreground);
--color-input-foreground: var(--vscode-input-foreground);
--color-input-background: var(--vscode-input-background);
--color-input-border: var(--vscode-input-border);
--color-input-placeholder: var(--vscode-input-placeholderForeground);
--color-selection: var(--vscode-list-activeSelectionBackground);
--color-selection-foreground: var(--vscode-list-activeSelectionForeground);
--color-button-background: var(--vscode-button-background);
--color-button-hover: var(--vscode-button-hoverBackground);
--color-button-foreground: var(--vscode-button-foreground);
--color-button-separator: var(--vscode-button-separator);
--color-button-secondary-background: var(--vscode-button-secondaryBackground);
--color-button-secondary-background-hover: var(--vscode-button-secondaryHoverBackground);
--color-button-secondary-foreground: var(--vscode-button-secondaryForeground);
--color-muted: var(--vscode-editor-foldBackground);
--color-muted-foreground: var(--vscode-editor-foldPlaceholderForeground);
--color-menu: var(--vscode-menu-background);
--color-menu-foreground: var(--vscode-menu-foreground);
--color-menu-border: var(--vscode-menu-border);
--color-menu-shadow: var(--vscode-menu-shadow);
--color-link: var(--vscode-textLink-foreground);
--color-link-hover: var(--vscode-textLink-activeForeground);
--color-list-hover: var(--vscode-list-hoverBackground);
--color-badge-foreground: var(--vscode-badge-foreground);
--color-badge-background: var(--vscode-badge-background);
--color-banner-background: var(--vscode-banner-background);
--color-banner-foreground: var(--vscode-banner-foreground);
--color-banner-icon: var(--vscode-banner-iconForeground);
--color-toolbar-default: var(--vscode-toolbar-background);
--color-toolbar-hover: var(--vscode-toolbar-hoverBackground);
--color-error: var(--vscode-errorForeground);
--color-description: var(--vscode-descriptionForeground);
--color-success: var(--vscode-charts-green);
--color-warning: var(--vscode-charts-yellow);
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--font-azeret-mono: Azeret Mono, monospace;
--text-xl: calc(2 * var(--vscode-font-size));
--text-lg: calc(1.5 * var(--vscode-font-size));
--text-md: calc(1.25 * var(--vscode-font-size));
--text-base: var(--vscode-font-size);
--text-sm: var(--vscode-font-size);
--text-xs: calc(0.75 * var(--vscode-font-size));
--text-xxs: calc(0.5 * var(--vscode-font-size));
}
vscode-button::part(control):focus {
outline: none;
@theme inline {
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
/*
Use vscode native scrollbar styles
https://github.com/gitkraken/vscode-gitlens/blob/b1d71d4844523e8b2ef16f9e007068e91f46fd88/src/webviews/apps/home/home.scss
*/
html {
height: 100%;
width: 100%;
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
body {
margin: 0;
padding: 0;
line-height: 1.25;
height: 100%;
width: 100%;
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
}
#root {
height: 100%;
width: 100%;
}
body.scrollable,
.scrollable,
body.code-block-scrollable,
.code-block-scrollable {
border-color: transparent;
transition: border-color 0.7s linear;
}
body:hover.scrollable,
body:hover .scrollable,
body:focus-within.scrollable,
body:focus-within .scrollable,
body:hover.code-block-scrollable,
body:hover .code-block-scrollable,
body:focus-within.code-block-scrollable,
body:focus-within .code-block-scrollable {
border-color: var(--vscode-scrollbarSlider-background);
transition: none;
}
.scrollable::-webkit-scrollbar-corner {
background-color: transparent !important;
}
.scrollable::-webkit-scrollbar-thumb {
background-color: transparent;
border-color: inherit;
border-right-style: inset;
border-right-width: calc(100vw + 100vh);
border-radius: unset !important;
}
.scrollable::-webkit-scrollbar-thumb:hover {
border-color: var(--vscode-scrollbarSlider-hoverBackground);
}
.scrollable::-webkit-scrollbar-thumb:active {
border-color: var(--vscode-scrollbarSlider-activeBackground);
}
/*
Fix VSCode ignoring webkit scrollbar modifiers
https://github.com/microsoft/vscode/issues/213045
*/
@supports selector(::-webkit-scrollbar) {
html {
scrollbar-color: unset;
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
/*
The above scrollbar styling uses some transparent background color magic to accomplish its animation. However this doesn't play nicely with SyntaxHighlighter, so we need to set a background color for the code blocks' horizontal scrollbar. This actually has the unintended consequence of always showing the scrollbar which I prefer since it makes it more obvious that there is more content to scroll to.
*/
.code-block-scrollable::-webkit-scrollbar-track {
background: transparent;
}
.code-block-scrollable::-webkit-scrollbar-thumb {
background-color: var(--vscode-scrollbarSlider-background);
border-radius: 5px;
border: 2px solid transparent;
background-clip: content-box;
}
.code-block-scrollable::-webkit-scrollbar-thumb:hover {
background-color: var(--vscode-scrollbarSlider-hoverBackground);
}
.code-block-scrollable::-webkit-scrollbar-thumb:active {
background-color: var(--vscode-scrollbarSlider-activeBackground);
}
.code-block-scrollable::-webkit-scrollbar-corner {
background-color: transparent;
}
/*
Dropdown label
https://github.com/microsoft/vscode-webview-ui-toolkit/tree/main/src/dropdown#with-label
*/
.dropdown-container {
box-sizing: border-box;
display: flex;
flex-flow: column nowrap;
align-items: flex-start;
justify-content: flex-start;
}
.dropdown-container label {
display: block;
color: var(--vscode-foreground);
cursor: pointer;
font-size: var(--vscode-font-size);
line-height: normal;
margin-bottom: 2px;
}
/* Fix scrollbar in dropdown */
vscode-dropdown::part(listbox) {
border-color: var(--vscode-scrollbarSlider-background);
transition: none;
scrollbar-color: var(--vscode-scrollbarSlider-background) transparent;
}
/* Faded icon buttons in textfields */
.input-icon-button {
cursor: pointer;
opacity: 0.65;
}
.input-icon-button:hover {
opacity: 1;
}
.input-icon-button.disabled {
cursor: not-allowed;
opacity: 0.4;
}
.input-icon-button.disabled:hover {
opacity: 0.4;
}
/* Context mentions */
.mention-context-textarea-highlight {
background-color: color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
border-radius: 3px;
box-shadow: 0 0 0 0.5px color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
color: transparent;
/* padding: 0.5px;
margin: -0.5px;
position: relative;
bottom: -0.5px; */
}
.mention-context-highlight {
background-color: color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
border-radius: 3px;
}
.mention-context-highlight-with-shadow {
background-color: color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
border-radius: 3px;
box-shadow: 0 0 0 0.5px color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
}
.slash-command-match-textarea-highlight {
background-color: color-mix(in srgb, var(--vscode-focusBorder) 30%, transparent);
border-radius: 3px;
box-shadow: 0 0 0 0.5px color-mix(in srgb, var(--vscode-focusBorder) 30%, transparent);
color: transparent;
}
+6
View File
@@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
+185
View File
@@ -0,0 +1,185 @@
textarea:focus {
outline: 1.5px solid var(--vscode-focusBorder, #007fd4);
}
vscode-button::part(control):focus {
outline: none;
}
/*
Use vscode native scrollbar styles
https://github.com/gitkraken/vscode-gitlens/blob/b1d71d4844523e8b2ef16f9e007068e91f46fd88/src/webviews/apps/home/home.scss
*/
html {
height: 100%;
width: 100%;
}
body {
margin: 0;
padding: 0;
line-height: 1.25;
height: 100%;
width: 100%;
background-color: var(--vscode-sideBar-background);
}
#root {
height: 100%;
width: 100%;
}
body.scrollable,
.scrollable,
body.code-block-scrollable,
.code-block-scrollable {
border-color: transparent;
transition: border-color 0.7s linear;
}
body:hover.scrollable,
body:hover .scrollable,
body:focus-within.scrollable,
body:focus-within .scrollable,
body:hover.code-block-scrollable,
body:hover .code-block-scrollable,
body:focus-within.code-block-scrollable,
body:focus-within .code-block-scrollable {
border-color: var(--vscode-scrollbarSlider-background);
transition: none;
}
.scrollable::-webkit-scrollbar-corner {
background-color: transparent !important;
}
.scrollable::-webkit-scrollbar-thumb {
background-color: transparent;
border-color: inherit;
border-right-style: inset;
border-right-width: calc(100vw + 100vh);
border-radius: unset !important;
}
.scrollable::-webkit-scrollbar-thumb:hover {
border-color: var(--vscode-scrollbarSlider-hoverBackground);
}
.scrollable::-webkit-scrollbar-thumb:active {
border-color: var(--vscode-scrollbarSlider-activeBackground);
}
/*
Fix VSCode ignoring webkit scrollbar modifiers
https://github.com/microsoft/vscode/issues/213045
*/
@supports selector(::-webkit-scrollbar) {
html {
scrollbar-color: unset;
}
}
/*
The above scrollbar styling uses some transparent background color magic to accomplish its animation. However this doesn't play nicely with SyntaxHighlighter, so we need to set a background color for the code blocks' horizontal scrollbar. This actually has the unintended consequence of always showing the scrollbar which I prefer since it makes it more obvious that there is more content to scroll to.
*/
.code-block-scrollable::-webkit-scrollbar-track {
background: transparent;
}
.code-block-scrollable::-webkit-scrollbar-thumb {
background-color: var(--vscode-scrollbarSlider-background);
border-radius: 5px;
border: 2px solid transparent;
background-clip: content-box;
}
.code-block-scrollable::-webkit-scrollbar-thumb:hover {
background-color: var(--vscode-scrollbarSlider-hoverBackground);
}
.code-block-scrollable::-webkit-scrollbar-thumb:active {
background-color: var(--vscode-scrollbarSlider-activeBackground);
}
.code-block-scrollable::-webkit-scrollbar-corner {
background-color: transparent;
}
/*
Dropdown label
https://github.com/microsoft/vscode-webview-ui-toolkit/tree/main/src/dropdown#with-label
*/
.dropdown-container {
box-sizing: border-box;
display: flex;
flex-flow: column nowrap;
align-items: flex-start;
justify-content: flex-start;
}
.dropdown-container label {
display: block;
color: var(--vscode-foreground);
cursor: pointer;
font-size: var(--vscode-font-size);
line-height: normal;
margin-bottom: 2px;
}
/* Fix scrollbar in dropdown */
vscode-dropdown::part(listbox) {
border-color: var(--vscode-scrollbarSlider-background);
transition: none;
scrollbar-color: var(--vscode-scrollbarSlider-background) transparent;
}
/* Faded icon buttons in textfields */
.input-icon-button {
cursor: pointer;
opacity: 0.65;
}
.input-icon-button:hover {
opacity: 1;
}
.input-icon-button.disabled {
cursor: not-allowed;
opacity: 0.4;
}
.input-icon-button.disabled:hover {
opacity: 0.4;
}
/* Context mentions */
.mention-context-textarea-highlight {
background-color: color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
border-radius: 3px;
box-shadow: 0 0 0 0.5px color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
color: transparent;
/* padding: 0.5px;
margin: -0.5px;
position: relative;
bottom: -0.5px; */
}
.mention-context-highlight {
background-color: color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
border-radius: 3px;
}
.mention-context-highlight-with-shadow {
background-color: color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
border-radius: 3px;
box-shadow: 0 0 0 0.5px color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
}
.slash-command-match-textarea-highlight {
background-color: color-mix(in srgb, var(--vscode-focusBorder) 30%, transparent);
border-radius: 3px;
box-shadow: 0 0 0 0.5px color-mix(in srgb, var(--vscode-focusBorder) 30%, transparent);
color: transparent;
}
+1
View File
@@ -1,5 +1,6 @@
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import "./main.css"
import "./index.css"
import App from "./App.tsx"

Some files were not shown because too many files have changed in this diff Show More