diff --git a/ROADMAP.md b/ROADMAP.md index 8ad4ed9..35176eb 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -83,8 +83,8 @@ Implemented foundation: Still worth adding: -- [ ] outdated Copilot CLI state -- [ ] active GitHub account or organization context when available +- [x] outdated Copilot CLI state +- [x] active GitHub account or organization context when available - [ ] clear model availability explanation when a model is unavailable - [ ] reconnect and troubleshooting actions beyond refresh - [ ] optional per-project or per-pattern account selection later if Kopaya supports multiple Copilot identities diff --git a/src/renderer/components/CopilotStatusCard.tsx b/src/renderer/components/CopilotStatusCard.tsx index 1ed9db2..5acab19 100644 --- a/src/renderer/components/CopilotStatusCard.tsx +++ b/src/renderer/components/CopilotStatusCard.tsx @@ -9,11 +9,15 @@ import { LogIn, Download, Cpu, + ArrowUpCircle, + User, + Building2, } from 'lucide-react'; import type { SidecarConnectionDiagnostics, SidecarConnectionStatus, + SidecarCopilotCliVersionStatus, } from '@shared/contracts/sidecar'; interface CopilotStatusCardProps { @@ -94,6 +98,85 @@ function shortenPath(fullPath: string): string { return `…/${parts.slice(-2).join('/')}`; } +function VersionBadge({ status, installedVersion }: { status: SidecarCopilotCliVersionStatus; installedVersion?: string }) { + const versionLabel = installedVersion ? `v${installedVersion}` : undefined; + + switch (status) { + case 'latest': + return ( + + + {versionLabel ?? 'Up to date'} + + ); + case 'outdated': + return ( + + + Update available + + ); + case 'unknown': + return ( + + {versionLabel ?? 'Version unknown'} + + ); + } +} + +function AccountSection({ connection }: { connection: SidecarConnectionDiagnostics }) { + const { account } = connection; + if (!account) return null; + + const hasLogin = !!account.login; + const hasOrgs = account.organizations && account.organizations.length > 0; + if (!hasLogin && !account.statusMessage) return null; + + const MAX_VISIBLE_ORGS = 5; + const visibleOrgs = hasOrgs ? account.organizations!.slice(0, MAX_VISIBLE_ORGS) : []; + const remainingOrgs = hasOrgs ? account.organizations!.length - MAX_VISIBLE_ORGS : 0; + + return ( +
+ {/* Identity row */} +
+ + {hasLogin ? ( +
+ {account.login} + {account.host && ( + · {account.host} + )} +
+ ) : ( + {account.statusMessage} + )} +
+ + {/* Organizations */} + {hasOrgs && ( +
+ +
+ {visibleOrgs.map((org) => ( + + {org} + + ))} + {remainingOrgs > 0 && ( + +{remainingOrgs} more + )} +
+
+ )} +
+ ); +} + export function CopilotStatusCard({ connection, modelCount, @@ -116,6 +199,8 @@ export function CopilotStatusCard({ const isHealthy = connection.status === 'ready'; const hasDetail = connection.copilotCliPath; const checkedLabel = formatCheckedAt(connection.checkedAt); + const hasVersionInfo = !!connection.copilotCliVersion; + const hasAccountInfo = !!connection.account; return (
@@ -131,6 +216,12 @@ export function CopilotStatusCard({ )}
+ {hasVersionInfo && ( + + )} {checkedLabel && ( {checkedLabel} )} @@ -146,6 +237,11 @@ export function CopilotStatusCard({
+ {/* Account info (when healthy) */} + {isHealthy && hasAccountInfo && ( + + )} + {/* Action hint for non-ready states */} {!isHealthy && config.actionLabel && (
@@ -154,7 +250,7 @@ export function CopilotStatusCard({
)} - {/* CLI path (always visible when healthy, expandable otherwise) */} + {/* Expandable details (healthy state) */} {isHealthy && hasDetail && (
)} + {hasVersionInfo && connection.copilotCliVersion!.status === 'outdated' && connection.copilotCliVersion!.latestVersion && ( +
+ Latest version +

+ {connection.copilotCliVersion!.latestVersion} +

+
+ )}
Last checked