diff --git a/.vscode/launch.json b/.vscode/launch.json index 9127df53f8..bc1cfa5dd7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,6 +16,31 @@ "IS_DEV": "true", "DEV_WORKSPACE_FOLDER": "${workspaceFolder}" } + }, + { + "name": "Run Extension (Fresh Install Mode)", + "type": "extensionHost", + "request": "launch", + "args": ["--extensionDevelopmentPath=${workspaceFolder} --profile-temp"], + "outFiles": ["${workspaceFolder}/dist/**/*.js"], + "preLaunchTask": "${defaultBuildTask}", + "env": { + "IS_DEV": "true", + "DEV_WORKSPACE_FOLDER": "${workspaceFolder}" + } + }, + { + "name": "Run Extension (Test Mode)", + "type": "extensionHost", + "request": "launch", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "outFiles": ["${workspaceFolder}/dist/**/*.js"], + "preLaunchTask": "${defaultBuildTask}", + "env": { + "IS_DEV": "true", + "IS_TEST": "true", + "DEV_WORKSPACE_FOLDER": "${workspaceFolder}" + } } ] } diff --git a/package.json b/package.json index dd38581ad8..878511be0e 100644 --- a/package.json +++ b/package.json @@ -118,6 +118,11 @@ "command": "cline.focusChatInput", "title": "Jump to Chat Input", "category": "Cline" + }, + { + "command": "cline.showWelcomeTab", + "title": "Show Welcome Tab", + "category": "Cline" } ], "keybindings": [ @@ -280,6 +285,11 @@ "type": "boolean", "default": true, "description": "Controls whether the MCP Marketplace is enabled." + }, + "cline.showWelcomeTabOnInstall": { + "type": "boolean", + "default": true, + "description": "Controls whether the welcome tab is shown when the extension is installed." } } } diff --git a/src/core/welcome/WelcomeTabProvider.ts b/src/core/welcome/WelcomeTabProvider.ts new file mode 100644 index 0000000000..0b8510a105 --- /dev/null +++ b/src/core/welcome/WelcomeTabProvider.ts @@ -0,0 +1,137 @@ +import * as vscode from "vscode" +import * as path from "path" +import { Logger } from "../../services/logging/Logger" +import { getUri } from "../webview/getUri" + +export class WelcomeTabProvider { + private static readonly WELCOME_TAB_SHOWN_KEY = "cline.welcomeTabShown" + + constructor(private readonly context: vscode.ExtensionContext) {} + + /** + * Shows the welcome tab if it hasn't been shown before and is enabled in settings + */ + public async showWelcomeTabIfNeeded(): Promise { + // Check if we've already shown the welcome tab + const hasShownWelcomeTab = this.context.globalState.get(WelcomeTabProvider.WELCOME_TAB_SHOWN_KEY, false) + + // Check if showing the welcome tab on install is enabled in settings + const showWelcomeTabOnInstall = vscode.workspace.getConfiguration("cline").get("showWelcomeTabOnInstall", true) + + if (!hasShownWelcomeTab && showWelcomeTabOnInstall) { + await this.showWelcomeTab() + // Mark that we've shown the welcome tab + await this.context.globalState.update(WelcomeTabProvider.WELCOME_TAB_SHOWN_KEY, true) + } + } + + /** + * Shows the welcome tab + */ + public async showWelcomeTab(): Promise { + Logger.log("Showing welcome tab") + + // Create and show the webview panel + const panel = vscode.window.createWebviewPanel( + "clineWelcomeTab", // Unique ID + "Welcome to Cline", // Title displayed in the tab + vscode.ViewColumn.One, // Show in the first column + { + enableScripts: true, + localResourceRoots: [this.context.extensionUri], + }, + ) + + // Set the icon for the tab + panel.iconPath = { + light: vscode.Uri.joinPath(this.context.extensionUri, "assets", "icons", "robot_panel_light.png"), + dark: vscode.Uri.joinPath(this.context.extensionUri, "assets", "icons", "robot_panel_dark.png"), + } + + // Get the content of the welcome tab + panel.webview.html = await this.getWelcomeTabContent(panel.webview) + + // Handle messages from the webview + panel.webview.onDidReceiveMessage( + (message) => { + switch (message.command) { + case "openCline": + // Open Cline when the button is clicked + vscode.commands.executeCommand("cline.openInNewTab") + return + } + }, + undefined, + this.context.subscriptions, + ) + + return panel + } + + /** + * Gets the HTML content for the welcome tab + */ + private async getWelcomeTabContent(webview: vscode.Webview): Promise { + // Get the path to the welcome tab HTML file + const htmlPath = vscode.Uri.joinPath(this.context.extensionUri, "webview-ui", "src", "assets", "welcome-tab.html") + + // Read the HTML file + const htmlContent = await vscode.workspace.fs.readFile(htmlPath) + let htmlString = Buffer.from(htmlContent).toString("utf-8") + + // Get the base path for assets + const assetsPath = vscode.Uri.joinPath(this.context.extensionUri, "webview-ui", "src", "assets") + + // Convert resource paths to webview URIs + const cssUri = webview.asWebviewUri(vscode.Uri.joinPath(assetsPath, "css", "styles.css")) + const jsUri = webview.asWebviewUri(vscode.Uri.joinPath(assetsPath, "js", "script.js")) + + // Replace CSS and JS paths + htmlString = htmlString.replace("css/styles.css", cssUri.toString()) + htmlString = htmlString.replace("js/script.js", jsUri.toString()) + + // Replace image paths + const imageFiles = [ + "back-icon.svg", + "vector.svg", + "vector-1.svg", + "feature-icon-1.svg", + "feature-icon-2.svg", + "feature-icon-3.svg", + "feature-icon-4.svg", + "action-icon-1.svg", + "action-icon-2.svg", + "button-icon-1.svg", + "anthropic-model.jpg", + "google-model.jpg", + "meta-model.jpg", + "feature-bg.png", + "50.svg", + ] + + for (const imageFile of imageFiles) { + const imageUri = webview.asWebviewUri(vscode.Uri.joinPath(assetsPath, "images", imageFile)) + htmlString = htmlString.replace(`images/${imageFile}`, imageUri.toString()) + } + + // Add Content Security Policy + const csp = ` + + ` + + // Insert CSP meta tag into the head section + htmlString = htmlString.replace("", `${csp}`) + + // Get the logo path (if needed) + const logoUri = webview.asWebviewUri(vscode.Uri.joinPath(this.context.extensionUri, "assets", "icons", "icon.png")) + + // Replace the placeholder with the actual logo path (if needed) + htmlString = htmlString.replace("{{logoPath}}", logoUri.toString()) + + return htmlString + } +} diff --git a/src/extension.ts b/src/extension.ts index 91200d06c4..1d39780fdb 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -12,6 +12,7 @@ import { telemetryService } from "./services/telemetry/TelemetryService" import { WebviewProvider } from "./core/webview" import { ErrorService } from "./services/error/ErrorService" import { initializeTestMode, cleanupTestMode } from "./services/test/TestMode" +import { WelcomeTabProvider } from "./core/welcome/WelcomeTabProvider" /* Built using https://github.com/microsoft/vscode-webview-ui-toolkit @@ -34,6 +35,23 @@ export function activate(context: vscode.ExtensionContext) { Logger.initialize(outputChannel) Logger.log("Cline extension activated") + // Create the welcome tab provider + const welcomeTabProvider = new WelcomeTabProvider(context) + + // Show the welcome tab if it hasn't been shown before + welcomeTabProvider.showWelcomeTabIfNeeded().catch((err) => { + Logger.error("Failed to show welcome tab", err) + }) + + // Register command to manually show the welcome tab + context.subscriptions.push( + vscode.commands.registerCommand("cline.showWelcomeTab", () => { + welcomeTabProvider.showWelcomeTab().catch((err) => { + Logger.error("Failed to show welcome tab", err) + }) + }), + ) + const sidebarWebview = new WebviewProvider(context, outputChannel) // Initialize test mode and add disposables to context diff --git a/webview-ui/src/assets/css/styles.css b/webview-ui/src/assets/css/styles.css new file mode 100644 index 0000000000..60886b5e45 --- /dev/null +++ b/webview-ui/src/assets/css/styles.css @@ -0,0 +1,943 @@ +/* Global Styles */ +:root { + /* Colores exactos del diseƱo Figma actualizado */ + --background-color: #181818; /* fill_8WL57W */ + --container-bg: #1c1c1c; /* fill_4TPDFH */ + --card-bg: #2b2b2b; /* fill_19R5B2 */ + --input-bg: #313131; /* fill_XT87QJ */ + --primary-color: #0078d4; /* fill_GFEHTQ */ + --secondary-color: #313131; /* fill_XT87QJ */ + --text-color: #ffffff; /* fill_UEF7SS */ + --text-secondary: #cccccc; + --text-placeholder: #c5c5c5; + --border-color: #333333; /* stroke_H0B5JR */ + --border-light: #3d3d3d; /* stroke_Y5VPFU */ + --border-card: #414141; /* stroke_H220VQ */ + --border-input: #3c3c3c; /* stroke_ZPDGQX */ + --border-primary: #2282d7; /* stroke_II32J1 */ + --border-tab: #4e4e4e; /* stroke_K3DJNL */ + --link-color: #4daafc; /* fill_AJL987 */ + --active-tab-color: #026ec1; /* fill_R3WUBX */ + --social-bg: #390e67; /* fill_DWNHUJ */ + --social-card-bg: #18181b; /* fill_73UY73 */ + --social-card-purple: #c8abff; /* fill_PRU1UJ */ + --social-card-purple-text: #8623de; /* fill_A5PYCT */ + --social-card-violet: #9535f2; /* fill_GCX7BL */ + --social-card-violet-text: #f7f3ff; /* fill_K63Y8F */ + + /* Espaciados exactos */ + --gap-small: 8px; + --gap-medium: 16px; + --gap-large: 24px; + --gap-xlarge: 40px; + --gap-xxlarge: 80px; + + /* Radios de borde */ + --radius-small: 4px; + --radius-medium: 8px; + --radius-large: 12px; + --radius-xlarge: 14px; + --radius-round: 9999px; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: "SF Pro", "Inter", sans-serif; + background-color: var(--background-color); + color: var(--text-color); + line-height: 1.3; + letter-spacing: -0.01em; +} + +/* Layout */ +.home { + display: flex; + flex-direction: column; + align-items: center; + padding: var(--gap-large); + min-height: 100vh; +} + +.main-container { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--gap-xxlarge); + padding: 16px var(--gap-large); + width: 100%; + max-width: 1200px; +} + +/* Back Button */ +.back-button-container { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--gap-small); + width: 100%; + margin-bottom: 20px; +} + +.back-icon { + display: flex; + align-items: center; + justify-content: center; +} + +.back-text { + font-size: 14px; + font-weight: 400; + color: var(--link-color); +} + +/* Account Info */ +.account-info { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--gap-xlarge); + width: 100%; +} + +.vector-icon { + width: 48px; + height: 48px; +} + +.welcome-message-container { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--gap-large); +} + +.welcome-message { + font-size: 48px; + font-weight: 510; + text-align: center; + line-height: 1em; +} + +.description { + font-family: "Inter", sans-serif; + font-size: 18px; + font-weight: 400; + text-align: center; + line-height: 1.3em; + letter-spacing: -1%; +} + +/* Buttons */ +.buttons-container { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: var(--gap-medium); + width: 100%; +} + +.primary-button { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--gap-small); + padding: 0 16px; + height: 36px; + background-color: var(--primary-color); + border: 1px solid var(--border-primary); + border-radius: var(--radius-small); + color: var(--text-color); + font-size: 14px; + font-weight: 510; + line-height: 1.3em; + letter-spacing: -1%; + cursor: pointer; +} + +.secondary-button { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: var(--gap-small); + padding: 0 16px; + width: 208px; + height: 36px; + background-color: var(--secondary-color); + border: 1px solid var(--border-light); + border-radius: var(--radius-small); + color: var(--text-color); + font-size: 14px; + font-weight: 510; + line-height: 1.3em; + letter-spacing: -1%; + cursor: pointer; +} + +/* Features Container */ +.features-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: var(--gap-medium); + padding: var(--gap-medium); + width: 100%; + background-color: var(--container-bg); + border: 1px solid var(--border-color); + border-radius: var(--radius-large); +} + +.feature-row { + display: flex; + flex-direction: row; + justify-content: stretch; + align-items: stretch; + gap: var(--gap-medium); + width: 100%; + flex-wrap: wrap; +} + +@media (max-width: 768px) { + .feature-row { + flex-direction: column; + } +} + +.feature-card { + display: flex; + flex-direction: column; + gap: 12px; + padding: 10px; + flex: 1; + min-width: 200px; + background-color: var(--card-bg); + border: 1px solid var(--border-card); + border-radius: var(--radius-medium); +} + +.feature-icon-container { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--gap-small); + width: 100%; +} + +.feature-icon { + width: 20px; + height: 20px; +} + +.feature-title { + font-size: 13px; + font-weight: 400; + line-height: 1; +} + +.feature-description { + font-size: 14px; + font-weight: 400; + line-height: 1.3em; + letter-spacing: -1%; + color: var(--text-secondary); +} + +/* Footer */ +.footer { + display: flex; + flex-direction: column; + justify-content: center; + width: 100%; + gap: var(--gap-medium); +} + +.input-field { + display: flex; + flex-direction: column; + justify-content: space-between; + width: 100%; + gap: var(--gap-small); + padding: 12px; + height: 104px; + background-color: var(--input-bg); + border: 1px solid var(--border-input); + border-radius: 6px; +} + +.input-placeholder { + font-size: 14px; + font-weight: 400; + line-height: 1.3em; + letter-spacing: -1%; + color: var(--text-placeholder); +} + +.input-actions-container { + display: flex; + flex-direction: row; + justify-content: flex-end; + align-items: center; + width: 100%; + gap: 10px; +} + +.input-actions { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--gap-medium); +} + +.action-button { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: var(--gap-small); + padding: 7px 8px; + border-radius: var(--radius-small); + font-size: 14px; + font-weight: 400; + line-height: 1.3em; + letter-spacing: -1%; + cursor: pointer; + opacity: 0.5; +} + +.action-button:hover { + opacity: 1; +} + +.action-button-secondary { + background-color: var(--secondary-color); + border: 1px solid var(--border-light); + color: var(--text-color); +} + +.action-button-primary { + background-color: var(--primary-color); + border: 1px solid var(--border-primary); + color: var(--text-color); +} + +.action-icon { + width: 14px; + height: 14px; +} + +.action-text { + font-size: 14px; + font-weight: 400; +} + +/* Divider */ +.divider { + width: 100%; + height: 1px; + background-color: var(--border-color); + border: none; + border-top: 1px dashed var(--border-color); + margin: 20px 0; +} + +.divider-dashed { + width: 100%; + height: 1px; + background-color: transparent; + border: none; + border-top: 1px dashed var(--border-color); + margin: 0; +} + +/* Features Section */ +.features-section-container { + display: grid; + grid-template-columns: 1fr 1fr; + width: 100%; + gap: var(--gap-xlarge); + margin-bottom: 40px; +} + +@media (max-width: 992px) { + .features-section-container { + grid-template-columns: 1fr; + justify-items: center; + } +} + +.features-text-container { + display: flex; + flex-direction: column; + gap: var(--gap-large); + flex: 1; +} + +.features-heading { + font-size: 32px; + font-weight: 510; + max-width: 320px; + line-height: 1.2em; +} + +.features-description { + font-family: "Inter", sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 1.3em; + letter-spacing: -1%; +} + +.features-tabs-container { + display: flex; + flex-direction: row; + width: 100%; + flex-wrap: wrap; + gap: var(--gap-small); +} + +.tab-button { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: var(--gap-small); + padding: 0 16px; + height: 32px; + background-color: transparent; + border: 1px solid var(--border-tab); + border-radius: var(--radius-round); + color: var(--text-color); + font-family: "Inter", sans-serif; + font-size: 14px; + font-weight: 400; + line-height: 1.3em; + letter-spacing: -1%; + cursor: pointer; +} + +.tab-button.active { + background-color: var(--active-tab-color); + border: none; +} + +.container { + display: flex; + flex-direction: column; + width: 100%; + gap: var(--gap-medium); +} + +.additional-feedback-container { + display: flex; + flex-direction: row; + width: 100%; + gap: var(--gap-medium); + padding: var(--gap-medium); +} + +.feature-highlight { + background-color: var(--card-bg); + border: 1px solid var(--border-card); + border-radius: var(--radius-large); +} + +.feature-option { + border-radius: var(--radius-large); +} + +.icon-container { + display: flex; + justify-content: center; + align-items: center; + width: 20px; + height: 20px; + background-color: var(--primary-color); + border-radius: var(--radius-round); +} + +.vector-1-icon { + width: 12px; + height: 12px; + stroke: var(--text-color); + stroke-width: 1.5px; +} + +.prompt-container { + display: flex; + flex-direction: column; + gap: var(--gap-medium); + padding: 1px 0 0; + flex: 1; +} + +.prompt-text-container { + display: flex; + flex-direction: column; + width: 100%; + gap: var(--gap-small); +} + +h3.feature-title { + font-size: 15px; + font-weight: 510; + line-height: 1em; +} + +.prompt-buttons-container { + display: flex; + flex-direction: row; + width: 100%; + flex-wrap: wrap; + gap: var(--gap-small); +} + +.feature-button { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: var(--gap-small); + padding: 0 8px; + height: 28px; + background-color: transparent; + border: 1px solid var(--border-light); + border-radius: var(--radius-medium); + color: var(--text-color); + font-size: 14px; + font-weight: 400; + line-height: 1em; + cursor: pointer; +} + +.button-icon { + width: 14px; + height: 14px; +} + +.button-text { + font-size: 14px; + font-weight: 400; + line-height: 1; +} + +.feature-circle { + width: 24px; + height: 24px; + border: 1.5px solid var(--primary-color); + border-radius: var(--radius-round); +} + +.feature-image-container { + position: relative; + width: 100%; + height: 100%; + display: block; + overflow: hidden; +} + +.feature-bg { + width: 100%; + height: 100%; + background-image: url("../images/feature-bg.png"); + background-size: cover; + background-position: center; + border-radius: var(--radius-xlarge); + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + object-fit: fill; +} + +.feature-image { + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + height: 100%; + width: auto; + object-fit: contain; + z-index: 2; +} + +@media (max-width: 992px) { + .feature-image-container { + margin-top: 30px; + width: 100%; + max-width: 500px; + height: 400px; + } +} + +/* Models Section */ +.models-container { + display: flex; + flex-direction: column; + width: 100%; + gap: var(--gap-xlarge); +} + +.models-heading { + font-size: 32px; + font-weight: 510; + text-align: center; + line-height: 1.2em; +} + +.models-section-container { + display: flex; + flex-direction: row; + justify-content: stretch; + align-items: stretch; + width: 100%; + gap: var(--gap-large); + flex-wrap: wrap; +} + +@media (max-width: 992px) { + .models-section-container { + flex-direction: column; + } +} + +.model-card { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: var(--gap-medium); + padding: var(--gap-medium); + flex: 1; + min-width: 250px; + background-color: var(--container-bg); + border: 1px solid var(--border-color); + border-radius: 8px; +} + +.model-header { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + width: 100%; + gap: var(--gap-large); +} + +.model-name { + font-size: 16px; + font-weight: 590; + text-transform: uppercase; + line-height: 1em; +} + +.model-badge { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + padding: 1px 8px; + height: 20px; + border-radius: var(--radius-round); + font-size: 11px; + font-weight: 510; + line-height: 1.3em; + letter-spacing: -1%; + text-transform: uppercase; + filter: drop-shadow(0px 25px 50px rgba(0, 0, 0, 0.25)); +} + +.model-badge.best { + background-color: #8078d4; +} + +.model-badge.trending { + background-color: var(--text-color); + color: var(--background-color); +} + +.model-badge.new { + background-color: var(--text-color); + color: var(--background-color); +} + +.model-description { + font-size: 14px; + font-weight: 400; + line-height: 1.3em; + letter-spacing: -1%; + width: 100%; +} + +.model-divider { + width: 100%; + height: 1px; + background-color: transparent; + border: none; + border-top: 1px dashed var(--border-light); + margin: 0; +} + +.model-image { + width: 100%; + height: 200px; + border-radius: 8px; + object-fit: cover; +} + +.model-button { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: var(--gap-small); + padding: 7px 8px; + width: 100%; + background-color: var(--secondary-color); + border: 1px solid var(--border-light); + border-radius: var(--radius-small); + color: var(--text-color); + font-size: 14px; + font-weight: 510; + line-height: 1.3em; + letter-spacing: -1%; + cursor: pointer; +} + +/* Plan/Act Mode Section */ +.plan-act-container { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + gap: var(--gap-large); + margin-bottom: 40px; +} + +.plan-act-heading { + font-size: 32px; + font-weight: 510; + text-align: center; + line-height: 1.2em; +} + +.plan-act-description { + font-family: "Inter", sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 1.3em; + letter-spacing: -1%; + text-align: center; +} + +.plan-act-content { + display: flex; + flex-direction: row; + justify-content: stretch; + align-items: stretch; + gap: 32px; + width: 100%; + max-width: 880px; + margin-top: 20px; + min-height: 500px; +} + +@media (max-width: 768px) { + .plan-act-content { + flex-direction: column; + } +} + +.plan-mode-container { + display: flex; + flex-direction: column; + padding: 4px; + flex: 1; + background-color: #f5a524; + border-radius: var(--radius-xlarge); + height: 500px; +} + +.act-mode-container { + display: flex; + flex-direction: column; + padding: 4px; + flex: 1; + background-color: #006fee; + border-radius: var(--radius-xlarge); + margin-top: 80px; + height: 500px; +} + +.mode-header { + display: flex; + flex-direction: row; + justify-content: stretch; + align-items: stretch; + width: 100%; + gap: var(--gap-small); + padding: var(--gap-medium); +} + +.mode-title { + font-family: "Azeret Mono", monospace; + font-weight: 500; + font-size: 20px; + line-height: 1.4em; + color: var(--text-color); + filter: drop-shadow(0px 4px 6px rgba(0, 0, 0, 0.1)) drop-shadow(0px 10px 15px rgba(0, 0, 0, 0.1)); +} + +.mode-content { + display: flex; + flex-direction: column; + width: 100%; + gap: var(--gap-medium); + padding: var(--gap-large); + background-color: var(--text-color); + border-radius: var(--radius-xlarge); + height: 100%; + justify-content: space-around; + min-height: 450px; +} + +.plan-mode-container .mode-content { + border: 2px solid #f5a524; +} + +.act-mode-container .mode-content { + border: 2px solid #006fee; +} + +.mode-item { + display: flex; + flex-direction: row; + align-items: center; + width: 100%; + gap: 10px; + height: 50px; + padding-left: 10px; +} + +.mode-item.disabled { + opacity: 0.5; +} + +.mode-icon { + display: flex; + align-items: center; + justify-content: center; +} + +.mode-text { + font-family: "Azeret Mono", monospace; + font-weight: 400; + font-size: 18px; + line-height: 1.5em; + color: #18181b; + text-align: left; + margin-left: 10px; +} + +.line-icon { + width: 14px; + height: 14px; + display: block; +} + +/* MCP Servers Section */ +.mcp-servers-container { + display: flex; + flex-direction: column; + width: 100%; + gap: var(--gap-large); + margin-bottom: 40px; +} + +.mcp-content-container { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + width: 100%; + gap: 56px; +} + +@media (max-width: 992px) { + .mcp-content-container { + flex-direction: column; + align-items: center; + text-align: center; + } +} + +.mcp-text-container { + display: flex; + flex-direction: column; + gap: var(--gap-large); + width: 100%; +} + +.mcp-heading { + font-size: 32px; + font-weight: 510; + line-height: 1.2em; +} + +.mcp-description { + font-family: "Inter", sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 1.3em; + letter-spacing: -1%; +} + +.mcp-button { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--gap-small); + padding: 0 16px; + height: 36px; + background-color: var(--primary-color); + border: 1px solid var(--border-primary); + border-radius: var(--radius-small); + color: var(--text-color); + font-size: 14px; + font-weight: 510; + line-height: 1.3em; + letter-spacing: -1%; + cursor: pointer; + width: fit-content; +} + +.mcp-count { + display: flex; + align-items: center; + justify-content: center; + width: 100%; +} + +.mcp-count svg { + width: auto; + height: auto; + max-width: 100%; +} + +@media (max-width: 992px) { + .mcp-count { + margin-top: 20px; + } + + .mcp-count svg { + transform: scale(0.8); + } +} diff --git a/webview-ui/src/assets/images/50.svg b/webview-ui/src/assets/images/50.svg new file mode 100644 index 0000000000..b046666454 --- /dev/null +++ b/webview-ui/src/assets/images/50.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/webview-ui/src/assets/images/action-icon-1.svg b/webview-ui/src/assets/images/action-icon-1.svg new file mode 100644 index 0000000000..0bdf581a48 --- /dev/null +++ b/webview-ui/src/assets/images/action-icon-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/webview-ui/src/assets/images/action-icon-2.svg b/webview-ui/src/assets/images/action-icon-2.svg new file mode 100644 index 0000000000..aaf2f5bb40 --- /dev/null +++ b/webview-ui/src/assets/images/action-icon-2.svg @@ -0,0 +1,3 @@ + + + diff --git a/webview-ui/src/assets/images/anthropic-model.jpg b/webview-ui/src/assets/images/anthropic-model.jpg new file mode 100644 index 0000000000..1b4dc36463 Binary files /dev/null and b/webview-ui/src/assets/images/anthropic-model.jpg differ diff --git a/webview-ui/src/assets/images/back-icon.svg b/webview-ui/src/assets/images/back-icon.svg new file mode 100644 index 0000000000..9ca7a08d31 --- /dev/null +++ b/webview-ui/src/assets/images/back-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/webview-ui/src/assets/images/button-icon-1.svg b/webview-ui/src/assets/images/button-icon-1.svg new file mode 100644 index 0000000000..43e9c9327a --- /dev/null +++ b/webview-ui/src/assets/images/button-icon-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/webview-ui/src/assets/images/feature-bg.png b/webview-ui/src/assets/images/feature-bg.png new file mode 100644 index 0000000000..b8e0ecc91c Binary files /dev/null and b/webview-ui/src/assets/images/feature-bg.png differ diff --git a/webview-ui/src/assets/images/feature-icon-1.svg b/webview-ui/src/assets/images/feature-icon-1.svg new file mode 100644 index 0000000000..0bdf581a48 --- /dev/null +++ b/webview-ui/src/assets/images/feature-icon-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/webview-ui/src/assets/images/feature-icon-2.svg b/webview-ui/src/assets/images/feature-icon-2.svg new file mode 100644 index 0000000000..1fedcb045c --- /dev/null +++ b/webview-ui/src/assets/images/feature-icon-2.svg @@ -0,0 +1,3 @@ + + + diff --git a/webview-ui/src/assets/images/feature-icon-3.svg b/webview-ui/src/assets/images/feature-icon-3.svg new file mode 100644 index 0000000000..15c386ac92 --- /dev/null +++ b/webview-ui/src/assets/images/feature-icon-3.svg @@ -0,0 +1,3 @@ + + + diff --git a/webview-ui/src/assets/images/feature-icon-4.svg b/webview-ui/src/assets/images/feature-icon-4.svg new file mode 100644 index 0000000000..c46fcba0ed --- /dev/null +++ b/webview-ui/src/assets/images/feature-icon-4.svg @@ -0,0 +1,3 @@ + + + diff --git a/webview-ui/src/assets/images/feature-image.jpg b/webview-ui/src/assets/images/feature-image.jpg new file mode 100644 index 0000000000..8e8df6a977 Binary files /dev/null and b/webview-ui/src/assets/images/feature-image.jpg differ diff --git a/webview-ui/src/assets/images/feature-image.png b/webview-ui/src/assets/images/feature-image.png new file mode 100644 index 0000000000..397f212c31 Binary files /dev/null and b/webview-ui/src/assets/images/feature-image.png differ diff --git a/webview-ui/src/assets/images/google-model.jpg b/webview-ui/src/assets/images/google-model.jpg new file mode 100644 index 0000000000..1fac8e38ae Binary files /dev/null and b/webview-ui/src/assets/images/google-model.jpg differ diff --git a/webview-ui/src/assets/images/meta-model.jpg b/webview-ui/src/assets/images/meta-model.jpg new file mode 100644 index 0000000000..3fa154bb63 Binary files /dev/null and b/webview-ui/src/assets/images/meta-model.jpg differ diff --git a/webview-ui/src/assets/images/plan-line.svg b/webview-ui/src/assets/images/plan-line.svg new file mode 100644 index 0000000000..e69de29bb2 diff --git a/webview-ui/src/assets/images/vector-1.svg b/webview-ui/src/assets/images/vector-1.svg new file mode 100644 index 0000000000..1e04dbf789 --- /dev/null +++ b/webview-ui/src/assets/images/vector-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/webview-ui/src/assets/images/vector.svg b/webview-ui/src/assets/images/vector.svg new file mode 100644 index 0000000000..e0623c12ad --- /dev/null +++ b/webview-ui/src/assets/images/vector.svg @@ -0,0 +1,3 @@ + + + diff --git a/webview-ui/src/assets/js/script.js b/webview-ui/src/assets/js/script.js new file mode 100644 index 0000000000..d4c660b282 --- /dev/null +++ b/webview-ui/src/assets/js/script.js @@ -0,0 +1,126 @@ +document.addEventListener("DOMContentLoaded", function () { + // Tab switching functionality + const tabButtons = document.querySelectorAll(".tab-button") + + tabButtons.forEach((button) => { + button.addEventListener("click", function () { + // Remove active class from all tabs + tabButtons.forEach((btn) => { + btn.classList.remove("active") + }) + + // Add active class to clicked tab + this.classList.add("active") + + // Here you would typically show/hide content based on the selected tab + // For this demo, we're just changing the active state of the tab + }) + }) + + // Button hover effects + const allButtons = document.querySelectorAll("button") + + allButtons.forEach((button) => { + button.addEventListener("mouseenter", function () { + this.style.opacity = "1" + this.style.transform = "scale(1.02)" + this.style.transition = "all 0.2s ease" + }) + + button.addEventListener("mouseleave", function () { + this.style.transform = "scale(1)" + + // Only reset opacity for action buttons that have default opacity of 0.5 + if (this.classList.contains("action-button")) { + this.style.opacity = "0.5" + } + }) + }) + + // Feature card hover effects + const featureCards = document.querySelectorAll(".feature-card") + + featureCards.forEach((card) => { + card.addEventListener("mouseenter", function () { + this.style.transform = "translateY(-5px)" + this.style.boxShadow = "0 10px 20px rgba(0, 0, 0, 0.2)" + this.style.transition = "all 0.3s ease" + }) + + card.addEventListener("mouseleave", function () { + this.style.transform = "translateY(0)" + this.style.boxShadow = "none" + }) + }) + + // Feature option hover effects + const featureOptions = document.querySelectorAll(".feature-option") + + featureOptions.forEach((option) => { + option.addEventListener("mouseenter", function () { + this.style.backgroundColor = "rgba(43, 43, 43, 0.5)" + this.style.transition = "all 0.2s ease" + }) + + option.addEventListener("mouseleave", function () { + this.style.backgroundColor = "transparent" + }) + }) + + // Input field focus effect + const inputField = document.querySelector(".input-field") + const inputPlaceholder = document.querySelector(".input-placeholder") + + if (inputField && inputPlaceholder) { + inputField.addEventListener("click", function () { + inputPlaceholder.style.opacity = "0.7" + inputField.style.borderColor = "#4DAAFC" + + // Create a temporary input element for demonstration + if (!document.querySelector(".temp-input")) { + const tempInput = document.createElement("input") + tempInput.type = "text" + tempInput.className = "temp-input" + tempInput.style.width = "100%" + tempInput.style.background = "transparent" + tempInput.style.border = "none" + tempInput.style.outline = "none" + tempInput.style.color = "#FFFFFF" + tempInput.style.fontSize = "14px" + tempInput.style.fontFamily = "SF Pro, Inter, sans-serif" + + inputPlaceholder.style.display = "none" + inputField.insertBefore(tempInput, inputField.firstChild) + tempInput.focus() + + tempInput.addEventListener("blur", function () { + if (!this.value) { + inputPlaceholder.style.display = "block" + inputPlaceholder.style.opacity = "1" + this.remove() + } + inputField.style.borderColor = "#3C3C3C" + }) + } + }) + } + + // Back button hover effect + const backButton = document.querySelector(".back-button-container") + + if (backButton) { + backButton.addEventListener("mouseenter", function () { + this.style.opacity = "0.8" + this.style.cursor = "pointer" + }) + + backButton.addEventListener("mouseleave", function () { + this.style.opacity = "1" + }) + + backButton.addEventListener("click", function () { + // This would typically navigate back to the main Cline page + alert("Navigating back to Cline...") + }) + } +}) diff --git a/webview-ui/src/assets/welcome-tab.html b/webview-ui/src/assets/welcome-tab.html new file mode 100644 index 0000000000..efd1d38a66 --- /dev/null +++ b/webview-ui/src/assets/welcome-tab.html @@ -0,0 +1,406 @@ + + + + + + Cline - Extension v1.0 + + + + + +
+
+ +
+
+ Back Icon +
+
Back to Cline
+
+ + + + +
+ + +
+
+

Work better with the best features of Cline

+

+ 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. +

+ +
+ +
+ + + +
+ +
+ + + + + + + +
+
+ +
+
+
+
+ +
+ + +
+

Top 3 models for coding

+ +
+ +
+
+
anthropic
+
Best
+
+ +
Leading model for agentic coding
+ +
+ + Anthropic Model + + +
+ + +
+
+
google
+ +
+ +
Large 1M context window, great value
+ +
+ + Google Model + + +
+ + +
+
+
meta-llama
+
New
+
+ +
Efficient performance at lower cost
+ +
+ + Meta-Llama Model + + +
+
+
+ +
+ + +
+

Plan/Act Mode

+

Lorem Ipsum about this topic

+ +
+ +
+ +
+
+

Plan Mode

+
+
+
+
+ Plan Icon +
+
Read files & Analysis
+
+
+
+ Plan Icon +
+
Strategy Development
+
+
+
+ Plan Icon +
+
Context Gathering
+
+
+
+ Plan Icon +
+
Requirements Analysis
+
+
+
+ Disabled Icon +
+
Code Modifications
+
+
+
+ Disabled Icon +
+
File Creation/Deletion
+
+
+
+ + +
+
+

Act Mode

+
+
+
+
+ Act Icon +
+
Read files & Analysis
+
+
+
+ Act Icon +
+
Code Modifications
+
+
+
+ Act Icon +
+
File Creation/Deletion
+
+
+
+ Act Icon +
+
Execute Commands
+
+
+
+ Act Icon +
+
Run Tests
+
+
+
+ Act Icon +
+
Deploy Changes
+
+
+
+
+
+ +
+ + +
+
+
+

The largest ecosystem of MCP Servers for VS Code

+

+ The Model Context Protocol (MCP) allows seamless communication with locally running servers that + extend Cline's functionality with additional tools and context-aware resources. +

+ +
+
+ + + + + + + + +
+
+
+
+
+ + + +