Compare commits

...
Author SHA1 Message Date
Trevor Hudson cc0ab31d8b webview logic 2025-05-08 08:24:32 -07:00
Trevor Hudson 7b0decba59 webview 2025-05-07 11:21:31 -07:00
Trevor Hudson 588e735493 fix webview 2025-05-07 09:59:22 -07:00
Trevor Hudson 552b562269 show welcome 2025-05-06 20:56:23 -07:00
Trevor Hudson d7ae62dca1 updates 2025-05-06 17:21:23 -07:00
Trevor Hudson 89b46c10b2 change installation status approach 2025-05-06 17:06:51 -07:00
Trevor Hudson df9b0a163d onboarding screen iteration 2025-05-06 13:23:37 -07:00
Tomás Barreiro 29af363090 Fix tests: compile esmodules to cjs and bundle them (#3030)
* Run pretest in CI to build all tests

* Alias paths when running tests

* Bundle ES modules with esbuild

* alias packages
2025-05-06 13:22:48 -07:00
Frostbourne 89338a2c13 [ENG-514] Introduce UI library (#3222)
* update tailwind

* install heroui

* Introduce HeroUIProvider and reorganize providers

* changeset

* use tailwind config

* use custom theme

* changeset

* Delete .changeset/large-boxes-behave.md
2025-05-06 13:22:44 -07:00
Evan 2adae27632 Remove showMcpView message (#3171)
* refactor to not pass message for showMcpView

* changeset
2025-05-06 13:21:09 -07:00
Trevor Hudson e3a343e8b0 format 2025-05-06 13:19:36 -07:00
Trevor Hudson e81147a8f7 onboarding screen iteration 2025-05-06 13:19:32 -07:00
Trevor Hudson d6d40ebe59 onboarding screen 2025-05-06 13:17:39 -07:00
41 changed files with 1854 additions and 81 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Introduce UI library for future UI development
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Refactor to not pass a message for showing the MCP View from the servers modal
+2 -1
View File
@@ -18,4 +18,5 @@ coverage
# But don't ignore the coverage scripts in .github/scripts/
!.github/scripts/coverage/
*evals.env
*evals.env
*.zip
+22
View File
@@ -16,6 +16,28 @@
"IS_DEV": "true",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}"
}
},
{
"name": "Run Extension (Fresh Install Mode)",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--profile-temp",
"--sync",
"off",
"--disable-extensions",
"--extensionDevelopmentPath=${workspaceFolder}",
"${workspaceFolder}"
],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "clean-sandbox",
"internalConsoleOptions": "openOnSessionStart",
"postDebugTask": "stop",
"env": {
"IS_DEV": "true",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}"
}
}
]
}
+6
View File
@@ -185,6 +185,12 @@
"label": "stop",
"command": "echo ${input:terminate}",
"type": "shell"
},
{
"label": "clean-sandbox",
"type": "shell",
"dependsOn": ["watch"],
"command": "rm -rf .vscode-dev"
}
],
"inputs": [
+5
View File
@@ -118,6 +118,11 @@
"command": "cline.focusChatInput",
"title": "Jump to Chat Input",
"category": "Cline"
},
{
"command": "cline.showWelcomeTab",
"title": "Show Welcome Tab",
"category": "Cline"
}
],
"keybindings": [
+2
View File
@@ -1672,6 +1672,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
planActSeparateModelsSetting,
globalClineRulesToggles,
shellIntegrationTimeout,
showWelcome,
} = await getAllExtensionState(this.context)
const localClineRulesToggles =
@@ -1704,6 +1705,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
mcpMarketplaceEnabled,
telemetrySetting,
planActSeparateModelsSetting,
showWelcome,
vscMachineId: vscode.env.machineId,
globalClineRulesToggles: globalClineRulesToggles || {},
localClineRulesToggles: localClineRulesToggles || {},
+1
View File
@@ -81,5 +81,6 @@ export type GlobalStateKey =
| "favoritedModelIds"
| "requestTimeoutMs"
| "shellIntegrationTimeout"
| "showWelcome"
export type LocalStateKey = "localClineRulesToggles"
+3
View File
@@ -131,6 +131,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
globalClineRulesToggles,
requestTimeoutMs,
shellIntegrationTimeout,
showWelcome,
] = await Promise.all([
getGlobalState(context, "apiProvider") as Promise<ApiProvider | undefined>,
getGlobalState(context, "apiModelId") as Promise<string | undefined>,
@@ -210,6 +211,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
getGlobalState(context, "globalClineRulesToggles") as Promise<ClineRulesToggles | undefined>,
getGlobalState(context, "requestTimeoutMs") as Promise<number | undefined>,
getGlobalState(context, "shellIntegrationTimeout") as Promise<number | undefined>,
getGlobalState(context, "showWelcome") as Promise<boolean | undefined>,
])
let apiProvider: ApiProvider
@@ -334,6 +336,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
telemetrySetting: telemetrySetting || "unset",
planActSeparateModelsSetting,
shellIntegrationTimeout: shellIntegrationTimeout || 4000,
showWelcome,
}
}
+44 -3
View File
@@ -7,7 +7,8 @@ import { Controller } from "@core/controller/index"
import { findLast } from "@shared/array"
import { readFile } from "fs/promises"
import path from "node:path"
import { WebviewType } from "@shared/WebviewMessage"
import { setTimeout as setTimeoutPromise } from "node:timers/promises"
/*
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
https://github.com/KumarVariable/vscode-extension-sidebar-html/blob/master/src/customSidebarViewProvider.ts
@@ -24,6 +25,7 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
constructor(
readonly context: vscode.ExtensionContext,
private readonly outputChannel: vscode.OutputChannel,
private readonly webviewType: WebviewType = "sidebar",
) {
WebviewProvider.activeInstances.add(this)
this.controller = new Controller(context, outputChannel, (message) => this.view?.webview.postMessage(message))
@@ -86,12 +88,31 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
// WebviewView and WebviewPanel have all the same properties except for this visibility listener
// panel
webviewView.onDidChangeViewState(
() => {
async () => {
if (this.view?.visible) {
// Check if showWelcome flag is set in global state
const showWelcome = await this.controller.context.globalState.get<boolean>("showWelcome")
// Send didBecomeVisible action
this.controller.postMessageToWebview({
type: "action",
action: "didBecomeVisible",
})
// If showWelcome flag is set, send showWelcome message and clear the flag
// We're now sending it to all webviews to ensure it's displayed
if (showWelcome) {
console.log(`Sending showWelcome message to ${this.webviewType} webview`)
this.controller.postMessageToWebview({
type: "showWelcome",
})
// Add a delay before clearing the flag to ensure the webview has time to process it
await setTimeoutPromise(1000)
// Clear the flag after sending the message
await this.controller.context.globalState.update("showWelcome", undefined)
}
}
},
null,
@@ -100,12 +121,30 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
} else if ("onDidChangeVisibility" in webviewView) {
// sidebar
webviewView.onDidChangeVisibility(
() => {
async () => {
if (this.view?.visible) {
// Check if showWelcome flag is set in global state
const showWelcome = await this.controller.context.globalState.get<boolean>("showWelcome")
// Send didBecomeVisible action
this.controller.postMessageToWebview({
type: "action",
action: "didBecomeVisible",
})
// If showWelcome flag is set, send showWelcome message and clear the flag
if (showWelcome) {
console.log(`Sending showWelcome message to ${this.webviewType} webview`)
this.controller.postMessageToWebview({
type: "showWelcome",
})
// Add a delay before clearing the flag to ensure the webview has time to process it
await setTimeoutPromise(1000)
// Clear the flag after sending the message
await this.controller.context.globalState.update("showWelcome", undefined)
}
}
},
null,
@@ -221,6 +260,7 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta name="webview-type" content="${this.webviewType}">
<link rel="stylesheet" type="text/css" href="${stylesUri}">
<link href="${codiconsUri}" rel="stylesheet" />
<link href="${katexCssUri}" rel="stylesheet" />
@@ -331,6 +371,7 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<meta http-equiv="Content-Security-Policy" content="${csp.join("; ")}">
<meta name="webview-type" content="${this.webviewType}">
<link rel="stylesheet" type="text/css" href="${stylesUri}">
<link href="${codiconsUri}" rel="stylesheet" />
<link href="${katexCssUri}" rel="stylesheet" />
+47 -13
View File
@@ -34,7 +34,7 @@ export function activate(context: vscode.ExtensionContext) {
Logger.initialize(outputChannel)
Logger.log("Cline extension activated")
const sidebarWebview = new WebviewProvider(context, outputChannel)
const sidebarWebview = new WebviewProvider(context, outputChannel, "sidebar")
// Initialize test mode and add disposables to context
context.subscriptions.push(...initializeTestMode(context, sidebarWebview))
@@ -82,37 +82,47 @@ export function activate(context: vscode.ExtensionContext) {
}),
)
const openClineInNewTab = async () => {
const openClineInNewTab = async (isWelcome = false): Promise<WebviewProvider> => {
Logger.log("Opening Cline in new tab")
// (this example uses webviewProvider activation event which is necessary to deserialize cached webview, but since we use retainContextWhenHidden, we don't need to use that event)
// https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts
const tabWebview = new WebviewProvider(context, outputChannel)
//const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined
const lastCol = Math.max(...vscode.window.visibleTextEditors.map((editor) => editor.viewColumn || 0))
const tabWebview = new WebviewProvider(context, outputChannel, "tab")
let targetCol: vscode.ViewColumn
// Check if there are any visible text editors, otherwise open a new group to the right
const hasVisibleEditors = vscode.window.visibleTextEditors.length > 0
if (!hasVisibleEditors) {
await vscode.commands.executeCommand("workbench.action.newGroupRight")
if (!isWelcome) {
//const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined
const lastCol = Math.max(...vscode.window.visibleTextEditors.map((editor) => editor.viewColumn || 0))
// Check if there are any visible text editors, otherwise open a new group to the right
const hasVisibleEditors = vscode.window.visibleTextEditors.length > 0
if (!hasVisibleEditors) {
await vscode.commands.executeCommand("workbench.action.newGroupRight")
}
targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two
} else {
targetCol = vscode.ViewColumn.One
}
const targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two
const panel = vscode.window.createWebviewPanel(WebviewProvider.tabPanelId, "Cline", targetCol, {
enableScripts: true,
retainContextWhenHidden: true,
localResourceRoots: [context.extensionUri],
})
// TODO: use better svg icon with light and dark variants (see https://stackoverflow.com/questions/58365687/vscode-extension-iconpath)
panel.iconPath = {
light: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "robot_panel_light.png"),
dark: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "robot_panel_dark.png"),
}
tabWebview.resolveWebviewView(panel)
await tabWebview.resolveWebviewView(panel)
// Lock the editor group so clicking on files doesn't open them over the panel
await setTimeoutPromise(100)
await vscode.commands.executeCommand("workbench.action.lockEditorGroup")
if (isWelcome) {
panel.reveal(targetCol)
}
return tabWebview
}
context.subscriptions.push(vscode.commands.registerCommand("cline.popoutButtonClicked", openClineInNewTab))
@@ -421,8 +431,32 @@ export function activate(context: vscode.ExtensionContext) {
})
}),
)
checkInstallationStatus(context)
return createClineAPI(outputChannel, sidebarWebview.controller)
async function checkInstallationStatus(context: vscode.ExtensionContext) {
const VERSION_KEY = "cline.version"
const current = context.extension.packageJSON.version as string
const previous = context.globalState.get<string>(VERSION_KEY)
if (!previous) {
console.log("First time installation detected, setting showWelcome flag")
// Set the showWelcome flag to true in global state
await context.globalState.update("showWelcome", true)
// Open a single tab with the welcome screen
const tab = await openClineInNewTab(true)
console.log("Tab opened for welcome screen")
// Send the showWelcome message directly to the webview
await tab.controller.postMessageToWebview({ type: "showWelcome" })
console.log("Sent showWelcome message directly to webview")
// persist for the next activation
await context.globalState.update(VERSION_KEY, current)
}
}
}
// TODO: Find a solution for automatically removing DEV related content from production builds.
+2
View File
@@ -47,6 +47,7 @@ export interface ExtensionMessage {
| "fileSearchResults"
| "grpc_response" // New type for gRPC responses
| "setActiveQuote"
| "showWelcome"
text?: string
action?:
| "chatButtonClicked"
@@ -143,6 +144,7 @@ export interface ExtensionState {
localClineRulesToggles: ClineRulesToggles
localCursorRulesToggles: ClineRulesToggles
localWindsurfRulesToggles: ClineRulesToggles
showWelcome?: boolean
}
export interface ClineMessage {
+2
View File
@@ -125,3 +125,5 @@ export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "message
export type ClineCheckpointRestore = "task" | "workspace" | "taskAndWorkspace"
export type TaskFeedbackType = "thumbs_up" | "thumbs_down"
export type WebviewType = "sidebar" | "tab"
+252
View File
@@ -5439,6 +5439,34 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz",
"integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz",
"integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz",
@@ -5453,6 +5481,174 @@
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz",
"integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz",
"integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz",
"integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz",
"integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz",
"integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz",
"integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz",
"integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz",
"integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz",
"integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz",
"integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz",
"integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz",
"integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz",
@@ -5466,6 +5662,62 @@
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz",
"integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz",
"integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz",
"integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz",
"integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rrweb/types": {
"version": "2.0.0-alpha.17",
"resolved": "https://registry.npmjs.org/@rrweb/types/-/types-2.0.0-alpha.17.tgz",
+22 -5
View File
@@ -1,23 +1,34 @@
import { useCallback, useEffect, useState } from "react"
import { useCallback, useEffect, useMemo, useState } from "react"
import { useEvent } from "react-use"
import { ExtensionMessage } from "@shared/ExtensionMessage"
import { WebviewType } from "@shared/WebviewMessage"
import ChatView from "./components/chat/ChatView"
import HistoryView from "./components/history/HistoryView"
import SettingsView from "./components/settings/SettingsView"
import WelcomeView from "./components/welcome/WelcomeView"
import AccountView from "./components/account/AccountView"
import { useExtensionState } from "./context/ExtensionStateContext"
import { vscode } from "./utils/vscode"
import McpView from "./components/mcp/configuration/McpConfigurationView"
import { Providers } from "./Providers"
import { McpViewTab } from "@shared/mcp"
import WelcomeWrapper from "./components/welcome/WelcomeWrapper"
const AppContent = () => {
const { didHydrateState, showWelcome, shouldShowAnnouncement, showMcp, mcpTab } = useExtensionState()
const { didHydrateState, shouldShowAnnouncement, showMcp, mcpTab } = useExtensionState()
const [showSettings, setShowSettings] = useState(false)
const hideSettings = useCallback(() => setShowSettings(false), [])
const [showHistory, setShowHistory] = useState(false)
const [showAccount, setShowAccount] = useState(false)
const [showAnnouncement, setShowAnnouncement] = useState(false)
// Use local state for welcome view, initialized from extension state
const [showWelcomeLocal, setShowWelcomeLocal] = useState(true)
const { setShowWelcome } = useExtensionState()
// Sync local state with extension state
useEffect(() => {
console.log("Setting showWelcome in extension state to true")
setShowWelcome(true)
}, [setShowWelcome])
const { setShowMcp, setMcpTab } = useExtensionState()
@@ -29,7 +40,12 @@ const AppContent = () => {
const handleMessage = useCallback(
(e: MessageEvent) => {
const message: ExtensionMessage = e.data
console.log("Received message in App.tsx:", message)
switch (message.type) {
case "showWelcome":
console.log("Received showWelcome message in App.tsx")
setShowWelcomeLocal(true)
break
case "action":
switch (message.action!) {
case "settingsButtonClicked":
@@ -64,6 +80,7 @@ const AppContent = () => {
setShowHistory(false)
closeMcpView()
setShowAccount(false)
setShowWelcomeLocal(false) // Hide welcome view when chat button is clicked
break
}
break
@@ -96,8 +113,8 @@ const AppContent = () => {
return (
<>
{showWelcome ? (
<WelcomeView />
{showWelcomeLocal ? (
<WelcomeWrapper showWelcome={showWelcomeLocal} />
) : (
<>
{showSettings && <SettingsView onDone={hideSettings} />}
+813
View File
@@ -0,0 +1,813 @@
/* Welcome Tab Styles */
/* General Styles */
.home {
font-family: "Inter", sans-serif;
color: var(--vscode-foreground);
background-color: var(--vscode-editor-background);
height: 100vh;
overflow-y: auto;
}
.main-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.divider {
height: 1px;
background-color: var(--vscode-panel-border);
margin: 40px 0;
}
.divider-dashed {
height: 1px;
background: repeating-linear-gradient(
to right,
var(--vscode-panel-border),
var(--vscode-panel-border) 6px,
transparent 6px,
transparent 12px
);
margin: 20px 0;
}
/* Account Info Section */
.account-info {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 0;
}
.vector-icon {
width: 60px;
height: 60px;
margin-bottom: 20px;
}
.welcome-message-container {
text-align: center;
margin-bottom: 30px;
}
.welcome-message {
font-size: 32px;
font-weight: 600;
margin-bottom: 10px;
color: var(--vscode-foreground);
}
.description {
font-size: 16px;
color: var(--vscode-descriptionForeground);
}
.buttons-container {
display: flex;
gap: 10px;
margin-bottom: 30px;
}
.primary-button {
background-color: var(--vscode-button-background);
color: var(--vscode-button-foreground);
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: background-color 0.2s;
}
.primary-button:hover {
background-color: var(--vscode-button-hoverBackground);
}
.secondary-button {
background-color: transparent;
color: var(--vscode-button-secondaryForeground);
border: 1px solid var(--vscode-button-secondaryBackground);
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: background-color 0.2s;
}
.secondary-button:hover {
background-color: var(--vscode-button-secondaryHoverBackground);
}
/* Features Container */
.features-container {
width: 100%;
max-width: 800px;
margin: 0 auto;
}
.feature-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 16px;
margin-bottom: 30px;
}
.feature-card {
background-color: var(--vscode-editor-background);
border: 1px solid var(--vscode-panel-border);
border-radius: 8px;
padding: 16px;
transition: all 0.3s ease;
}
.feature-card:hover {
border-color: var(--vscode-focusBorder);
transform: translateY(-2px);
}
.feature-icon-container {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.feature-icon {
width: 24px;
height: 24px;
margin-right: 10px;
}
.feature-icon-1 {
background-image: url("../images/rocket.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.feature-icon-2 {
background-image: url("../images/rocket.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.feature-icon-3 {
background-image: url("../images/play.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.feature-icon-4 {
background-image: url("../images/rules.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.action-icon-1 {
background-image: url("../images/star-check.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.action-icon-2 {
background-image: url("../images/rocket.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.button-icon-1 {
background-image: url("../images/button-icon-1.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.feature-title {
font-size: 16px;
font-weight: 500;
}
.feature-description {
font-size: 14px;
color: var(--vscode-descriptionForeground);
margin-top: 8px;
}
/* Footer */
.footer {
margin-top: 30px;
}
.input-field {
background-color: var(--vscode-input-background);
border: 1px solid var(--vscode-input-border);
border-radius: 4px;
padding: 12px;
position: relative;
cursor: text;
}
.input-placeholder {
color: var(--vscode-input-placeholderForeground);
font-size: 14px;
}
.input-actions-container {
margin-top: 12px;
}
.input-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.action-button {
display: flex;
align-items: center;
background: none;
border: none;
cursor: pointer;
padding: 6px 12px;
border-radius: 4px;
transition: all 0.2s ease;
}
.action-button:hover {
opacity: 1 !important;
}
.action-button-secondary {
color: var(--vscode-button-secondaryForeground);
}
.action-button-secondary:hover {
background-color: var(--vscode-button-secondaryHoverBackground);
}
.action-button-primary {
color: var(--vscode-button-foreground);
background-color: var(--vscode-button-background);
}
.action-button-primary:hover {
background-color: var(--vscode-button-hoverBackground);
}
.action-icon {
width: 16px;
height: 16px;
margin-right: 6px;
}
.action-text {
font-size: 12px;
}
/* Features Section */
.features-section-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
margin-bottom: 40px;
}
.features-text-container {
padding-right: 20px;
}
.features-heading {
font-size: 32px;
font-weight: 600;
margin-bottom: 16px;
font-family: "SF Pro", "Inter", sans-serif;
}
.features-description {
font-size: 16px;
color: var(--vscode-descriptionForeground);
margin-bottom: 20px;
line-height: 1.3;
letter-spacing: -0.01em;
}
.features-tabs-container {
display: flex;
gap: 8px;
margin-bottom: 24px;
}
.tab-button {
background-color: #181818;
border: 1px solid #4e4e4e;
border-radius: 9999px;
padding: 0px 16px;
height: 32px;
font-size: 14px;
cursor: pointer;
color: var(--vscode-foreground);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.tab-button.active-tab {
background-color: #026ec1;
border: none;
font-weight: 500;
}
.feature-content {
margin-top: 24px;
}
.feature-selected {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.feature-check-icon {
width: 20px;
height: 20px;
background-color: #0078d4;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
margin-right: 12px;
}
.feature-info h3 {
font-size: 15px;
font-weight: 600;
margin: 0 0 8px 0;
}
.feature-subtitle {
font-size: 14px;
color: #cccccc;
margin: 0;
}
.feature-buttons-container {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 20px;
border-top: 1px dashed #3d3d3d;
border-bottom: 1px dashed #3d3d3d;
padding: 16px 0;
}
.feature-action-button {
display: flex;
align-items: center;
background: none;
border: 1px solid #3d3d3d;
padding: 0 8px;
height: 28px;
cursor: pointer;
border-radius: 8px;
color: white;
font-size: 14px;
transition: background-color 0.2s;
}
.feature-action-button:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.feature-action-icon {
width: 16px;
height: 16px;
margin-right: 8px;
}
.feature-options {
display: flex;
flex-direction: column;
gap: 16px;
}
.feature-option {
display: flex;
align-items: center;
cursor: pointer;
padding: 8px 0;
}
.feature-option:hover {
opacity: 0.8;
}
.feature-circle {
width: 20px;
height: 20px;
border-radius: 50%;
border: 1.5px solid #0078d4;
margin-right: 12px;
}
.feature-option span {
font-size: 15px;
font-weight: 500;
}
.feature-image-container {
display: flex;
align-items: center;
justify-content: center;
}
.feature-bg {
width: 100%;
height: 100%;
background-image: url("../images/feature-bg.png");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
border-radius: 14px;
position: relative;
overflow: hidden;
min-height: 400px;
}
.feature-diagram {
position: relative;
width: 100%;
height: 100%;
}
.feature-pill {
position: absolute;
background-color: white;
color: #0a4b8c;
border-radius: 50px;
padding: 8px 16px;
display: flex;
align-items: center;
font-weight: 500;
font-size: 14px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.feature-pill-icon {
width: 16px;
height: 16px;
margin-right: 8px;
}
.terminal-icon,
.code-icon,
.browser-icon {
margin-right: 8px;
font-weight: bold;
}
.file-editing {
top: 15%;
right: 10%;
}
.terminal-commands {
top: 40%;
left: 10%;
}
.code-analysis {
bottom: 30%;
right: 15%;
}
.browser-integration {
bottom: 15%;
left: 20%;
}
.feature-robot-container {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 120px;
}
.feature-robot {
width: 100%;
height: auto;
}
/* Models Section */
.models-container {
padding: 20px 0;
}
.models-heading {
font-size: 24px;
font-weight: 600;
margin-bottom: 30px;
text-align: center;
}
.models-section-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.model-card {
background-color: var(--vscode-editor-background);
border: 1px solid var(--vscode-panel-border);
border-radius: 8px;
padding: 20px;
display: flex;
flex-direction: column;
}
.model-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.model-name {
font-size: 18px;
font-weight: 500;
}
.model-badge {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.best {
background-color: #4caf50;
color: white;
}
.trending {
background-color: #2196f3;
color: white;
}
.new {
background-color: #ff9800;
color: white;
}
.model-description {
font-size: 14px;
color: var(--vscode-descriptionForeground);
margin-bottom: 16px;
}
.model-divider {
height: 1px;
background-color: var(--vscode-panel-border);
margin: 16px 0;
}
.model-image {
width: 100%;
height: 150px;
border-radius: 4px;
margin-bottom: 16px;
background-size: cover;
background-position: center;
}
.anthropic-model {
background-image: url("../images/anthropic-model.jpg");
}
.google-model {
background-image: url("../images/google-model.jpg");
}
.meta-model {
background-image: url("../images/meta-model.jpg");
}
.model-button {
background-color: var(--vscode-button-background);
color: var(--vscode-button-foreground);
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: background-color 0.2s;
margin-top: auto;
}
.model-button:hover {
background-color: var(--vscode-button-hoverBackground);
}
/* Plan/Act Mode Section */
.plan-act-container {
padding: 20px 0;
}
.plan-act-heading {
font-size: 24px;
font-weight: 600;
margin-bottom: 10px;
}
.plan-act-description {
font-size: 14px;
color: var(--vscode-descriptionForeground);
margin-bottom: 20px;
}
.plan-act-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.plan-mode-container,
.act-mode-container {
background-color: var(--vscode-editor-background);
border: 1px solid var(--vscode-panel-border);
border-radius: 8px;
padding: 20px;
}
.mode-header {
margin-bottom: 20px;
}
.mode-title {
font-size: 18px;
font-weight: 500;
}
.mode-content {
display: flex;
flex-direction: column;
gap: 12px;
}
.mode-item {
display: flex;
align-items: center;
}
.mode-item.disabled {
opacity: 0.5;
}
.mode-icon {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
}
.plan-icon {
color: #f5a524;
}
.plan-icon-disabled {
color: #f31260;
}
.act-icon {
color: #006fee;
}
.line-icon {
width: 14px;
height: 14px;
}
.plan-line {
background-image: url("../images/plan-line.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.act-line {
background-image: url("../images/act-line.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.disabled-line {
background-image: url("../images/disabled-line.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.mode-text {
font-size: 14px;
}
/* MCP Servers Section */
.mcp-servers-container {
padding: 20px 0;
}
.mcp-content-container {
display: flex;
flex-direction: column;
align-items: center;
}
.mcp-text-container {
text-align: center;
max-width: 600px;
margin-bottom: 30px;
}
.mcp-heading {
font-size: 24px;
font-weight: 600;
margin-bottom: 16px;
}
.mcp-description {
font-size: 14px;
color: var(--vscode-descriptionForeground);
margin-bottom: 20px;
}
.mcp-button {
background-color: var(--vscode-button-background);
color: var(--vscode-button-foreground);
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: background-color 0.2s;
}
.mcp-button:hover {
background-color: var(--vscode-button-hoverBackground);
}
.mcp-count {
margin-top: 30px;
width: 100%;
max-width: 800px;
}
/* Responsive Styles */
@media (max-width: 768px) {
.features-section-container {
grid-template-columns: 1fr;
}
.plan-act-content {
grid-template-columns: 1fr;
}
.models-section-container {
grid-template-columns: 1fr;
}
.feature-row {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 480px) {
.feature-row {
grid-template-columns: 1fr;
}
.buttons-container {
flex-direction: column;
width: 100%;
}
.primary-button,
.secondary-button {
width: 100%;
}
}
+8
View File
@@ -0,0 +1,8 @@
<svg width="1048" height="187" viewBox="0 0 1048 187" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M64.0535 186.531C51.9601 186.531 41.2428 184.279 31.9016 179.775C22.6439 175.271 15.221 169.141 9.63304 161.385C4.12845 153.545 0.959134 144.704 0.125104 134.863L0 133.361H26.7724L26.8975 134.237C27.7315 139.658 29.8166 144.579 33.1527 148.999C36.5722 153.336 40.9509 156.798 46.2886 159.383C51.6264 161.885 57.5898 163.136 64.1786 163.136C71.7683 163.136 78.4822 161.51 84.3204 158.257C90.1586 155.004 94.7041 150.501 97.9568 144.746C101.21 138.991 102.836 132.402 102.836 124.979V124.729C102.836 117.39 101.168 110.884 97.8317 105.213C94.579 99.5414 90.0752 95.0794 84.3204 91.8267C78.5656 88.574 71.9768 86.9476 64.5539 86.9476C58.9659 86.9476 53.7949 87.7816 49.0409 89.4497C44.287 91.0343 40.0751 93.453 36.4054 96.7057C34.8207 98.1236 33.2778 99.7082 31.7765 101.46C30.3587 103.211 29.0659 105.088 27.8983 107.089H3.25272L12.1351 3.00251H119.85V26.6472H34.9041L29.3995 83.82H30.0251C32.694 79.7332 36.0718 76.272 40.1585 73.4363C44.3287 70.6006 48.9992 68.4738 54.1702 67.056C59.4246 65.5547 65.0126 64.8041 70.9342 64.8041C82.4438 64.8041 92.6607 67.3896 101.585 72.5606C110.509 77.6482 117.515 84.654 122.602 93.5781C127.773 102.502 130.359 112.719 130.359 124.229V124.479C130.359 136.656 127.523 147.415 121.852 156.756C116.18 166.097 108.34 173.395 98.3321 178.649C88.4071 183.904 76.9809 186.531 64.0535 186.531Z" fill="white"/>
<path d="M218.933 186.531C204.587 186.531 192.244 182.736 181.902 175.146C171.643 167.557 163.762 156.798 158.257 142.869C152.753 128.941 150 112.427 150 93.3279V93.0777C150 73.9784 152.753 57.4646 158.257 43.5363C163.762 29.6081 171.643 18.8908 181.902 11.3845C192.244 3.79483 204.587 0 218.933 0C233.278 0 245.58 3.79483 255.839 11.3845C266.181 18.8908 274.104 29.6081 279.608 43.5363C285.196 57.4646 287.99 73.9784 287.99 93.0777V93.3279C287.99 112.427 285.196 128.941 279.608 142.869C274.104 156.798 266.181 167.557 255.839 175.146C245.58 182.736 233.278 186.531 218.933 186.531ZM218.933 163.011C227.523 163.011 234.821 160.217 240.826 154.629C246.914 149.041 251.543 141.034 254.713 130.609C257.965 120.184 259.592 107.757 259.592 93.3279V93.0777C259.592 78.649 257.965 66.2637 254.713 55.9217C251.543 45.4963 246.914 37.4896 240.826 31.9016C234.821 26.3136 227.523 23.5196 218.933 23.5196C210.342 23.5196 203.003 26.3136 196.914 31.9016C190.909 37.4896 186.322 45.4963 183.153 55.9217C179.984 66.2637 178.399 78.649 178.399 93.0777V93.3279C178.399 107.757 179.984 120.184 183.153 130.609C186.322 141.034 190.909 149.041 196.914 154.629C203.003 160.217 210.342 163.011 218.933 163.011Z" fill="white"/>
<path d="M313.512 122.602V97.9568H443.745V122.602H313.512ZM366.306 173.019V47.5397H390.826V173.019H366.306Z" fill="white"/>
<path d="M526.44 183.528V3.00251H558.842L617.265 146.998H618.016L676.44 3.00251H708.967V183.528H683.321V50.4171H670.435L699.709 8.00668L628.024 183.528H607.257L535.697 8.00668L564.972 50.4171H552.086V183.528H526.44Z" fill="white"/>
<path d="M819.559 186.531C802.545 186.531 787.741 182.736 775.147 175.146C762.637 167.557 752.962 156.798 746.123 142.869C739.284 128.858 735.864 112.302 735.864 93.2028V93.0777C735.864 73.9784 739.284 57.4646 746.123 43.5363C752.962 29.6081 762.637 18.8908 775.147 11.3845C787.658 3.79483 802.462 0 819.559 0C832.82 0 844.789 2.54379 855.464 7.63137C866.14 12.719 874.939 19.7665 881.861 28.774C888.784 37.7815 893.162 48.2486 894.997 60.1752L895.122 61.4263H867.224L866.473 58.9242C864.555 51.8349 861.428 45.7465 857.091 40.6589C852.837 35.5714 847.541 31.6931 841.202 29.0242C834.947 26.2719 827.733 24.8958 819.559 24.8958C808.383 24.8958 798.667 27.6898 790.41 33.2778C782.236 38.8658 775.898 46.7474 771.394 56.9225C766.89 67.0977 764.638 79.1494 764.638 93.0777V93.2028C764.638 107.048 766.89 119.141 771.394 129.483C775.898 139.742 782.278 147.665 790.535 153.253C798.792 158.841 808.467 161.635 819.559 161.635C827.566 161.635 834.697 160.301 840.952 157.632C847.291 154.879 852.67 150.876 857.091 145.622C861.511 140.367 864.805 133.945 866.974 126.355L867.349 125.104H895.247L894.997 126.355C893.162 138.366 888.784 148.916 881.861 158.007C875.022 167.014 866.265 174.02 855.589 179.024C844.914 184.029 832.904 186.531 819.559 186.531Z" fill="white"/>
<path d="M934.78 121.852V98.3321H981.069C993.163 98.3321 1002.55 95.2045 1009.22 88.9493C1015.97 82.6106 1019.35 73.8533 1019.35 62.6773V62.4271C1019.35 51.0843 1015.97 42.2853 1009.22 36.0301C1002.55 29.7749 993.163 26.6472 981.069 26.6472H934.78V3.00251H988.075C999.918 3.00251 1010.3 5.5046 1019.23 10.5088C1028.23 15.4295 1035.28 22.352 1040.37 31.2761C1045.46 40.1168 1048 50.4171 1048 62.1769V62.4271C1048 74.1869 1045.46 84.5706 1040.37 93.5781C1035.28 102.502 1028.23 109.466 1019.23 114.471C1010.3 119.391 999.918 121.852 988.075 121.852H934.78ZM920.769 183.528V3.00251H948.792V183.528H920.769Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7H13" stroke="#006FEE" stroke-width="2.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

@@ -0,0 +1,3 @@
<svg width="11" height="20" viewBox="0 0 11 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.8648 9.99911L10.6222 18.7565L9.37867 20L-0.000461927 10.6209V9.37736L9.37867 -0.00177104L10.6222 1.24174L1.8648 9.99911Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 292 B

@@ -0,0 +1,3 @@
<svg width="26" height="28" viewBox="0 0 26 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.14 0.28L25.7 6.88L26 7.6V27L25 28H18V26H24V10H17L16 9V2H4V10H2V1L3 0H18.44L19.14 0.28ZM18 8H24L18 2V8ZM15 12H1L0 13V27L1 28H15L16 27V13L15 12ZM14 26H2V14H14V26ZM12 17V23H9.99999V19.414L4.70799 24.708L3.29199 23.292L8.58599 18H5.05999V16H11L12 17Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 419 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="19" height="16" viewBox="0 0 19 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.20874 8.03249L5.75511 13.5892L17.8788 1.08664" stroke="white" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="28" height="22" viewBox="0 0 28 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 8.38L13.28 21.66H14.72L28 8.38V6.94L21.36 0.3L20.64 0H7.36L6.64 0.3L0 6.94V8.38ZM14 19.5L2.16 7.66L7.78 2H20.22L25.84 7.66L14 19.5ZM14 4.04004H19.38L23 7.66004L14 16.46V4.04004Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 349 B

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7H13" stroke="#F31260" stroke-width="2.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="122" height="128" viewBox="0 0 122 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M121.441 72.2998L113.772 56.859V47.9663C113.772 33.2261 101.976 21.2884 87.4275 21.2884H74.3218C75.2704 19.3347 75.7914 17.1385 75.7914 14.8211C75.7914 6.62905 69.2185 0 61.096 0C52.9735 0 46.4006 6.62905 46.4006 14.8211C46.4006 17.1385 46.9217 19.3347 47.8702 21.2884H34.7646C20.2162 21.2884 8.41978 33.2261 8.41978 47.9663V56.859L0.591155 72.2594C-0.197052 73.8088 -0.197052 75.6547 0.591155 77.2042L8.41978 92.4295V101.322C8.41978 116.062 20.2162 128 34.7646 128H87.4275C101.976 128 113.772 116.062 113.772 101.322V92.4295L121.427 77.1503C122.189 75.6278 122.189 73.8358 121.441 72.2998ZM53.0804 84.8842C53.0804 91.5806 47.6965 97.0105 41.0569 97.0105C34.4172 97.0105 29.0334 91.5806 29.0334 84.8842V63.3263C29.0334 56.6299 34.4172 51.2 41.0569 51.2C47.6965 51.2 53.0804 56.6299 53.0804 63.3263V84.8842ZM91.8227 84.8842C91.8227 91.5806 86.4389 97.0105 79.7992 97.0105C73.1596 97.0105 67.7757 91.5806 67.7757 84.8842V63.3263C67.7757 56.6299 73.1596 51.2 79.7992 51.2C86.4389 51.2 91.8227 56.6299 91.8227 63.3263V84.8842Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7H13" stroke="#F5A524" stroke-width="2.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 214 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.8572 12.5L0 25V0L17.8572 12.5ZM1.99311 21.1699L14.3843 12.5L1.99311 3.83016V21.1699ZM7.01095 25V22.5375L21.3953 12.5L7.01095 2.46254V0L24.8681 12.5L7.01095 25Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 332 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M26.982 0C19.7852 0.00900726 13.6746 3.96537 9.31269 8H1L0 9V15L0.294 15.708L2.27591 17.6899L2.27799 17.708L10.278 25.708L10.296 25.7101L12.294 27.7081L13 28.0001H19L20 27.0001V18.692C24.0381 14.3368 27.992 8.22601 27.984 1L26.982 0ZM2 10H7.28614C5.18494 12.208 3.6634 14.2399 2.83687 15.4219L2 14.586V10ZM13.4 26.0001L12.561 25.1531C13.7452 24.331 15.7799 22.8169 17.99 20.7222V26.0001H13.4ZM11.114 23.712L4.27199 16.874C6.25599 14 14.758 2.71 25.956 2.032C25.304 13.256 13.982 21.738 11.114 23.712ZM6 28V26H2V22H0V28H6ZM19.4951 12.6657C19.8243 12.1726 20 11.5929 20 11C20 10.6052 19.9221 10.2143 19.7707 9.84965C19.6193 9.48502 19.3974 9.15386 19.1178 8.87515C18.8382 8.59645 18.5063 8.37568 18.1411 8.2255C17.776 8.07533 17.3848 7.9987 16.99 8.00002C16.3971 8.00199 15.8181 8.17962 15.326 8.51046C14.834 8.84131 14.451 9.31053 14.2255 9.85888C14 10.4072 13.942 11.0101 14.0589 11.5914C14.1758 12.1727 14.4623 12.7063 14.8822 13.1248C15.3022 13.5434 15.8367 13.8281 16.4184 13.9431C17.0001 14.058 17.6028 13.998 18.1504 13.7707C18.6979 13.5433 19.1659 13.1588 19.4951 12.6657Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="26" height="27" viewBox="0 0 26 27" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.26 12L22 4H24V2H14V0H12V2H2V4H4L0.76 12H0V14H0.3C0.612778 14.9958 1.24551 15.8606 2.1 16.46C2.9433 17.0715 3.95832 17.4008 5 17.4008C6.04168 17.4008 7.0567 17.0715 7.9 16.46C8.75472 15.8563 9.39277 14.9939 9.72 14H10V12H9.1L5.76 4H12V20H8L7.22 20.36L3.22 25.38L4 27H22L22.78 25.38L18.78 20.36L18 20H14V4H20.26L16.92 12H16V14H16.3C16.6271 14.9909 17.2584 15.8534 18.104 16.4647C18.9496 17.0761 19.9665 17.4052 21.01 17.4052C22.0535 17.4052 23.0704 17.0761 23.916 16.4647C24.7616 15.8534 25.3929 14.9909 25.72 14H26V12H25.26ZM6.44 15.02C5.99893 15.2641 5.50408 15.3947 5 15.4C4.50189 15.3974 4.01284 15.2665 3.58 15.02C3.13862 14.7781 2.76675 14.4268 2.5 14H7.5C7.2355 14.4217 6.87151 14.7719 6.44 15.02ZM6.94 12H2.94L4.94 7.2L6.94 12ZM17.52 22L20 25H6L8.48 22H17.52ZM21.08 7.24L23.08 12.04H19.08L21.08 7.24ZM22.44 15.06C22.0027 15.3134 21.5054 15.4447 21 15.44C20.5006 15.4471 20.0091 15.3156 19.58 15.06C19.138 14.8013 18.7669 14.4371 18.5 14H23.5C23.2521 14.445 22.885 14.8121 22.44 15.06Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,3 @@
<svg width="27" height="27" viewBox="0 0 27 27" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.7866 27.0006H14.214L17.672 23.5426H22.4972L23.5024 22.5374V17.672L27.0006 14.214V12.7866L23.5627 9.32859V4.42304L22.5575 3.43791H17.672L14.214 0H12.7866L9.42911 3.43791H4.46325L3.3977 4.44314V9.32859L0 12.7866V14.214L3.3977 17.672V22.5374L4.46325 23.5426H9.42911L12.7866 27.0006ZM9.75079 21.5321H5.40817V17.2498L5.16691 16.5462L2.1311 13.4903L5.1066 10.4544L5.40817 9.75079V5.44838H9.75079L10.4746 5.16691L13.4501 2.1311L16.5462 5.16691L17.2498 5.44838H21.4919V9.75079L21.8337 10.4544L24.8896 13.4903L21.7734 16.5462L21.4919 17.2498V21.5321H17.2498L16.4859 21.8337L13.4501 24.8695L10.4746 21.8136L9.75079 21.5321ZM10.8968 18.476H12.3243L19.9036 10.8967L18.4762 9.46924L11.6206 16.3449L8.84607 13.5703L7.41864 14.9977L10.8968 18.476Z" fill="#E2D5FF"/>
</svg>

After

Width:  |  Height:  |  Size: 907 B

@@ -0,0 +1,58 @@
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { useEffect, useState } from "react"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { validateApiConfiguration } from "@/utils/validate"
import { vscode } from "@/utils/vscode"
import ApiOptions from "@/components/settings/ApiOptions"
// import { WebviewType } from "@shared/WebviewMessage" // Removed duplicate
import { Dispatch, SetStateAction } from "react" // Added import
import { WebviewType } from "@shared/WebviewMessage"
interface ApiOptionsSectionProps {
showApiOptions: boolean
webviewType: WebviewType
setShowApiOptions?: Dispatch<SetStateAction<boolean>> // Added prop
}
const ApiOptionsSection = ({ showApiOptions, webviewType, setShowApiOptions }: ApiOptionsSectionProps) => {
const { apiConfiguration } = useExtensionState()
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
const disableLetsGoButton = apiErrorMessage != null
const handleSubmit = () => {
vscode.postMessage({ type: "apiConfiguration", apiConfiguration })
const isConfigValid = validateApiConfiguration(apiConfiguration) === undefined
if (setShowApiOptions && isConfigValid) {
setShowApiOptions(false)
}
if (webviewType === "sidebar" && isConfigValid) {
// This message should be handled by the Controller,
// which can then send an appropriate ExtensionMessage back to the webview
// to hide the welcome screen.
vscode.postMessage({ type: "showChatView" })
}
}
useEffect(() => {
setApiErrorMessage(validateApiConfiguration(apiConfiguration))
}, [apiConfiguration])
if (!showApiOptions) {
return null
}
return (
<div className="mt-4.5 mb-8">
<div>
<ApiOptions showModelOptions={false} />
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} className="mt-0.75">
Let's go!
</VSCodeButton>
</div>
</div>
)
}
export default ApiOptionsSection
@@ -0,0 +1,470 @@
import { useState, useRef, useEffect, memo, Dispatch, SetStateAction } from "react"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { AccountServiceClient } from "@/services/grpc-client"
import { EmptyRequest } from "@shared/proto/common"
import ClineLogoWhite from "@/assets/ClineLogoWhite"
// Import CSS styles
import "@/assets/css/styles.css"
import { validateApiConfiguration } from "@/utils/validate" // Added import
const WelcomeTabView = ({ showWelcome }: { showWelcome: boolean }) => {
const { apiConfiguration } = useExtensionState()
const isApiConfigValid = validateApiConfiguration(apiConfiguration) === undefined
const [activeTab, setActiveTab] = useState("Key Features")
const inputFieldRef = useRef<HTMLDivElement>(null)
const inputRef = useRef<HTMLInputElement>(null)
const [inputValue, setInputValue] = useState("")
const [showPlaceholder, setShowPlaceholder] = useState(true)
const handleLogin = () => {
AccountServiceClient.accountLoginClicked(EmptyRequest.create()).catch((err) =>
console.error("Failed to get login URL:", err),
)
}
const handleTabClick = (tabName: string) => {
setActiveTab(tabName)
}
const handleInputFieldClick = () => {
setShowPlaceholder(false)
inputRef.current?.focus()
}
const handleInputBlur = () => {
if (!inputValue) {
setShowPlaceholder(true)
}
}
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value)
}
const handleCreateProject = (model: string) => {
console.log(`Creating project with ${model} model`)
// Implement project creation logic
}
// Equivalent to document.addEventListener("DOMContentLoaded", ...)
useEffect(() => {
// No need to implement DOM manipulation here as we're using React state
}, [])
return (
<div className="home">
<div className="main-container">
{/* Account Info */}
<div className="account-info">
<ClineLogoWhite className="vector-icon" />
<div className="welcome-message-container">
<h1 className="welcome-message">Get Started on Cline</h1>
<p className="description">Let's start and learn the basics of Cline</p>
</div>
<div className="buttons-container">
<button className="primary-button" onClick={handleLogin}>
Let's Create your Account!
</button>
</div>
{/* Conditionally render features-container */}
{isApiConfigValid && !showWelcome ? (
<div className="features-container">
<div className="feature-row">
<div
className="feature-card"
style={{
transition: "all 0.3s ease",
cursor: "pointer",
}}>
<div className="feature-icon-container">
<div className="feature-icon feature-icon-1"></div>
<div className="feature-title">Snake game</div>
</div>
<div className="feature-description">&gt; Build a Snake game...</div>
</div>
<div
className="feature-card"
style={{
transition: "all 0.3s ease",
cursor: "pointer",
}}>
<div className="feature-icon-container">
<div className="feature-icon feature-icon-2"></div>
<div className="feature-title">To-do App</div>
</div>
<div className="feature-description">&gt; Generate a to-do App...</div>
</div>
<div
className="feature-card"
style={{
transition: "all 0.3s ease",
cursor: "pointer",
}}>
<div className="feature-icon-container">
<div className="feature-icon feature-icon-3"></div>
<div className="feature-title">Script</div>
</div>
<div className="feature-description">&gt; Write a script and then...</div>
</div>
<div
className="feature-card"
style={{
transition: "all 0.3s ease",
cursor: "pointer",
}}>
<div className="feature-icon-container">
<div className="feature-icon feature-icon-4"></div>
<div className="feature-title">New Feature</div>
</div>
<div className="feature-description">&gt; Read my code &amp; suggest...</div>
</div>
</div>
<div className="footer">
<div
className="input-field"
ref={inputFieldRef}
onClick={handleInputFieldClick}
style={{
position: "relative",
cursor: "text",
}}>
{showPlaceholder && <div className="input-placeholder">Type your prompt here...</div>}
{!showPlaceholder && (
<input
ref={inputRef}
type="text"
value={inputValue}
onChange={handleInputChange}
onBlur={handleInputBlur}
style={{
width: "100%",
background: "transparent",
border: "none",
outline: "none",
color: "#FFFFFF",
fontSize: "14px",
fontFamily: "SF Pro, Inter, sans-serif",
}}
/>
)}
<div className="input-actions-container">
<div className="input-actions">
<button
className="action-button action-button-secondary"
style={{
transition: "all 0.2s ease",
opacity: 0.5,
}}>
<div className="action-icon action-icon-1"></div>
<span className="action-text">Improve prompt</span>
</button>
<button
className="action-button action-button-primary"
style={{
transition: "all 0.2s ease",
opacity: 0.5,
}}>
<div className="action-icon action-icon-2"></div>
<span className="action-text">Start construction</span>
</button>
</div>
</div>
</div>
</div>
</div>
) : null}
{/* End of conditional rendering for features-container */}
</div>
<div className="divider"></div>
{/* Features Section */}
<div className="features-section-container">
<div className="features-text-container">
<h2 className="features-heading">Work better with the best features of Cline</h2>
<p className="features-description">
It's easy to lose control as they make rapid changes to your codebase. That's why we built safe
features to make the control on your code.
</p>
<div className="divider-dashed"></div>
<div className="features-tabs-container">
<button
className={`tab-button ${activeTab === "Key Features" ? "active-tab" : ""}`}
onClick={() => handleTabClick("Key Features")}>
Key Features
</button>
<button
className={`tab-button ${activeTab === "Tools" ? "active-tab" : ""}`}
onClick={() => handleTabClick("Tools")}>
Tools
</button>
<button
className={`tab-button ${activeTab === "Checkpoints" ? "active-tab" : ""}`}
onClick={() => handleTabClick("Checkpoints")}>
Checkpoints
</button>
</div>
{activeTab === "Key Features" && (
<div className="feature-content">
<div className="feature-selected">
<div className="feature-check-icon"></div>
<div className="feature-info">
<h3 className="feature-title">File Editing</h3>
<p className="feature-subtitle">Cline can write your code for you</p>
</div>
</div>
<div className="feature-buttons-container">
<button className="feature-action-button">
<div className="feature-action-icon button-icon-1"></div>
Create new files
</button>
<button className="feature-action-button">
<div className="feature-action-icon button-icon-1"></div>
Modify existing code
</button>
<button className="feature-action-button">
<div className="feature-action-icon button-icon-1"></div>
Search and replace across files
</button>
</div>
<div className="feature-options">
<div className="feature-option">
<div className="feature-circle"></div>
<span>Terminal Commands</span>
</div>
<div className="feature-option">
<div className="feature-circle"></div>
<span>Code Analysis</span>
</div>
<div className="feature-option">
<div className="feature-circle"></div>
<span>Browser Integration</span>
</div>
</div>
</div>
)}
{activeTab === "Tools" && (
<div className="feature-content">
<p className="feature-description">Cline provides powerful tools to help you code faster</p>
</div>
)}
{activeTab === "Checkpoints" && (
<div className="feature-content">
<p className="feature-description">Cline creates checkpoints to help you track changes</p>
</div>
)}
</div>
</div>
<img alt="Feature Background" className="feature-bg" />
<div className="divider"></div>
{/* Models Section */}
<div className="models-container">
<h2 className="models-heading">Top 3 models for coding</h2>
<div className="models-section-container">
{/* Anthropic Model Card */}
<div className="model-card">
<div className="model-header">
<div className="model-name">anthropic</div>
<div className="model-badge best">Best</div>
</div>
<div className="model-description">Leading model for agentic coding</div>
<div className="model-divider"></div>
<div className="model-image anthropic-model"></div>
<button className="model-button" onClick={() => handleCreateProject("anthropic")}>
Create a new project
</button>
</div>
{/* Google Model Card */}
<div className="model-card">
<div className="model-header">
<div className="model-name">google</div>
<div className="model-badge trending">Trending</div>
</div>
<div className="model-description">Large 1M context window, great value</div>
<div className="model-divider"></div>
<div className="model-image google-model"></div>
<button className="model-button" onClick={() => handleCreateProject("google")}>
Create a new project
</button>
</div>
{/* Meta-Llama Model Card */}
<div className="model-card">
<div className="model-header">
<div className="model-name">meta-llama</div>
<div className="model-badge new">New</div>
</div>
<div className="model-description">Efficient performance at lower cost</div>
<div className="model-divider"></div>
<div className="model-image meta-model"></div>
<button className="model-button" onClick={() => handleCreateProject("meta-llama")}>
Create a new project
</button>
</div>
</div>
</div>
<div className="divider"></div>
{/* Plan/Act Mode Section */}
<div className="plan-act-container">
<h2 className="plan-act-heading">Plan/Act Mode</h2>
<p className="plan-act-description">
Switch between planning and execution modes to better control how Cline assists you. Plan mode helps you
strategize while Act mode implements your solutions.
</p>
<div className="divider-dashed"></div>
<div className="plan-act-content">
{/* Plan Mode Column */}
<div className="plan-mode-container">
<div className="mode-header">
<h3 className="mode-title">Plan Mode</h3>
</div>
<div className="mode-content">
<div className="mode-item">
<div className="mode-icon plan-icon">
<div className="line-icon plan-line"></div>
</div>
<div className="mode-text">Read files & Analysis</div>
</div>
<div className="mode-item">
<div className="mode-icon plan-icon">
<div className="line-icon plan-line"></div>
</div>
<div className="mode-text">Strategy Development</div>
</div>
<div className="mode-item">
<div className="mode-icon plan-icon">
<div className="line-icon plan-line"></div>
</div>
<div className="mode-text">Context Gathering</div>
</div>
<div className="mode-item">
<div className="mode-icon plan-icon">
<div className="line-icon plan-line"></div>
</div>
<div className="mode-text">Requirements Analysis</div>
</div>
<div className="mode-item disabled">
<div className="mode-icon plan-icon-disabled">
<div className="line-icon disabled-line"></div>
</div>
<div className="mode-text">Code Modifications</div>
</div>
<div className="mode-item disabled">
<div className="mode-icon plan-icon-disabled">
<div className="line-icon disabled-line"></div>
</div>
<div className="mode-text">File Creation/Deletion</div>
</div>
</div>
</div>
{/* Act Mode Column */}
<div className="act-mode-container">
<div className="mode-header">
<h3 className="mode-title">Act Mode</h3>
</div>
<div className="mode-content">
<div className="mode-item">
<div className="mode-icon act-icon">
<div className="line-icon act-line"></div>
</div>
<div className="mode-text">Read files & Analysis</div>
</div>
<div className="mode-item">
<div className="mode-icon act-icon">
<div className="line-icon act-line"></div>
</div>
<div className="mode-text">Code Modifications</div>
</div>
<div className="mode-item">
<div className="mode-icon act-icon">
<div className="line-icon act-line"></div>
</div>
<div className="mode-text">File Creation/Deletion</div>
</div>
<div className="mode-item">
<div className="mode-icon act-icon">
<div className="line-icon act-line"></div>
</div>
<div className="mode-text">Execute Commands</div>
</div>
<div className="mode-item">
<div className="mode-icon act-icon">
<div className="line-icon act-line"></div>
</div>
<div className="mode-text">Run Tests</div>
</div>
<div className="mode-item">
<div className="mode-icon act-icon">
<div className="line-icon act-line"></div>
</div>
<div className="mode-text">Deploy Changes</div>
</div>
</div>
</div>
</div>
</div>
<div className="divider"></div>
{/* MCP Servers Section */}
<div className="mcp-servers-container">
<div className="mcp-content-container">
<div className="mcp-text-container">
<h2 className="mcp-heading">The largest ecosystem of MCP Servers for VS Code</h2>
<p className="mcp-description">
The Model Context Protocol (MCP) allows seamless communication with locally running servers that
extend Cline's functionality with additional tools and context-aware resources.
</p>
<button className="mcp-button">Add an MCP Server</button>
</div>
<div className="mcp-count"></div>
</div>
</div>
</div>
</div>
)
}
export default WelcomeTabView
@@ -1,34 +1,24 @@
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { useEffect, useState, memo } from "react"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { validateApiConfiguration } from "@/utils/validate"
import { vscode } from "@/utils/vscode"
import ApiOptions from "@/components/settings/ApiOptions"
import { memo, Dispatch, SetStateAction } from "react"
import ClineLogoWhite from "@/assets/ClineLogoWhite"
import { AccountServiceClient } from "@/services/grpc-client"
import { EmptyRequest } from "@shared/proto/common"
import ApiOptionsSection from "./ApiOptionsSection"
import { WebviewType } from "@shared/WebviewMessage"
const WelcomeView = memo(() => {
const { apiConfiguration } = useExtensionState()
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
const [showApiOptions, setShowApiOptions] = useState(false)
const disableLetsGoButton = apiErrorMessage != null
interface WelcomeViewProps {
showApiOptions: boolean
setShowApiOptions: Dispatch<SetStateAction<boolean>>
webviewType: WebviewType
}
const WelcomeView = memo(({ showApiOptions, setShowApiOptions, webviewType }: WelcomeViewProps) => {
const handleLogin = () => {
AccountServiceClient.accountLoginClicked(EmptyRequest.create()).catch((err) =>
console.error("Failed to get login URL:", err),
)
}
const handleSubmit = () => {
vscode.postMessage({ type: "apiConfiguration", apiConfiguration })
}
useEffect(() => {
setApiErrorMessage(validateApiConfiguration(apiConfiguration))
}, [apiConfiguration])
return (
<div className="fixed inset-0 p-0 flex flex-col">
<div className="h-full px-5 overflow-auto">
@@ -64,16 +54,11 @@ const WelcomeView = memo(() => {
</VSCodeButton>
)}
<div className="mt-4.5">
{showApiOptions && (
<div>
<ApiOptions showModelOptions={false} />
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} className="mt-0.75">
Let's go!
</VSCodeButton>
</div>
)}
</div>
<ApiOptionsSection
showApiOptions={showApiOptions}
webviewType={webviewType}
setShowApiOptions={setShowApiOptions}
/>
</div>
</div>
)
@@ -0,0 +1,23 @@
import { useState, memo } from "react"
import { WebviewType } from "@shared/WebviewMessage"
import WelcomeView from "./WelcomeView"
import WelcomeTabView from "./WelcomeTabView"
const WelcomeWrapper = memo(({ showWelcome }: { showWelcome: boolean }) => {
const [showApiOptions, setShowApiOptions] = useState(false)
// Determine which welcome view to show based on webview type
const webviewType = (document.querySelector('meta[name="webview-type"]')?.getAttribute("content") || "sidebar") as WebviewType
return (
<>
{webviewType === "sidebar" ? (
<WelcomeView showApiOptions={showApiOptions} setShowApiOptions={setShowApiOptions} webviewType={webviewType} />
) : (
<WelcomeTabView showWelcome={showWelcome} />
)}
</>
)
})
export default WelcomeWrapper
@@ -20,7 +20,6 @@ import { TelemetrySetting } from "@shared/TelemetrySetting"
interface ExtensionStateContextType extends ExtensionState {
didHydrateState: boolean
showWelcome: boolean
theme: Record<string, string> | undefined
openRouterModels: Record<string, ModelInfo>
openAiModels: string[]
@@ -32,6 +31,7 @@ interface ExtensionStateContextType extends ExtensionState {
// View state
showMcp: boolean
mcpTab?: McpViewTab
showWelcome: boolean
// Setters
setApiConfiguration: (config: ApiConfiguration) => void
@@ -41,6 +41,7 @@ interface ExtensionStateContextType extends ExtensionState {
setPlanActSeparateModelsSetting: (value: boolean) => void
setShellIntegrationTimeout: (value: number) => void
setMcpServers: (value: McpServer[]) => void
setShowWelcome: (value: boolean) => void
// Navigation
setShowMcp: (value: boolean) => void
@@ -74,8 +75,9 @@ export const ExtensionStateContextProvider: React.FC<{
localWindsurfRulesToggles: {},
shellIntegrationTimeout: 4000, // default timeout for shell integration
})
const [didHydrateState, setDidHydrateState] = useState(false)
// UI state for welcome screen
const [showWelcome, setShowWelcome] = useState(false)
const [didHydrateState, setDidHydrateState] = useState(false)
const [theme, setTheme] = useState<Record<string, string>>()
const [filePaths, setFilePaths] = useState<string[]>([])
const [openRouterModels, setOpenRouterModels] = useState<Record<string, ModelInfo>>({
@@ -106,33 +108,6 @@ export const ExtensionStateContextProvider: React.FC<{
: prevState.autoApprovalSettings,
}
})
const config = message.state?.apiConfiguration
const hasKey = config
? [
config.apiKey,
config.openRouterApiKey,
config.awsRegion,
config.vertexProjectId,
config.openAiApiKey,
config.ollamaModelId,
config.lmStudioModelId,
config.liteLlmApiKey,
config.geminiApiKey,
config.openAiNativeApiKey,
config.deepSeekApiKey,
config.requestyApiKey,
config.togetherApiKey,
config.qwenApiKey,
config.doubaoApiKey,
config.mistralApiKey,
config.vsCodeLmModelSelector,
config.clineApiKey,
config.asksageApiKey,
config.xaiApiKey,
config.sambanovaApiKey,
].some((key) => key !== undefined)
: false
setShowWelcome(!hasKey)
setDidHydrateState(true)
break
}
@@ -196,6 +171,10 @@ export const ExtensionStateContextProvider: React.FC<{
setTotalTasksSize(message.totalTasksSize ?? null)
break
}
case "showWelcome": {
setShowWelcome(true)
break
}
}
}, [])
@@ -208,7 +187,6 @@ export const ExtensionStateContextProvider: React.FC<{
const contextValue: ExtensionStateContextType = {
...state,
didHydrateState,
showWelcome,
theme,
openRouterModels,
openAiModels,
@@ -219,6 +197,7 @@ export const ExtensionStateContextProvider: React.FC<{
totalTasksSize,
showMcp,
mcpTab,
showWelcome,
globalClineRulesToggles: state.globalClineRulesToggles || {},
localClineRulesToggles: state.localClineRulesToggles || {},
localCursorRulesToggles: state.localCursorRulesToggles || {},
@@ -254,6 +233,7 @@ export const ExtensionStateContextProvider: React.FC<{
shellIntegrationTimeout: value,
})),
setMcpServers: (mcpServers: McpServer[]) => setMcpServers(mcpServers),
setShowWelcome,
setShowMcp,
setMcpTab,
}
+2
View File
@@ -7,6 +7,8 @@
@config "../tailwind.config.js";
@config "../tailwind.config.js";
textarea:focus {
outline: 1.5px solid var(--vscode-focusBorder, #007fd4);
}