Compare commits

...

16 Commits

Author SHA1 Message Date
abeatrix cab5183297 Merge branch 'main' into bee/webview-tailwind-v4 2025-10-15 05:46:08 +08:00
abeatrix 870e58280d Clean up 2025-10-14 12:39:58 +08:00
abeatrix 010a23c795 Fix e2e test 2025-10-14 12:38:19 +08:00
abeatrix da9dd52bf2 Merge branch 'main' into bee/webview-tailwind-v4 2025-10-14 12:28:26 +08:00
Beatrix Woo ff5b6584bc update logo id 2025-10-03 14:19:18 +08:00
Beatrix Woo 04204c14bf fix spacing and sizes 2025-10-03 13:59:38 +08:00
Beatrix Woo 40a5f5ffad Merge branch 'main' into bee/webview-tailwind-v4 2025-10-03 09:08:37 +08:00
abeatrix 9fff0bd34d Merge branch 'main' into bee/webview-tailwind-v4 2025-10-02 09:13:48 +08:00
abeatrix 2e7dd7f841 Unify styles with theme 2025-10-02 09:12:20 +08:00
abeatrix 18cf97431c Fix auto approve modal z-index number 2025-10-02 06:35:07 +08:00
abeatrix 805fb85371 Update mode switch test 2025-09-30 07:27:14 +08:00
abeatrix 488e377a44 clean up 2025-09-30 07:02:41 +08:00
abeatrix 16a378840b Update e2e test text 2025-09-30 06:56:27 +08:00
abeatrix 0bbc718766 import main.css 2025-09-30 06:47:12 +08:00
abeatrix 74c6d1d2bc Migrate HeroUITooltip to radix-ui shadcn components 2025-09-30 06:33:27 +08:00
abeatrix 82b549c2ca Set up Tailwind v4
npx @tailwindcss/upgrade                                                                                                                                   1 ↵
≈ tailwindcss v4.1.13

│ Searching for CSS files in the current directory and its subdirectories…

│ Migrating stylesheets…

│ ↳ Migrated stylesheet: `./src/index.css`

│ Updating dependencies…

│ ↳ Updated package: `tailwindcss`

│ ↳ Updated package: `@tailwindcss/vite`

│ Migrating templates…

│ ↳ Migrated templates for: `./src/index.css`

│ Verify the changes and commit them to your repository.
2025-09-30 05:50:41 +08:00
97 changed files with 2386 additions and 1387 deletions
+13 -13
View File
@@ -70,7 +70,7 @@
"noControlCharactersInRegex": "off",
"noShadowRestrictedNames": "off",
"noArrayIndexKey": "info",
"noAssignInExpressions": "warn"
"noAssignInExpressions": "info"
},
"complexity": {
"noUselessConstructor": "off",
@@ -82,7 +82,7 @@
"noStaticOnlyClass": "off"
},
"security": {
"noDangerouslySetInnerHtml": "warn"
"noDangerouslySetInnerHtml": "info"
}
}
},
@@ -114,17 +114,17 @@
"files": {
"includes": [
"**",
"!**/dist/**",
"!**/dist-*/**",
"!**/out/**",
"!**/evals/**",
"!**/playwright/**",
"!**/test-results/**",
"!**/node_modules/**",
"!**/webview-ui/build/**",
"!**/generated/**",
"!**/proto/**",
"!**/tests/specs/**"
"!**/dist",
"!**/dist-*",
"!**/out",
"!**/evals",
"!**/playwright",
"!**/test-results",
"!**/node_modules",
"!**/webview-ui/build",
"!**/generated",
"!**/proto",
"!**/tests/specs"
]
},
"plugins": [
+2 -2
View File
@@ -1,9 +1,9 @@
import execa from "execa"
import * as path from "path"
import * as fs from "fs"
import fetch from "node-fetch"
import * as os from "os"
import { installRequiredExtensions, configureExtensionSettings } from "./extensions"
import * as path from "path"
import { configureExtensionSettings, installRequiredExtensions } from "./extensions"
// Store temporary directories for cleanup
interface VSCodeResources {
@@ -124,7 +124,6 @@ export class WriteToFileToolHandler implements IFullyManagedTool {
const { relPath, absolutePath, fileExists, diff, content, newContent, workspaceContext } = result
// Handle approval flow
const sharedMessageProps: ClineSayTool = {
tool: fileExists ? "editedExistingFile" : "newFileCreated",
+2 -1
View File
@@ -43,7 +43,8 @@ e2e("Views - can set up API keys and navigate to Settings from Chat", async ({ s
await expect(providerSelectorInput).not.toBeVisible()
// Verify you are now in the chat page after setup was completed
const clineLogo = sidebar.getByRole("img").filter({ hasText: /^$/ }).locator("path")
// cline logo has the name "clline-logo"
const clineLogo = sidebar.locator(".size-20 > path")
await expect(clineLogo).toBeVisible()
const chatInputBox = sidebar.getByTestId("chat-input")
await expect(chatInputBox).toBeVisible()
+7 -6
View File
@@ -17,7 +17,7 @@ e2e("Chat - can send messages and switch between modes", async ({ helper, sideba
await expect(sidebar.getByText("API Request...")).toBeVisible()
// Starting a new task should clear the current chat view and show the recent tasks
await sidebar.getByRole("button", { name: "New Task" }).click()
await sidebar.getByRole("button", { name: "New Task", exact: true }).first().click()
await expect(sidebar.getByText("Recent Tasks")).toBeVisible()
await expect(sidebar.getByText("Hello, Cline!")).toBeVisible()
@@ -26,12 +26,13 @@ e2e("Chat - can send messages and switch between modes", async ({ helper, sideba
const actButton = sidebar.getByRole("switch", { name: "Act" })
const planButton = sidebar.getByRole("switch", { name: "Plan" })
await expect(actButton).toBeChecked()
await expect(planButton).not.toBeChecked()
// Act button should be active. It doesn't have c
await expect(actButton).toHaveAttribute("aria-checked", "true")
await expect(planButton).not.toHaveAttribute("aria-checked", "true")
await actButton.click()
await expect(actButton).not.toBeChecked()
await expect(planButton).toBeChecked()
await planButton.click()
await expect(planButton).toHaveAttribute("aria-checked", "true")
await expect(actButton).not.toHaveAttribute("aria-checked", "true")
// === slash commands preserve following text ===
await expect(inputbox).toHaveValue("")
+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": {}
}
+734 -24
View File
@@ -12,7 +12,14 @@
"@fontsource/azeret-mono": "^5.2.9",
"@heroui/react": "^2.8.0-beta.2",
"@paper-design/shaders-react": "^0.0.46",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tooltip": "^1.2.8",
"@vscode/webview-ui-toolkit": "^1.4.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"debounce": "^2.1.1",
"dompurify": "^3.2.4",
"fast-deep-equal": "^3.1.3",
@@ -35,12 +42,14 @@
"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",
"unified": "^11.0.5",
"uuid": "^9.0.1"
},
"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",
@@ -56,7 +65,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"
@@ -2893,6 +2902,15 @@
"react": ">=18 || >=19.0.0-rc.0"
}
},
"node_modules/@heroui/system-rsc/node_modules/clsx": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/@heroui/table": {
"version": "2.2.26",
"resolved": "https://registry.npmjs.org/@heroui/table/-/table-2.2.26.tgz",
@@ -2965,6 +2983,15 @@
"tailwindcss": ">=4.0.0"
}
},
"node_modules/@heroui/theme/node_modules/clsx": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/@heroui/toast": {
"version": "2.0.16",
"resolved": "https://registry.npmjs.org/@heroui/toast/-/toast-2.0.16.tgz",
@@ -3724,6 +3751,547 @@
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
"license": "BSD-3-Clause"
},
"node_modules/@radix-ui/primitive": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
"license": "MIT"
},
"node_modules/@radix-ui/react-arrow": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
"integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-context": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-escape-keydown": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-focus-guards": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
"integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-focus-scope": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-hover-card": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.15.tgz",
"integrity": "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-dismissable-layer": "1.1.11",
"@radix-ui/react-popper": "1.2.8",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-id": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
"integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-dismissable-layer": "1.1.11",
"@radix-ui/react-focus-guards": "1.1.3",
"@radix-ui/react-focus-scope": "1.1.7",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-popper": "1.2.8",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"aria-hidden": "^1.2.4",
"react-remove-scroll": "^2.6.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popper": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
"integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.0.0",
"@radix-ui/react-arrow": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.1",
"@radix-ui/react-use-rect": "1.1.1",
"@radix-ui/react-use-size": "1.1.1",
"@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-portal": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-presence": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-primitive": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "1.2.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-progress": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz",
"integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz",
"integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-dismissable-layer": "1.1.11",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-popper": "1.2.8",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-visually-hidden": "1.2.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-controllable-state": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-effect-event": "0.0.2",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-effect-event": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
"integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-escape-keydown": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
"integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-callback-ref": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
"integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
"license": "MIT",
"dependencies": {
"@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-size": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-visually-hidden": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
"integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
"license": "MIT"
},
"node_modules/@react-aria/breadcrumbs": {
"version": "3.5.28",
"resolved": "https://registry.npmjs.org/@react-aria/breadcrumbs/-/breadcrumbs-3.5.28.tgz",
@@ -3899,15 +4467,6 @@
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
"node_modules/@react-aria/focus/node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/@react-aria/form": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@react-aria/form/-/form-3.1.1.tgz",
@@ -4425,15 +4984,6 @@
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
"node_modules/@react-aria/utils/node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/@react-aria/visually-hidden": {
"version": "3.8.27",
"resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.27.tgz",
@@ -6697,7 +7247,7 @@
"version": "18.3.7",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"dev": true,
"devOptional": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^18.0.0"
@@ -7014,6 +7564,18 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/aria-hidden": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/aria-query": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
@@ -7359,6 +7921,18 @@
"node": ">=8"
}
},
"node_modules/class-variance-authority": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
"license": "Apache-2.0",
"dependencies": {
"clsx": "^2.1.1"
},
"funding": {
"url": "https://polar.sh/cva"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -7423,9 +7997,9 @@
}
},
"node_modules/clsx": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
@@ -8263,6 +8837,12 @@
"node": ">=8"
}
},
"node_modules/detect-node-es": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
"license": "MIT"
},
"node_modules/devlop": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
@@ -8775,6 +9355,15 @@
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-nonce": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@@ -11449,6 +12038,75 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/react-remove-scroll": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
"integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==",
"license": "MIT",
"dependencies": {
"react-remove-scroll-bar": "^2.3.7",
"react-style-singleton": "^2.2.3",
"tslib": "^2.1.0",
"use-callback-ref": "^1.3.3",
"use-sidecar": "^1.1.3"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-remove-scroll-bar": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
"license": "MIT",
"dependencies": {
"react-style-singleton": "^2.2.2",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-style-singleton": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
"license": "MIT",
"dependencies": {
"get-nonce": "^1.0.0",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-textarea-autosize": {
"version": "8.5.9",
"resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz",
@@ -12463,6 +13121,15 @@
"integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==",
"license": "MIT"
},
"node_modules/tailwindcss-animate": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz",
"integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==",
"license": "MIT",
"peerDependencies": {
"tailwindcss": ">=3.0.0 || insiders"
}
},
"node_modules/tapable": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz",
@@ -13016,6 +13683,27 @@
"browserslist": ">= 4.21.0"
}
},
"node_modules/use-callback-ref": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/use-composed-ref": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz",
@@ -13061,6 +13749,28 @@
}
}
},
"node_modules/use-sidecar": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
"license": "MIT",
"dependencies": {
"detect-node-es": "^1.1.0",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/use-sync-external-store": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
+11 -2
View File
@@ -20,7 +20,14 @@
"@fontsource/azeret-mono": "^5.2.9",
"@heroui/react": "^2.8.0-beta.2",
"@paper-design/shaders-react": "^0.0.46",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tooltip": "^1.2.8",
"@vscode/webview-ui-toolkit": "^1.4.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"debounce": "^2.1.1",
"dompurify": "^3.2.4",
"fast-deep-equal": "^3.1.3",
@@ -43,12 +50,14 @@
"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",
"unified": "^11.0.5",
"uuid": "^9.0.1"
},
"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",
@@ -64,7 +73,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"
@@ -5,11 +5,12 @@ import { VSCodeButton, VSCodeDivider, VSCodeDropdown, VSCodeOption, VSCodeTag }
import deepEqual from "fast-deep-equal"
import { memo, useCallback, useEffect, useRef, useState } from "react"
import { useInterval } from "react-use"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { type ClineUser, handleSignOut } from "@/context/ClineAuthContext"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { cn } from "@/lib/utils"
import { AccountServiceClient } from "@/services/grpc-client"
import { getEnvironmentColor } from "@/utils/environmentColors"
import HeroTooltip from "../common/HeroTooltip"
import { getClineEnvironmentClassname } from "@/utils/environmentColors"
import VSCodeButtonLink from "../common/VSCodeButtonLink"
import { AccountWelcomeView } from "./AccountWelcomeView"
import { CreditBalance } from "./CreditBalance"
@@ -38,17 +39,15 @@ type CachedData = {
const AccountView = ({ onDone, clineUser, organizations, activeOrganization }: AccountViewProps) => {
const { environment } = useExtensionState()
const titleColor = getEnvironmentColor(environment)
const titleColor = getClineEnvironmentClassname(environment)
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="m-0" style={{ color: titleColor }}>
Account
</h3>
<h3 className={cn("text-(--vscode-foreground) m-0", titleColor)}>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
@@ -310,21 +309,19 @@ 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">
{isLockedByRemoteConfig ? (
<HeroTooltip content="This cannot be changed while your organization has remote configuration enabled.">
<Tooltip>
<TooltipTrigger>
<VSCodeDropdown
className="w-full"
currentValue={dropdownValue}
@@ -339,23 +336,11 @@ export const ClineAccountView = ({ clineUser, userOrganizations, activeOrganizat
</VSCodeOption>
))}
</VSCodeDropdown>
</HeroTooltip>
) : (
<VSCodeDropdown
className="w-full"
currentValue={dropdownValue}
disabled={isLoading}
onChange={handleOrganizationChange}>
<VSCodeOption key="personal" value={uid}>
Personal
</VSCodeOption>
{userOrganizations?.map((org: UserOrganization) => (
<VSCodeOption key={org.organizationId} value={org.organizationId}>
{org.name}
</VSCodeOption>
))}
</VSCodeDropdown>
)}
</TooltipTrigger>
<TooltipContent hidden={isLockedByRemoteConfig}>
This cannot be changed while your organization has remote configuration enabled.
</TooltipContent>
</Tooltip>
{activeOrganization && (
<VSCodeTag className="text-xs p-2" title="Role">
{getMainRole(activeOrganization.roles)}
@@ -389,7 +374,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}
@@ -3,11 +3,14 @@ import { handleSignIn } from "@/context/ClineAuthContext"
import { useExtensionState } from "@/context/ExtensionStateContext"
import ClineLogoVariable from "../../assets/ClineLogoVariable"
// export const AccountWelcomeView = () => (
// <div className="flex flex-col items-center pr-3 gap-2.5">
// <ClineLogoWhite className="size-16 mb-4" />
export const AccountWelcomeView = () => {
const { environment } = useExtensionState()
return (
<div className="flex flex-col items-center pr-3">
<div className="flex flex-col items-center pr-3 gap-2.5">
<ClineLogoVariable className="size-16 mb-4" environment={environment} />
<p>
@@ -19,7 +22,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="font-bold text-2xl 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>
) : (
<>
@@ -55,6 +55,7 @@ const CreditsHistoryTable = ({ isLoading, usageData, paymentsData, showPayments
</VSCodeDataGridRow>
{usageData.map((row, index) => (
// biome-ignore lint/suspicious/noArrayIndexKey: use index as key
<VSCodeDataGridRow key={index}>
<VSCodeDataGridCell grid-column="1">
{formatTimestamp(row.createdAt)}
@@ -67,7 +68,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>
))}
@@ -88,6 +89,7 @@ const CreditsHistoryTable = ({ isLoading, usageData, paymentsData, showPayments
</VSCodeDataGridRow>
{paymentsData.map((row, index) => (
// biome-ignore lint/suspicious/noArrayIndexKey: use index as key
<VSCodeDataGridRow key={index}>
<VSCodeDataGridCell grid-column="1">{formatTimestamp(row.paidAt)}</VSCodeDataGridCell>
<VSCodeDataGridCell grid-column="2">{`$${formatDollars(row.amountCents)}`}</VSCodeDataGridCell>
@@ -97,7 +99,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>
))}
</>
@@ -52,9 +52,9 @@ export const StyledCreditDisplay = ({ balance }: { balance: number }) => {
const lastTwoDecimals = decimalPart.slice(2)
return (
<span className="font-azeret-mono font-light tabular-nums">
<span className="font-azeret-mono font-light tabular-nums text-2xl">
{wholePart}.{firstTwoDecimals}
<span className="text-[var(--vscode-descriptionForeground)]">{lastTwoDecimals}</span>
<span className="text-description text-2xl">{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)",
@@ -161,9 +161,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:">
@@ -8,7 +8,6 @@ import { useSize } from "react-use"
import styled from "styled-components"
import { BrowserSettingsMenu } from "@/components/browser/BrowserSettingsMenu"
import { ChatRowContent, ProgressIndicator } from "@/components/chat/ChatRow"
import { CheckpointControls } from "@/components/common/CheckpointControls"
import CodeBlock, { CODE_BLOCK_BG_COLOR } from "@/components/common/CodeBlock"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { FileServiceClient } from "@/services/grpc-client"
@@ -630,10 +629,6 @@ const BrowserCursor: React.FC<{ style?: CSSProperties }> = ({ style }) => {
const BrowserSessionRowContainer = styled.div`
padding: 10px 6px 10px 15px;
position: relative;
&:hover ${CheckpointControls} {
opacity: 1;
}
`
export default BrowserSessionRow
+3 -8
View File
@@ -27,7 +27,6 @@ import McpToolRow from "@/components/mcp/configuration/tabs/installed/server-row
import { useExtensionState } from "@/context/ExtensionStateContext"
import { FileServiceClient, TaskServiceClient, UiServiceClient } from "@/services/grpc-client"
import { findMatchingResourceOrTemplate, getMcpServerDisplayName } from "@/utils/mcp"
import { CheckpointControls } from "../common/CheckpointControls"
import CodeAccordian, { cleanPathPrefix } from "../common/CodeAccordian"
import { ErrorBlockTitle } from "./ErrorBlockTitle"
import ErrorRow from "./ErrorRow"
@@ -45,10 +44,6 @@ const _cancelledColor = "var(--vscode-descriptionForeground)"
const ChatRowContainer = styled.div`
padding: 10px 6px 10px 15px;
position: relative;
&:hover ${CheckpointControls} {
opacity: 1;
}
`
interface ChatRowProps {
@@ -1401,7 +1396,7 @@ export const ChatRowContent = memo(
}
return (
<>
<div>
{title && (
<div style={headerStyle}>
{icon}
@@ -1409,10 +1404,10 @@ export const ChatRowContent = memo(
</div>
)}
<WithCopyButton
className="pt-2.5"
onMouseUp={handleMouseUp}
position="bottom-right"
ref={contentRef}
style={{ paddingTop: 10 }}
textToCopy={question}>
<Markdown markdown={question} />
<OptionsButtons
@@ -1434,7 +1429,7 @@ export const ChatRowContent = memo(
/>
)}
</WithCopyButton>
</>
</div>
)
case "new_task":
return (
+71 -57
View File
@@ -1,4 +1,3 @@
import { cn } from "@heroui/react"
import { PulsingBorder } from "@paper-design/shaders-react"
import { mentionRegex, mentionRegexGlobal } from "@shared/context-mentions"
import { EmptyRequest, StringRequest } from "@shared/proto/cline/common"
@@ -19,12 +18,13 @@ 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"
import { cn } from "@/lib/utils"
import { FileServiceClient, ModelsServiceClient, StateServiceClient } from "@/services/grpc-client"
import {
ContextMenuOptionType,
@@ -589,13 +589,13 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
}
// Find the index of the next selectable option
const currentSelectableIndex = selectableOptions.findIndex((option) => option === options[prevIndex])
const currentSelectableIndex = selectableOptions.indexOf(options[prevIndex])
const newSelectableIndex =
(currentSelectableIndex + direction + selectableOptions.length) % selectableOptions.length
// Find the index of the selected option in the original options array
return options.findIndex((option) => option === selectableOptions[newSelectableIndex])
return options.indexOf(selectableOptions[newSelectableIndex])
})
return
}
@@ -1622,7 +1622,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-xs absolute bottom-5 left-6.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>
)}
@@ -1708,35 +1708,41 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
<div className="relative flex-1 min-w-0 h-5">
{/* ButtonGroup - always in DOM but visibility controlled */}
<ButtonGroup className="absolute top-0 left-0 right-0 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"
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"
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 />
@@ -1749,7 +1755,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
role="button"
tabIndex={0}
title="Select Model / API Provider">
<ModelButtonContent>{modelDisplayName}</ModelButtonContent>
<ModelButtonContent className="text-xs">{modelDisplayName}</ModelButtonContent>
</ModelDisplayButton>
</ModelButtonWrapper>
{showModelSelector && (
@@ -1772,30 +1778,38 @@ 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>
<TooltipContent
className="text-xs text-description p-2 flex flex-col gap-1"
hidden={shownTooltipMode === null}
side="top">
{`In ${shownTooltipMode === "act" ? "Act" : "Plan"} mode, Cline will ${shownTooltipMode === "act" ? "complete the task immediately" : "gather information to architect a plan"}`}
<p className="text-description/80 text-xs">
Toggle w/ <kbd className="text-muted-foreground mx-1">{togglePlanActKeys}</kbd>
</p>
</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
@@ -366,7 +366,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">
@@ -29,14 +29,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 />
)
@@ -47,20 +47,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 Request 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
details.title = "API Request"
+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>
@@ -61,7 +61,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">
{errorMessageToDisplay}
{requestId && <div>Request ID: {requestId}</div>}
{clineErrorMessage?.toLowerCase()?.includes("powershell") && (
@@ -83,9 +83,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
@@ -98,20 +96,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,48 @@ 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("configure")
}}>
<span className="codicon codicon-gear text-[10px]"></span>
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>
</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">MCP Servers</div>
<VSCodeButton
appearance="icon"
aria-label="Go to MCP server settings"
onClick={() => {
setIsVisible(false)
navigateToMcp("configure")
}}>
<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,11 @@
import { cn } from "@heroui/react"
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, TooltipTrigger } 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 +241,50 @@ const VoiceRecorder: React.FC<VoiceRecorderProps> = ({
? "Starting recording..."
: error
? `Error: ${error}`
: null
: "Voice Input"
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-error", 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 ${isRecording ? "mt-0.5" : "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-muted 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-muted opacity-60" key={action?.id}>
{action?.shortName}
{index < minusFavorites.length - 1 && ","}
</span>
@@ -73,21 +73,20 @@ const AutoApproveBar = ({ style }: AutoApproveBarProps) => {
return (
<div
className="px-[10px] mx-[15px] select-none rounded-[10px_10px_0_0]"
className="px-3.5 mx-3.5 select-none rounded-tl-sm rounded-tr-sm overflow-y-auto break-words"
style={{
borderTop: `0.5px solid color-mix(in srgb, ${getAsVar(VSC_TITLEBAR_INACTIVE_FOREGROUND)} 20%, transparent)`,
overflowY: "auto",
backgroundColor: isModalVisible ? CODE_BLOCK_BG_COLOR : "transparent",
...style,
}}>
<div
className="cursor-pointer py-[8px] pr-[2px] flex items-center justify-between gap-[8px]"
className="cursor-pointer py-1 pr-1 flex items-center justify-between gap-2"
onClick={() => {
setIsModalVisible((prev) => !prev)
}}
ref={buttonRef}>
<div
className="flex flex-nowrap items-center overflow-x-auto gap-[4px] whitespace-nowrap"
className="flex flex-nowrap items-center overflow-x-auto gap-1 whitespace-nowrap"
style={{
msOverflowStyle: "none",
scrollbarWidth: "none",
@@ -1,7 +1,8 @@
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import React from "react"
import styled from "styled-components"
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 { ActionMetadata } from "./types"
interface AutoApproveMenuItemProps {
@@ -14,46 +15,6 @@ interface AutoApproveMenuItemProps {
showIcon?: boolean
}
const CheckboxContainer = styled.div.withConfig({
shouldForwardProp: (prop) => !["isFavorited"].includes(prop),
})<{ isFavorited?: boolean; onClick?: (e: MouseEvent) => void; onMouseDown?: (e: React.MouseEvent) => void }>`
display: flex;
align-items: center;
justify-content: space-between; /* Push content to edges */
padding-left: 4px;
padding-right: 1px;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background-color: var(--vscode-textBlockQuote-background);
}
.left-content {
display: flex;
align-items: center;
gap: 8px;
}
.icon {
color: var(--vscode-foreground);
font-size: 14px;
}
.label {
color: var(--vscode-foreground);
font-size: 12px;
font-weight: 500;
}
.star {
color: ${(props) => (props.isFavorited ? "var(--vscode-terminal-ansiYellow)" : "var(--vscode-descriptionForeground)")};
opacity: ${(props) => (props.isFavorited ? 1 : 0.6)};
font-size: 14px;
}
`
const SubOptionAnimateIn = styled.div<{ show: boolean }>`
position: relative;
transform: ${(props) => (props.show ? "scaleY(1)" : "scaleY(0)")};
@@ -87,36 +48,44 @@ const AutoApproveMenuItem = ({
}
const content = (
<>
<ActionButtonContainer>
<HeroTooltip content={action.description} delay={500}>
<CheckboxContainer isFavorited={favorited} onClick={onChange}>
<div className="left-content">
<div className="w-full">
<ActionButtonContainer className="w-full">
<Tooltip>
<TooltipContent>{action.description}</TooltipContent>
<TooltipTrigger asChild>
<Button
className={cn("w-full flex text-sm items-center justify-start text-foreground gap-2")}
onClick={(e) => onChange(e as unknown as Event)}
size="icon"
variant="icon">
{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>
<Tooltip>
<TooltipContent>
{favorited ? "Remove from quick-access menu" : "Add to quick-access menu"}
</TooltipContent>
<TooltipTrigger asChild>
<span
className={cn("p-0.5 codicon", {
"codicon-star-full text-(--vscode-terminal-ansiYellow)": favorited,
"codicon-star-empty text-description opacity-60": !favorited,
})}
onClick={async (e) => {
e.stopPropagation()
if (action.id === "enableAll") {
return
}
await onToggleFavorite?.(action.id)
}}
/>
</TooltipTrigger>
</Tooltip>
)}
<VSCodeCheckbox checked={checked} />
{showIcon && <span className={`codicon ${action.icon} icon`}></span>}
<span className="label">{condensed ? action.shortName : action.label}</span>
</div>
</CheckboxContainer>
</HeroTooltip>
</Button>
</TooltipTrigger>
</Tooltip>
</ActionButtonContainer>
{action.subAction && !condensed && (
<SubOptionAnimateIn show={checked}>
@@ -129,7 +98,7 @@ const AutoApproveMenuItem = ({
/>
</SubOptionAnimateIn>
)}
</>
</div>
)
return content
@@ -1,8 +1,10 @@
import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { XIcon } from "lucide-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 { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useAutoApproveActions } from "@/hooks/useAutoApproveActions"
import { getAsVar, VSC_TITLEBAR_INACTIVE_FOREGROUND } from "@/utils/vscStyles"
@@ -125,35 +127,38 @@ 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-3.5 right-3.5 border border-(--vscode-editorGroup-border) rounded z-20 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-3.5 h-3.5 z-0 rotate-45 border-r border-b border-(--vscode-editorGroup-border) bg-code"
style={{
bottom: `calc(100vh - ${menuPosition}px)`,
right: arrowPosition,
background: CODE_BLOCK_BG_COLOR,
}}
/>
{/* 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>
<VSCodeButton appearance="icon" onClick={() => setIsVisible(false)}>
<span className="codicon codicon-close text-[10px]"></span>
</VSCodeButton>
<div className="overflow-y-auto p-3 flex-1 min-h-0 overscroll-contain w-full">
<div className="flex justify-between items-center mb-3 w-full">
<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-md font-semibold mb-1">Auto-approve Settings</div>
</TooltipTrigger>
</Tooltip>
<Button onClick={() => setIsVisible(false)} variant="icon">
<XIcon />
</Button>
</div>
<div className="mb-2.5">
<span className="text-[color:var(--vscode-foreground)] font-medium">Actions:</span>
<span className="text-foreground font-medium">Actions:</span>
</div>
<div
className="relative mb-6"
className="relative mb-6 w-full"
ref={itemsContainerRef}
style={{
columnCount: containerWidth > breakpoint ? 2 : 1,
@@ -162,7 +167,7 @@ const AutoApproveModal: React.FC<AutoApproveModalProps> = ({
{/* Vertical separator line - only visible in two-column mode */}
{containerWidth > breakpoint && (
<div
className="absolute left-1/2 top-0 bottom-0 w-[0.5px] opacity-20"
className="absolute left-1/2 top-0 bottom-0 opacity-20"
style={{
background: getAsVar(VSC_TITLEBAR_INACTIVE_FOREGROUND),
transform: "translateX(-50%)", // Center the line
@@ -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="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-base 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>
@@ -117,11 +117,11 @@ export const ActionButtons: React.FC<ActionButtonsProps> = ({
}
return (
<div className="flex px-[15px]">
<div className="flex px-3.5">
<VSCodeButton
appearance="icon"
aria-label={showScrollToBottom ? "Scroll to bottom" : "Scroll to top"}
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={showScrollToBottom ? handleScrollToBottom : handleScrollToTop}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
@@ -146,11 +146,11 @@ export const ActionButtons: React.FC<ActionButtonsProps> = ({
const opacity = canInteract || isStreaming ? 1 : 0.5
return (
<div className="flex px-[15px]" style={{ opacity }}>
<div className="flex px-3.5" style={{ opacity }}>
{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}
@@ -159,7 +159,7 @@ export const ActionButtons: React.FC<ActionButtonsProps> = ({
{secondaryText && secondaryAction && (
<VSCodeButton
appearance="secondary"
className={primaryText ? "flex-1" : "flex-[2]"}
className={primaryText ? "flex-1" : "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)
@@ -28,7 +28,7 @@ export const CheckpointError: React.FC<CheckpointErrorProps> = ({
return (
<div className="flex items-center justify-center w-full">
<Alert
className="rounded-sm border-0 bg-[var(--vscode-inputValidation-errorBackground)] text-[var(--vscode-inputValidation-errorForeground)] pl-1 pr-1.5 py-1"
className="rounded-sm border-0 bg-(--vscode-inputValidation-errorBackground) text-(--vscode-inputValidation-errorForeground) pl-1 pr-1.5 py-1"
color="warning"
description={
<div className="flex gap-2">
@@ -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"
@@ -29,12 +30,12 @@ const ConfirmationDialog = memo<{
onConfirm: (e: React.MouseEvent) => void
onCancel: (e: React.MouseEvent) => void
}>(({ onConfirm, onCancel }) => (
<div className="text-xs my-2 flex items-center gap-0 justify-between">
<div className="text-sm my-2 flex items-center gap-0 justify-between">
<span className="font-semibold text-sm">Compact the current task?</span>
<span className="flex gap-1">
<VSCodeButton
appearance="secondary"
className="text-xs"
className="text-sm"
onClick={onCancel}
title="No, keep the task as is"
type="button">
@@ -43,7 +44,7 @@ const ConfirmationDialog = memo<{
<VSCodeButton
appearance="primary"
autoFocus={true}
className="text-xs"
className="text-sm"
onClick={onConfirm}
title="Yes, compact the task"
type="button">
@@ -205,14 +206,13 @@ const ContextWindow: React.FC<ContextWindowProgressProps> = ({
return (
<div className="flex flex-col my-1.5" onMouseLeave={debounceCloseHover}>
<div className="flex gap-1 flex-row @max-xs:flex-col @max-xs:items-start items-center text-sm">
<div className="flex items-center gap-1.5 flex-1 whitespace-nowrap text-xs">
<span className="cursor-pointer" title="Current tokens used in this request">
<div className="flex items-center gap-1.5 flex-1 whitespace-nowrap">
<span className="cursor-pointer text-sm" title="Current tokens used in this request">
{formatTokenNumber(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">
<ContextWindowSummary
autoCompactThreshold={useAutoCondense ? threshold : undefined}
cacheReads={cacheReads}
@@ -223,54 +223,40 @@ 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-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-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">
<span className="cursor-pointer text-sm" title="Maximum context window size for this model">
{formatTokenNumber(tokenData.max)}
</span>
</div>
@@ -42,9 +42,9 @@ const AccordionItem = memo<{
)
return (
<div className="flex flex-col">
<div className="flex flex-col w-full">
<div
className="flex justify-between items-center gap-3 cursor-pointer hover:bg-foreground/5 rounded px-1 py-0.5 transition-colors"
className="flex justify-between items-center gap-1 cursor-pointer hover:bg-foreground/5 rounded p-0.5 transition-colors w-full"
onClick={handleClick}>
<div className="flex items-center gap-1">
{isExpanded ? <ChevronDownIcon size={12} /> : <ChevronRightIcon size={12} />}
@@ -52,7 +52,7 @@ const AccordionItem = memo<{
</div>
<div className="text-muted-foreground text-sm">{value}</div>
</div>
{isExpanded && children && <div className="ml-4 mt-2 mb-1 text-xs text-muted-foreground">{children}</div>}
{isExpanded && children && <div className="ml-5 my-1 text-xs text-muted-foreground">{children}</div>}
</div>
)
})
@@ -77,13 +77,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>
))}
@@ -124,7 +121,7 @@ export const ContextWindowSummary: React.FC<TaskContextWindowButtonsProps> = ({
const totalTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 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 bg-menu rounded shadow-sm z-100 w-60 p-1">
{autoCompactThreshold > 0 && (
<AccordionItem
isExpanded={expandedSections.has("threshold")}
@@ -146,7 +143,7 @@ export const ContextWindowSummary: React.FC<TaskContextWindowButtonsProps> = ({
isExpanded={expandedSections.has("context")}
onToggle={(event) => toggleSection("context", event)}
title="Context Window"
value={percentage ? `${percentage.toFixed(1)}% used` : formatTokenNumber(contextWindow)}>
value={percentage ? `${percentage.toFixed(1)}%` : formatTokenNumber(contextWindow)}>
<div className="space-y-1">
<div className="flex justify-between">
<span>Used:</span>
@@ -168,7 +165,7 @@ export const ContextWindowSummary: React.FC<TaskContextWindowButtonsProps> = ({
isExpanded={expandedSections.has("tokens")}
onToggle={(event) => toggleSection("tokens", event)}
title="Token Usage"
value={`${formatTokenNumber(totalTokens)} total`}>
value={`${formatTokenNumber(totalTokens)}`}>
<TokenUsageDetails
cacheReads={cacheReads}
cacheWrites={cacheWrites}
@@ -55,7 +55,6 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
checkpointManagerErrorMessage,
clineMessages,
navigateToSettings,
useAutoCondense,
mode,
expandTaskHeader: isTaskExpanded,
setExpandTaskHeader: setIsTaskExpanded,
@@ -100,10 +99,10 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
{/* Task Header */}
<div
className={cn(
"relative overflow-hidden cursor-pointer 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 rounded-sm flex flex-col gap-1.5 z-10 pt-2 pb-2 px-2 hover:opacity-100 bg-(--vscode-toolbar-hoverBackground)/65",
{
"opacity-100 border-1": isTaskExpanded, // No hover effects when expanded, add border
"hover:bg-[var(--vscode-toolbar-hoverBackground)] border-1": !isTaskExpanded, // Hover effects only when collapsed
"hover:bg-(--vscode-toolbar-hoverBackground) border-1": !isTaskExpanded, // Hover effects only when collapsed
},
)}
style={{
@@ -114,7 +113,7 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
<div className="flex justify-between items-center">
{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="mt-1 max-h-3 flex justify-end flex-wrap cursor-pointer opacity-80 gap-2 mx-2">
<CopyTaskButton className={BUTTON_CLASS} taskText={task.text} />
<DeleteTaskButton
className={BUTTON_CLASS}
@@ -128,19 +127,19 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
</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">
<span className="ph-no-capture">{highlightText(task.text, false)}</span>
<div className="whitespace-nowrap overflow-hidden text-ellipsis grow min-w-0">
<span className="ph-no-capture text-base">{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"
id="price-tag">
<span className="text-xs">${totalCost?.toFixed(4)}</span>
<span className="text-sm">${totalCost?.toFixed(4)}</span>
</div>
)}
<NewTaskButton className={BUTTON_CLASS} onClick={onClose} />
@@ -150,10 +149,10 @@ 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 mt-1"
"ph-no-capture overflow-hidden whitespace-pre-wrap break-words px-0.5 text-base cursor-pointer my-1 pt-1"
}>
{highlightedText}
</div>
@@ -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,34 +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 { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { cn } from "@/lib/utils"
const CompactTaskButton: React.FC<{
className?: string
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
}> = ({ onClick, className }) => {
return (
<Tooltip
content={
<div className="flex flex-col gap-1 bg-menu rounded shadow-sm border border-menu-border z-100 max-w-xs py-1 px-2">
<div className="text-xs font-medium">Compact Task</div>
<div className="text-xs text-muted-foreground">
Reduces the number of tokens used by summarizing the task.
</div>
</div>
}
delay={0}
disableAnimation={true}
placement="bottom">
<VSCodeButton
appearance="icon"
className={cn(
"text-foreground flex items-center text-sm font-bold hover:bg-transparent hover:opacity-80",
className,
)}
onClick={onClick}
type="button">
<FoldVerticalIcon size={12} />
</VSCodeButton>
<Tooltip>
<TooltipContent side="bottom">Compact Task</TooltipContent>
<TooltipTrigger className={cn("flex items-center", className)}>
<Button
aria-label="Compact Task"
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
onClick(e)
}}
size="icon"
variant="icon">
<FoldVerticalIcon />
</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"
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="13" /> : <CopyIcon size="13" />}
</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 /> : <CopyIcon />}
</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="13" />
</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 />
</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="New Task"
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
onClick()
}}
size="icon"
variant="icon">
<XIcon />
</Button>
</TooltipTrigger>
</Tooltip>
)
}
@@ -1,7 +1,8 @@
import { Button, cn } from "@heroui/react"
import { StringRequest } from "@shared/proto/cline/common"
import { ArrowDownToLineIcon } 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 { FileServiceClient } from "@/services/grpc-client"
const OpenDiskConversationHistoryButton: React.FC<{
@@ -19,18 +20,20 @@ const OpenDiskConversationHistoryButton: React.FC<{
}
return (
<HeroTooltip content="Open Conversation History File" placement="right">
<Button
aria-label="Open Disk Conversation History"
className={cn("flex items-center border-0 text-sm font-bold bg-transparent hover:opacity-100", className)}
isIconOnly={true}
onPress={() => handleOpenDiskConversationHistory()}
radius="sm"
size="sm">
<ArrowDownToLineIcon size="14" />
</Button>
</HeroTooltip>
<Tooltip>
<TooltipContent>Open Conversation History File</TooltipContent>
<TooltipTrigger className={cn("flex items-center", className)}>
<Button
aria-label="Open Disk Conversation History"
onClick={() => handleOpenDiskConversationHistory()}
size="xs"
variant="icon">
<ArrowDownToLineIcon />
</Button>
</TooltipTrigger>
</Tooltip>
)
}
OpenDiskConversationHistoryButton.displayName = "OpenDiskConversationHistoryButton"
export default OpenDiskConversationHistoryButton
@@ -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 p-2 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>
)
}
@@ -75,7 +75,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 p-2 rounded bg-(--vscode-textCodeBlock-background) h-[18px] ${
enabled ? "opacity-100" : "opacity-60"
}`}>
<span className="flex-1 overflow-hidden break-all whitespace-normal flex items-center mr-1" title={rulePath}>
@@ -89,8 +89,8 @@ const RuleRow: React.FC<{
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"
? "bg-(--vscode-testing-iconPassed) opacity-90"
: "bg-(--vscode-titleBar-inactiveForeground) opacity-50"
}`}
onClick={() => toggleRule(rulePath, !enabled)}
onKeyDown={(e) => {
@@ -102,7 +102,7 @@ const RuleRow: React.FC<{
role="switch"
tabIndex={0}>
<div
className={`w-[6px] h-[6px] bg-white border border-[#66666699] rounded-full absolute top-[1px] transition-all duration-200 ${
className={`w-[6px] h-[6px] bg-white border border-[#66666699] rounded-full absolute top-px transition-all duration-200 ${
enabled ? "left-[12px]" : "left-[2px]"
}`}
/>
@@ -45,7 +45,7 @@ const RulesToggleList = ({
) : (
<>
{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>
)}
@@ -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>
@@ -284,7 +284,7 @@ const Container = styled.div<{ isMenuOpen?: boolean; $isCheckedOut?: boolean }>`
margin-top: -10px;
margin-bottom: -10px;
opacity: ${(props) => (props.$isCheckedOut ? 1 : props.isMenuOpen ? 1 : 0.5)};
height: 0.5rem;
&:hover {
opacity: 1;
}
@@ -313,7 +313,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 }>`
@@ -333,7 +333,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 }>`
@@ -150,7 +150,7 @@ const CodeBlock = memo(({ source, forceWrap = false }: CodeBlockProps) => {
maxHeight: forceWrap ? "none" : "100%",
backgroundColor: CODE_BLOCK_BG_COLOR,
}}>
<StyledMarkdown className="ph-no-capture" forceWrap={forceWrap}>
<StyledMarkdown className="ph-no-capture markdown" forceWrap={forceWrap}>
{reactContent}
</StyledMarkdown>
</div>
@@ -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
+10 -10
View File
@@ -1,5 +1,6 @@
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { XIcon } from "lucide-react"
import { useCallback } from "react"
import { Button } from "@/components/ui/button"
import { PlatformType } from "@/config/platform.config"
import { usePlatform } from "@/context/PlatformContext"
import { StateServiceClient } from "@/services/grpc-client"
@@ -13,27 +14,26 @@ export const InfoBanner: React.FC = () => {
if (usePlatform().type === PlatformType.VSCODE) {
return (
<a
className="bg-banner-background px-3 py-2 flex flex-col gap-1 shrink-0 mb-1 relative text-sm m-4 no-underline transition-colors hover:brightness-120"
className="bg-banner-background px-3 py-2 flex flex-col gap-1 shrink-0 mb-1 relative text-sm m-4 no-underline transition-colors hover:brightness-120 rounded-sm"
href="https://docs.cline.bot/features/customization/opening-cline-in-sidebar"
rel="noopener noreferrer"
style={{ color: "var(--vscode-foreground)", outline: "none" }}
target="_blank">
<h4 className="m-0" style={{ paddingRight: "18px" }}>
💡 Cline in the Right Sidebar
</h4>
<h4 className="m-0">💡 Cline in the Right Sidebar</h4>
<p className="m-0">
Keep your files visible when chatting with Cline. Drag the Cline icon to the right sidebar panel for a better
experience. <span className="text-link cursor-pointer">See how </span>
</p>
{/* Close button */}
<VSCodeButton
appearance="icon"
<Button
className="absolute top-2.5 right-2"
data-testid="info-banner-close-button"
onClick={handleClose}
style={{ position: "absolute", top: "6px", right: "6px" }}>
<span className="codicon codicon-close"></span>
</VSCodeButton>
size="icon"
variant="icon">
<XIcon />
</Button>
</a>
)
}
@@ -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>
@@ -60,13 +60,17 @@ const remarkUrlToLink = () => {
visit(tree, "text", (node: any, index, parent) => {
const urlRegex = /https?:\/\/[^\s<>)"]+/g
const matches = node.value.match(urlRegex)
if (!matches) return
if (!matches) {
return
}
const parts = node.value.split(urlRegex)
const children: any[] = []
parts.forEach((part: string, i: number) => {
if (part) children.push({ type: "text", value: part })
if (part) {
children.push({ type: "text", value: part })
}
if (matches[i]) {
children.push({
type: "link",
@@ -97,19 +101,25 @@ const remarkHighlightActMode = () => {
// Added negative lookahead to avoid matching if already followed by the shortcut
const actModeRegex = /\bto\s+Act\s+Mode\b(?!\s*\(⌘⇧A\))/i
if (!node.value.match(actModeRegex)) return
if (!node.value.match(actModeRegex)) {
return
}
// Split the text by the matches
const parts = node.value.split(actModeRegex)
const matches = node.value.match(actModeRegex)
if (!matches || parts.length <= 1) return
if (!matches || parts.length <= 1) {
return
}
const children: any[] = []
parts.forEach((part: string, i: number) => {
// Add the text before the match
if (part) children.push({ type: "text", value: part })
if (part) {
children.push({ type: "text", value: part })
}
// Add the match, but only make "Act Mode" bold (not the "to" part)
if (matches[i]) {
@@ -157,22 +167,32 @@ const remarkPreventBoldFilenames = () => {
return (tree: any) => {
visit(tree, "strong", (node: any, index: number | undefined, parent: any) => {
// Only process if there's a next node (potential file extension)
if (!parent || typeof index === "undefined" || index === parent.children.length - 1) return
if (!parent || typeof index === "undefined" || index === parent.children.length - 1) {
return
}
const nextNode = parent.children[index + 1]
// Check if next node is text and starts with . followed by extension
if (nextNode.type !== "text" || !nextNode.value.match(/^\.[a-zA-Z0-9]+/)) return
if (nextNode.type !== "text" || !nextNode.value.match(/^\.[a-zA-Z0-9]+/)) {
return
}
// If the strong node has multiple children, something weird is happening
if (node.children?.length !== 1) return
if (node.children?.length !== 1) {
return
}
// Get the text content from inside the strong node
const strongContent = node.children?.[0]?.value
if (!strongContent || typeof strongContent !== "string") return
if (!strongContent || typeof strongContent !== "string") {
return
}
// Validate that the strong content is a valid filename
if (!strongContent.match(/^[a-zA-Z0-9_-]+$/)) return
if (!strongContent.match(/^[a-zA-Z0-9_-]+$/)) {
return
}
// Combine into a single text node
const newNode = {
@@ -285,7 +305,9 @@ const PreWithCopyButton = ({ children, ...preProps }: React.HTMLAttributes<HTMLP
const codeElement = preRef.current.querySelector("code")
const textToCopy = codeElement ? codeElement.textContent : preRef.current.textContent
if (!textToCopy) return
if (!textToCopy) {
return
}
return textToCopy
}
return null
@@ -307,7 +329,7 @@ const PreWithCopyButton = ({ children, ...preProps }: React.HTMLAttributes<HTMLP
const remarkFilePathDetection = () => {
return async (tree: Node) => {
const fileNameRegex = /^(?!\/)[\w\-./]+(?<!\/)$/
const inlineCodeNodes: any[] = []
const _inlineCodeNodes: any[] = []
const filePathPromises: Promise<void>[] = []
// Collect all inline code nodes that might be file paths
@@ -385,7 +407,7 @@ const MarkdownBlock = memo(({ markdown, compact }: 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"
@@ -401,9 +423,12 @@ const MarkdownBlock = memo(({ markdown, compact }: MarkdownBlockProps) => {
// Handle both string children and array of children cases
const childrenText = React.Children.toArray(props.children)
.map((child) => {
if (typeof child === "string") return child
if (typeof child === "object" && "props" in child && child.props.children)
if (typeof child === "string") {
return child
}
if (typeof child === "object" && "props" in child && child.props.children) {
return String(child.props.children)
}
return ""
})
.join("")
@@ -1,8 +1,8 @@
import { EmptyRequest, Int64Request } from "@shared/proto/index.cline"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { Megaphone } from "lucide-react"
import { Megaphone, XIcon } from "lucide-react"
import { useCallback } from "react"
import { useMount } from "react-use"
import { Button } from "@/components/ui/button"
import { useClineAuth } from "@/context/ClineAuthContext"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { AccountServiceClient, StateServiceClient } from "@/services/grpc-client"
@@ -67,14 +67,12 @@ export const NewModelBanner: React.FC = () => {
return (
<div
className="px-3 py-2 flex flex-col gap-1 shrink-0 mb-1 relative text-sm mt-1.5 m-4 no-underline transition-colors hover:brightness-120 border-0 cursor-pointer text-left w-auto"
className="px-3 py-2 flex flex-col gap-1 shrink-0 mb-1 relative text-sm mt-1.5 m-4 no-underline transition-colors hover:brightness-120 border-0 cursor-pointer text-left w-auto rounded-sm"
onClick={handleBannerClick}
style={{
backgroundColor: getAsVar(VSC_INACTIVE_SELECTION_BACKGROUND),
borderRadius: "3px",
color: "var(--vscode-foreground)",
}}>
<h4 className="m-0 flex items-center gap-2" style={{ paddingRight: "18px" }}>
<h4 className="m-0 flex items-center gap-2">
<Megaphone className="w-4 h-4" />
Claude Sonnet 4.5
</h4>
@@ -84,13 +82,14 @@ export const NewModelBanner: React.FC = () => {
</p>
{/* Close button */}
<VSCodeButton
appearance="icon"
<Button
className="absolute top-2.5 right-2"
data-testid="info-banner-close-button"
onClick={handleClose}
style={{ position: "absolute", top: "6px", right: "6px" }}>
<span className="codicon codicon-close"></span>
</VSCodeButton>
size="icon"
variant="icon">
<XIcon />
</Button>
</div>
)
}
@@ -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
@@ -29,16 +29,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>
@@ -298,17 +298,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
}
`}
</style>
<div
style={{
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
display: "flex",
flexDirection: "column",
overflow: "hidden",
}}>
<div className="fixed overflow-hidden inset-0 flex flex-col">
<div
style={{
display: "flex",
@@ -326,12 +316,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
<VSCodeButton onClick={() => onDone()}>Done</VSCodeButton>
</div>
<div style={{ padding: "5px 17px 6px 17px" }}>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "6px",
}}>
<div className="flex flex-col gap-3">
<VSCodeTextField
onInput={(e) => {
const newValue = (e.target as HTMLInputElement)?.value
@@ -368,8 +353,8 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
)}
</VSCodeTextField>
<VSCodeRadioGroup
className="flex flex-wrap"
onChange={(e) => setSortOption((e.target as HTMLInputElement).value as SortOption)}
style={{ display: "flex", flexWrap: "wrap" }}
value={sortOption}>
<VSCodeRadio value="newest">Newest</VSCodeRadio>
<VSCodeRadio value="oldest">Oldest</VSCodeRadio>
@@ -8,10 +8,10 @@ type AddLocalServerFormProps = {
onServerAdded: () => void
}
const AddLocalServerForm = ({ onServerAdded }: AddLocalServerFormProps) => {
const AddLocalServerForm = ({}: 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>
)
+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, TooltipTrigger } 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>
<TooltipTrigger asChild>
<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>
</TooltipTrigger>
</Tooltip>
))}
</nav>
)
@@ -5,8 +5,8 @@ import Fuse from "fuse.js"
import { KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from "react"
import { useInterval } from "react-use"
import styled from "styled-components"
import HeroTooltip from "@/components/common/HeroTooltip"
import { normalizeApiConfiguration } from "@/components/settings/utils/providerUtils"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { PLATFORM_CONFIG, PlatformType } from "@/config/platform.config"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { ModelsServiceClient } from "@/services/grpc-client"
@@ -297,14 +297,17 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is
</style>
<DropdownContainer className="dropdown-container">
{remoteConfigSettings?.planModeApiProvider !== undefined ? (
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<div className="flex items-center gap-2 mb-1">
<label htmlFor="api-provider">
<span style={{ fontWeight: 500 }}>API Provider</span>
</label>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
</HeroTooltip>
<Tooltip>
<TooltipTrigger>
<div className="flex items-center gap-2 mb-1">
<label htmlFor="api-provider">
<span style={{ fontWeight: 500 }}>API Provider</span>
</label>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
</TooltipTrigger>
<TooltipContent>This setting is managed by your organization's remote configuration</TooltipContent>
</Tooltip>
) : (
<label htmlFor="api-provider">
<span style={{ fontWeight: 500 }}>API Provider</span>
@@ -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.
@@ -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"
@@ -14,7 +15,7 @@ import {
} 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 { getEnvironmentColor } from "@/utils/environmentColors"
@@ -33,11 +34,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 {
@@ -251,16 +252,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>
)
}
@@ -50,19 +50,12 @@ export const ModelSelector = ({ models, selectedModelId, onChange, zIndex, label
return (
<DropdownContainer className="dropdown-container" zIndex={zIndex}>
<label htmlFor="model-id">
<span style={{ fontWeight: 500 }}>{label}</span>
<span className="font-medium">{label}</span>
</label>
<VSCodeDropdown id="model-id" onChange={onChange} style={{ width: "100%" }} value={selectedModelId}>
<VSCodeDropdown className="w-full" id="model-id" onChange={onChange} value={selectedModelId}>
<VSCodeOption value="">Select a model...</VSCodeOption>
{Object.keys(models).map((modelId) => (
<VSCodeOption
key={modelId}
style={{
whiteSpace: "normal",
wordWrap: "break-word",
maxWidth: "100%",
}}
value={modelId}>
<VSCodeOption className="break-words whitespace-normal max-w-full" key={modelId} value={modelId}>
{modelId}
</VSCodeOption>
))}
@@ -2,7 +2,7 @@ import { bedrockDefaultModelId, bedrockModels, CLAUDE_SONNET_1M_SUFFIX } from "@
import { Mode } from "@shared/storage/types"
import { VSCodeCheckbox, VSCodeDropdown, VSCodeOption, VSCodeRadio, VSCodeRadioGroup } from "@vscode/webview-ui-toolkit/react"
import { useState } from "react"
import HeroTooltip from "@/components/common/HeroTooltip"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { DebouncedTextField } from "../common/DebouncedTextField"
import { ModelInfoView } from "../common/ModelInfoView"
@@ -97,51 +97,54 @@ export const BedrockProvider = ({ showModelOptions, isPopup, currentMode }: Bedr
)}
{remoteConfigSettings?.awsRegion !== undefined ? (
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<DropdownContainer className="dropdown-container mb-2.5" zIndex={DROPDOWN_Z_INDEX - 1}>
<div className="flex items-center gap-2 mb-1">
<label htmlFor="aws-region-dropdown">
<span style={{ fontWeight: 500 }}>AWS Region</span>
</label>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm flex items-center" />
</div>
<VSCodeDropdown
disabled={true}
id="aws-region-dropdown"
onChange={(e: any) => handleFieldChange("awsRegion", e.target.value)}
style={{ width: "100%" }}
value={apiConfiguration?.awsRegion || ""}>
<VSCodeOption value="">Select a region...</VSCodeOption>
{/* The user will have to choose a region that supports the model they use, but this shouldn't be a problem since they'd have to request access for it in that region in the first place. */}
<VSCodeOption value="us-east-1">us-east-1</VSCodeOption>
<VSCodeOption value="us-east-2">us-east-2</VSCodeOption>
<VSCodeOption value="us-west-1">us-west-1</VSCodeOption>
<VSCodeOption value="us-west-2">us-west-2</VSCodeOption>
{/* <VSCodeOption value="af-south-1">af-south-1</VSCodeOption> */}
{/* <VSCodeOption value="ap-east-1">ap-east-1</VSCodeOption> */}
<VSCodeOption value="ap-south-1">ap-south-1</VSCodeOption>
<VSCodeOption value="ap-northeast-1">ap-northeast-1</VSCodeOption>
<VSCodeOption value="ap-northeast-2">ap-northeast-2</VSCodeOption>
<VSCodeOption value="ap-northeast-3">ap-northeast-3</VSCodeOption>
<VSCodeOption value="ap-southeast-1">ap-southeast-1</VSCodeOption>
<VSCodeOption value="ap-southeast-2">ap-southeast-2</VSCodeOption>
<VSCodeOption value="ca-central-1">ca-central-1</VSCodeOption>
<VSCodeOption value="eu-central-1">eu-central-1</VSCodeOption>
<VSCodeOption value="eu-central-2">eu-central-2</VSCodeOption>
<VSCodeOption value="eu-west-1">eu-west-1</VSCodeOption>
<VSCodeOption value="eu-west-2">eu-west-2</VSCodeOption>
<VSCodeOption value="eu-west-3">eu-west-3</VSCodeOption>
<VSCodeOption value="eu-north-1">eu-north-1</VSCodeOption>
<VSCodeOption value="eu-south-1">eu-south-1</VSCodeOption>
<VSCodeOption value="eu-south-2">eu-south-2</VSCodeOption>
{/* <VSCodeOption value="me-south-1">me-south-1</VSCodeOption> */}
<VSCodeOption value="sa-east-1">sa-east-1</VSCodeOption>
<VSCodeOption value="us-gov-east-1">us-gov-east-1</VSCodeOption>
<VSCodeOption value="us-gov-west-1">us-gov-west-1</VSCodeOption>
{/* <VSCodeOption value="us-gov-east-1">us-gov-east-1</VSCodeOption> */}
</VSCodeDropdown>
</DropdownContainer>
</HeroTooltip>
<Tooltip>
<TooltipContent>This setting is managed by your organization's remote configuration</TooltipContent>
<TooltipTrigger>
<DropdownContainer className="dropdown-container mb-2.5" zIndex={DROPDOWN_Z_INDEX - 1}>
<div className="flex items-center gap-2 mb-1">
<label htmlFor="aws-region-dropdown">
<span style={{ fontWeight: 500 }}>AWS Region</span>
</label>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm flex items-center" />
</div>
<VSCodeDropdown
disabled={true}
id="aws-region-dropdown"
onChange={(e: any) => handleFieldChange("awsRegion", e.target.value)}
style={{ width: "100%" }}
value={apiConfiguration?.awsRegion || ""}>
<VSCodeOption value="">Select a region...</VSCodeOption>
{/* The user will have to choose a region that supports the model they use, but this shouldn't be a problem since they'd have to request access for it in that region in the first place. */}
<VSCodeOption value="us-east-1">us-east-1</VSCodeOption>
<VSCodeOption value="us-east-2">us-east-2</VSCodeOption>
<VSCodeOption value="us-west-1">us-west-1</VSCodeOption>
<VSCodeOption value="us-west-2">us-west-2</VSCodeOption>
{/* <VSCodeOption value="af-south-1">af-south-1</VSCodeOption> */}
{/* <VSCodeOption value="ap-east-1">ap-east-1</VSCodeOption> */}
<VSCodeOption value="ap-south-1">ap-south-1</VSCodeOption>
<VSCodeOption value="ap-northeast-1">ap-northeast-1</VSCodeOption>
<VSCodeOption value="ap-northeast-2">ap-northeast-2</VSCodeOption>
<VSCodeOption value="ap-northeast-3">ap-northeast-3</VSCodeOption>
<VSCodeOption value="ap-southeast-1">ap-southeast-1</VSCodeOption>
<VSCodeOption value="ap-southeast-2">ap-southeast-2</VSCodeOption>
<VSCodeOption value="ca-central-1">ca-central-1</VSCodeOption>
<VSCodeOption value="eu-central-1">eu-central-1</VSCodeOption>
<VSCodeOption value="eu-central-2">eu-central-2</VSCodeOption>
<VSCodeOption value="eu-west-1">eu-west-1</VSCodeOption>
<VSCodeOption value="eu-west-2">eu-west-2</VSCodeOption>
<VSCodeOption value="eu-west-3">eu-west-3</VSCodeOption>
<VSCodeOption value="eu-north-1">eu-north-1</VSCodeOption>
<VSCodeOption value="eu-south-1">eu-south-1</VSCodeOption>
<VSCodeOption value="eu-south-2">eu-south-2</VSCodeOption>
{/* <VSCodeOption value="me-south-1">me-south-1</VSCodeOption> */}
<VSCodeOption value="sa-east-1">sa-east-1</VSCodeOption>
<VSCodeOption value="us-gov-east-1">us-gov-east-1</VSCodeOption>
<VSCodeOption value="us-gov-west-1">us-gov-west-1</VSCodeOption>
{/* <VSCodeOption value="us-gov-east-1">us-gov-east-1</VSCodeOption> */}
</VSCodeDropdown>
</DropdownContainer>
</TooltipTrigger>
</Tooltip>
) : (
<DropdownContainer className="dropdown-container" zIndex={DROPDOWN_Z_INDEX - 1}>
<label htmlFor="aws-region-dropdown">
@@ -186,36 +189,39 @@ export const BedrockProvider = ({ showModelOptions, isPopup, currentMode }: Bedr
<div style={{ display: "flex", flexDirection: "column" }}>
{remoteConfigSettings?.awsBedrockEndpoint !== undefined ? (
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<div>
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={awsEndpointSelected}
disabled={true}
onChange={(e: any) => {
const isChecked = e.target.checked === true
setAwsEndpointSelected(isChecked)
if (!isChecked) {
handleFieldChange("awsBedrockEndpoint", "")
}
}}>
Use custom VPC endpoint
</VSCodeCheckbox>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
<Tooltip>
<TooltipContent>This setting is managed by your organization's remote configuration</TooltipContent>
<TooltipTrigger>
<div>
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={awsEndpointSelected}
disabled={true}
onChange={(e: any) => {
const isChecked = e.target.checked === true
setAwsEndpointSelected(isChecked)
if (!isChecked) {
handleFieldChange("awsBedrockEndpoint", "")
}
}}>
Use custom VPC endpoint
</VSCodeCheckbox>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
{awsEndpointSelected && (
<DebouncedTextField
disabled={true}
initialValue={apiConfiguration?.awsBedrockEndpoint || ""}
onChange={(value) => handleFieldChange("awsBedrockEndpoint", value)}
placeholder="Enter VPC Endpoint URL (optional)"
style={{ width: "100%", marginTop: 3, marginBottom: 5 }}
type="text"
/>
)}
</div>
</HeroTooltip>
{awsEndpointSelected && (
<DebouncedTextField
disabled={true}
initialValue={apiConfiguration?.awsBedrockEndpoint || ""}
onChange={(value) => handleFieldChange("awsBedrockEndpoint", value)}
placeholder="Enter VPC Endpoint URL (optional)"
style={{ width: "100%", marginTop: 3, marginBottom: 5 }}
type="text"
/>
)}
</div>
</TooltipTrigger>
</Tooltip>
) : (
<>
<VSCodeCheckbox
@@ -243,21 +249,24 @@ export const BedrockProvider = ({ showModelOptions, isPopup, currentMode }: Bedr
)}
{remoteConfigSettings?.awsUseCrossRegionInference !== undefined ? (
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={apiConfiguration?.awsUseCrossRegionInference || false}
disabled={true}
onChange={(e: any) => {
const isChecked = e.target.checked === true
<Tooltip>
<TooltipContent>This setting is managed by your organization's remote configuration</TooltipContent>
<TooltipTrigger>
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={apiConfiguration?.awsUseCrossRegionInference || false}
disabled={true}
onChange={(e: any) => {
const isChecked = e.target.checked === true
handleFieldChange("awsUseCrossRegionInference", isChecked)
}}>
Use cross-region inference
</VSCodeCheckbox>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
</HeroTooltip>
handleFieldChange("awsUseCrossRegionInference", isChecked)
}}>
Use cross-region inference
</VSCodeCheckbox>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>{" "}
</TooltipTrigger>
</Tooltip>
) : (
<VSCodeCheckbox
checked={apiConfiguration?.awsUseCrossRegionInference || false}
@@ -273,20 +282,23 @@ export const BedrockProvider = ({ showModelOptions, isPopup, currentMode }: Bedr
{apiConfiguration?.awsUseCrossRegionInference &&
selectedModelInfo.supportsGlobalEndpoint &&
(remoteConfigSettings?.awsUseGlobalInference !== undefined ? (
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={apiConfiguration?.awsUseGlobalInference || false}
disabled={true}
onChange={(e: any) => {
const isChecked = e.target.checked === true
handleFieldChange("awsUseGlobalInference", isChecked)
}}>
Use global inference profile
</VSCodeCheckbox>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
</HeroTooltip>
<Tooltip>
<TooltipContent>This setting is managed by your organization's remote configuration</TooltipContent>
<TooltipTrigger>
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={apiConfiguration?.awsUseGlobalInference || false}
disabled={true}
onChange={(e: any) => {
const isChecked = e.target.checked === true
handleFieldChange("awsUseGlobalInference", isChecked)
}}>
Use global inference profile
</VSCodeCheckbox>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
</TooltipTrigger>
</Tooltip>
) : (
<VSCodeCheckbox
checked={apiConfiguration?.awsUseGlobalInference || false}
@@ -300,20 +312,23 @@ export const BedrockProvider = ({ showModelOptions, isPopup, currentMode }: Bedr
{selectedModelInfo.supportsPromptCache &&
(remoteConfigSettings?.awsBedrockUsePromptCache !== undefined ? (
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={apiConfiguration?.awsBedrockUsePromptCache || false}
disabled={true}
onChange={(e: any) => {
const isChecked = e.target.checked === true
handleFieldChange("awsBedrockUsePromptCache", isChecked)
}}>
Use prompt caching
</VSCodeCheckbox>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
</HeroTooltip>
<Tooltip>
<TooltipContent>This setting is managed by your organization's remote configuration</TooltipContent>
<TooltipTrigger>
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={apiConfiguration?.awsBedrockUsePromptCache || false}
disabled={true}
onChange={(e: any) => {
const isChecked = e.target.checked === true
handleFieldChange("awsBedrockUsePromptCache", isChecked)
}}>
Use prompt caching
</VSCodeCheckbox>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
</TooltipTrigger>
</Tooltip>
) : (
<VSCodeCheckbox
checked={apiConfiguration?.awsBedrockUsePromptCache || false}
@@ -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">
@@ -306,7 +306,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
@@ -1,9 +1,10 @@
import { TooltipContent, TooltipTrigger } from "@radix-ui/react-tooltip"
import { azureOpenAiDefaultApiVersion, openAiModelInfoSaneDefaults } from "@shared/api"
import { OpenAiModelsRequest } from "@shared/proto/cline/models"
import { Mode } from "@shared/storage/types"
import { VSCodeButton, VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { useCallback, useEffect, useRef, useState } from "react"
import HeroTooltip from "@/components/common/HeroTooltip"
import { Tooltip } from "@/components/ui/tooltip"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { ModelsServiceClient } from "@/services/grpc-client"
import { getAsVar, VSC_DESCRIPTION_FOREGROUND } from "@/utils/vscStyles"
@@ -71,25 +72,28 @@ export const OpenAICompatibleProvider = ({ showModelOptions, isPopup, currentMod
return (
<div>
{remoteConfigSettings?.openAiBaseUrl !== undefined ? (
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<div className="mb-2.5">
<div className="flex items-center gap-2 mb-1">
<span style={{ fontWeight: 500 }}>Base URL</span>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
<Tooltip>
<TooltipTrigger>
<div className="mb-2.5">
<div className="flex items-center gap-2 mb-1">
<span style={{ fontWeight: 500 }}>Base URL</span>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
<DebouncedTextField
disabled={true}
initialValue={apiConfiguration?.openAiBaseUrl || ""}
onChange={(value) => {
handleFieldChange("openAiBaseUrl", value)
debouncedRefreshOpenAiModels(value, apiConfiguration?.openAiApiKey)
}}
placeholder={"Enter base URL..."}
style={{ width: "100%" }}
type="url"
/>
</div>
<DebouncedTextField
disabled={true}
initialValue={apiConfiguration?.openAiBaseUrl || ""}
onChange={(value) => {
handleFieldChange("openAiBaseUrl", value)
debouncedRefreshOpenAiModels(value, apiConfiguration?.openAiApiKey)
}}
placeholder={"Enter base URL..."}
style={{ width: "100%" }}
type="url"
/>
</div>
</HeroTooltip>
</TooltipTrigger>
<TooltipContent>This setting is managed by your organization's remote configuration</TooltipContent>
</Tooltip>
) : (
<DebouncedTextField
initialValue={apiConfiguration?.openAiBaseUrl || ""}
@@ -131,12 +135,17 @@ export const OpenAICompatibleProvider = ({ showModelOptions, isPopup, currentMod
<div style={{ marginBottom: 10 }}>
{remoteConfigSettings?.openAiHeaders !== undefined ? (
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<div className="flex items-center gap-2">
<span style={{ fontWeight: 500 }}>Custom Headers</span>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
</HeroTooltip>
<Tooltip>
<TooltipTrigger>
<div className="flex items-center gap-2">
<span style={{ fontWeight: 500 }}>Custom Headers</span>
<i className="codicon codicon-lock text-(--vscode-descriptionForeground) text-sm" />
</div>
</TooltipTrigger>
<TooltipContent>
This setting is managed by your organization's remote configuration
</TooltipContent>
</Tooltip>
<VSCodeButton disabled={true}>Add Header</VSCodeButton>
</div>
) : (
@@ -202,16 +211,19 @@ export const OpenAICompatibleProvider = ({ showModelOptions, isPopup, currentMod
})()}
{remoteConfigSettings?.azureApiVersion !== undefined ? (
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<BaseUrlField
disabled={true}
initialValue={apiConfiguration?.azureApiVersion}
label="Set Azure API version"
onChange={(value) => handleFieldChange("azureApiVersion", value)}
placeholder={`Default: ${azureOpenAiDefaultApiVersion}`}
showLockIcon={true}
/>
</HeroTooltip>
<Tooltip>
<TooltipTrigger>
<BaseUrlField
disabled={true}
initialValue={apiConfiguration?.azureApiVersion}
label="Set Azure API version"
onChange={(value) => handleFieldChange("azureApiVersion", value)}
placeholder={`Default: ${azureOpenAiDefaultApiVersion}`}
showLockIcon={true}
/>
</TooltipTrigger>
<TooltipContent>This setting is managed by your organization's remote configuration</TooltipContent>
</Tooltip>
) : (
<BaseUrlField
initialValue={apiConfiguration?.azureApiVersion}
@@ -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>
)}
@@ -10,15 +10,15 @@ const AboutSection = ({ version, renderSectionHeader }: AboutSectionProps) => {
<div>
{renderSectionHeader("about")}
<Section>
<div style={{ padding: "0 16px" }}>
<h2>Cline v{version}</h2>
<div className="flex px-4 flex-col gap-2">
<h2 className="text-lg font-semibold">Cline v{version}</h2>
<p>
An AI assistant that can use your CLI and Editor. Cline can handle complex software development tasks
step-by-step with tools that let him create & edit files, explore large projects, use the browser, and
execute terminal commands (after you grant permission).
</p>
<h3>Community & Support</h3>
<h3 className="text-md font-semibold">Community & Support</h3>
<p>
<VSCodeLink href="https://x.com/cline">X</VSCodeLink>
{" • "}
@@ -27,7 +27,7 @@ const AboutSection = ({ version, renderSectionHeader }: AboutSectionProps) => {
<VSCodeLink href="https://www.reddit.com/r/cline/"> r/cline</VSCodeLink>
</p>
<h3>Development</h3>
<h3 className="text-md font-semibold">Development</h3>
<p>
<VSCodeLink href="https://github.com/cline/cline">GitHub</VSCodeLink>
{" • "}
@@ -39,7 +39,7 @@ const AboutSection = ({ version, renderSectionHeader }: AboutSectionProps) => {
</VSCodeLink>
</p>
<h3>Resources</h3>
<h3 className="text-md font-semibold">Resources</h3>
<p>
<VSCodeLink href="https://docs.cline.bot/getting-started/for-new-coders">Documentation</VSCodeLink>
{" • "}
@@ -25,7 +25,7 @@ const ApiConfigurationSection = ({ renderSectionHeader }: ApiConfigurationSectio
{/* Tabs container */}
{planActSeparateModelsSetting ? (
<div className="rounded-md mb-5">
<div className="flex gap-[1px] mb-[10px] -mt-2 border-0 border-b border-solid border-[var(--vscode-panel-border)]">
<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>
@@ -3,8 +3,8 @@ import { McpDisplayMode } from "@shared/McpDisplayMode"
import { OpenaiReasoningEffort } from "@shared/storage/types"
import { VSCodeCheckbox, VSCodeDropdown, VSCodeOption, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { memo } from "react"
import HeroTooltip from "@/components/common/HeroTooltip"
import McpDisplayModeDropdown from "@/components/mcp/chat-display/McpDisplayModeDropdown"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { useExtensionState } from "@/context/ExtensionStateContext"
import Section from "../Section"
import { updateSetting } from "../utils/settingsHandlers"
@@ -48,27 +48,32 @@ 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>
</div>
<div style={{ marginTop: 10 }}>
{remoteConfigSettings?.mcpMarketplaceEnabled !== undefined ? (
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={mcpMarketplaceEnabled}
disabled={true}
onChange={(e: any) => {
const checked = e.target.checked === true
updateSetting("mcpMarketplaceEnabled", checked)
}}>
Enable MCP Marketplace
</VSCodeCheckbox>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
</HeroTooltip>
<Tooltip>
<TooltipTrigger>
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={mcpMarketplaceEnabled}
disabled={true}
onChange={(e: any) => {
const checked = e.target.checked === true
updateSetting("mcpMarketplaceEnabled", checked)
}}>
Enable MCP Marketplace
</VSCodeCheckbox>
<i className="codicon codicon-lock text-(--vscode-descriptionForeground) text-sm" />
</div>
</TooltipTrigger>
<TooltipContent>
This setting is managed by your organization's remote configuration
</TooltipContent>
</Tooltip>
) : (
<VSCodeCheckbox
checked={mcpMarketplaceEnabled}
@@ -80,13 +85,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>
@@ -96,7 +101,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>
@@ -110,13 +115,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>
@@ -133,7 +138,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>
@@ -146,7 +151,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>
@@ -160,7 +165,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>
@@ -169,7 +174,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>
@@ -187,7 +192,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>
@@ -258,10 +263,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">
@@ -296,7 +301,7 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
Enable Hooks
</VSCodeCheckbox>
<p className="text-xs">
<span className="text-[var(--vscode-errorForeground)]">Experimental: </span>{" "}
<span className="text-(--vscode-errorForeground)">Experimental: </span>{" "}
<span className="text-description">
Allows execution of hooks from .clinerules/hooks/ directory.
</span>
@@ -304,33 +309,30 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
</div>
)}
<div style={{ marginTop: 10 }}>
{remoteConfigSettings?.yoloModeToggled !== undefined ? (
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={yoloModeToggled}
disabled={true}
disabled={remoteConfigSettings?.yoloModeToggled !== undefined}
onChange={(e: any) => {
const checked = e.target.checked === true
updateSetting("yoloModeToggled", checked)
}}>
Enable YOLO Mode
</VSCodeCheckbox>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
<i className="codicon codicon-lock text-(--vscode-descriptionForeground) text-sm" />
</div>
</HeroTooltip>
) : (
<VSCodeCheckbox
checked={yoloModeToggled}
disabled={false}
onChange={(e: any) => {
const checked = e.target.checked === true
updateSetting("yoloModeToggled", checked)
}}>
Enable YOLO Mode
</VSCodeCheckbox>
)}
<p className="text-xs text-[var(--vscode-errorForeground)]">
</TooltipTrigger>
<TooltipContent
className="max-w-xs"
hidden={remoteConfigSettings?.yoloModeToggled === undefined}
side="top">
This setting is managed by your organization's remote configuration
</TooltipContent>
</Tooltip>
<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>
@@ -1,5 +1,5 @@
import { VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import HeroTooltip from "@/components/common/HeroTooltip"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { useExtensionState } from "@/context/ExtensionStateContext"
import PreferredLanguageSetting from "../PreferredLanguageSetting"
import Section from "../Section"
@@ -21,20 +21,23 @@ const GeneralSettingsSection = ({ renderSectionHeader }: GeneralSettingsSectionP
<div className="mb-[5px]">
{isDisabledByRemoteConfig ? (
<HeroTooltip content="This setting is managed by your organization's remote configuration">
<div className="flex items-center gap-2 mb-[5px]">
<VSCodeCheckbox
checked={telemetrySetting !== "disabled"}
disabled={true}
onChange={(e: any) => {
const checked = e.target.checked === true
updateSetting("telemetrySetting", checked ? "enabled" : "disabled")
}}>
Allow error and usage reporting
</VSCodeCheckbox>
<i className="codicon codicon-lock text-[var(--vscode-descriptionForeground)] text-sm" />
</div>
</HeroTooltip>
<Tooltip>
<TooltipContent>This setting is managed by your organization's remote configuration</TooltipContent>
<TooltipTrigger asChild>
<div className="flex items-center gap-2 mb-[5px]">
<VSCodeCheckbox
checked={telemetrySetting !== "disabled"}
disabled={true}
onChange={(e: any) => {
const checked = e.target.checked === true
updateSetting("telemetrySetting", checked ? "enabled" : "disabled")
}}>
Allow error and usage reporting
</VSCodeCheckbox>
<i className="codicon codicon-lock text--vscode-descriptionForeground) text-sm" />
</div>
</TooltipTrigger>
</Tooltip>
) : (
<VSCodeCheckbox
checked={telemetrySetting !== "disabled"}
@@ -47,7 +50,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
@@ -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">
+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]:shrink-0 cursor-pointer [&_svg]:size-2",
{
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/10 m-0 p-0",
link: "text-primary underline-offset-4 hover:underline",
icon: "bg-transparent hover:opacity-80 p-0 h-auto m-0 border-0 cursor-pointer hover:bg-transparent hover:shadow-none focus:ring-0 focus:ring-offset-0",
},
size: {
default: "h-5 px-4 py-2",
sm: "h-3 rounded-md px-3 text-sm",
xs: "h-1 rounded-xs px-1 text-xs",
lg: "h-8 rounded-md px-8",
icon: "px-0.5 m-0 [&_svg]:size-2",
},
},
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 }
@@ -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 }
+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 }
+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 wrap-break-word max-w-md p-2 overflow-visible",
className,
)}
data-slot="tooltip-content"
sideOffset={sideOffset}
{...props}>
<span className="text-xs leading-tight wrap-break-word">{children}</span>
<TooltipPrimitive.Arrow className="bg-background fill-background z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-xs 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 { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { UiServiceClient } from "@/services/grpc-client"
@@ -21,24 +22,25 @@ const HomeHeader = ({ shouldShowQuickWins = false }: HomeHeaderProps) => {
return (
<div className="flex flex-col items-center mb-5">
<div className="my-5">
<ClineLogoVariable className="size-16" environment={environment} />
<div className="my-7">
<ClineLogoVariable className="size-20" environment={environment} />
</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>
<h1 className="m-0 font-bold">What can I do for you?</h1>
<Tooltip>
<TooltipContent side="bottom">
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.
</TooltipContent>
<TooltipTrigger asChild>
<InfoIcon className="ml-2 cursor-pointer text-link text-sm size-2" />
</TooltipTrigger>
</Tooltip>
</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>
)
@@ -34,8 +34,8 @@ const WelcomeView = memo(() => {
return (
<div className="fixed inset-0 p-0 flex flex-col">
<div className="h-full px-5 overflow-auto">
<h2>Hi, I'm Cline</h2>
<div className="h-full px-5 overflow-auto flex flex-col gap-2.5">
<h2 className="text-lg font-semibold">Hi, I'm Cline</h2>
<div className="flex justify-center my-5">
<ClineLogoWhite className="size-16" />
</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>
+48 -209
View File
@@ -1,215 +1,54 @@
@layer theme, base, components, utilities;
/*
Import order matters for Tailwind CSS v4 compatibility
External @import statements MUST come before Tailwind imports because:
1. CSS spec requires ALL @import statements to precede other CSS rules (except @charset/@layer)
2. Tailwind CSS v4 expands its imports into thousands of lines of generated utility CSS
3. If external imports come after Tailwind imports, they end up after the generated CSS
4. This causes PostCSS to throw: "@import must precede all other statements"
The correct order is:
- @layer declarations (empty)
- External @import statements (fonts, icons, etc.)
- Tailwind @import statements (theme, utilities)
- @config directives
- Regular CSS rules
*/
/* External imports must come first, before Tailwind imports */
/* Import external CSS files */
@import url("../../node_modules/@vscode/codicons/dist/codicon.css");
@import url("../node_modules/@fontsource/azeret-mono/index.css");
/* Tailwind imports come after external imports */
@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);
@import "tailwindcss";
@import "./theme.css";
@plugin "tailwindcss-animate";
@custom-variant dark (&:is(.dark *));
/* Configuration directives come after all imports */
@config "../tailwind.config.mjs";
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%;
}
#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 text-base;
}
body {
@apply bg-background text-foreground;
}
p {
@apply my-0 text-base leading-5 block line-clamp-3;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0px;
margin-inline-end: 0px;
unicode-bidi: isolate;
}
h1 {
font-size: var(--text-lg);
font-weight: var(--font-weight-semibold);
}
h2 {
font-size: var(--text-md);
font-weight: var(--font-weight-semibold);
}
h3 {
font-size: var(--text-sm);
font-weight: var(--font-weight-semibold);
}
h4 {
font-size: var(--text-base);
font-weight: var(--font-weight-bold);
}
a {
@apply text-link hover:text-link-hover;
}
ol {
@apply list-decimal pl-6;
}
ul {
@apply list-disc pl-6;
}
[role="radio"],
[role="checkbox"] {
margin: calc(var(--design-unit) * 1px) 0;
}
}
/*
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;
}
.scrollable::-webkit-scrollbar-thumb {
background-color: transparent;
border-color: inherit;
border-right-style: inset;
border-right-width: calc(100vw + 100vh);
border-radius: unset;
}
.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"
+165
View File
@@ -0,0 +1,165 @@
/* 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-icon-foreground: var(--vscode-icon-foreground);
--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-2xl: calc(2.25 * var(--vscode-font-size));
--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: calc(0.9 * var(--vscode-font-size));
--text-xs: calc(0.75 * var(--vscode-font-size));
--text-xxs: calc(0.5 * var(--vscode-font-size));
}
@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-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);
--size-1: calc(0.5 * var(--vscode-font-size));
--size-2: calc(1 * var(--vscode-font-size));
--size-3: calc(1.5 * var(--vscode-font-size));
}
: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);
}
.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);
}
+24
View File
@@ -27,3 +27,27 @@ export const getEnvironmentColor = (environment: Environment | undefined, type:
? "var(--vscode-focusBorder)" // Blue for staging
: "var(--vscode-foreground)" // Default for production
}
export function getClineEnvironmentClassname(environment: Environment | undefined, type = "text") {
if (type === "border") {
switch (environment) {
case "local":
return "border-(--vscode-activityWarningBadge-background)"
case "staging":
return "border-(--vscode-focusBorder)"
case "production":
default:
return "border-(--vscode-editorGroup-border)"
}
}
switch (environment) {
case "local":
return "var(--vscode-activityWarningBadge-background)"
case "staging":
return "var(--vscode-focusBorder)"
case "production":
default:
return "var(--vscode-foreground)"
}
}
+9 -1
View File
@@ -7,5 +7,13 @@
{
"path": "./tsconfig.node.json"
}
]
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
}
}