Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/warm-balance-chip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---

Show account balance in the VS Code header account switcher and surface Kilo Pass details on the profile page.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/kilo-docs/source-links.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<!-- packages/opencode/src/config/config.ts -->
- <https://app.kilo.ai/credits>
<!-- packages/kilo-vscode/src/services/autocomplete/AutocompleteServiceManager.ts -->
<!-- packages/kilo-vscode/webview-ui/src/components/profile/ProfileView.tsx -->
- <https://app.kilo.ai/profile>
<!-- packages/kilo-vscode/webview-ui/src/components/profile/ProfileView.tsx -->
- <https://app.kilo.ai/tui.json>
Expand Down Expand Up @@ -143,6 +144,8 @@
<!-- packages/opencode/src/kilocode/components/dialog-claw-setup.tsx -->
- <https://kilo.ai/pricing>
<!-- packages/opencode/src/cli/cmd/tui/component/dialog-retry-action.tsx -->
- <https://kilo.ai/pricing/kilo-pass>
<!-- packages/kilo-vscode/webview-ui/src/components/profile/ProfileView.tsx -->
- <https://kilo.ai/support>
<!-- packages/kilo-vscode/webview-ui/src/components/chat/FeedbackDialog.tsx -->
<!-- packages/kilo-vscode/webview-ui/src/components/settings/AboutKiloCodeTab.tsx -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useVSCode } from "../../context/vscode"
import { TaskTimeline } from "./TaskTimeline"
import { ContextProgress } from "./ContextProgress"
import { SessionRenameEditor } from "../shared/SessionRenameEditor"
import { BalanceChip } from "../shared/BalanceChip"
import { target as todoTarget } from "../../context/todo-revert"
import type { Part, TodoItem, ExtensionMessage } from "../../types/messages"

Expand Down Expand Up @@ -185,6 +186,7 @@ export const TaskHeader: Component<TaskHeaderProps> = (props) => {
</Show>
</div>
<div data-slot="task-header-stats">
<BalanceChip />
<Show when={cost()}>
{(c) => (
<Tooltip value={costTooltip()} placement="bottom">
Expand Down
181 changes: 149 additions & 32 deletions packages/kilo-vscode/webview-ui/src/components/profile/ProfileView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Select } from "@kilocode/kilo-ui/select"
import { Tooltip } from "@kilocode/kilo-ui/tooltip"
import { useVSCode } from "../../context/vscode"
import { useLanguage } from "../../context/language"
import { localeToBcp47, type Locale } from "../../context/language-utils"
import DeviceAuthCard from "./DeviceAuthCard"
import type { ProfileData, DeviceAuthState } from "../../types/messages"

Expand All @@ -21,6 +22,15 @@ const formatBalance = (amount: number): string => {
return `$${amount.toFixed(2)}`
}

const short = (amount: number): string => `$${Math.round(amount)}`

const resetLabel = (iso: string | null | undefined, loc: Locale): string | undefined => {
if (!iso) return undefined
const date = new Date(iso)
if (Number.isNaN(date.getTime())) return undefined
return new Intl.DateTimeFormat(localeToBcp47(loc), { month: "short", day: "numeric", timeZone: "UTC" }).format(date)
}

const PERSONAL = "personal"

interface OrgOption {
Expand Down Expand Up @@ -93,6 +103,14 @@ const ProfileView: Component<ProfileViewProps> = (props) => {
vscode.postMessage({ type: "openExternal", url: "https://app.kilo.ai/profile" })
}

const handleTopUp = () => {
vscode.postMessage({ type: "openExternal", url: "https://app.kilo.ai/credits" })
}

const handleGetPass = () => {
vscode.postMessage({ type: "openExternal", url: "https://kilo.ai/pricing/kilo-pass" })
}

const handleCancelLogin = () => {
vscode.postMessage({ type: "cancelLogin" })
}
Expand Down Expand Up @@ -207,41 +225,137 @@ const ProfileView: Component<ProfileViewProps> = (props) => {
{/* Balance */}
<Show when={data().balance}>
{(balance) => (
<Card
style={{
display: "flex",
"align-items": "center",
"justify-content": "space-between",
}}
>
<div>
<p
style={{
"font-size": "var(--kilo-font-size-11)",
"text-transform": "uppercase",
"letter-spacing": "0.5px",
color: "var(--vscode-descriptionForeground)",
margin: "0 0 4px 0",
}}
>
{language.t("profile.balance.title")}
</p>
<p
<Card style={{ display: "flex", "flex-direction": "column", gap: "12px" }}>
<div style={{ display: "flex", "align-items": "center", "justify-content": "space-between" }}>
<div>
<p
style={{
"font-size": "var(--kilo-font-size-11)",
"text-transform": "uppercase",
"letter-spacing": "0.5px",
color: "var(--vscode-descriptionForeground)",
margin: "0 0 4px 0",
}}
>
{language.t("profile.balance.title")}
</p>
<p
style={{
"font-size": "var(--kilo-font-size-18)",
"font-weight": "600",
color: "var(--vscode-foreground)",
margin: 0,
}}
>
{formatBalance(balance().balance)}
</p>
</div>
<Tooltip value={language.t("profile.balance.refresh")} placement="left">
<Button variant="ghost" size="small" onClick={handleRefresh}>
↻ {language.t("common.refresh")}
</Button>
</Tooltip>
</div>

{/* Kilo Pass is part of personal credits, so only show it on the personal account */}
<Show when={(data().currentOrgId ?? null) === null ? data().kiloPass : null}>
{(pass) => (
<div
style={{
"border-top": "1px solid var(--border-weak-base)",
"padding-top": "12px",
display: "flex",
"flex-direction": "column",
gap: "6px",
}}
>
<div
style={{
display: "flex",
"align-items": "baseline",
"justify-content": "space-between",
"font-size": "var(--kilo-font-size-13)",
}}
>
<span style={{ "font-weight": "600", color: "var(--vscode-foreground)" }}>Kilo Pass</span>
<span style={{ color: "var(--vscode-descriptionForeground)" }}>
{short(pass().currentPeriodUsageUsd)} / {short(pass().currentPeriodBaseCreditsUsd)}
</span>
</div>
<div
style={{
height: "6px",
"border-radius": "3px",
background: "var(--border-weak-base)",
overflow: "hidden",
}}
>
<div
style={{
height: "100%",
width: `${Math.min(100, (pass().currentPeriodUsageUsd / Math.max(1, pass().currentPeriodBaseCreditsUsd)) * 100)}%`,
background: "var(--vscode-progressBar-background, var(--vscode-button-background))",
}}
/>
</div>
<Show when={pass().currentPeriodBonusCreditsUsd > 0}>
<div
style={{
display: "flex",
"justify-content": "space-between",
"font-size": "var(--kilo-font-size-11)",
color: "var(--vscode-descriptionForeground)",
}}
>
<span>{language.t("profile.pass.bonus")}</span>
<span>+{formatBalance(pass().currentPeriodBonusCreditsUsd)}</span>
</div>
</Show>
<Show when={resetLabel(pass().nextBillingAt, language.locale())}>
{(date) => (
<div
style={{
display: "flex",
"justify-content": "space-between",
"font-size": "var(--kilo-font-size-11)",
color: "var(--vscode-descriptionForeground)",
}}
>
<span>{language.t("profile.pass.renews")}</span>
<span>{date()}</span>
</div>
)}
</Show>
</div>
)}
</Show>

{/* No active Kilo Pass on the personal account — nudge to subscribe */}
<Show when={(data().currentOrgId ?? null) === null && !data().kiloPass}>
<div
style={{
"font-size": "var(--kilo-font-size-18)",
"font-weight": "600",
color: "var(--vscode-foreground)",
margin: 0,
"border-top": "1px solid var(--border-weak-base)",
"padding-top": "12px",
}}
>
{formatBalance(balance().balance)}
</p>
</div>
<Tooltip value={language.t("profile.balance.refresh")} placement="left">
<Button variant="ghost" size="small" onClick={handleRefresh}>
↻ {language.t("common.refresh")}
</Button>
</Tooltip>
<button
type="button"
onClick={handleGetPass}
Comment thread
johnnyeric marked this conversation as resolved.
style={{
background: "none",
border: 0,
padding: 0,
"font-size": "var(--kilo-font-size-13)",
"font-family": "inherit",
color: "var(--vscode-textLink-foreground)",
cursor: "pointer",
"text-align": "left",
}}
>
{language.t("profile.pass.subscribe")}
</button>
</div>
</Show>
</Card>
)}
</Show>
Expand All @@ -251,6 +365,9 @@ const ProfileView: Component<ProfileViewProps> = (props) => {
<Button variant="secondary" onClick={handleDashboard} style={{ flex: "1" }}>
{language.t("profile.action.dashboard")}
</Button>
<Button variant="secondary" onClick={handleTopUp} style={{ flex: "1" }}>
{language.t("profile.action.topUp")}
</Button>
<Button
variant="ghost"
onClick={handleLogout}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Spinner } from "@kilocode/kilo-ui/spinner"
import { useServer } from "../../context/server"
import { useVSCode } from "../../context/vscode"
import { useLanguage } from "../../context/language"
import { BalanceChip } from "./BalanceChip"

const PERSONAL = "personal"

Expand Down Expand Up @@ -92,6 +93,7 @@ export const AccountSwitcher: Component<{ class?: string }> = (props) => {
}
>
<span class="account-switcher-label">{label()}</span>
<BalanceChip class="account-switcher-balance" />
<span class="account-switcher-badges">
<Show when={selected() && !switching()}>
<span class="account-switcher-role">{selected()!.role.toUpperCase()}</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Component, Show } from "solid-js"
import { useServer } from "../../context/server"

export const BalanceChip: Component<{ class?: string }> = (props) => {
const server = useServer()
const balance = () => server.profileData()?.balance?.balance

return (
<Show when={balance() !== undefined}>
<span class={props.class}>${(balance() ?? 0).toFixed(2)}</span>
</Show>
)
}
4 changes: 4 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/ar.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/br.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/bs.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/da.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1266,6 +1266,10 @@ export const dict = {
"profile.balance.title": "Guthaben",
"profile.balance.refresh": "Guthaben aktualisieren",
"profile.action.dashboard": "Dashboard",
"profile.action.topUp": "Aufladen",
"profile.pass.subscribe": "Hol dir Kilo Pass, um Guthaben hinzuzufügen und Boni zu verdienen",
"profile.pass.bonus": "Bonus",
"profile.pass.renews": "Verlängert sich",
"profile.action.logout": "Abmelden",

"settings.section.configuration": "Konfiguration",
Expand Down
4 changes: 4 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,10 @@ export const dict = {
"profile.balance.title": "Balance",
"profile.balance.refresh": "Refresh balance",
"profile.action.dashboard": "Dashboard",
"profile.action.topUp": "Top up",
"profile.pass.subscribe": "Get Kilo Pass to add credits and earn bonuses",
"profile.pass.bonus": "Bonus",
"profile.pass.renews": "Renews",
"profile.action.logout": "Log Out",

"settings.section.configuration": "Configuration",
Expand Down
4 changes: 4 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/es.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/fr.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/kilo-vscode/webview-ui/src/i18n/it.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading