From cc882d2f098458e4507f4a7702cfcf968efe5d20 Mon Sep 17 00:00:00 2001 From: David Kaya Date: Sun, 22 Mar 2026 13:56:34 +0100 Subject: [PATCH] refactor: redesign settings page with sidebar navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace single-scroll settings with two-column layout: left sidebar nav (Connection, Patterns) + content area - Consistent back arrow (←) on the left for all navigation levels: settings → app, pattern editor → settings list - Fix CopilotStatusCard path overflow: use break-all for long Windows paths, structured detail sections with proper wrapping - Simplify status card: cleaner status dot indicator, separated detail sections with labeled fields and proper text overflow Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/renderer/components/CopilotStatusCard.tsx | 185 ++++++++------- src/renderer/components/PatternEditor.tsx | 4 +- src/renderer/components/SettingsPanel.tsx | 215 +++++++++++++----- 3 files changed, 245 insertions(+), 159 deletions(-) diff --git a/src/renderer/components/CopilotStatusCard.tsx b/src/renderer/components/CopilotStatusCard.tsx index 68bb097..1ed9db2 100644 --- a/src/renderer/components/CopilotStatusCard.tsx +++ b/src/renderer/components/CopilotStatusCard.tsx @@ -1,7 +1,6 @@ import { useState } from 'react'; import { CheckCircle2, - AlertTriangle, XCircle, RefreshCw, ChevronDown, @@ -28,8 +27,7 @@ interface StatusConfig { icon: React.ReactNode; label: string; accentClasses: string; - borderClasses: string; - bgClasses: string; + dotClasses: string; actionIcon?: React.ReactNode; actionLabel?: string; } @@ -39,38 +37,34 @@ function getStatusConfig(status: SidecarConnectionStatus): StatusConfig { case 'ready': return { icon: , - label: 'Connected', + label: 'Connected to GitHub Copilot', accentClasses: 'text-emerald-400', - borderClasses: 'border-emerald-500/20', - bgClasses: 'bg-emerald-500/5', + dotClasses: 'bg-emerald-400', }; case 'copilot-cli-missing': return { icon: , - label: 'CLI Not Found', + label: 'Copilot CLI not found', accentClasses: 'text-amber-400', - borderClasses: 'border-amber-500/20', - bgClasses: 'bg-amber-500/5', + dotClasses: 'bg-amber-400', actionIcon: , - actionLabel: 'Install Copilot CLI', + actionLabel: 'Install the copilot CLI and ensure it is on your PATH', }; case 'copilot-auth-required': return { icon: , - label: 'Sign-in Required', + label: 'Sign-in required', accentClasses: 'text-blue-400', - borderClasses: 'border-blue-500/20', - bgClasses: 'bg-blue-500/5', + dotClasses: 'bg-blue-400', actionIcon: , - actionLabel: 'Run copilot auth login', + actionLabel: 'Run copilot auth login in your terminal, then refresh', }; case 'copilot-error': return { icon: , - label: 'Connection Error', + label: 'Connection error', accentClasses: 'text-red-400', - borderClasses: 'border-red-500/20', - bgClasses: 'bg-red-500/5', + dotClasses: 'bg-red-400', }; } } @@ -94,6 +88,12 @@ function formatCheckedAt(iso: string): string { } } +function shortenPath(fullPath: string): string { + const parts = fullPath.replace(/\\/g, '/').split('/'); + if (parts.length <= 3) return fullPath; + return `…/${parts.slice(-2).join('/')}`; +} + export function CopilotStatusCard({ connection, modelCount, @@ -104,107 +104,102 @@ export function CopilotStatusCard({ if (!connection) { return ( -
-
- - Checking Copilot connection… - -
+
+ + Checking connection… +
); } const config = getStatusConfig(connection.status); const isHealthy = connection.status === 'ready'; - const hasDetail = connection.detail || connection.copilotCliPath; + const hasDetail = connection.copilotCliPath; const checkedLabel = formatCheckedAt(connection.checkedAt); return ( -
- {/* Main status row */} -
-
{config.icon}
- -
- {/* Title row */} -
- - {config.label} - - {isHealthy && ( - - {modelCount} model{modelCount === 1 ? '' : 's'} - - )} -
- - {/* Summary */} -

- {connection.summary} -

- - {/* Action hint for non-ready states */} - {!isHealthy && config.actionLabel && ( -
- {config.actionIcon} - {config.actionLabel} -
+
+ {/* Status indicator */} +
+
+ + {config.label} + + {isHealthy && ( + + · {modelCount} model{modelCount === 1 ? '' : 's'} available + + )} +
+ {checkedLabel && ( + {checkedLabel} )} +
- - {/* Refresh button */} -
- {/* Expandable details */} - {hasDetail && ( -
+ {/* Action hint for non-ready states */} + {!isHealthy && config.actionLabel && ( +
+
{config.actionIcon}
+

{config.actionLabel}

+
+ )} + + {/* CLI path (always visible when healthy, expandable otherwise) */} + {isHealthy && hasDetail && ( +
{showDetails && ( -
+
{connection.copilotCliPath && ( -
- CLI path - {connection.copilotCliPath} -
- )} - {connection.detail && ( -
- Detail - {connection.detail} -
- )} - {checkedLabel && ( -
- Checked - {connection.checkedAt} +
+ CLI path +

+ {connection.copilotCliPath} +

)} +
+ Last checked +

+ {new Date(connection.checkedAt).toLocaleString()} +

+
+
+ )} +
+ )} + + {/* Error detail for non-ready states */} + {!isHealthy && hasDetail && ( +
+
+ CLI path +

+ {shortenPath(connection.copilotCliPath!)} +

+
+ {connection.detail && ( +
+ Error detail +

{connection.detail}

)}
diff --git a/src/renderer/components/PatternEditor.tsx b/src/renderer/components/PatternEditor.tsx index bc3807c..6acdb64 100644 --- a/src/renderer/components/PatternEditor.tsx +++ b/src/renderer/components/PatternEditor.tsx @@ -224,8 +224,8 @@ export function PatternEditor({ return (
- {/* Header — top padding clears the title bar overlay zone */} -
+ {/* Header — consistent ← navigation */} +
+

Settings

- {/* Body */} -
-
- {/* Copilot Connection Status */} -
-

GitHub Copilot

- + {/* Two-column layout */} +
+ {/* Left navigation */} + - {/* Separator */} -
- -
-
-

Orchestration Patterns

-

- Define reusable agent configurations for your sessions -

-
- -
- -
- {patterns.map((pattern) => ( - - ))} + {/* Content area */} +
+
+ {activeSection === 'connection' && ( + + )} + {activeSection === 'patterns' && ( + setEditingPattern(structuredClone(p))} + onNewPattern={() => setEditingPattern(onNewPattern())} + patterns={patterns} + /> + )}
); } + +/* ---------- Section components ---------- */ + +function ConnectionSection({ + connection, + modelCount, + isRefreshing, + onRefresh, +}: { + connection?: SidecarCapabilities['connection']; + modelCount: number; + isRefreshing: boolean; + onRefresh: () => void; +}) { + return ( +
+
+

GitHub Copilot

+

+ Kopaya uses your installed GitHub Copilot CLI for AI capabilities +

+
+
+ +
+
+ ); +} + +function PatternsSection({ + patterns, + onEditPattern, + onNewPattern, +}: { + patterns: PatternDefinition[]; + onEditPattern: (pattern: PatternDefinition) => void; + onNewPattern: () => void; +}) { + return ( +
+
+
+

Orchestration Patterns

+

+ Define reusable agent configurations for your sessions +

+
+ +
+ +
+ {patterns.map((pattern) => ( + + ))} +
+
+ ); +}