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
2 changes: 1 addition & 1 deletion packages/kilo-vscode/docs/ui-implementation-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ Each phase has specific acceptance criteria (listed above). The general verifica
| Session history | List renders, selection works, navigation back to chat |
| Login flow | Device auth card renders, QR code displays, success/error states |
| Theme switching | Change VS Code theme → webview updates automatically |
| Sidebar ↔ tab | Both webview hosts render identically |
| Sidebar ↔ tab | Both webview hosts render identically |
| Markdown content | Headers, lists, code blocks, links render correctly |
| Long conversations | Auto-scroll works, performance is acceptable |

Expand Down
45 changes: 27 additions & 18 deletions packages/kilo-vscode/webview-ui/src/components/DeviceAuthCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Card } from "@kilocode/kilo-ui/card"
import { Spinner } from "@kilocode/kilo-ui/spinner"
import { showToast } from "@kilocode/kilo-ui/toast"
import { useVSCode } from "../context/vscode"
import { useLanguage } from "../context/language"
import { generateQRCode } from "../utils/qrcode"
import type { DeviceAuthStatus } from "../types/messages"

Expand All @@ -25,6 +26,7 @@ const formatTime = (seconds: number): string => {

const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
const vscode = useVSCode()
const language = useLanguage()
const [timeRemaining, setTimeRemaining] = createSignal(props.expiresIn ?? 900)
const [qrDataUrl, setQrDataUrl] = createSignal("")

Expand All @@ -51,14 +53,14 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
const handleCopyUrl = () => {
if (props.verificationUrl) {
navigator.clipboard.writeText(props.verificationUrl)
showToast({ variant: "success", title: "URL copied to clipboard" })
showToast({ variant: "success", title: language.t("deviceAuth.toast.urlCopied") })
}
}

const handleCopyCode = () => {
if (props.code) {
navigator.clipboard.writeText(props.code)
showToast({ variant: "success", title: "Code copied to clipboard" })
showToast({ variant: "success", title: language.t("deviceAuth.toast.codeCopied") })
}
}

Expand All @@ -75,7 +77,9 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
<Card>
<div style={{ display: "flex", "align-items": "center", gap: "8px" }}>
<Spinner style={{ width: "14px", height: "14px" }} />
<span style={{ "font-size": "13px", color: "var(--vscode-descriptionForeground)" }}>Starting login...</span>
<span style={{ "font-size": "13px", color: "var(--vscode-descriptionForeground)" }}>
{language.t("deviceAuth.status.initiating")}
</span>
</div>
</Card>
</Match>
Expand All @@ -92,7 +96,7 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
"text-align": "center",
}}
>
Sign in to Kilo Code
{language.t("deviceAuth.title")}
</h3>

{/* Step 1: URL */}
Expand All @@ -107,7 +111,7 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
"letter-spacing": "0.5px",
}}
>
Step 1: Open this URL
{language.t("deviceAuth.step1")}
</p>
<div
style={{
Expand All @@ -132,11 +136,16 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
>
{props.verificationUrl}
</div>
<Button variant="secondary" size="small" onClick={handleCopyUrl} title="Copy URL">
<Button
variant="secondary"
size="small"
onClick={handleCopyUrl}
title={language.t("deviceAuth.action.copyUrl")}
>
📋
</Button>
<Button variant="secondary" size="small" onClick={handleOpenBrowser}>
Open Browser
{language.t("deviceAuth.action.openBrowser")}
</Button>
</div>
</div>
Expand All @@ -152,7 +161,7 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
>
<img
src={qrDataUrl()}
alt="QR Code"
alt={language.t("deviceAuth.qrCode.alt")}
style={{
width: "160px",
height: "160px",
Expand All @@ -175,7 +184,7 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
"letter-spacing": "0.5px",
}}
>
Step 2: Enter this code
{language.t("deviceAuth.step2")}
</p>
<div
onClick={handleCopyCode}
Expand All @@ -187,7 +196,7 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
"text-align": "center",
cursor: "pointer",
}}
title="Click to copy"
title={language.t("deviceAuth.action.clickToCopy")}
>
<span
style={{
Expand All @@ -207,7 +216,7 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
margin: "4px 0 0 0",
}}
>
Click to copy
{language.t("deviceAuth.action.clickToCopy")}
</p>
</div>
</div>
Expand All @@ -230,13 +239,13 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
color: "var(--vscode-descriptionForeground)",
}}
>
Waiting for authorization... ({formatTime(timeRemaining())})
{language.t("deviceAuth.status.waiting")} ({formatTime(timeRemaining())})
</span>
</div>

{/* Cancel button */}
<Button variant="ghost" onClick={props.onCancel} style={{ width: "100%" }}>
Cancel
{language.t("common.cancel")}
</Button>
</Card>
</Match>
Expand All @@ -254,7 +263,7 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
margin: "8px 0 0 0",
}}
>
Login successful!
{language.t("deviceAuth.status.success")}
</p>
</div>
</Card>
Expand All @@ -272,10 +281,10 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
margin: "8px 0 12px 0",
}}
>
{props.error || "Login failed"}
{props.error || language.t("deviceAuth.status.failed")}
</p>
<Button variant="primary" onClick={props.onRetry}>
Retry
{language.t("common.retry")}
</Button>
</div>
</Card>
Expand All @@ -292,10 +301,10 @@ const DeviceAuthCard: Component<DeviceAuthCardProps> = (props) => {
margin: "0 0 12px 0",
}}
>
Login cancelled
{language.t("deviceAuth.status.cancelled")}
</p>
<Button variant="primary" onClick={props.onRetry}>
Try Again
{language.t("deviceAuth.action.tryAgain")}
</Button>
</div>
</Card>
Expand Down
18 changes: 10 additions & 8 deletions packages/kilo-vscode/webview-ui/src/components/ProfileView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Button } from "@kilocode/kilo-ui/button"
import { Card } from "@kilocode/kilo-ui/card"
import { Tooltip } from "@kilocode/kilo-ui/tooltip"
import { useVSCode } from "../context/vscode"
import { useLanguage } from "../context/language"
import DeviceAuthCard from "./DeviceAuthCard"
import type { ProfileData, DeviceAuthState } from "../types/messages"

Expand All @@ -20,6 +21,7 @@ const formatBalance = (amount: number): string => {

const ProfileView: Component<ProfileViewProps> = (props) => {
const vscode = useVSCode()
const language = useLanguage()

const handleLogin = () => {
props.onLogin()
Expand Down Expand Up @@ -52,7 +54,7 @@ const ProfileView: Component<ProfileViewProps> = (props) => {
color: "var(--vscode-foreground)",
}}
>
Profile
{language.t("profile.title")}
</h2>

<div
Expand All @@ -78,10 +80,10 @@ const ProfileView: Component<ProfileViewProps> = (props) => {
margin: "0 0 8px 0",
}}
>
Not logged in
{language.t("profile.notLoggedIn")}
</p>
<Button variant="primary" onClick={handleLogin}>
Login with Kilo Code
{language.t("profile.action.login")}
</Button>
</>
}
Expand Down Expand Up @@ -144,7 +146,7 @@ const ProfileView: Component<ProfileViewProps> = (props) => {
margin: "0 0 4px 0",
}}
>
Balance
{language.t("profile.balance.title")}
</p>
<p
style={{
Expand All @@ -157,9 +159,9 @@ const ProfileView: Component<ProfileViewProps> = (props) => {
{formatBalance(balance().balance)}
</p>
</div>
<Tooltip value="Refresh balance" placement="left">
<Tooltip value={language.t("profile.balance.refresh")} placement="left">
<Button variant="ghost" size="small" onClick={handleRefresh}>
Refresh
{language.t("common.refresh")}
</Button>
</Tooltip>
</Card>
Expand All @@ -169,14 +171,14 @@ const ProfileView: Component<ProfileViewProps> = (props) => {
{/* Action buttons */}
<div style={{ display: "flex", gap: "8px" }}>
<Button variant="secondary" onClick={handleDashboard} style={{ flex: "1" }}>
Dashboard
{language.t("profile.action.dashboard")}
</Button>
<Button
variant="ghost"
onClick={handleLogout}
style={{ flex: "1", color: "var(--vscode-errorForeground)" }}
>
Log Out
{language.t("profile.action.logout")}
</Button>
</div>
</div>
Expand Down
Loading
Loading