From b7f44fbe98b31e8428aeb4efeb46d421943a5fca Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Tue, 17 Feb 2026 00:51:15 -0300 Subject: [PATCH 1/4] feat(webview): redesign profile tab --- .../webview-ui/src/components/ProfileView.tsx | 452 ++++++++++++++---- .../kilo-vscode/webview-ui/src/i18n/en.ts | 17 + 2 files changed, 374 insertions(+), 95 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/ProfileView.tsx b/packages/kilo-vscode/webview-ui/src/components/ProfileView.tsx index 18a1d1325e..7393e3e6e7 100644 --- a/packages/kilo-vscode/webview-ui/src/components/ProfileView.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/ProfileView.tsx @@ -1,4 +1,4 @@ -import { Component, Show, createSignal, createMemo, createEffect, onMount } from "solid-js" +import { Component, Show, For, createSignal, createMemo, createEffect, onMount } from "solid-js" import { Button } from "@kilocode/kilo-ui/button" import { Card } from "@kilocode/kilo-ui/card" import { Select } from "@kilocode/kilo-ui/select" @@ -14,6 +14,7 @@ export interface ProfileViewProps { profileData: ProfileData | null | undefined deviceAuth: DeviceAuthState onLogin: () => void + onDone?: () => void } const formatBalance = (amount: number): string => { @@ -21,6 +22,13 @@ const formatBalance = (amount: number): string => { } const PERSONAL = "personal" +const APP_BASE_URL = "https://app.kilo.ai" +const CREDIT_PACKAGES = [ + { credits: 20, popular: false }, + { credits: 50, popular: true }, + { credits: 100, popular: false }, + { credits: 200, popular: false }, +] interface OrgOption { value: string @@ -28,6 +36,30 @@ interface OrgOption { description?: string } +const getInitial = (value: string): string => { + const trimmed = value.trim() + if (!trimmed) { + return "?" + } + return trimmed.charAt(0).toUpperCase() +} + +const KiloBrandLogo: Component = () => ( + +) + const ProfileView: Component = (props) => { const vscode = useVSCode() const language = useLanguage() @@ -40,29 +72,29 @@ const ProfileView: Component = (props) => { // Reset pending target whenever profileData changes (success or failure both send a fresh profile) createEffect(() => { - props.profileData // track + props.profileData setTarget(null) }) const switching = createMemo(() => { - const t = target() - if (t === null) return false + const nextTarget = target() + if (nextTarget === null) return false const current = props.profileData?.currentOrgId ?? PERSONAL - return current !== t + return current !== nextTarget }) const orgOptions = createMemo(() => { const orgs = props.profileData?.profile.organizations ?? [] if (orgs.length === 0) return [] return [ - { value: PERSONAL, label: "Personal Account" }, + { value: PERSONAL, label: language.t("profile.account.personal") }, ...orgs.map((org) => ({ value: org.id, label: org.name, description: org.role })), ] }) const currentOrg = createMemo(() => { const id = props.profileData?.currentOrgId ?? PERSONAL - return orgOptions().find((o) => o.value === id) + return orgOptions().find((option) => option.value === id) }) const selectOrg = (option: OrgOption | undefined) => { @@ -89,7 +121,29 @@ const ProfileView: Component = (props) => { } const handleDashboard = () => { - vscode.postMessage({ type: "openExternal", url: "https://app.kilo.ai/profile" }) + vscode.postMessage({ type: "openExternal", url: `${APP_BASE_URL}/profile` }) + } + + const handleUsageDetails = () => { + const orgId = props.profileData?.currentOrgId + if (!orgId) { + return + } + vscode.postMessage({ + type: "openExternal", + url: `${APP_BASE_URL}/organizations/${orgId}/usage-details`, + }) + } + + const handleCreateOrganization = () => { + vscode.postMessage({ type: "openExternal", url: `${APP_BASE_URL}/organizations/new` }) + } + + const handleBuyCredits = (credits: number) => { + vscode.postMessage({ + type: "openExternal", + url: `${APP_BASE_URL}/profile?buyCredits=${credits}`, + }) } const handleCancelLogin = () => { @@ -98,25 +152,31 @@ const ProfileView: Component = (props) => { return (
-

- {language.t("profile.title")} -

- -
+

+ {language.t("profile.title")} +

+
+ + + +
= (props) => { +
+ +

+ {language.t("profile.welcome.greeting")} +

- {language.t("profile.notLoggedIn")} + {language.t("profile.welcome.introText1")}

- - +
} > = (props) => { > {(data) => (
- {/* User header */} - -

- {data().profile.name || data().profile.email} -

-

+ + {getInitial(data().profile.name || data().profile.email)} +

+ } > - {data().profile.email} -

+ {(avatar) => ( + Profile + )} +
+
+

+ {data().profile.name || data().profile.email} +

+

+ {data().profile.email} +

+
- {/* Organization selector */} 0}>

= (props) => { margin: "0 0 8px 0", }} > - Account + {language.t("profile.account.label")}

= (props) => { triggerVariant="settings" disabled={switching()} /> -
+
-
- -
- - - - - - +
+ + + + + + +
{(balance) => ( - -

{language.t("profile.balance.title")} -

-

+

- {formatBalance(balance().balance)} -

- - - - +
+ {formatBalance(balance().balance)} +
+ + + +
+
)} - +
{language.t("profile.shop.title")} @@ -447,8 +463,9 @@ const ProfileView: Component = (props) => {
@@ -459,9 +476,11 @@ const ProfileView: Component = (props) => { border: `1px solid ${ pkg.popular ? "var(--vscode-button-background)" : "var(--vscode-input-border)" }`, + "box-shadow": pkg.popular ? "0 0 0 1px var(--vscode-button-background)" : "none", "border-radius": "8px", - padding: "8px", + padding: "16px", background: "var(--vscode-editor-background)", + transition: "box-shadow 120ms ease", }} > @@ -473,57 +492,58 @@ const ProfileView: Component = (props) => { transform: "translateX(-50%)", background: "var(--vscode-button-background)", color: "var(--vscode-button-foreground)", - "font-size": "10px", + "font-size": "12px", "font-weight": "600", - padding: "2px 6px", + padding: "2px 8px", "border-radius": "999px", "text-transform": "uppercase", "letter-spacing": "0.4px", - }} - > + }} + > {language.t("profile.shop.popular")}
-
- ${pkg.credits} -
-
- {language.t("profile.shop.credits")} +
+
+ ${pkg.credits} +
+
+ {language.t("profile.shop.credits")} +
+
-
)}
-
-
- +
)} From 76bf62fe6440c8686816e6af421c96acf3721de3 Mon Sep 17 00:00:00 2001 From: Bernardo Ferrari Date: Tue, 17 Feb 2026 01:48:04 -0300 Subject: [PATCH 4/4] tiny adjustments --- .../kilo-vscode/webview-ui/src/components/ProfileView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/ProfileView.tsx b/packages/kilo-vscode/webview-ui/src/components/ProfileView.tsx index bf3619d1ca..6721082092 100644 --- a/packages/kilo-vscode/webview-ui/src/components/ProfileView.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/ProfileView.tsx @@ -206,7 +206,7 @@ const ProfileView: Component = (props) => {

@@ -214,7 +214,7 @@ const ProfileView: Component = (props) => {