Refactor Kodu links

This commit is contained in:
Saoud Rizwan
2024-08-25 16:08:57 -04:00
parent 9c26980c06
commit 7fa7589ed0
12 changed files with 115 additions and 71 deletions

View File

@@ -1,23 +1,11 @@
import { Anthropic } from "@anthropic-ai/sdk" import { Anthropic } from "@anthropic-ai/sdk"
import axios from "axios"
import { ApiHandler, withoutImageData } from "." import { ApiHandler, withoutImageData } from "."
import { ApiHandlerOptions, koduDefaultModelId, KoduModelId, koduModels, ModelInfo } from "../shared/api" import { ApiHandlerOptions, koduDefaultModelId, KoduModelId, koduModels, ModelInfo } from "../shared/api"
import axios from "axios" import { getKoduCreditsUrl, getKoduInferenceUrl } from "../shared/kodu"
import * as vscode from "vscode"
const KODU_BASE_URL = "https://claude-dev.com"
export function didClickKoduSignIn() {
const loginUrl = `${KODU_BASE_URL}/auth/login?redirectTo=${vscode.env.uriScheme}://saoudrizwan.claude-dev&ext=1`
vscode.env.openExternal(vscode.Uri.parse(loginUrl))
}
export function didClickKoduAddCredits() {
const addCreditsUrl = `${KODU_BASE_URL}/user/addCredits?redirectTo=${vscode.env.uriScheme}://saoudrizwan.claude-dev&ext=1`
vscode.env.openExternal(vscode.Uri.parse(addCreditsUrl))
}
export async function fetchKoduCredits({ apiKey }: { apiKey: string }) { export async function fetchKoduCredits({ apiKey }: { apiKey: string }) {
const response = await axios.get(`${KODU_BASE_URL}/api/credits`, { const response = await axios.get(getKoduCreditsUrl(), {
headers: { headers: {
"x-api-key": apiKey, "x-api-key": apiKey,
}, },
@@ -89,7 +77,7 @@ export class KoduHandler implements ApiHandler {
tool_choice: { type: "auto" }, tool_choice: { type: "auto" },
} }
} }
const response = await axios.post(`${KODU_BASE_URL}/api/inference`, requestBody, { const response = await axios.post(getKoduInferenceUrl(), requestBody, {
headers: { headers: {
"x-api-key": this.options.koduApiKey, "x-api-key": this.options.koduApiKey,
}, },

View File

@@ -8,7 +8,7 @@ import { downloadTask, getNonce, getUri, selectImages } from "../utils"
import * as path from "path" import * as path from "path"
import fs from "fs/promises" import fs from "fs/promises"
import { HistoryItem } from "../shared/HistoryItem" import { HistoryItem } from "../shared/HistoryItem"
import { didClickKoduAddCredits, didClickKoduSignIn, fetchKoduCredits } from "../api/kodu" import { fetchKoduCredits } from "../api/kodu"
/* /*
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
@@ -358,15 +358,9 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
case "exportTaskWithId": case "exportTaskWithId":
this.exportTaskWithId(message.text!) this.exportTaskWithId(message.text!)
break break
case "didClickKoduSignIn":
didClickKoduSignIn()
break
case "didClickKoduSignOut": case "didClickKoduSignOut":
await this.signOutKodu() await this.signOutKodu()
break break
case "didClickKoduAddCredits":
didClickKoduAddCredits()
break
case "fetchKoduCredits": case "fetchKoduCredits":
const koduApiKey = await this.getSecret("koduApiKey") const koduApiKey = await this.getSecret("koduApiKey")
if (koduApiKey) { if (koduApiKey) {
@@ -515,6 +509,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
customInstructions, customInstructions,
alwaysAllowReadOnly, alwaysAllowReadOnly,
themeName: vscode.workspace.getConfiguration("workbench").get<string>("colorTheme"), themeName: vscode.workspace.getConfiguration("workbench").get<string>("colorTheme"),
uriScheme: vscode.env.uriScheme,
claudeMessages: this.claudeDev?.claudeMessages || [], claudeMessages: this.claudeDev?.claudeMessages || [],
taskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts), taskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts),
shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId, shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,

View File

@@ -25,6 +25,7 @@ export interface ExtensionState {
customInstructions?: string customInstructions?: string
alwaysAllowReadOnly?: boolean alwaysAllowReadOnly?: boolean
themeName?: string themeName?: string
uriScheme?: string
claudeMessages: ClaudeMessage[] claudeMessages: ClaudeMessage[]
taskHistory: HistoryItem[] taskHistory: HistoryItem[]
shouldShowAnnouncement: boolean shouldShowAnnouncement: boolean

View File

@@ -16,9 +16,7 @@ export interface WebviewMessage {
| "showTaskWithId" | "showTaskWithId"
| "deleteTaskWithId" | "deleteTaskWithId"
| "exportTaskWithId" | "exportTaskWithId"
| "didClickKoduSignIn"
| "didClickKoduSignOut" | "didClickKoduSignOut"
| "didClickKoduAddCredits"
| "fetchKoduCredits" | "fetchKoduCredits"
text?: string text?: string
askResponse?: ClaudeAskResponse askResponse?: ClaudeAskResponse

17
src/shared/kodu.ts Normal file
View File

@@ -0,0 +1,17 @@
const KODU_BASE_URL = "https://claude-dev.com"
export function getKoduSignInUrl(uriScheme?: string) {
return `${KODU_BASE_URL}/auth/login?redirectTo=${uriScheme}://saoudrizwan.claude-dev&ext=1`
}
export function getKoduAddCreditsUrl(uriScheme?: string) {
return `${KODU_BASE_URL}/user/addCredits?redirectTo=${uriScheme}://saoudrizwan.claude-dev&ext=1`
}
export function getKoduCreditsUrl() {
return `${KODU_BASE_URL}/api/credits`
}
export function getKoduInferenceUrl() {
return `${KODU_BASE_URL}/api/inference`
}

View File

@@ -28,6 +28,7 @@ const App: React.FC = () => {
const [customInstructions, setCustomInstructions] = useState<string>("") const [customInstructions, setCustomInstructions] = useState<string>("")
const [alwaysAllowReadOnly, setAlwaysAllowReadOnly] = useState<boolean>(false) const [alwaysAllowReadOnly, setAlwaysAllowReadOnly] = useState<boolean>(false)
const [vscodeThemeName, setVscodeThemeName] = useState<string | undefined>(undefined) const [vscodeThemeName, setVscodeThemeName] = useState<string | undefined>(undefined)
const [vscodeUriScheme, setVscodeUriScheme] = useState<string | undefined>(undefined)
const [claudeMessages, setClaudeMessages] = useState<ClaudeMessage[]>([]) const [claudeMessages, setClaudeMessages] = useState<ClaudeMessage[]>([])
const [taskHistory, setTaskHistory] = useState<HistoryItem[]>([]) const [taskHistory, setTaskHistory] = useState<HistoryItem[]>([])
const [showAnnouncement, setShowAnnouncement] = useState(false) const [showAnnouncement, setShowAnnouncement] = useState(false)
@@ -62,6 +63,7 @@ const App: React.FC = () => {
setCustomInstructions(message.state!.customInstructions || "") setCustomInstructions(message.state!.customInstructions || "")
setAlwaysAllowReadOnly(message.state!.alwaysAllowReadOnly || false) setAlwaysAllowReadOnly(message.state!.alwaysAllowReadOnly || false)
setVscodeThemeName(message.state!.themeName) setVscodeThemeName(message.state!.themeName)
setVscodeUriScheme(message.state!.uriScheme)
setClaudeMessages(message.state!.claudeMessages) setClaudeMessages(message.state!.claudeMessages)
setTaskHistory(message.state!.taskHistory) setTaskHistory(message.state!.taskHistory)
setKoduCredits(message.state!.koduCredits) setKoduCredits(message.state!.koduCredits)
@@ -121,7 +123,11 @@ const App: React.FC = () => {
return ( return (
<> <>
{showWelcome ? ( {showWelcome ? (
<WelcomeView apiConfiguration={apiConfiguration} setApiConfiguration={setApiConfiguration} /> <WelcomeView
apiConfiguration={apiConfiguration}
setApiConfiguration={setApiConfiguration}
vscodeUriScheme={vscodeUriScheme}
/>
) : ( ) : (
<> <>
{showSettings && ( {showSettings && (
@@ -137,6 +143,7 @@ const App: React.FC = () => {
alwaysAllowReadOnly={alwaysAllowReadOnly} alwaysAllowReadOnly={alwaysAllowReadOnly}
setAlwaysAllowReadOnly={setAlwaysAllowReadOnly} setAlwaysAllowReadOnly={setAlwaysAllowReadOnly}
onDone={() => setShowSettings(false)} onDone={() => setShowSettings(false)}
vscodeUriScheme={vscodeUriScheme}
/> />
)} )}
{showHistory && <HistoryView taskHistory={taskHistory} onDone={() => setShowHistory(false)} />} {showHistory && <HistoryView taskHistory={taskHistory} onDone={() => setShowHistory(false)} />}
@@ -159,6 +166,7 @@ const App: React.FC = () => {
setShowAnnouncement(false) setShowAnnouncement(false)
}} }}
apiConfiguration={apiConfiguration} apiConfiguration={apiConfiguration}
vscodeUriScheme={vscodeUriScheme}
/> />
</> </>
)} )}

View File

@@ -1,16 +1,18 @@
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { vscode } from "../utils/vscode"
import { ApiConfiguration } from "../../../src/shared/api" import { ApiConfiguration } from "../../../src/shared/api"
import { getKoduSignInUrl } from "../../../src/shared/kodu"
import VSCodeButtonLink from "./VSCodeButtonLink"
interface AnnouncementProps { interface AnnouncementProps {
version: string version: string
hideAnnouncement: () => void hideAnnouncement: () => void
apiConfiguration?: ApiConfiguration apiConfiguration?: ApiConfiguration
vscodeUriScheme?: string
} }
/* /*
You must update the latestAnnouncementId in ClaudeDevProvider for new announcements to show to users. This new id will be compared with whats in state for the 'last announcement shown', and if it's different then the announcement will render. As soon as an announcement is shown, the id will be updated in state. This ensures that announcements are not shown more than once, even if the user doesn't close it themselves. You must update the latestAnnouncementId in ClaudeDevProvider for new announcements to show to users. This new id will be compared with whats in state for the 'last announcement shown', and if it's different then the announcement will render. As soon as an announcement is shown, the id will be updated in state. This ensures that announcements are not shown more than once, even if the user doesn't close it themselves.
*/ */
const Announcement = ({ version, hideAnnouncement, apiConfiguration }: AnnouncementProps) => { const Announcement = ({ version, hideAnnouncement, apiConfiguration, vscodeUriScheme }: AnnouncementProps) => {
return ( return (
<div <div
style={{ style={{
@@ -33,23 +35,23 @@ const Announcement = ({ version, hideAnnouncement, apiConfiguration }: Announcem
<ul style={{ margin: "0 0 8px", paddingLeft: "20px" }}> <ul style={{ margin: "0 0 8px", paddingLeft: "20px" }}>
<li> <li>
Excited to announce that{" "} Excited to announce that{" "}
<VSCodeLink href="https://claude-dev.com" style={{ display: "inline" }}> <VSCodeLink href={getKoduSignInUrl(vscodeUriScheme)} style={{ display: "inline" }}>
Kodu Kodu
</VSCodeLink>{" "} </VSCodeLink>{" "}
is offering $10 free credits to help new users get the most out of Claude Dev with high rate limits is offering $10 free credits to help new users get the most out of Claude Dev with high rate limits
and prompt caching! Stay tuned for some exciting updates like easier billing and deploying live and prompt caching! Stay tuned for some exciting updates like easier billing and deploying live
websites. websites.
{apiConfiguration?.koduApiKey === undefined && ( {apiConfiguration?.koduApiKey === undefined && (
<VSCodeButton <VSCodeButtonLink
appearance="secondary" appearance="secondary"
onClick={() => vscode.postMessage({ type: "didClickKoduSignIn" })} href={getKoduSignInUrl(vscodeUriScheme)}
style={{ style={{
transform: "scale(0.8)", transform: "scale(0.8)",
transformOrigin: "left center", transformOrigin: "left center",
margin: "4px 0 2px 0", margin: "4px 0 2px 0",
}}> }}>
Claim $10 Free Credits Claim $10 Free Credits
</VSCodeButton> </VSCodeButtonLink>
)} )}
</li> </li>
<li> <li>

View File

@@ -1,11 +1,6 @@
import { import { VSCodeDropdown, VSCodeLink, VSCodeOption, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
VSCodeButton,
VSCodeDropdown,
VSCodeLink,
VSCodeOption,
VSCodeTextField,
} from "@vscode/webview-ui-toolkit/react"
import React, { useCallback, useEffect, useMemo, useState } from "react" import React, { useCallback, useEffect, useMemo, useState } from "react"
import { useEvent } from "react-use"
import { import {
ApiConfiguration, ApiConfiguration,
ApiModelId, ApiModelId,
@@ -19,9 +14,10 @@ import {
openRouterDefaultModelId, openRouterDefaultModelId,
openRouterModels, openRouterModels,
} from "../../../src/shared/api" } from "../../../src/shared/api"
import { vscode } from "../utils/vscode"
import { useEvent } from "react-use"
import { ExtensionMessage } from "../../../src/shared/ExtensionMessage" import { ExtensionMessage } from "../../../src/shared/ExtensionMessage"
import { getKoduAddCreditsUrl, getKoduSignInUrl } from "../../../src/shared/kodu"
import { vscode } from "../utils/vscode"
import VSCodeButtonLink from "./VSCodeButtonLink"
interface ApiOptionsProps { interface ApiOptionsProps {
showModelOptions: boolean showModelOptions: boolean
@@ -29,6 +25,7 @@ interface ApiOptionsProps {
setApiConfiguration: React.Dispatch<React.SetStateAction<ApiConfiguration | undefined>> setApiConfiguration: React.Dispatch<React.SetStateAction<ApiConfiguration | undefined>>
koduCredits?: number koduCredits?: number
apiErrorMessage?: string apiErrorMessage?: string
vscodeUriScheme?: string
} }
const ApiOptions: React.FC<ApiOptionsProps> = ({ const ApiOptions: React.FC<ApiOptionsProps> = ({
@@ -37,6 +34,7 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({
setApiConfiguration, setApiConfiguration,
koduCredits, koduCredits,
apiErrorMessage, apiErrorMessage,
vscodeUriScheme,
}) => { }) => {
const [didFetchKoduCredits, setDidFetchKoduCredits] = useState(false) const [didFetchKoduCredits, setDidFetchKoduCredits] = useState(false)
const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => { const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => {
@@ -184,22 +182,19 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({
{formatPrice(koduCredits || 0)} {formatPrice(koduCredits || 0)}
</span> </span>
</div> </div>
<VSCodeButton <VSCodeButtonLink
appearance="primary" href={getKoduAddCreditsUrl(vscodeUriScheme)}
onClick={() => vscode.postMessage({ type: "didClickKoduAddCredits" })}
style={{ style={{
width: "fit-content", width: "fit-content",
}}> }}>
Add Credits Add Credits
</VSCodeButton> </VSCodeButtonLink>
</> </>
) : ( ) : (
<div style={{ margin: "4px 0px" }}> <div style={{ margin: "4px 0px" }}>
<VSCodeButton <VSCodeButtonLink href={getKoduSignInUrl(vscodeUriScheme)}>
appearance="primary"
onClick={() => vscode.postMessage({ type: "didClickKoduSignIn" })}>
Sign in to Kodu Sign in to Kodu
</VSCodeButton> </VSCodeButtonLink>
</div> </div>
)} )}
<p <p
@@ -210,7 +205,7 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({
}}> }}>
Kodu is recommended for its high rate limits and access to the latest features like prompt Kodu is recommended for its high rate limits and access to the latest features like prompt
caching. caching.
<VSCodeLink href="https://claude-dev.com" style={{ display: "inline", fontSize: "12px" }}> <VSCodeLink href="https://kodu.ai" style={{ display: "inline", fontSize: "12px" }}>
Learn more about Kodu here. Learn more about Kodu here.
</VSCodeLink> </VSCodeLink>
</p> </p>

View File

@@ -30,6 +30,7 @@ interface ChatViewProps {
hideAnnouncement: () => void hideAnnouncement: () => void
showHistoryView: () => void showHistoryView: () => void
apiConfiguration?: ApiConfiguration apiConfiguration?: ApiConfiguration
vscodeUriScheme?: string
} }
const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images
@@ -46,6 +47,7 @@ const ChatView = ({
hideAnnouncement, hideAnnouncement,
showHistoryView, showHistoryView,
apiConfiguration, apiConfiguration,
vscodeUriScheme,
}: ChatViewProps) => { }: ChatViewProps) => {
//const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined //const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined
const task = messages.length > 0 ? messages[0] : undefined // leaving this less safe version here since if the first message is not a task, then the extension is in a bad state and needs to be debugged (see ClaudeDev.abort) const task = messages.length > 0 ? messages[0] : undefined // leaving this less safe version here since if the first message is not a task, then the extension is in a bad state and needs to be debugged (see ClaudeDev.abort)
@@ -490,6 +492,7 @@ const ChatView = ({
version={version} version={version}
hideAnnouncement={hideAnnouncement} hideAnnouncement={hideAnnouncement}
apiConfiguration={apiConfiguration} apiConfiguration={apiConfiguration}
vscodeUriScheme={vscodeUriScheme}
/> />
)} )}
<div style={{ padding: "0 20px", flexGrow: taskHistory.length > 0 ? undefined : 1 }}> <div style={{ padding: "0 20px", flexGrow: taskHistory.length > 0 ? undefined : 1 }}>

View File

@@ -10,6 +10,7 @@ import { ApiConfiguration } from "../../../src/shared/api"
import { validateApiConfiguration, validateMaxRequestsPerTask } from "../utils/validate" import { validateApiConfiguration, validateMaxRequestsPerTask } from "../utils/validate"
import { vscode } from "../utils/vscode" import { vscode } from "../utils/vscode"
import ApiOptions from "./ApiOptions" import ApiOptions from "./ApiOptions"
import { getKoduSignInUrl } from "../../../src/shared/kodu"
type SettingsViewProps = { type SettingsViewProps = {
version: string version: string
@@ -23,6 +24,7 @@ type SettingsViewProps = {
onDone: () => void onDone: () => void
alwaysAllowReadOnly: boolean alwaysAllowReadOnly: boolean
setAlwaysAllowReadOnly: React.Dispatch<React.SetStateAction<boolean>> setAlwaysAllowReadOnly: React.Dispatch<React.SetStateAction<boolean>>
vscodeUriScheme?: string
} }
const SettingsView = ({ const SettingsView = ({
@@ -37,6 +39,7 @@ const SettingsView = ({
onDone, onDone,
alwaysAllowReadOnly, alwaysAllowReadOnly,
setAlwaysAllowReadOnly, setAlwaysAllowReadOnly,
vscodeUriScheme,
}: SettingsViewProps) => { }: SettingsViewProps) => {
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined) const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
const [maxRequestsErrorMessage, setMaxRequestsErrorMessage] = useState<string | undefined>(undefined) const [maxRequestsErrorMessage, setMaxRequestsErrorMessage] = useState<string | undefined>(undefined)
@@ -104,27 +107,34 @@ const SettingsView = ({
<div <div
style={{ flexGrow: 1, overflowY: "scroll", paddingRight: 8, display: "flex", flexDirection: "column" }}> style={{ flexGrow: 1, overflowY: "scroll", paddingRight: 8, display: "flex", flexDirection: "column" }}>
{apiConfiguration?.koduApiKey === undefined && ( {apiConfiguration?.koduApiKey === undefined && (
<div <a
href={getKoduSignInUrl(vscodeUriScheme)}
style={{ style={{
display: "flex", textDecoration: "none",
alignItems: "center", color: "inherit",
backgroundColor: "var(--vscode-editor-inactiveSelectionBackground)", outline: "none",
color: "var(--vscode-textLink-foreground)", }}>
padding: "6px 8px", <div
borderRadius: "3px",
margin: "0 0 8px 0px",
fontSize: "12px",
cursor: "pointer",
}}
onClick={() => vscode.postMessage({ type: "didClickKoduSignIn" })}>
<i
className="codicon codicon-info"
style={{ style={{
marginRight: 6, display: "flex",
fontSize: 16, alignItems: "center",
}}></i> backgroundColor: "var(--vscode-editor-inactiveSelectionBackground)",
<span>Claim $10 free credits from Kodu</span> color: "var(--vscode-textLink-foreground)",
</div> padding: "6px 8px",
borderRadius: "3px",
margin: "0 0 8px 0px",
fontSize: "12px",
cursor: "pointer",
}}>
<i
className="codicon codicon-info"
style={{
marginRight: 6,
fontSize: 16,
}}></i>
<span>Claim $10 free credits from Kodu</span>
</div>
</a>
)} )}
<div style={{ marginBottom: 5 }}> <div style={{ marginBottom: 5 }}>
<ApiOptions <ApiOptions
@@ -133,6 +143,7 @@ const SettingsView = ({
showModelOptions={true} showModelOptions={true}
koduCredits={koduCredits} koduCredits={koduCredits}
apiErrorMessage={apiErrorMessage} apiErrorMessage={apiErrorMessage}
vscodeUriScheme={vscodeUriScheme}
/> />
</div> </div>

View File

@@ -0,0 +1,23 @@
import React from "react"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
interface VSCodeButtonLinkProps {
href: string
children: React.ReactNode
[key: string]: any
}
const VSCodeButtonLink: React.FC<VSCodeButtonLinkProps> = ({ href, children, ...props }) => {
return (
<a
href={href}
style={{
textDecoration: "none",
color: "inherit",
}}>
<VSCodeButton {...props}>{children}</VSCodeButton>
</a>
)
}
export default VSCodeButtonLink

View File

@@ -4,13 +4,15 @@ import { ApiConfiguration } from "../../../src/shared/api"
import { validateApiConfiguration } from "../utils/validate" import { validateApiConfiguration } from "../utils/validate"
import { vscode } from "../utils/vscode" import { vscode } from "../utils/vscode"
import ApiOptions from "./ApiOptions" import ApiOptions from "./ApiOptions"
import { getKoduSignInUrl } from "../../../src/shared/kodu"
interface WelcomeViewProps { interface WelcomeViewProps {
apiConfiguration?: ApiConfiguration apiConfiguration?: ApiConfiguration
setApiConfiguration: React.Dispatch<React.SetStateAction<ApiConfiguration | undefined>> setApiConfiguration: React.Dispatch<React.SetStateAction<ApiConfiguration | undefined>>
vscodeUriScheme?: string
} }
const WelcomeView: React.FC<WelcomeViewProps> = ({ apiConfiguration, setApiConfiguration }) => { const WelcomeView: React.FC<WelcomeViewProps> = ({ apiConfiguration, setApiConfiguration, vscodeUriScheme }) => {
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined) const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
const disableLetsGoButton = apiErrorMessage != null const disableLetsGoButton = apiErrorMessage != null
@@ -59,7 +61,7 @@ const WelcomeView: React.FC<WelcomeViewProps> = ({ apiConfiguration, setApiConfi
}}></i> }}></i>
<span> <span>
Explore Claude's capabilities with $10 free credits from{" "} Explore Claude's capabilities with $10 free credits from{" "}
<VSCodeLink href="https://claude-dev.com" style={{ display: "inline" }}> <VSCodeLink href={getKoduSignInUrl(vscodeUriScheme)} style={{ display: "inline" }}>
Kodu Kodu
</VSCodeLink> </VSCodeLink>
</span> </span>
@@ -70,6 +72,7 @@ const WelcomeView: React.FC<WelcomeViewProps> = ({ apiConfiguration, setApiConfi
apiConfiguration={apiConfiguration} apiConfiguration={apiConfiguration}
setApiConfiguration={setApiConfiguration} setApiConfiguration={setApiConfiguration}
showModelOptions={false} showModelOptions={false}
vscodeUriScheme={vscodeUriScheme}
/> />
{apiConfiguration?.apiProvider !== "kodu" && ( {apiConfiguration?.apiProvider !== "kodu" && (
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} style={{ marginTop: "3px" }}> <VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} style={{ marginTop: "3px" }}>