diff --git a/ui/src/components/account-flow-viz.tsx b/ui/src/components/account-flow-viz.tsx index 59339640..c23e95ea 100644 --- a/ui/src/components/account-flow-viz.tsx +++ b/ui/src/components/account-flow-viz.tsx @@ -252,8 +252,8 @@ export function AccountFlowViz({ providerData, onBack }: AccountFlowVizProps) { if (newPulsing.size > 0) { setPulsingAccounts(newPulsing); - // Clear pulse after animation - const timer = setTimeout(() => setPulsingAccounts(new Set()), 600); + // Clear pulse after animation completes (match CSS animation duration) + const timer = setTimeout(() => setPulsingAccounts(new Set()), 2000); return () => clearTimeout(timer); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -429,15 +429,29 @@ export function AccountFlowViz({ providerData, onBack }: AccountFlowVizProps) { /> {/* Pulse layer - only shows when new activity detected */} {isPulsing && ( - + <> + {/* Glowing path pulse */} + + {/* Traveling dot along path */} + + )} ); @@ -731,20 +745,20 @@ export function AccountFlowViz({ providerData, onBack }: AccountFlowVizProps) { {/* Stats */}
- + SUCCESSFUL
-
+
{selectedAccount.successCount.toLocaleString()}
- + FAILED
-
+
{selectedAccount.failureCount.toLocaleString()}
diff --git a/ui/src/components/auth-monitor.tsx b/ui/src/components/auth-monitor.tsx index cdac5e5c..4f812050 100644 --- a/ui/src/components/auth-monitor.tsx +++ b/ui/src/components/auth-monitor.tsx @@ -48,18 +48,18 @@ function cleanEmail(email: string): string { return email.replace(/@(gmail|yahoo|hotmail|outlook|icloud)\.com$/i, ''); } -// Vibrant colors for account segments +// Vibrant colors for account segments - darker for light theme contrast const ACCOUNT_COLORS = [ - '#277da1', // Cerulean - '#43aa8b', // Seaweed - '#f9c74f', // Tuscan Sun - '#f94144', // Strawberry - '#f3722c', // Pumpkin - '#90be6d', // Willow - '#577590', // Blue Slate - '#f8961e', // Carrot - '#4d908e', // Dark Cyan - '#a78bfa', // Purple + '#1e6091', // Deep Cerulean (was #277da1) + '#2d8a6e', // Deep Seaweed (was #43aa8b) + '#d4a012', // Dark Tuscan (was #f9c74f) + '#c92a2d', // Deep Strawberry (was #f94144) + '#c45a1a', // Deep Pumpkin (was #f3722c) + '#6b9c4d', // Dark Willow (was #90be6d) + '#3d5a73', // Deep Blue Slate (was #577590) + '#cc7614', // Dark Carrot (was #f8961e) + '#3a7371', // Deep Cyan (was #4d908e) + '#7c5fc4', // Deep Purple (was #a78bfa) ]; /** Enhanced live pulse indicator with multi-ring animation */ @@ -94,15 +94,15 @@ function InlineStatsBadge({ success, failure }: { success: number; failure: numb return (
- - + + {success.toLocaleString()}
{failure > 0 && (
- - + + {failure.toLocaleString()}
@@ -308,7 +308,7 @@ export function AuthMonitor() {
{/* Flow Visualization */} -
+
{selectedProviderData ? ( // Account-level flow view 0.40 */ - --border: oklch(0.85 0.015 91.6); /* Based on Cloudy */ - --input: oklch(0.85 0.015 91.6); + --border: oklch(0.78 0.02 91.6); /* Darker: 0.85 -> 0.78 */ + --input: oklch(0.78 0.02 91.6); - --ring: oklch(0.2 0.02 40); /* Match primary */ + --ring: oklch(0.15 0.02 40); /* Match primary */ --radius: 0.5rem; - --popover: oklch(0.9635 0.0067 97.35); /* Match background */ - --popover-foreground: oklch(0.2 0.02 40); /* Match foreground */ + --popover: oklch(0.98 0.005 97.35); /* Slight depth */ + --popover-foreground: oklch(0.15 0.02 40); /* Match foreground */ - --card: oklch(0.9635 0.0067 97.35); /* Match background */ - --card-foreground: oklch(0.2 0.02 40); /* Match foreground */ + --card: oklch(0.98 0.005 97.35); /* Slight depth from background */ + --card-foreground: oklch(0.15 0.02 40); /* Match foreground */ - --destructive: oklch(0.577 0.245 27.325); /* Red 600 */ - --destructive-foreground: oklch(0.9635 0.0067 97.35); /* White */ + --destructive: oklch(0.5 0.22 27); /* Darker red for visibility */ + --destructive-foreground: oklch(0.98 0.005 97.35); /* White */ /* Sidebar colors - Light */ --sidebar: oklch(0.9635 0.0067 97.35); /* Pampas */ - --sidebar-foreground: oklch(0.2 0.02 40); - --sidebar-primary: oklch(0.2 0.02 40); /* Neutral */ + --sidebar-foreground: oklch(0.15 0.02 40); + --sidebar-primary: oklch(0.15 0.02 40); /* Neutral - darker */ --sidebar-primary-foreground: oklch(0.9635 0.0067 97.35); - --sidebar-accent: oklch(0.5971 0.1352 39.87); /* Crail */ + --sidebar-accent: oklch(0.52 0.15 39.87); /* Crail - darker */ --sidebar-accent-foreground: oklch(1 0 0); - --sidebar-border: oklch(0.85 0.015 91.6); - --sidebar-ring: oklch(0.2 0.02 40); + --sidebar-border: oklch(0.78 0.02 91.6); /* Darker */ + --sidebar-ring: oklch(0.15 0.02 40); } .dark { @@ -203,25 +203,57 @@ /* Flow visualization animations */ @keyframes request-pulse { 0% { - stroke-opacity: 0.9; + stroke-opacity: 1; stroke-width: inherit; + filter: drop-shadow(0 0 2px currentColor); } - 50% { - stroke-opacity: 0.6; + 30% { + stroke-opacity: 0.9; + filter: drop-shadow(0 0 8px currentColor); + } + 60% { + stroke-opacity: 0.5; + stroke-width: calc(inherit * 1.5); + filter: drop-shadow(0 0 4px currentColor); } 100% { stroke-opacity: 0; stroke-width: calc(inherit * 2); + filter: none; + } +} + +/* Traveling dot along path */ +@keyframes travel-dot { + 0% { + offset-distance: 0%; + opacity: 1; + } + 80% { + opacity: 1; + } + 100% { + offset-distance: 100%; + opacity: 0; } } @keyframes glow-pulse { 0%, 100% { - box-shadow: 0 0 0 0 var(--glow-color, rgba(16, 185, 129, 0.2)); + box-shadow: 0 0 0 0 var(--glow-color, rgba(16, 185, 129, 0.3)); + transform: scale(1); + } + 25% { + box-shadow: 0 0 15px 3px var(--glow-color, rgba(16, 185, 129, 0.5)); + transform: scale(1.02); } 50% { - box-shadow: 0 0 20px 4px var(--glow-color, rgba(16, 185, 129, 0.3)); + box-shadow: 0 0 25px 6px var(--glow-color, rgba(16, 185, 129, 0.4)); + transform: scale(1.01); + } + 75% { + box-shadow: 0 0 12px 2px var(--glow-color, rgba(16, 185, 129, 0.35)); } } @@ -258,11 +290,15 @@ } .animate-request-pulse { - animation: request-pulse 0.6s ease-out forwards; + animation: request-pulse 2s cubic-bezier(0.4, 0, 0.2, 1) forwards; } .animate-glow-pulse { - animation: glow-pulse 2s ease-in-out infinite; + animation: glow-pulse 2.5s ease-in-out infinite; +} + +.animate-travel-dot { + animation: travel-dot 1.5s cubic-bezier(0.4, 0, 0.2, 1) forwards; } .animate-subtle-float { diff --git a/ui/src/lib/utils.ts b/ui/src/lib/utils.ts index 417ed674..326a4440 100644 --- a/ui/src/lib/utils.ts +++ b/ui/src/lib/utils.ts @@ -29,11 +29,11 @@ const PROVIDER_COLORS: Record = { qwen: '#f9c74f', // Tuscan }; -// Status colors (from Analytics Cost breakdown) +// Status colors (from Analytics Cost breakdown) - darker for light theme contrast export const STATUS_COLORS = { - success: '#43aa8b', // Seaweed - degraded: '#e09f3e', // Ochre - failed: '#9e2a2b', // Merlot + success: '#15803d', // Green-700 (was Seaweed #43aa8b) + degraded: '#b45309', // Amber-700 (was Ochre #e09f3e) + failed: '#b91c1c', // Red-700 (was Merlot #9e2a2b) } as const; export function getModelColor(model: string): string { diff --git a/ui/src/pages/home.tsx b/ui/src/pages/home.tsx index 1a8adc5c..ccc5bf92 100644 --- a/ui/src/pages/home.tsx +++ b/ui/src/pages/home.tsx @@ -19,10 +19,10 @@ type StatVariant = 'default' | 'success' | 'warning' | 'error' | 'accent'; const variantStyles: Record = { default: { iconBg: 'bg-muted', iconColor: 'text-muted-foreground' }, - success: { iconBg: 'bg-green-500/10', iconColor: 'text-green-600' }, - warning: { iconBg: 'bg-yellow-500/10', iconColor: 'text-yellow-500' }, - error: { iconBg: 'bg-red-500/10', iconColor: 'text-red-500' }, - accent: { iconBg: 'bg-accent/10', iconColor: 'text-accent' }, + success: { iconBg: 'bg-green-600/15', iconColor: 'text-green-700 dark:text-green-500' }, + warning: { iconBg: 'bg-amber-500/15', iconColor: 'text-amber-700 dark:text-amber-400' }, + error: { iconBg: 'bg-red-600/15', iconColor: 'text-red-700 dark:text-red-500' }, + accent: { iconBg: 'bg-accent/15', iconColor: 'text-accent' }, }; function InlineStat({