From 79250e9b57ba4bc87af8da67be3c6184824c360c Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Sun, 25 Aug 2024 17:38:48 -0400 Subject: [PATCH] Improve Kodu promo behavior --- src/providers/ClaudeDevProvider.ts | 11 ++++ src/shared/ExtensionMessage.ts | 1 + src/shared/WebviewMessage.ts | 1 + webview-ui/src/App.tsx | 8 ++- webview-ui/src/components/Announcement.tsx | 18 ++---- webview-ui/src/components/ChatView.tsx | 6 ++ webview-ui/src/components/KoduPromo.tsx | 69 ++++++++++++++++++++++ webview-ui/src/components/SettingsView.tsx | 33 +---------- webview-ui/src/components/WelcomeView.tsx | 2 +- 9 files changed, 101 insertions(+), 48 deletions(-) create mode 100644 webview-ui/src/components/KoduPromo.tsx diff --git a/src/providers/ClaudeDevProvider.ts b/src/providers/ClaudeDevProvider.ts index 796cac0..1a9a414 100644 --- a/src/providers/ClaudeDevProvider.ts +++ b/src/providers/ClaudeDevProvider.ts @@ -28,6 +28,7 @@ type GlobalStateKey = | "customInstructions" | "alwaysAllowReadOnly" | "taskHistory" + | "shouldShowKoduPromo" export class ClaudeDevProvider implements vscode.WebviewViewProvider { public static readonly sideBarId = "claude-dev.SidebarProvider" // used in package.json as the view's id. This value cannot be changed due to how vscode caches views based on their id, and updating the id would break existing instances of the extension. @@ -373,6 +374,10 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { }) } break + case "didDismissKoduPromo": + await this.updateGlobalState("shouldShowKoduPromo", false) + await this.postStateToWebview() + break // Add more switch case statements here as more webview message commands // are created within the webview context (i.e. inside media/main.js) } @@ -388,6 +393,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { await this.storeSecret("koduApiKey", apiKey) await this.updateGlobalState("koduEmail", email) await this.updateGlobalState("apiProvider", "kodu") + await this.updateGlobalState("shouldShowKoduPromo", false) await this.postStateToWebview() await this.postMessageToWebview({ type: "action", action: "koduAuthenticated" }) this.claudeDev?.updateApi({ apiProvider: "kodu", koduApiKey: apiKey }) @@ -501,6 +507,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { alwaysAllowReadOnly, taskHistory, koduCredits, + shouldShowKoduPromo, } = await this.getState() return { version: this.context.extension?.packageJSON?.version ?? "", @@ -514,6 +521,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { taskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts), shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId, koduCredits, + shouldShowKoduPromo, } } @@ -620,6 +628,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { customInstructions, alwaysAllowReadOnly, taskHistory, + shouldShowKoduPromo, ] = await Promise.all([ this.getGlobalState("apiProvider") as Promise, this.getGlobalState("apiModelId") as Promise, @@ -636,6 +645,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { this.getGlobalState("customInstructions") as Promise, this.getGlobalState("alwaysAllowReadOnly") as Promise, this.getGlobalState("taskHistory") as Promise, + this.getGlobalState("shouldShowKoduPromo") as Promise, ]) let apiProvider: ApiProvider @@ -670,6 +680,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { alwaysAllowReadOnly, taskHistory, koduCredits, + shouldShowKoduPromo: shouldShowKoduPromo ?? true, } } diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 65ef170..50decea 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -30,6 +30,7 @@ export interface ExtensionState { taskHistory: HistoryItem[] shouldShowAnnouncement: boolean koduCredits?: number + shouldShowKoduPromo: boolean } export interface ClaudeMessage { diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 6286c5d..9f36099 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -18,6 +18,7 @@ export interface WebviewMessage { | "exportTaskWithId" | "didClickKoduSignOut" | "fetchKoduCredits" + | "didDismissKoduPromo" text?: string askResponse?: ClaudeAskResponse apiConfiguration?: ApiConfiguration diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index 21d38c2..db14491 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -2,14 +2,14 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" import { useEvent } from "react-use" import { ApiConfiguration } from "../../src/shared/api" import { ClaudeMessage, ExtensionMessage } from "../../src/shared/ExtensionMessage" +import { HistoryItem } from "../../src/shared/HistoryItem" import "./App.css" import { normalizeApiConfiguration } from "./components/ApiOptions" import ChatView from "./components/ChatView" +import HistoryView from "./components/HistoryView" import SettingsView from "./components/SettingsView" import WelcomeView from "./components/WelcomeView" import { vscode } from "./utils/vscode" -import HistoryView from "./components/HistoryView" -import { HistoryItem } from "../../src/shared/HistoryItem" /* The contents of webviews however are created when the webview becomes visible and destroyed when the webview is moved into the background. Any state inside the webview will be lost when the webview is moved to a background tab. @@ -34,7 +34,7 @@ const App: React.FC = () => { const [showAnnouncement, setShowAnnouncement] = useState(false) const [koduCredits, setKoduCredits] = useState(undefined) const [isNewUser, setIsNewUser] = useState(false) - + const [shouldShowKoduPromo, setShouldShowKoduPromo] = useState(true) useEffect(() => { vscode.postMessage({ type: "webviewDidLaunch" }) }, []) @@ -71,6 +71,7 @@ const App: React.FC = () => { if (message.state!.shouldShowAnnouncement) { setShowAnnouncement(true) } + setShouldShowKoduPromo(message.state!.shouldShowKoduPromo) setDidHydrateState(true) break case "action": @@ -167,6 +168,7 @@ const App: React.FC = () => { }} apiConfiguration={apiConfiguration} vscodeUriScheme={vscodeUriScheme} + shouldShowKoduPromo={shouldShowKoduPromo} /> )} diff --git a/webview-ui/src/components/Announcement.tsx b/webview-ui/src/components/Announcement.tsx index 8da32b0..fcf3e5d 100644 --- a/webview-ui/src/components/Announcement.tsx +++ b/webview-ui/src/components/Announcement.tsx @@ -1,6 +1,6 @@ import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" import { ApiConfiguration } from "../../../src/shared/api" -import { getKoduHomepageUrl, getKoduSignInUrl } from "../../../src/shared/kodu" +import { getKoduSignInUrl } from "../../../src/shared/kodu" import VSCodeButtonLink from "./VSCodeButtonLink" interface AnnouncementProps { @@ -34,25 +34,19 @@ const Announcement = ({ version, hideAnnouncement, apiConfiguration, vscodeUriSc
  • - Excited to announce that{" "} - - Kodu - {" "} - 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 - websites. + Excited to announce that we've partnered with Anthropic and are offering $20 free credits to help + users get the most out of Claude Dev with high rate limits and prompt caching! Stay tuned for some + exciting updates like easier billing, voice mode and one click deployment. {apiConfiguration?.koduApiKey === undefined && ( - Claim $10 Free Credits + Claim $20 Free Credits with Kodu )}
  • diff --git a/webview-ui/src/components/ChatView.tsx b/webview-ui/src/components/ChatView.tsx index c0162d3..1d38958 100644 --- a/webview-ui/src/components/ChatView.tsx +++ b/webview-ui/src/components/ChatView.tsx @@ -17,6 +17,7 @@ import TaskHeader from "./TaskHeader" import Thumbnails from "./Thumbnails" import { HistoryItem } from "../../../src/shared/HistoryItem" import { ApiConfiguration } from "../../../src/shared/api" +import KoduPromo from "./KoduPromo" interface ChatViewProps { version: string @@ -31,6 +32,7 @@ interface ChatViewProps { showHistoryView: () => void apiConfiguration?: ApiConfiguration vscodeUriScheme?: string + shouldShowKoduPromo: boolean } const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images @@ -48,6 +50,7 @@ const ChatView = ({ showHistoryView, apiConfiguration, vscodeUriScheme, + shouldShowKoduPromo, }: ChatViewProps) => { //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) @@ -495,6 +498,9 @@ const ChatView = ({ vscodeUriScheme={vscodeUriScheme} /> )} + {apiConfiguration?.koduApiKey === undefined && !showAnnouncement && shouldShowKoduPromo && ( + + )}
    0 ? undefined : 1 }}>

    What can I do for you?

    diff --git a/webview-ui/src/components/KoduPromo.tsx b/webview-ui/src/components/KoduPromo.tsx new file mode 100644 index 0000000..8b38dde --- /dev/null +++ b/webview-ui/src/components/KoduPromo.tsx @@ -0,0 +1,69 @@ +import React from "react" +import { getKoduSignInUrl } from "../../../src/shared/kodu" +import { vscode } from "../utils/vscode" + +interface KoduPromoProps { + vscodeUriScheme?: string + style?: React.CSSProperties +} + +const KoduPromo: React.FC = ({ vscodeUriScheme, style }) => { + function onClose() { + vscode.postMessage({ type: "didDismissKoduPromo" }) + } + + return ( +

    +
    + + + Claim $20 free credits from Kodu + + +
    +
    + ) +} + +export default KoduPromo diff --git a/webview-ui/src/components/SettingsView.tsx b/webview-ui/src/components/SettingsView.tsx index 9e8d7e5..3697572 100644 --- a/webview-ui/src/components/SettingsView.tsx +++ b/webview-ui/src/components/SettingsView.tsx @@ -1,16 +1,15 @@ import { VSCodeButton, + VSCodeCheckbox, VSCodeLink, VSCodeTextArea, VSCodeTextField, - VSCodeCheckbox, } from "@vscode/webview-ui-toolkit/react" import React, { useEffect, useState } from "react" import { ApiConfiguration } from "../../../src/shared/api" import { validateApiConfiguration, validateMaxRequestsPerTask } from "../utils/validate" import { vscode } from "../utils/vscode" import ApiOptions from "./ApiOptions" -import { getKoduSignInUrl } from "../../../src/shared/kodu" type SettingsViewProps = { version: string @@ -106,36 +105,6 @@ const SettingsView = ({
    - {apiConfiguration?.koduApiKey === undefined && ( - -
    - - Claim $10 free credits from Kodu -
    -
    - )}
    = ({ apiConfiguration, setApiConfi color: "var(--vscode-infoIcon-foreground)", }}> - Explore Claude's capabilities with $10 free credits from{" "} + Explore Claude's capabilities with $20 free credits from{" "} Kodu