Update welcome page and cline account view

This commit is contained in:
Saoud Rizwan
2025-03-05 00:13:22 -08:00
parent 17da90ff47
commit 76e1b68115
5 changed files with 176 additions and 107 deletions
+1 -11
View File
@@ -90,11 +90,6 @@
"title": "Settings",
"icon": "$(settings-gear)"
},
{
"command": "cline.accountLoginClicked",
"title": "Account",
"icon": "$(account)"
},
{
"command": "cline.openInNewTab",
"title": "Open In New Tab",
@@ -123,14 +118,9 @@
"group": "navigation@4",
"when": "view == claude-dev.SidebarProvider"
},
{
"command": "cline.accountLoginClicked",
"group": "navigation@5",
"when": "view == claude-dev.SidebarProvider"
},
{
"command": "cline.settingsButtonClicked",
"group": "navigation@6",
"group": "navigation@5",
"when": "view == claude-dev.SidebarProvider"
}
]
+2
View File
@@ -604,6 +604,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
asksageApiUrl,
xaiApiKey,
thinkingBudgetTokens,
clineApiKey,
} = message.apiConfiguration
await this.updateGlobalState("apiProvider", apiProvider)
await this.updateGlobalState("apiModelId", apiModelId)
@@ -650,6 +651,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.storeSecret("asksageApiKey", asksageApiKey)
await this.updateGlobalState("asksageApiUrl", asksageApiUrl)
await this.updateGlobalState("thinkingBudgetTokens", thinkingBudgetTokens)
await this.storeSecret("clineApiKey", clineApiKey)
if (this.cline) {
this.cline.api = buildApiHandler(message.apiConfiguration)
}
+124 -37
View File
@@ -1,26 +1,14 @@
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { VSCodeButton, VSCodeDivider } from "@vscode/webview-ui-toolkit/react"
import { memo } from "react"
import { useFirebaseAuth } from "../../context/FirebaseAuthContext"
import { vscode } from "../../utils/vscode"
import VSCodeButtonLink from "../common/VSCodeButtonLink"
type AccountViewProps = {
onDone: () => void
}
const AccountView = ({ onDone }: AccountViewProps) => {
const { user, handleSignOut } = useFirebaseAuth()
const handleLogin = () => {
vscode.postMessage({ type: "accountLoginClicked" })
}
const handleLogout = () => {
// First notify extension to clear API keys and state
vscode.postMessage({ type: "accountLogoutClicked" })
// Then sign out of Firebase
handleSignOut()
}
return (
<div
style={{
@@ -54,33 +42,132 @@ const AccountView = ({ onDone }: AccountViewProps) => {
flexDirection: "column",
}}>
<div style={{ marginBottom: 5 }}>
{user ? (
<>
{user.photoURL && (
<img
src={user.photoURL}
alt="Profile"
style={{
width: 48,
height: 48,
borderRadius: "50%",
marginBottom: 10,
}}
/>
)}
<div style={{ fontSize: "14px", marginBottom: 10 }}>
{user.displayName && <div>{user.displayName}</div>}
{user.email && <div>{user.email}</div>}
</div>
<VSCodeButton onClick={handleLogout}>Log out</VSCodeButton>
</>
) : (
<VSCodeButton onClick={handleLogin}>Log in to Cline</VSCodeButton>
)}
<ClineAccountView />
</div>
</div>
</div>
)
}
export const ClineAccountView = () => {
const { user, handleSignOut } = useFirebaseAuth()
const handleLogin = () => {
vscode.postMessage({ type: "accountLoginClicked" })
}
const handleLogout = () => {
// First notify extension to clear API keys and state
vscode.postMessage({ type: "accountLogoutClicked" })
// Then sign out of Firebase
handleSignOut()
}
return (
<div style={{ maxWidth: "600px" }}>
{user ? (
<div
style={{
padding: "12px 16px",
border: "1px solid var(--vscode-widget-border)",
borderRadius: "6px",
backgroundColor: "var(--vscode-editor-background)",
}}>
<div
style={{
display: "flex",
alignItems: "center",
gap: "16px",
}}>
{user.photoURL ? (
<img
src={user.photoURL}
alt="Profile"
style={{
width: 48,
height: 48,
borderRadius: "50%",
}}
/>
) : (
<div
style={{
width: 48,
height: 48,
borderRadius: "50%",
backgroundColor: "var(--vscode-button-background)",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "20px",
color: "var(--vscode-button-foreground)",
}}>
{user.displayName?.[0] || user.email?.[0] || "?"}
</div>
)}
<div
style={{
display: "flex",
flexDirection: "column",
gap: "4px",
}}>
{user.displayName && (
<div
style={{
fontSize: "13px",
fontWeight: "bold",
color: "var(--vscode-foreground)",
}}>
{user.displayName}
</div>
)}
{user.email && (
<div
style={{
fontSize: "13px",
color: "var(--vscode-descriptionForeground)",
}}>
{user.email}
</div>
)}
<div style={{ display: "flex", gap: "8px" }}>
<VSCodeButtonLink
href="https://app.cline.bot/account"
appearance="primary"
style={{
transform: "scale(0.85)",
transformOrigin: "left center",
width: "fit-content",
marginTop: 2,
marginBottom: -2,
marginRight: -8,
}}>
Account
</VSCodeButtonLink>
<VSCodeButton
appearance="secondary"
onClick={handleLogout}
style={{
transform: "scale(0.85)",
transformOrigin: "left center",
width: "fit-content",
marginTop: 2,
marginBottom: -2,
}}>
Log out
</VSCodeButton>
</div>
</div>
</div>
</div>
) : (
<div style={{}}>
<VSCodeButton onClick={handleLogin} style={{ marginTop: 6 }}>
Sign Up with Cline
</VSCodeButton>
</div>
)}
</div>
)
}
export default memo(AccountView)
@@ -49,6 +49,7 @@ import { vscode } from "../../utils/vscode"
import { getAsVar, VSC_DESCRIPTION_FOREGROUND } from "../../utils/vscStyles"
import VSCodeButtonLink from "../common/VSCodeButtonLink"
import OpenRouterModelPicker, { ModelDescriptionMarkdown } from "./OpenRouterModelPicker"
import AccountView, { ClineAccountView } from "../account/AccountView"
interface ApiOptionsProps {
showModelOptions: boolean
@@ -91,10 +92,6 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is
const [modelConfigurationSelected, setModelConfigurationSelected] = useState(false)
const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
const handleClineLogin = () => {
vscode.postMessage({ type: "accountLoginClicked" })
}
const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => {
setApiConfiguration({
...apiConfiguration,
@@ -213,36 +210,8 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is
</DropdownContainer>
{selectedProvider === "cline" && (
<div>
{apiConfiguration?.clineApiKey && (
<VSCodeTextField
value={apiConfiguration.clineApiKey}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("clineApiKey")}
placeholder={"enterApiKey"}>
<span style={{ fontWeight: 500 }}>Cline API Key</span>
</VSCodeTextField>
)}
{apiConfiguration?.clineApiKey && (
<p
style={{
fontSize: "12px",
marginTop: 3,
color: "var(--vscode-descriptionForeground)",
}}>
{"apiKeyInfo"}
</p>
)}
{!apiConfiguration?.clineApiKey && (
<div style={{ marginTop: 2 }}>
<VSCodeButton appearance="primary" onClick={handleClineLogin}>
Get Cline API Key
</VSCodeButton>
</div>
)}
<div style={{ marginBottom: 8, marginTop: 4 }}>
<ClineAccountView />
</div>
)}
@@ -45,37 +45,40 @@ const WelcomeView = () => {
overflow: "auto",
}}>
<h2>Hi, I'm Cline</h2>
<div style={{ display: "flex", justifyContent: "center", margin: "20px 0" }}>
<ClineLogo />
</div>
<p>
I can do all kinds of tasks thanks to breakthroughs in Claude 3.7 Sonnet's agentic coding capabilities and
access to tools that let me create & edit files, explore complex projects, use the browser, and execute
terminal commands (with your permission, of course). I can even use MCP to create new tools and extend my own
capabilities.
I can do all kinds of tasks thanks to breakthroughs in{" "}
<VSCodeLink href="https://www.anthropic.com/claude/sonnet" style={{ display: "inline" }}>
Claude 3.7 Sonnet's
</VSCodeLink>
agentic coding capabilities and access to tools that let me create & edit files, explore complex projects, use
a browser, and execute terminal commands <i>(with your permission, of course)</i>. I can even use MCP to
create new tools and extend my own capabilities.
</p>
<div style={{ marginTop: "20px", marginBottom: "20px" }}>
<VSCodeButton appearance="primary" onClick={handleLogin}>
Sign Up with Cline
<p style={{ color: "var(--vscode-descriptionForeground)" }}>
Sign up for an account to get started for free, or use an API key that provides access to models like Claude
3.7 Sonnet.
</p>
<VSCodeButton appearance="primary" onClick={handleLogin} style={{ width: "100%" }}>
Get Started for Free
</VSCodeButton>
{!showApiOptions && (
<VSCodeButton
appearance="secondary"
onClick={() => setShowApiOptions(!showApiOptions)}
style={{ marginTop: 10, width: "100%" }}>
Use your own API key
</VSCodeButton>
)}
<div style={{ marginTop: "10px" }}>
<ul style={{ paddingLeft: "20px", margin: "10px 0" }}>
<li>Choose from hundreds of models like Claude 3.5 Sonnet and DeepSeek V3</li>
<li>Get started for free with no credit card required</li>
</ul>
</div>
</div>
<VSCodeDivider />
<div style={{ marginTop: "20px" }}>
{!showApiOptions && (
<VSCodeButton appearance="secondary" onClick={() => setShowApiOptions(!showApiOptions)}>
Use your own API key
</VSCodeButton>
)}
<div style={{ marginTop: "18px" }}>
{showApiOptions && (
<div style={{ marginTop: "10px" }}>
<div>
<ApiOptions showModelOptions={false} />
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} style={{ marginTop: "3px" }}>
Let's go!
@@ -88,4 +91,22 @@ const WelcomeView = () => {
)
}
const ClineLogo: React.FC<{ style?: React.CSSProperties }> = ({ style }) => {
// (can't use svgs in vsc extensions)
const logoBase64 =
"data:image/png;base64,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"
return (
<img
src={logoBase64}
style={{
width: "57px",
height: "60px",
...style,
}}
alt="Cline Logo"
/>
)
}
export default WelcomeView