From 42bb0b413a9872a1733f9cc04b92d1f9f860c85f Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 14 Apr 2026 20:39:03 +0200 Subject: [PATCH 1/5] [unit: frontend-design] Slices 10-12: Telemetry, Error Pages, Integration Tests --- frontend/src/lib/api/telemetry.ts | 63 ++++++++++ .../lib/components/shared/DataState.svelte | 44 +++++++ .../lib/components/shared/Pagination.svelte | 68 ++++++++++ .../components/telemetry/HealthCards.svelte | 103 ++++++++++++++++ .../lib/components/ui/button/button.svelte | 3 + .../lib/components/ui/toaster/Toaster.svelte | 47 +++++++ frontend/src/routes/(app)/+page.svelte | 35 +++++- .../src/routes/(app)/telemetry/+page.svelte | 116 ++++++++++++++++-- frontend/src/routes/(errors)/404/+page.svelte | 31 +++++ frontend/src/routes/+layout.svelte | 2 + frontend/src/test/integration/auth.test.ts | 93 ++++++++++++++ 11 files changed, 594 insertions(+), 11 deletions(-) create mode 100644 frontend/src/lib/api/telemetry.ts create mode 100644 frontend/src/lib/components/shared/DataState.svelte create mode 100644 frontend/src/lib/components/shared/Pagination.svelte create mode 100644 frontend/src/lib/components/telemetry/HealthCards.svelte create mode 100644 frontend/src/lib/components/ui/toaster/Toaster.svelte create mode 100644 frontend/src/routes/(errors)/404/+page.svelte create mode 100644 frontend/src/test/integration/auth.test.ts diff --git a/frontend/src/lib/api/telemetry.ts b/frontend/src/lib/api/telemetry.ts new file mode 100644 index 00000000..195c58bb --- /dev/null +++ b/frontend/src/lib/api/telemetry.ts @@ -0,0 +1,63 @@ +import { apiClient } from './client'; +import type { + SpansResponse, + MetricsResponse, + UsageResponse, + TelemetryHealthResponse, + SpanQueryParams, + MetricQueryParams, + UsageQueryParams +} from './types'; + +export async function getHealth(): Promise { + return apiClient.request({ + method: 'GET', + path: '/telemetry/health' + }); +} + +export async function getSpans(params?: SpanQueryParams): Promise { + const searchParams = new URLSearchParams(); + if (params?.service) searchParams.set('service', params.service); + if (params?.operation) searchParams.set('operation', params.operation); + if (params?.status) searchParams.set('status', params.status); + if (params?.start_time) searchParams.set('start_time', params.start_time); + if (params?.end_time) searchParams.set('end_time', params.end_time); + if (params?.limit) searchParams.set('limit', String(params.limit)); + if (params?.offset) searchParams.set('offset', String(params.offset)); + + const query = searchParams.toString(); + return apiClient.request({ + method: 'GET', + path: `/telemetry/spans${query ? `?${query}` : ''}` + }); +} + +export async function getMetrics(params?: MetricQueryParams): Promise { + const searchParams = new URLSearchParams(); + if (params?.name) searchParams.set('name', params.name); + if (params?.window) searchParams.set('window', params.window); + if (params?.limit) searchParams.set('limit', String(params.limit)); + + const query = searchParams.toString(); + return apiClient.request({ + method: 'GET', + path: `/telemetry/metrics${query ? `?${query}` : ''}` + }); +} + +export async function getUsage(params?: UsageQueryParams): Promise { + const searchParams = new URLSearchParams(); + if (params?.agent_id) searchParams.set('agent_id', params.agent_id); + if (params?.event_type) searchParams.set('event_type', params.event_type); + if (params?.from) searchParams.set('from', params.from); + if (params?.to) searchParams.set('to', params.to); + if (params?.limit) searchParams.set('limit', String(params.limit)); + if (params?.offset) searchParams.set('offset', String(params.offset)); + + const query = searchParams.toString(); + return apiClient.request({ + method: 'GET', + path: `/telemetry/usage${query ? `?${query}` : ''}` + }); +} diff --git a/frontend/src/lib/components/shared/DataState.svelte b/frontend/src/lib/components/shared/DataState.svelte new file mode 100644 index 00000000..f311d1ae --- /dev/null +++ b/frontend/src/lib/components/shared/DataState.svelte @@ -0,0 +1,44 @@ + + +{#if loading} +
+ + + +
+{:else if error} +
+ +

{error}

+ +
+{:else if empty} +
+ +

{emptyMessage}

+
+{:else if children} + {@render children()} +{/if} diff --git a/frontend/src/lib/components/shared/Pagination.svelte b/frontend/src/lib/components/shared/Pagination.svelte new file mode 100644 index 00000000..33cab846 --- /dev/null +++ b/frontend/src/lib/components/shared/Pagination.svelte @@ -0,0 +1,68 @@ + + + diff --git a/frontend/src/lib/components/telemetry/HealthCards.svelte b/frontend/src/lib/components/telemetry/HealthCards.svelte new file mode 100644 index 00000000..23494a51 --- /dev/null +++ b/frontend/src/lib/components/telemetry/HealthCards.svelte @@ -0,0 +1,103 @@ + + +
+ {#if loading} + {#each subsystems as _} + + + + + + + + + + + {/each} + {:else if error} + {#each subsystems as subsystem} + + + {subsystem} + + + +
Error
+

Failed to load

+
+
+ {/each} + {:else if health} + {#each subsystems as subsystem} + {@const { status, icon: Icon, label } = getSubsystemStatus(subsystem)} + + + {label} + + + +
{status}
+

+ {#if health.checks[subsystem]} + {#if health.checks[subsystem].spans_last_hour !== undefined} + {health.checks[subsystem].spans_last_hour} spans/hr + {:else if health.checks[subsystem].metrics_last_hour !== undefined} + {health.checks[subsystem].metrics_last_hour} metrics/hr + {:else if health.checks[subsystem].connections !== undefined} + {health.checks[subsystem].connections} connections + {:else if health.checks[subsystem].hit_rate !== undefined} + {Math.round(health.checks[subsystem].hit_rate * 100)}% hit rate + {/if} + {:else} + No data + {/if} +

+
+
+ {/each} + {/if} +
diff --git a/frontend/src/lib/components/ui/button/button.svelte b/frontend/src/lib/components/ui/button/button.svelte index 31569c1d..87b5550c 100644 --- a/frontend/src/lib/components/ui/button/button.svelte +++ b/frontend/src/lib/components/ui/button/button.svelte @@ -12,6 +12,7 @@ disabled?: boolean; 'onclick'?: (e: MouseEvent) => void; 'aria-label'?: string; + 'aria-current'?: 'page' | 'step' | 'location' | 'date' | 'time' | boolean; children?: import('svelte').Snippet; } @@ -23,6 +24,7 @@ disabled = false, onclick, 'aria-label': ariaLabel, + 'aria-current': ariaCurrent, children }: Props = $props(); @@ -54,6 +56,7 @@ )} {onclick} aria-label={ariaLabel} + aria-current={ariaCurrent} > {#if children} {@render children()} diff --git a/frontend/src/lib/components/ui/toaster/Toaster.svelte b/frontend/src/lib/components/ui/toaster/Toaster.svelte new file mode 100644 index 00000000..affb8f13 --- /dev/null +++ b/frontend/src/lib/components/ui/toaster/Toaster.svelte @@ -0,0 +1,47 @@ + + +
+ {#each notificationStore.toasts as toast (toast.id)} + {@const Icon = icons[toast.variant]} + + {/each} +
diff --git a/frontend/src/routes/(app)/+page.svelte b/frontend/src/routes/(app)/+page.svelte index 1a5f9a57..2a8f1782 100644 --- a/frontend/src/routes/(app)/+page.svelte +++ b/frontend/src/routes/(app)/+page.svelte @@ -3,9 +3,32 @@ import { ROUTES } from '$lib/utils/constants'; import { Card, CardHeader, CardTitle, CardContent } from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; - import { Activity, Users, User, ArrowRight } from 'lucide-svelte'; + import { Activity, Users, User, ArrowRight, RefreshCw } from 'lucide-svelte'; + import HealthCards from '$lib/components/telemetry/HealthCards.svelte'; + import { getHealth } from '$lib/api/telemetry'; + import type { TelemetryHealthResponse } from '$lib/api/types'; let username = $derived(authStore.user?.username ?? 'User'); + + let health = $state(); + let healthLoading = $state(true); + let healthError = $state(null); + + async function loadHealth() { + healthLoading = true; + healthError = null; + try { + health = await getHealth(); + } catch (err) { + healthError = err instanceof Error ? err.message : 'Failed to load'; + } finally { + healthLoading = false; + } + } + + $effect(() => { + loadHealth(); + });
@@ -14,6 +37,16 @@

Here's an overview of your ACE Framework dashboard.

+
+
+

System Health

+ +
+ +
+
diff --git a/frontend/src/routes/(app)/telemetry/+page.svelte b/frontend/src/routes/(app)/telemetry/+page.svelte index 006ce5a3..2a13563c 100644 --- a/frontend/src/routes/(app)/telemetry/+page.svelte +++ b/frontend/src/routes/(app)/telemetry/+page.svelte @@ -1,6 +1,31 @@
@@ -12,13 +37,84 @@
- - - Telemetry Dashboard - Coming soon - - -

Telemetry visualization will be implemented in a future update.

-
-
+
+

System Health

+ + + +
+ + + + Overview + Spans + Metrics + Usage + + + + + + Telemetry Overview + Real-time system monitoring + + +
+
+ + Database +
+
+ + NATS +
+
+ + Cache +
+
+ + Traces +
+
+
+
+
+ + + + + Distributed Traces + Request spans across services + + +

Spans data will be displayed here.

+
+
+
+ + + + + System Metrics + Performance and usage metrics + + +

Metrics data will be displayed here.

+
+
+
+ + + + + Usage Events + Token usage and cost tracking + + +

Usage data will be displayed here.

+
+
+
+
diff --git a/frontend/src/routes/(errors)/404/+page.svelte b/frontend/src/routes/(errors)/404/+page.svelte new file mode 100644 index 00000000..4f5bd548 --- /dev/null +++ b/frontend/src/routes/(errors)/404/+page.svelte @@ -0,0 +1,31 @@ + + +
+ + + 404 + Page not found + + +

+ The page you're looking for doesn't exist or has been moved. +

+
+ + +
+
+
+
diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 11eb4198..aa2b28bb 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -2,6 +2,7 @@ import '../app.css'; import { uiStore } from '$lib/stores/ui.svelte'; import { onMount } from 'svelte'; + import Toaster from '$lib/components/ui/toaster/Toaster.svelte'; let { children } = $props(); @@ -11,3 +12,4 @@ {@render children()} + diff --git a/frontend/src/test/integration/auth.test.ts b/frontend/src/test/integration/auth.test.ts new file mode 100644 index 00000000..29330f8f --- /dev/null +++ b/frontend/src/test/integration/auth.test.ts @@ -0,0 +1,93 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { authStore } from '$lib/stores/auth.svelte'; + +// Mock the auth API +vi.mock('$lib/api/auth', () => ({ + login: vi.fn().mockResolvedValue({ + access_token: 'test-access-token', + refresh_token: 'test-refresh-token', + expires_in: 3600, + user: { + id: '1', + username: 'testuser', + role: 'user', + status: 'active', + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + } + }), + logout: vi.fn().mockResolvedValue(undefined), + refresh: vi.fn().mockResolvedValue({ + access_token: 'new-access-token', + refresh_token: 'new-refresh-token', + expires_in: 3600, + user: { + id: '1', + username: 'testuser', + role: 'user', + status: 'active', + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + } + }), + me: vi.fn().mockResolvedValue({ + id: '1', + username: 'testuser', + role: 'user', + status: 'active', + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + }) +})); + +// Mock goto +vi.mock('$app/navigation', () => ({ + goto: vi.fn() +})); + +describe('Auth Flow Integration', () => { + beforeEach(() => { + authStore.clear(); + vi.clearAllMocks(); + }); + + it('authStore should initialize with empty state', () => { + expect(authStore.user).toBeNull(); + expect(authStore.accessToken).toBe(''); + expect(authStore.isAuthenticated).toBe(false); + }); + + it('authStore should handle login successfully', async () => { + await authStore.login('testuser', '123456'); + expect(authStore.user).not.toBeNull(); + expect(authStore.user?.username).toBe('testuser'); + expect(authStore.isAuthenticated).toBe(true); + }); + + it('authStore should handle logout', async () => { + await authStore.login('testuser', '123456'); + expect(authStore.isAuthenticated).toBe(true); + + await authStore.logout(); + expect(authStore.user).toBeNull(); + expect(authStore.isAuthenticated).toBe(false); + }); + + it('authStore should track loading state during login', async () => { + expect(authStore.isLoading).toBe(false); + + const loginPromise = authStore.login('testuser', '123456'); + expect(authStore.isLoading).toBe(true); + + await loginPromise; + expect(authStore.isLoading).toBe(false); + }); + + it('authStore should handle login errors', async () => { + const { login } = await import('$lib/api/auth'); + vi.mocked(login).mockRejectedValueOnce(new Error('Invalid credentials')); + + await expect(authStore.login('wronguser', 'wrongpin')).rejects.toThrow('Invalid credentials'); + expect(authStore.user).toBeNull(); + }); +}); From d7a4c743d5e7d446148cad156cd86e3b1299983c Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 14 Apr 2026 21:03:52 +0200 Subject: [PATCH 2/5] [unit: frontend-design] Enable telemetry button in sidebar --- frontend/src/lib/components/layout/Sidebar.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/components/layout/Sidebar.svelte b/frontend/src/lib/components/layout/Sidebar.svelte index 3f894bde..ea41471c 100644 --- a/frontend/src/lib/components/layout/Sidebar.svelte +++ b/frontend/src/lib/components/layout/Sidebar.svelte @@ -37,7 +37,7 @@ { href: '/agents', icon: Bot, label: 'Agents', adminOnly: false, disabled: true }, { href: '/chat', icon: MessageSquare, label: 'Chat', adminOnly: false, disabled: true }, { href: '/memory', icon: HardDrive, label: 'Memory', adminOnly: false, disabled: true }, - { href: '/telemetry', icon: Activity, label: 'Telemetry', adminOnly: false, disabled: true }, + { href: '/telemetry', icon: Activity, label: 'Telemetry', adminOnly: false }, { href: '/admin/users', icon: Shield, label: 'Admin', adminOnly: true } ]); From 662925fc82a949804f4e5dd07bf8d92e087926c0 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 14 Apr 2026 21:09:58 +0200 Subject: [PATCH 3/5] [unit: frontend-design] Fix health endpoint, move to telemetry, add all themes, clean dashboard --- frontend/src/lib/api/telemetry.ts | 3 +- frontend/src/lib/themes/colors.ts | 660 +++++++++++++++++++++++++ frontend/src/routes/(app)/+page.svelte | 59 +-- 3 files changed, 663 insertions(+), 59 deletions(-) diff --git a/frontend/src/lib/api/telemetry.ts b/frontend/src/lib/api/telemetry.ts index 195c58bb..5db5f7a5 100644 --- a/frontend/src/lib/api/telemetry.ts +++ b/frontend/src/lib/api/telemetry.ts @@ -12,7 +12,8 @@ import type { export async function getHealth(): Promise { return apiClient.request({ method: 'GET', - path: '/telemetry/health' + path: '/health/live', + requiresAuth: false }); } diff --git a/frontend/src/lib/themes/colors.ts b/frontend/src/lib/themes/colors.ts index 1be23de1..1d3489b3 100644 --- a/frontend/src/lib/themes/colors.ts +++ b/frontend/src/lib/themes/colors.ts @@ -207,6 +207,666 @@ export const themes: ThemePreset[] = [ input: '60 30% 80%', ring: '150 80% 40%' } + }, + { + name: 'one-light', + label: 'One Light', + darkClass: 'one-light-dark', + lightClass: 'one-light-light', + dark: { + background: '220 15% 15%', + foreground: '220 10% 90%', + card: '220 18% 18%', + cardForeground: '220 10% 90%', + primary: '262 83% 60%', + primaryForeground: '0 0% 98%', + secondary: '220 15% 22%', + secondaryForeground: '220 10% 90%', + muted: '220 15% 22%', + mutedForeground: '220 10% 55%', + accent: '220 18% 26%', + accentForeground: '220 10% 90%', + destructive: '0 72% 51%', + destructiveForeground: '0 0% 98%', + border: '220 15% 28%', + input: '220 15% 28%', + ring: '262 83% 60%' + }, + light: { + background: '220 15% 98%', + foreground: '220 10% 15%', + card: '0 0% 100%', + cardForeground: '220 10% 15%', + primary: '262 83% 55%', + primaryForeground: '0 0% 98%', + secondary: '220 15% 94%', + secondaryForeground: '220 10% 25%', + muted: '220 15% 94%', + mutedForeground: '220 10% 42%', + accent: '220 15% 92%', + accentForeground: '220 10% 25%', + destructive: '0 72% 50%', + destructiveForeground: '0 0% 98%', + border: '220 15% 88%', + input: '220 15% 88%', + ring: '262 83% 55%' + } + }, + { + name: 'catppuccin-latte', + label: 'Catppuccin Latte', + darkClass: 'catppuccin-latte-dark', + lightClass: 'catppuccin-latte-light', + dark: { + background: '25 15% 90%', + foreground: '25 10% 15%', + card: '0 0% 100%', + cardForeground: '25 10% 15%', + primary: '345 75% 55%', + primaryForeground: '25 10% 96%', + secondary: '25 15% 85%', + secondaryForeground: '25 10% 25%', + muted: '25 15% 85%', + mutedForeground: '25 10% 42%', + accent: '25 15% 82%', + accentForeground: '25 10% 25%', + destructive: '15 75% 55%', + destructiveForeground: '25 10% 96%', + border: '25 15% 78%', + input: '25 15% 78%', + ring: '345 75% 55%' + }, + light: { + background: '25 15% 95%', + foreground: '25 10% 20%', + card: '0 0% 100%', + cardForeground: '25 10% 20%', + primary: '345 75% 52%', + primaryForeground: '25 10% 96%', + secondary: '25 15% 90%', + secondaryForeground: '25 10% 30%', + muted: '25 15% 90%', + mutedForeground: '25 10% 48%', + accent: '25 15% 88%', + accentForeground: '25 10% 30%', + destructive: '15 75% 50%', + destructiveForeground: '25 10% 96%', + border: '25 15% 82%', + input: '25 15% 82%', + ring: '345 75% 52%' + } + }, + { + name: 'gruvbox-dark', + label: 'Gruvbox Dark', + darkClass: 'gruvbox-dark-dark', + lightClass: 'gruvbox-dark-light', + dark: { + background: '30 25% 8%', + foreground: '60 50% 90%', + card: '30 20% 12%', + cardForeground: '60 50% 90%', + primary: '30 65% 55%', + primaryForeground: '30 25% 8%', + secondary: '30 15% 18%', + secondaryForeground: '60 50% 90%', + muted: '30 15% 18%', + mutedForeground: '60 30% 55%', + accent: '150 40% 50%', + accentForeground: '30 25% 8%', + destructive: '0 65% 50%', + destructiveForeground: '60 50% 90%', + border: '30 15% 22%', + input: '30 15% 22%', + ring: '30 65% 55%' + }, + light: { + background: '60 25% 95%', + foreground: '30 25% 15%', + card: '30 20% 92%', + cardForeground: '30 25% 15%', + primary: '30 60% 50%', + primaryForeground: '30 25% 92%', + secondary: '60 15% 88%', + secondaryForeground: '30 25% 25%', + muted: '60 15% 88%', + mutedForeground: '30 25% 40%', + accent: '150 35% 45%', + accentForeground: '30 25% 92%', + destructive: '0 60% 45%', + destructiveForeground: '60 25% 95%', + border: '30 15% 82%', + input: '30 15% 82%', + ring: '30 60% 50%' + } + }, + { + name: 'gruvbox-light', + label: 'Gruvbox Light', + darkClass: 'gruvbox-light-dark', + lightClass: 'gruvbox-light-light', + dark: { + background: '30 25% 92%', + foreground: '30 25% 15%', + card: '30 20% 96%', + cardForeground: '30 25% 15%', + primary: '30 60% 50%', + primaryForeground: '30 25% 92%', + secondary: '60 15% 88%', + secondaryForeground: '30 25% 25%', + muted: '60 15% 88%', + mutedForeground: '30 25% 40%', + accent: '150 35% 45%', + accentForeground: '30 25% 92%', + destructive: '0 60% 45%', + destructiveForeground: '30 25% 92%', + border: '30 15% 82%', + input: '30 15% 82%', + ring: '30 60% 50%' + }, + light: { + background: '60 25% 98%', + foreground: '30 25% 12%', + card: '30 20% 100%', + cardForeground: '30 25% 12%', + primary: '30 55% 48%', + primaryForeground: '30 25% 98%', + secondary: '60 15% 92%', + secondaryForeground: '30 25% 22%', + muted: '60 15% 92%', + mutedForeground: '30 25% 38%', + accent: '150 30% 42%', + accentForeground: '30 25% 98%', + destructive: '0 55% 42%', + destructiveForeground: '30 25% 98%', + border: '30 15% 85%', + input: '30 15% 85%', + ring: '30 55% 48%' + } + }, + { + name: 'tokyo-night', + label: 'Tokyo Night', + darkClass: 'tokyo-night-dark', + lightClass: 'tokyo-night-light', + dark: { + background: '230 35% 10%', + foreground: '230 20% 90%', + card: '230 30% 14%', + cardForeground: '230 20% 90%', + primary: '272 85% 65%', + primaryForeground: '0 0% 98%', + secondary: '230 25% 20%', + secondaryForeground: '230 20% 90%', + muted: '230 25% 20%', + mutedForeground: '230 15% 55%', + accent: '230 30% 24%', + accentForeground: '230 20% 90%', + destructive: '0 70% 55%', + destructiveForeground: '0 0% 98%', + border: '230 25% 26%', + input: '230 25% 26%', + ring: '272 85% 65%' + }, + light: { + background: '230 20% 96%', + foreground: '230 15% 22%', + card: '0 0% 100%', + cardForeground: '230 15% 22%', + primary: '272 80% 58%', + primaryForeground: '0 0% 98%', + secondary: '230 18% 90%', + secondaryForeground: '230 15% 30%', + muted: '230 18% 90%', + mutedForeground: '230 15% 45%', + accent: '230 18% 88%', + accentForeground: '230 15% 30%', + destructive: '0 68% 52%', + destructiveForeground: '0 0% 98%', + border: '230 18% 84%', + input: '230 18% 84%', + ring: '272 80% 58%' + } + }, + { + name: 'dracula', + label: 'Dracula', + darkClass: 'dracula-dark', + lightClass: 'dracula-light', + dark: { + background: '240 30% 10%', + foreground: '240 15% 90%', + card: '240 28% 14%', + cardForeground: '240 15% 90%', + primary: '280 85% 65%', + primaryForeground: '0 0% 98%', + secondary: '240 25% 18%', + secondaryForeground: '240 15% 90%', + muted: '240 25% 18%', + mutedForeground: '240 12% 55%', + accent: '240 28% 22%', + accentForeground: '240 15% 90%', + destructive: '0 75% 55%', + destructiveForeground: '0 0% 98%', + border: '240 25% 24%', + input: '240 25% 24%', + ring: '280 85% 65%' + }, + light: { + background: '240 20% 96%', + foreground: '240 15% 18%', + card: '0 0% 100%', + cardForeground: '240 15% 18%', + primary: '280 80% 60%', + primaryForeground: '0 0% 98%', + secondary: '240 18% 90%', + secondaryForeground: '240 15% 28%', + muted: '240 18% 90%', + mutedForeground: '240 15% 42%', + accent: '240 18% 88%', + accentForeground: '240 15% 28%', + destructive: '0 72% 52%', + destructiveForeground: '0 0% 98%', + border: '240 18% 84%', + input: '240 18% 84%', + ring: '280 80% 60%' + } + }, + { + name: 'ayu-dark', + label: 'Ayu Dark', + darkClass: 'ayu-dark-dark', + lightClass: 'ayu-dark-light', + dark: { + background: '220 25% 12%', + foreground: '40 100% 90%', + card: '220 22% 16%', + cardForeground: '40 100% 90%', + primary: '35 90% 55%', + primaryForeground: '220 25% 12%', + secondary: '220 18% 20%', + secondaryForeground: '40 100% 90%', + muted: '220 18% 20%', + mutedForeground: '40 70% 55%', + accent: '200 80% 60%', + accentForeground: '220 25% 12%', + destructive: '0 75% 55%', + destructiveForeground: '40 100% 90%', + border: '220 18% 26%', + input: '220 18% 26%', + ring: '35 90% 55%' + }, + light: { + background: '40 15% 96%', + foreground: '220 15% 20%', + card: '0 0% 100%', + cardForeground: '220 15% 20%', + primary: '35 85% 50%', + primaryForeground: '40 15% 96%', + secondary: '220 15% 90%', + secondaryForeground: '220 15% 28%', + muted: '220 15% 90%', + mutedForeground: '220 15% 42%', + accent: '200 75% 55%', + accentForeground: '40 15% 96%', + destructive: '0 72% 50%', + destructiveForeground: '40 15% 96%', + border: '220 15% 84%', + input: '220 15% 84%', + ring: '35 85% 50%' + } + }, + { + name: 'ayu-light', + label: 'Ayu Light', + darkClass: 'ayu-light-dark', + lightClass: 'ayu-light-light', + dark: { + background: '220 15% 90%', + foreground: '220 10% 20%', + card: '0 0% 100%', + cardForeground: '220 10% 20%', + primary: '35 85% 50%', + primaryForeground: '220 15% 90%', + secondary: '220 15% 86%', + secondaryForeground: '220 10% 28%', + muted: '220 15% 86%', + mutedForeground: '220 10% 42%', + accent: '200 75% 55%', + accentForeground: '220 15% 90%', + destructive: '0 72% 50%', + destructiveForeground: '220 15% 90%', + border: '220 15% 78%', + input: '220 15% 78%', + ring: '35 85% 50%' + }, + light: { + background: '40 15% 98%', + foreground: '220 10% 15%', + card: '0 0% 100%', + cardForeground: '220 10% 15%', + primary: '35 80% 48%', + primaryForeground: '40 15% 98%', + secondary: '220 12% 92%', + secondaryForeground: '220 10% 25%', + muted: '220 12% 92%', + mutedForeground: '220 10% 38%', + accent: '200 70% 52%', + accentForeground: '40 15% 98%', + destructive: '0 68% 48%', + destructiveForeground: '40 15% 98%', + border: '220 12% 85%', + input: '220 12% 85%', + ring: '35 80% 48%' + } + }, + { + name: 'everforest', + label: 'Everforest', + darkClass: 'everforest-dark', + lightClass: 'everforest-light', + dark: { + background: '120 15% 12%', + foreground: '120 10% 88%', + card: '120 14% 16%', + cardForeground: '120 10% 88%', + primary: '75 55% 55%', + primaryForeground: '120 15% 12%', + secondary: '120 12% 20%', + secondaryForeground: '120 10% 88%', + muted: '120 12% 20%', + mutedForeground: '120 10% 55%', + accent: '150 40% 50%', + accentForeground: '120 15% 12%', + destructive: '15 65% 55%', + destructiveForeground: '120 10% 88%', + border: '120 12% 24%', + input: '120 12% 24%', + ring: '75 55% 55%' + }, + light: { + background: '120 18% 94%', + foreground: '120 10% 22%', + card: '0 0% 100%', + cardForeground: '120 10% 22%', + primary: '75 50% 50%', + primaryForeground: '120 18% 94%', + secondary: '120 15% 88%', + secondaryForeground: '120 10% 30%', + muted: '120 15% 88%', + mutedForeground: '120 10% 45%', + accent: '150 35% 48%', + accentForeground: '120 18% 94%', + destructive: '15 60% 52%', + destructiveForeground: '120 18% 94%', + border: '120 15% 82%', + input: '120 15% 82%', + ring: '75 50% 50%' + } + }, + { + name: 'kanagawa', + label: 'Kanagawa', + darkClass: 'kanagawa-dark', + lightClass: 'kanagawa-light', + dark: { + background: '200 25% 12%', + foreground: '200 15% 88%', + card: '200 22% 16%', + cardForeground: '200 15% 88%', + primary: '200 65% 60%', + primaryForeground: '200 25% 12%', + secondary: '200 18% 20%', + secondaryForeground: '200 15% 88%', + muted: '200 18% 20%', + mutedForeground: '200 10% 55%', + accent: '180 45% 55%', + accentForeground: '200 25% 12%', + destructive: '0 65% 55%', + destructiveForeground: '200 15% 88%', + border: '200 18% 24%', + input: '200 18% 24%', + ring: '200 65% 60%' + }, + light: { + background: '200 15% 94%', + foreground: '200 10% 22%', + card: '0 0% 100%', + cardForeground: '200 10% 22%', + primary: '200 60% 55%', + primaryForeground: '200 15% 94%', + secondary: '200 15% 88%', + secondaryForeground: '200 10% 30%', + muted: '200 15% 88%', + mutedForeground: '200 10% 45%', + accent: '180 40% 52%', + accentForeground: '200 15% 94%', + destructive: '0 62% 52%', + destructiveForeground: '200 15% 94%', + border: '200 15% 82%', + input: '200 15% 82%', + ring: '200 60% 55%' + } + }, + { + name: 'rose-pine', + label: 'Rosé Pine', + darkClass: 'rose-pine-dark', + lightClass: 'rose-pine-light', + dark: { + background: '330 25% 10%', + foreground: '330 15% 90%', + card: '330 22% 14%', + cardForeground: '330 15% 90%', + primary: '330 60% 65%', + primaryForeground: '0 0% 98%', + secondary: '330 18% 18%', + secondaryForeground: '330 15% 90%', + muted: '330 18% 18%', + mutedForeground: '330 10% 55%', + accent: '330 25% 22%', + accentForeground: '330 15% 90%', + destructive: '0 65% 55%', + destructiveForeground: '0 0% 98%', + border: '330 18% 24%', + input: '330 18% 24%', + ring: '330 60% 65%' + }, + light: { + background: '330 15% 96%', + foreground: '330 10% 22%', + card: '0 0% 100%', + cardForeground: '330 10% 22%', + primary: '330 55% 58%', + primaryForeground: '0 0% 98%', + secondary: '330 15% 90%', + secondaryForeground: '330 10% 30%', + muted: '330 15% 90%', + mutedForeground: '330 10% 45%', + accent: '330 15% 88%', + accentForeground: '330 10% 30%', + destructive: '0 62% 52%', + destructiveForeground: '0 0% 98%', + border: '330 15% 84%', + input: '330 15% 84%', + ring: '330 55% 58%' + } + }, + { + name: 'solarized-dark', + label: 'Solarized Dark', + darkClass: 'solarized-dark-dark', + lightClass: 'solarized-dark-light', + dark: { + background: '192 25% 10%', + foreground: '192 10% 90%', + card: '192 22% 14%', + cardForeground: '192 10% 90%', + primary: '192 55% 55%', + primaryForeground: '192 25% 10%', + secondary: '192 18% 18%', + secondaryForeground: '192 10% 90%', + muted: '192 18% 18%', + mutedForeground: '192 10% 55%', + accent: '147 50% 55%', + accentForeground: '192 25% 10%', + destructive: '0 65% 55%', + destructiveForeground: '192 10% 90%', + border: '192 18% 24%', + input: '192 18% 24%', + ring: '192 55% 55%' + }, + light: { + background: '192 15% 96%', + foreground: '192 10% 18%', + card: '0 0% 100%', + cardForeground: '192 10% 18%', + primary: '192 50% 50%', + primaryForeground: '192 15% 96%', + secondary: '192 15% 92%', + secondaryForeground: '192 10% 28%', + muted: '192 15% 92%', + mutedForeground: '192 10% 42%', + accent: '147 45% 52%', + accentForeground: '192 15% 96%', + destructive: '0 62% 50%', + destructiveForeground: '192 15% 96%', + border: '192 15% 86%', + input: '192 15% 86%', + ring: '192 50% 50%' + } + }, + { + name: 'solarized-light', + label: 'Solarized Light', + darkClass: 'solarized-light-dark', + lightClass: 'solarized-light-light', + dark: { + background: '192 15% 96%', + foreground: '192 10% 18%', + card: '0 0% 100%', + cardForeground: '192 10% 18%', + primary: '192 50% 50%', + primaryForeground: '192 15% 96%', + secondary: '192 15% 92%', + secondaryForeground: '192 10% 28%', + muted: '192 15% 92%', + mutedForeground: '192 10% 42%', + accent: '147 45% 52%', + accentForeground: '192 15% 96%', + destructive: '0 62% 50%', + destructiveForeground: '192 15% 96%', + border: '192 15% 86%', + input: '192 15% 86%', + ring: '192 50% 50%' + }, + light: { + background: '192 20% 98%', + foreground: '192 10% 15%', + card: '0 0% 100%', + cardForeground: '192 10% 15%', + primary: '192 45% 48%', + primaryForeground: '192 20% 98%', + secondary: '192 12% 94%', + secondaryForeground: '192 10% 25%', + muted: '192 12% 94%', + mutedForeground: '192 10% 38%', + accent: '147 40% 50%', + accentForeground: '192 20% 98%', + destructive: '0 58% 48%', + destructiveForeground: '192 20% 98%', + border: '192 12% 88%', + input: '192 12% 88%', + ring: '192 45% 48%' + } + }, + { + name: 'night-owl', + label: 'Night Owl', + darkClass: 'night-owl-dark', + lightClass: 'night-owl-light', + dark: { + background: '210 30% 10%', + foreground: '210 15% 90%', + card: '210 28% 14%', + cardForeground: '210 15% 90%', + primary: '210 80% 65%', + primaryForeground: '0 0% 98%', + secondary: '210 22% 18%', + secondaryForeground: '210 15% 90%', + muted: '210 22% 18%', + mutedForeground: '210 10% 55%', + accent: '210 30% 22%', + accentForeground: '210 15% 90%', + destructive: '0 75% 55%', + destructiveForeground: '0 0% 98%', + border: '210 22% 24%', + input: '210 22% 24%', + ring: '210 80% 65%' + }, + light: { + background: '210 18% 96%', + foreground: '210 12% 20%', + card: '0 0% 100%', + cardForeground: '210 12% 20%', + primary: '210 75% 58%', + primaryForeground: '0 0% 98%', + secondary: '210 15% 90%', + secondaryForeground: '210 12% 28%', + muted: '210 15% 90%', + mutedForeground: '210 12% 42%', + accent: '210 15% 88%', + accentForeground: '210 12% 28%', + destructive: '0 72% 52%', + destructiveForeground: '0 0% 98%', + border: '210 15% 84%', + input: '210 15% 84%', + ring: '210 75% 58%' + } + }, + { + name: 'palenight', + label: 'Palenight', + darkClass: 'palenight-dark', + lightClass: 'palenight-light', + dark: { + background: '225 25% 15%', + foreground: '225 15% 88%', + card: '225 22% 18%', + cardForeground: '225 15% 88%', + primary: '265 65% 65%', + primaryForeground: '0 0% 98%', + secondary: '225 18% 22%', + secondaryForeground: '225 15% 88%', + muted: '225 18% 22%', + mutedForeground: '225 10% 55%', + accent: '225 25% 26%', + accentForeground: '225 15% 88%', + destructive: '0 70% 55%', + destructiveForeground: '0 0% 98%', + border: '225 18% 28%', + input: '225 18% 28%', + ring: '265 65% 65%' + }, + light: { + background: '225 15% 96%', + foreground: '225 10% 22%', + card: '0 0% 100%', + cardForeground: '225 10% 22%', + primary: '265 60% 58%', + primaryForeground: '0 0% 98%', + secondary: '225 15% 90%', + secondaryForeground: '225 10% 30%', + muted: '225 15% 90%', + mutedForeground: '225 10% 45%', + accent: '225 15% 88%', + accentForeground: '225 10% 30%', + destructive: '0 68% 52%', + destructiveForeground: '0 0% 98%', + border: '225 15% 84%', + input: '225 15% 84%', + ring: '265 60% 58%' + } } ]; diff --git a/frontend/src/routes/(app)/+page.svelte b/frontend/src/routes/(app)/+page.svelte index 2a8f1782..6f10197c 100644 --- a/frontend/src/routes/(app)/+page.svelte +++ b/frontend/src/routes/(app)/+page.svelte @@ -3,32 +3,9 @@ import { ROUTES } from '$lib/utils/constants'; import { Card, CardHeader, CardTitle, CardContent } from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; - import { Activity, Users, User, ArrowRight, RefreshCw } from 'lucide-svelte'; - import HealthCards from '$lib/components/telemetry/HealthCards.svelte'; - import { getHealth } from '$lib/api/telemetry'; - import type { TelemetryHealthResponse } from '$lib/api/types'; + import { Activity, Users, User, ArrowRight } from 'lucide-svelte'; let username = $derived(authStore.user?.username ?? 'User'); - - let health = $state(); - let healthLoading = $state(true); - let healthError = $state(null); - - async function loadHealth() { - healthLoading = true; - healthError = null; - try { - health = await getHealth(); - } catch (err) { - healthError = err instanceof Error ? err.message : 'Failed to load'; - } finally { - healthLoading = false; - } - } - - $effect(() => { - loadHealth(); - });
@@ -37,16 +14,6 @@

Here's an overview of your ACE Framework dashboard.

-
-
-

System Health

- -
- -
-
@@ -92,28 +59,4 @@ {/if}
- -
- - - Quick Actions - - - - - {#if authStore.user?.role === 'admin'} - - {/if} - - -
From 74f51c9aa352f8f3d148ed8dfcc115e2d41b7807 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 15 Apr 2026 17:31:21 +0200 Subject: [PATCH 4/5] [unit: frontend-design] Fix theme colors and telemetry error handling --- frontend/src/lib/api/telemetry.ts | 23 +++- frontend/src/lib/api/types.ts | 4 +- .../components/telemetry/HealthCards.svelte | 10 +- frontend/src/lib/themes/colors.ts | 122 +++++++++--------- 4 files changed, 89 insertions(+), 70 deletions(-) diff --git a/frontend/src/lib/api/telemetry.ts b/frontend/src/lib/api/telemetry.ts index 5db5f7a5..b6306525 100644 --- a/frontend/src/lib/api/telemetry.ts +++ b/frontend/src/lib/api/telemetry.ts @@ -10,11 +10,24 @@ import type { } from './types'; export async function getHealth(): Promise { - return apiClient.request({ - method: 'GET', - path: '/health/live', - requiresAuth: false - }); + try { + // Health endpoint returns simple {"status":"ok"} without envelope + const response = await fetch('/api/health/live'); + if (!response.ok) { + const error = await response.text(); + console.error('Health check failed:', response.status, error); + throw new Error(`Health check failed: ${response.status}`); + } + const data = await response.json(); + // Return in expected format even if backend sends simple format + return { + status: data.status ?? 'ok', + checks: data.checks + }; + } catch (err) { + console.error('Health check error:', err); + throw err; + } } export async function getSpans(params?: SpanQueryParams): Promise { diff --git a/frontend/src/lib/api/types.ts b/frontend/src/lib/api/types.ts index 477f58c9..fda444c8 100644 --- a/frontend/src/lib/api/types.ts +++ b/frontend/src/lib/api/types.ts @@ -164,7 +164,7 @@ export interface UsageResponse { } // --- Telemetry: Health --- -export type HealthStatus = 'healthy' | 'degraded' | 'error'; +export type HealthStatus = 'healthy' | 'degraded' | 'error' | 'ok'; export interface SubsystemCheck { status: string; @@ -182,7 +182,7 @@ export interface SubsystemCheck { export interface TelemetryHealthResponse { status: HealthStatus; - checks: Record; + checks?: Record; } // --- Health --- diff --git a/frontend/src/lib/components/telemetry/HealthCards.svelte b/frontend/src/lib/components/telemetry/HealthCards.svelte index 23494a51..bd2335f6 100644 --- a/frontend/src/lib/components/telemetry/HealthCards.svelte +++ b/frontend/src/lib/components/telemetry/HealthCards.svelte @@ -26,7 +26,11 @@ } function getSubsystemStatus(subsystem: string): { status: HealthStatus; icon: typeof Database; label: string } { - const check = health?.checks[subsystem]; + // Handle simple {"status":"ok"} response without checks + if (!health?.checks) { + return { status: health?.status as HealthStatus ?? 'unknown', icon: Database, label: subsystem }; + } + const check = health.checks[subsystem]; const status = (check?.status ?? 'unknown') as HealthStatus; switch (subsystem) { @@ -82,7 +86,9 @@
{status}

- {#if health.checks[subsystem]} + {#if !health?.checks} + Backend responding + {:else if health.checks[subsystem]} {#if health.checks[subsystem].spans_last_hour !== undefined} {health.checks[subsystem].spans_last_hour} spans/hr {:else if health.checks[subsystem].metrics_last_hour !== undefined} diff --git a/frontend/src/lib/themes/colors.ts b/frontend/src/lib/themes/colors.ts index 1d3489b3..636529d2 100644 --- a/frontend/src/lib/themes/colors.ts +++ b/frontend/src/lib/themes/colors.ts @@ -258,22 +258,22 @@ export const themes: ThemePreset[] = [ darkClass: 'catppuccin-latte-dark', lightClass: 'catppuccin-latte-light', dark: { - background: '25 15% 90%', - foreground: '25 10% 15%', - card: '0 0% 100%', - cardForeground: '25 10% 15%', + background: '25 15% 15%', + foreground: '25 10% 90%', + card: '25 15% 18%', + cardForeground: '25 10% 90%', primary: '345 75% 55%', primaryForeground: '25 10% 96%', - secondary: '25 15% 85%', - secondaryForeground: '25 10% 25%', - muted: '25 15% 85%', - mutedForeground: '25 10% 42%', - accent: '25 15% 82%', - accentForeground: '25 10% 25%', + secondary: '25 15% 22%', + secondaryForeground: '25 10% 90%', + muted: '25 15% 22%', + mutedForeground: '25 10% 60%', + accent: '25 15% 26%', + accentForeground: '25 10% 90%', destructive: '15 75% 55%', destructiveForeground: '25 10% 96%', - border: '25 15% 78%', - input: '25 15% 78%', + border: '25 15% 28%', + input: '25 15% 28%', ring: '345 75% 55%' }, light: { @@ -346,22 +346,22 @@ export const themes: ThemePreset[] = [ darkClass: 'gruvbox-light-dark', lightClass: 'gruvbox-light-light', dark: { - background: '30 25% 92%', - foreground: '30 25% 15%', - card: '30 20% 96%', - cardForeground: '30 25% 15%', + background: '30 25% 15%', + foreground: '60 50% 90%', + card: '30 20% 18%', + cardForeground: '60 50% 90%', primary: '30 60% 50%', - primaryForeground: '30 25% 92%', - secondary: '60 15% 88%', - secondaryForeground: '30 25% 25%', - muted: '60 15% 88%', - mutedForeground: '30 25% 40%', - accent: '150 35% 45%', - accentForeground: '30 25% 92%', - destructive: '0 60% 45%', - destructiveForeground: '30 25% 92%', - border: '30 15% 82%', - input: '30 15% 82%', + primaryForeground: '30 25% 15%', + secondary: '30 15% 22%', + secondaryForeground: '60 50% 90%', + muted: '30 15% 22%', + mutedForeground: '60 30% 55%', + accent: '150 40% 50%', + accentForeground: '30 25% 15%', + destructive: '0 65% 50%', + destructiveForeground: '60 50% 90%', + border: '30 15% 26%', + input: '30 15% 26%', ring: '30 60% 50%' }, light: { @@ -522,23 +522,23 @@ export const themes: ThemePreset[] = [ darkClass: 'ayu-light-dark', lightClass: 'ayu-light-light', dark: { - background: '220 15% 90%', - foreground: '220 10% 20%', - card: '0 0% 100%', - cardForeground: '220 10% 20%', - primary: '35 85% 50%', - primaryForeground: '220 15% 90%', - secondary: '220 15% 86%', - secondaryForeground: '220 10% 28%', - muted: '220 15% 86%', - mutedForeground: '220 10% 42%', - accent: '200 75% 55%', - accentForeground: '220 15% 90%', - destructive: '0 72% 50%', - destructiveForeground: '220 15% 90%', - border: '220 15% 78%', - input: '220 15% 78%', - ring: '35 85% 50%' + background: '220 15% 15%', + foreground: '40 100% 90%', + card: '220 18% 18%', + cardForeground: '40 100% 90%', + primary: '35 90% 55%', + primaryForeground: '220 15% 15%', + secondary: '220 15% 22%', + secondaryForeground: '40 100% 90%', + muted: '220 15% 22%', + mutedForeground: '40 70% 55%', + accent: '200 80% 60%', + accentForeground: '220 15% 15%', + destructive: '0 75% 55%', + destructiveForeground: '40 100% 90%', + border: '220 15% 26%', + input: '220 15% 26%', + ring: '35 90% 55%' }, light: { background: '40 15% 98%', @@ -742,23 +742,23 @@ export const themes: ThemePreset[] = [ darkClass: 'solarized-light-dark', lightClass: 'solarized-light-light', dark: { - background: '192 15% 96%', - foreground: '192 10% 18%', - card: '0 0% 100%', - cardForeground: '192 10% 18%', - primary: '192 50% 50%', - primaryForeground: '192 15% 96%', - secondary: '192 15% 92%', - secondaryForeground: '192 10% 28%', - muted: '192 15% 92%', - mutedForeground: '192 10% 42%', - accent: '147 45% 52%', - accentForeground: '192 15% 96%', - destructive: '0 62% 50%', - destructiveForeground: '192 15% 96%', - border: '192 15% 86%', - input: '192 15% 86%', - ring: '192 50% 50%' + background: '192 25% 12%', + foreground: '192 10% 90%', + card: '192 22% 16%', + cardForeground: '192 10% 90%', + primary: '192 55% 55%', + primaryForeground: '192 25% 12%', + secondary: '192 18% 18%', + secondaryForeground: '192 10% 90%', + muted: '192 18% 18%', + mutedForeground: '192 10% 55%', + accent: '147 50% 55%', + accentForeground: '192 25% 12%', + destructive: '0 65% 55%', + destructiveForeground: '192 10% 90%', + border: '192 18% 24%', + input: '192 18% 24%', + ring: '192 55% 55%' }, light: { background: '192 20% 98%', From d421d23c78933210294cfbaedb2fbc1612eed03c Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 15 Apr 2026 17:46:08 +0200 Subject: [PATCH 5/5] [unit: frontend-design] Add 26 more themes from Opencode (45 total) --- frontend/src/lib/themes/colors.ts | 1144 +++++++++++++++++++++++++++++ 1 file changed, 1144 insertions(+) diff --git a/frontend/src/lib/themes/colors.ts b/frontend/src/lib/themes/colors.ts index 636529d2..9b7f380c 100644 --- a/frontend/src/lib/themes/colors.ts +++ b/frontend/src/lib/themes/colors.ts @@ -867,6 +867,1150 @@ export const themes: ThemePreset[] = [ input: '225 15% 84%', ring: '265 60% 58%' } + }, + { + name: 'github', + label: 'GitHub', + darkClass: 'github-dark', + lightClass: 'github-light', + dark: { + background: '210 30% 8%', + foreground: '210 10% 85%', + card: '210 28% 12%', + cardForeground: '210 10% 85%', + primary: '210 80% 60%', + primaryForeground: '210 30% 8%', + secondary: '210 22% 16%', + secondaryForeground: '210 10% 85%', + muted: '210 22% 16%', + mutedForeground: '210 10% 55%', + accent: '210 28% 20%', + accentForeground: '210 10% 85%', + destructive: '0 65% 58%', + destructiveForeground: '0 0% 98%', + border: '210 22% 22%', + input: '210 22% 22%', + ring: '210 80% 60%' + }, + light: { + background: '0 0% 100%', + foreground: '210 15% 18%', + card: '0 0% 100%', + cardForeground: '210 15% 18%', + primary: '210 85% 42%', + primaryForeground: '0 0% 100%', + secondary: '210 15% 96%', + secondaryForeground: '210 15% 28%', + muted: '210 15% 96%', + mutedForeground: '210 10% 45%', + accent: '210 15% 94%', + accentForeground: '210 15% 28%', + destructive: '0 75% 48%', + destructiveForeground: '0 0% 100%', + border: '210 15% 88%', + input: '210 15% 88%', + ring: '210 85% 42%' + } + }, + { + name: 'catppuccin', + label: 'Catppuccin', + darkClass: 'catppuccin-dark', + lightClass: 'catppuccin-light', + dark: { + background: '234 25% 10%', + foreground: '234 15% 88%', + card: '234 22% 14%', + cardForeground: '234 15% 88%', + primary: '270 60% 68%', + primaryForeground: '234 25% 10%', + secondary: '234 18% 16%', + secondaryForeground: '234 15% 88%', + muted: '234 18% 16%', + mutedForeground: '234 10% 55%', + accent: '234 25% 20%', + accentForeground: '234 15% 88%', + destructive: '345 60% 65%', + destructiveForeground: '234 25% 10%', + border: '234 18% 22%', + input: '234 18% 22%', + ring: '270 60% 68%' + }, + light: { + background: '340 20% 96%', + foreground: '340 10% 22%', + card: '0 0% 100%', + cardForeground: '340 10% 22%', + primary: '235 85% 65%', + primaryForeground: '340 20% 96%', + secondary: '340 15% 90%', + secondaryForeground: '340 10% 30%', + muted: '340 15% 90%', + mutedForeground: '340 10% 48%', + accent: '340 15% 88%', + accentForeground: '340 10% 30%', + destructive: '345 80% 42%', + destructiveForeground: '340 20% 96%', + border: '340 15% 84%', + input: '340 15% 84%', + ring: '235 85% 65%' + } + }, + { + name: 'catppuccin-frappe', + label: 'Catppuccin Frappe', + darkClass: 'catppuccin-frappe-dark', + lightClass: 'catppuccin-frappe-light', + dark: { + background: '230 20% 18%', + foreground: '230 15% 88%', + card: '230 18% 22%', + cardForeground: '230 15% 88%', + primary: '230 55% 70%', + primaryForeground: '230 20% 18%', + secondary: '230 15% 25%', + secondaryForeground: '230 15% 88%', + muted: '230 15% 25%', + mutedForeground: '230 10% 60%', + accent: '330 45% 75%', + accentForeground: '230 20% 18%', + destructive: '0 55% 65%', + destructiveForeground: '230 20% 18%', + border: '230 15% 30%', + input: '230 15% 30%', + ring: '230 55% 70%' + }, + light: { + background: '230 15% 96%', + foreground: '230 10% 22%', + card: '0 0% 100%', + cardForeground: '230 10% 22%', + primary: '230 55% 65%', + primaryForeground: '230 15% 96%', + secondary: '230 15% 90%', + secondaryForeground: '230 10% 30%', + muted: '230 15% 90%', + mutedForeground: '230 10% 48%', + accent: '330 40% 72%', + accentForeground: '230 15% 96%', + destructive: '0 55% 60%', + destructiveForeground: '230 15% 96%', + border: '230 15% 84%', + input: '230 15% 84%', + ring: '230 55% 65%' + } + }, + { + name: 'catppuccin-macchiato', + label: 'Catppuccin Macchiato', + darkClass: 'catppuccin-macchiato-dark', + lightClass: 'catppuccin-macchiato-light', + dark: { + background: '230 20% 16%', + foreground: '230 15% 88%', + card: '230 18% 20%', + cardForeground: '230 15% 88%', + primary: '230 55% 72%', + primaryForeground: '230 20% 16%', + secondary: '230 15% 24%', + secondaryForeground: '230 15% 88%', + muted: '230 15% 24%', + mutedForeground: '230 10% 58%', + accent: '330 45% 78%', + accentForeground: '230 20% 16%', + destructive: '0 55% 68%', + destructiveForeground: '230 20% 16%', + border: '230 15% 28%', + input: '230 15% 28%', + ring: '230 55% 72%' + }, + light: { + background: '230 15% 96%', + foreground: '230 10% 22%', + card: '0 0% 100%', + cardForeground: '230 10% 22%', + primary: '230 55% 68%', + primaryForeground: '230 15% 96%', + secondary: '230 15% 90%', + secondaryForeground: '230 10% 30%', + muted: '230 15% 90%', + mutedForeground: '230 10% 48%', + accent: '330 40% 75%', + accentForeground: '230 15% 96%', + destructive: '0 55% 62%', + destructiveForeground: '230 15% 96%', + border: '230 15% 84%', + input: '230 15% 84%', + ring: '230 55% 68%' + } + }, + { + name: 'cobalt2', + label: 'Cobalt2', + darkClass: 'cobalt2-dark', + lightClass: 'cobalt2-light', + dark: { + background: '210 35% 12%', + foreground: '0 0% 95%', + card: '210 32% 16%', + cardForeground: '0 0% 95%', + primary: '210 100% 55%', + primaryForeground: '0 0% 100%', + secondary: '210 28% 20%', + secondaryForeground: '0 0% 95%', + muted: '210 28% 20%', + mutedForeground: '210 15% 65%', + accent: '170 100% 55%', + accentForeground: '210 35% 12%', + destructive: '330 100% 55%', + destructiveForeground: '0 0% 100%', + border: '210 28% 26%', + input: '210 28% 26%', + ring: '210 100% 55%' + }, + light: { + background: '0 0% 100%', + foreground: '210 25% 15%', + card: '0 0% 100%', + cardForeground: '210 25% 15%', + primary: '210 100% 40%', + primaryForeground: '0 0% 100%', + secondary: '210 20% 96%', + secondaryForeground: '210 25% 25%', + muted: '210 20% 96%', + mutedForeground: '210 15% 40%', + accent: '185 100% 40%', + accentForeground: '0 0% 100%', + destructive: '340 80% 50%', + destructiveForeground: '0 0% 100%', + border: '210 15% 88%', + input: '210 15% 88%', + ring: '210 100% 40%' + } + }, + { + name: 'material', + label: 'Material', + darkClass: 'material-dark', + lightClass: 'material-light', + dark: { + background: '200 20% 12%', + foreground: '200 30% 95%', + card: '200 18% 16%', + cardForeground: '200 30% 95%', + primary: '220 65% 65%', + primaryForeground: '200 20% 12%', + secondary: '200 15% 20%', + secondaryForeground: '200 30% 95%', + muted: '200 15% 20%', + mutedForeground: '200 15% 60%', + accent: '190 60% 55%', + accentForeground: '200 20% 12%', + destructive: '0 65% 58%', + destructiveForeground: '200 30% 95%', + border: '200 15% 26%', + input: '200 15% 26%', + ring: '220 65% 65%' + }, + light: { + background: '200 10% 98%', + foreground: '200 20% 15%', + card: '0 0% 100%', + cardForeground: '200 20% 15%', + primary: '220 55% 52%', + primaryForeground: '0 0% 100%', + secondary: '200 10% 96%', + secondaryForeground: '200 20% 25%', + muted: '200 10% 96%', + mutedForeground: '200 10% 45%', + accent: '185 50% 45%', + accentForeground: '0 0% 100%', + destructive: '0 65% 50%', + destructiveForeground: '0 0% 100%', + border: '200 10% 90%', + input: '200 10% 90%', + ring: '220 55% 52%' + } + }, + { + name: 'shadesofpurple', + label: 'Shades of Purple', + darkClass: 'shadesofpurple-dark', + lightClass: 'shadesofpurple-light', + dark: { + background: '270 35% 10%', + foreground: '270 20% 95%', + card: '270 30% 14%', + cardForeground: '270 20% 95%', + primary: '270 60% 68%', + primaryForeground: '270 35% 10%', + secondary: '270 25% 18%', + secondaryForeground: '270 20% 95%', + muted: '270 25% 18%', + mutedForeground: '270 15% 60%', + accent: '330 65% 65%', + accentForeground: '270 35% 10%', + destructive: '330 100% 60%', + destructiveForeground: '270 35% 10%', + border: '270 25% 24%', + input: '270 25% 24%', + ring: '270 60% 68%' + }, + light: { + background: '280 30% 97%', + foreground: '280 20% 18%', + card: '0 0% 100%', + cardForeground: '280 20% 18%', + primary: '265 60% 58%', + primaryForeground: '280 30% 97%', + secondary: '280 20% 94%', + secondaryForeground: '280 20% 28%', + muted: '280 20% 94%', + mutedForeground: '280 15% 45%', + accent: '320 55% 62%', + accentForeground: '280 30% 97%', + destructive: '320 80% 58%', + destructiveForeground: '280 30% 97%', + border: '280 20% 88%', + input: '280 20% 88%', + ring: '265 60% 58%' + } + }, + { + name: 'synthwave84', + label: 'Synthwave 84', + darkClass: 'synthwave84-dark', + lightClass: 'synthwave84-light', + dark: { + background: '260 25% 12%', + foreground: '0 0% 98%', + card: '260 22% 16%', + cardForeground: '0 0% 98%', + primary: '180 90% 55%', + primaryForeground: '260 25% 12%', + secondary: '280 30% 22%', + secondaryForeground: '0 0% 98%', + muted: '280 30% 22%', + mutedForeground: '220 20% 55%', + accent: '280 65% 65%', + accentForeground: '0 0% 98%', + destructive: '0 75% 55%', + destructiveForeground: '0 0% 98%', + border: '280 25% 28%', + input: '280 25% 28%', + ring: '180 90% 55%' + }, + light: { + background: '30 15% 98%', + foreground: '260 15% 15%', + card: '0 0% 100%', + cardForeground: '260 15% 15%', + primary: '190 100% 40%', + primaryForeground: '0 0% 100%', + secondary: '30 10% 96%', + secondaryForeground: '260 15% 25%', + muted: '30 10% 96%', + mutedForeground: '30 10% 40%', + accent: '290 55% 50%', + accentForeground: '0 0% 100%', + destructive: '0 65% 52%', + destructiveForeground: '0 0% 100%', + border: '30 10% 88%', + input: '30 10% 88%', + ring: '190 100% 40%' + } + }, + { + name: 'vercel', + label: 'Vercel', + darkClass: 'vercel-dark', + lightClass: 'vercel-light', + dark: { + background: '0 0% 5%', + foreground: '0 0% 92%', + card: '0 0% 8%', + cardForeground: '0 0% 92%', + primary: '210 100% 55%', + primaryForeground: '0 0% 100%', + secondary: '0 0% 15%', + secondaryForeground: '0 0% 92%', + muted: '0 0% 15%', + mutedForeground: '0 0% 55%', + accent: '280 55% 60%', + accentForeground: '0 0% 98%', + destructive: '0 70% 50%', + destructiveForeground: '0 0% 98%', + border: '0 0% 20%', + input: '0 0% 20%', + ring: '210 100% 55%' + }, + light: { + background: '0 0% 100%', + foreground: '0 0% 10%', + card: '0 0% 100%', + cardForeground: '0 0% 10%', + primary: '210 100% 45%', + primaryForeground: '0 0% 100%', + secondary: '0 0% 98%', + secondaryForeground: '0 0% 20%', + muted: '0 0% 98%', + mutedForeground: '0 0% 45%', + accent: '280 50% 55%', + accentForeground: '0 0% 100%', + destructive: '0 70% 48%', + destructiveForeground: '0 0% 100%', + border: '0 0% 90%', + input: '0 0% 90%', + ring: '210 100% 45%' + } + }, + { + name: 'nightowl', + label: 'Night Owl', + darkClass: 'nightowl-dark', + lightClass: 'nightowl-light', + dark: { + background: '210 35% 8%', + foreground: '210 20% 90%', + card: '210 30% 12%', + cardForeground: '210 20% 90%', + primary: '220 70% 65%', + primaryForeground: '210 35% 8%', + secondary: '210 25% 16%', + secondaryForeground: '210 20% 90%', + muted: '210 25% 16%', + mutedForeground: '210 15% 55%', + accent: '30 70% 65%', + accentForeground: '210 35% 8%', + destructive: '0 65% 55%', + destructiveForeground: '0 0% 98%', + border: '210 25% 22%', + input: '210 25% 22%', + ring: '220 70% 65%' + }, + light: { + background: '210 15% 96%', + foreground: '210 15% 22%', + card: '0 0% 100%', + cardForeground: '210 15% 22%', + primary: '220 65% 55%', + primaryForeground: '0 0% 100%', + secondary: '210 15% 92%', + secondaryForeground: '210 15% 28%', + muted: '210 15% 92%', + mutedForeground: '210 12% 42%', + accent: '320 50% 52%', + accentForeground: '0 0% 100%', + destructive: '0 60% 50%', + destructiveForeground: '0 0% 100%', + border: '210 15% 86%', + input: '210 15% 86%', + ring: '220 65% 55%' + } + }, + { + name: 'zenburn', + label: 'Zenburn', + darkClass: 'zenburn-dark', + lightClass: 'zenburn-light', + dark: { + background: '60 5% 25%', + foreground: '60 10% 85%', + card: '60 5% 28%', + cardForeground: '60 10% 85%', + primary: '180 35% 55%', + primaryForeground: '60 5% 25%', + secondary: '60 5% 32%', + secondaryForeground: '60 10% 85%', + muted: '60 5% 32%', + mutedForeground: '60 8% 55%', + accent: '180 30% 60%', + accentForeground: '60 5% 25%', + destructive: '0 40% 55%', + destructiveForeground: '60 10% 85%', + border: '60 5% 38%', + input: '60 5% 38%', + ring: '180 35% 55%' + }, + light: { + background: '60 30% 97%', + foreground: '60 10% 22%', + card: '0 0% 100%', + cardForeground: '60 10% 22%', + primary: '180 25% 45%', + primaryForeground: '60 30% 97%', + secondary: '60 15% 94%', + secondaryForeground: '60 10% 28%', + muted: '60 15% 94%', + mutedForeground: '60 10% 42%', + accent: '180 20% 42%', + accentForeground: '60 30% 97%', + destructive: '0 35% 48%', + destructiveForeground: '60 30% 97%', + border: '60 15% 88%', + input: '60 15% 88%', + ring: '180 25% 45%' + } + }, + { + name: 'flexoki', + label: 'Flexoki', + darkClass: 'flexoki-dark', + lightClass: 'flexoki-light', + dark: { + background: '60 10% 8%', + foreground: '40 10% 85%', + card: '60 8% 12%', + cardForeground: '40 10% 85%', + primary: '25 75% 55%', + primaryForeground: '60 10% 8%', + secondary: '40 8% 18%', + secondaryForeground: '40 10% 85%', + muted: '40 8% 18%', + mutedForeground: '40 8% 55%', + accent: '260 45% 60%', + accentForeground: '40 10% 85%', + destructive: '5 55% 50%', + destructiveForeground: '40 10% 85%', + border: '40 8% 22%', + input: '40 8% 22%', + ring: '25 75% 55%' + }, + light: { + background: '50 35% 98%', + foreground: '40 15% 10%', + card: '0 0% 100%', + cardForeground: '40 15% 10%', + primary: '30 70% 48%', + primaryForeground: '0 0% 100%', + secondary: '50 20% 95%', + secondaryForeground: '40 15% 22%', + muted: '50 20% 95%', + mutedForeground: '40 10% 40%', + accent: '260 40% 55%', + accentForeground: '0 0% 100%', + destructive: '5 50% 45%', + destructiveForeground: '0 0% 100%', + border: '50 15% 90%', + input: '50 15% 90%', + ring: '30 70% 48%' + } + }, + { + name: 'mercury', + label: 'Mercury', + darkClass: 'mercury-dark', + lightClass: 'mercury-light', + dark: { + background: '240 25% 10%', + foreground: '240 10% 90%', + card: '240 22% 14%', + cardForeground: '240 10% 90%', + primary: '230 65% 70%', + primaryForeground: '240 25% 10%', + secondary: '240 18% 18%', + secondaryForeground: '240 10% 90%', + muted: '240 18% 18%', + mutedForeground: '240 8% 58%', + accent: '230 60% 68%', + accentForeground: '240 25% 10%', + destructive: '330 65% 58%', + destructiveForeground: '240 25% 10%', + border: '240 18% 24%', + input: '240 18% 24%', + ring: '230 65% 70%' + }, + light: { + background: '250 15% 98%', + foreground: '250 10% 20%', + card: '0 0% 100%', + cardForeground: '250 10% 20%', + primary: '230 65% 58%', + primaryForeground: '0 0% 100%', + secondary: '250 12% 94%', + secondaryForeground: '250 10% 28%', + muted: '250 12% 94%', + mutedForeground: '250 10% 42%', + accent: '225 50% 55%', + accentForeground: '0 0% 100%', + destructive: '330 70% 50%', + destructiveForeground: '0 0% 100%', + border: '250 12% 88%', + input: '250 12% 88%', + ring: '230 65% 58%' + } + }, + { + name: 'osaka-jade', + label: 'Osaka Jade', + darkClass: 'osaka-jade-dark', + lightClass: 'osaka-jade-light', + dark: { + background: '160 25% 10%', + foreground: '80 20% 75%', + card: '160 22% 14%', + cardForeground: '80 20% 75%', + primary: '170 65% 50%', + primaryForeground: '160 25% 10%', + secondary: '160 18% 18%', + secondaryForeground: '80 20% 75%', + muted: '160 18% 18%', + mutedForeground: '80 15% 45%', + accent: '150 40% 40%', + accentForeground: '160 25% 10%', + destructive: '5 65% 55%', + destructiveForeground: '80 20% 75%', + border: '160 18% 24%', + input: '160 18% 24%', + ring: '170 65% 50%' + }, + light: { + background: '55 40% 95%', + foreground: '160 20% 12%', + card: '0 0% 100%', + cardForeground: '160 20% 12%', + primary: '170 60% 45%', + primaryForeground: '55 40% 95%', + secondary: '55 30% 92%', + secondaryForeground: '160 20% 22%', + muted: '55 30% 92%', + mutedForeground: '160 15% 38%', + accent: '150 35% 38%', + accentForeground: '55 40% 95%', + destructive: '5 60% 48%', + destructiveForeground: '55 40% 95%', + border: '55 25% 86%', + input: '55 25% 86%', + ring: '170 60% 45%' + } + }, + { + name: 'cursor', + label: 'Cursor', + darkClass: 'cursor-dark', + lightClass: 'cursor-light', + dark: { + background: '0 0% 10%', + foreground: '0 0% 90%', + card: '0 0% 14%', + cardForeground: '0 0% 90%', + primary: '195 45% 58%', + primaryForeground: '0 0% 10%', + secondary: '0 0% 18%', + secondaryForeground: '0 0% 90%', + muted: '0 0% 18%', + mutedForeground: '0 0% 60%', + accent: '195 40% 55%', + accentForeground: '0 0% 10%', + destructive: '345 60% 55%', + destructiveForeground: '0 0% 98%', + border: '0 0% 22%', + input: '0 0% 22%', + ring: '195 45% 58%' + }, + light: { + background: '0 0% 99%', + foreground: '0 0% 10%', + card: '0 0% 100%', + cardForeground: '0 0% 10%', + primary: '190 35% 45%', + primaryForeground: '0 0% 99%', + secondary: '0 0% 98%', + secondaryForeground: '0 0% 18%', + muted: '0 0% 98%', + mutedForeground: '0 0% 42%', + accent: '185 30% 42%', + accentForeground: '0 0% 99%', + destructive: '345 70% 48%', + destructiveForeground: '0 0% 100%', + border: '0 0% 92%', + input: '0 0% 92%', + ring: '190 35% 45%' + } + }, + { + name: 'lucent-orng', + label: 'Lucent Orange', + darkClass: 'lucent-orng-dark', + lightClass: 'lucent-orng-light', + dark: { + background: '20 35% 12%', + foreground: '0 0% 92%', + card: '20 30% 16%', + cardForeground: '0 0% 92%', + primary: '20 80% 55%', + primaryForeground: '20 35% 12%', + secondary: '20 25% 20%', + secondaryForeground: '0 0% 92%', + muted: '20 25% 20%', + mutedForeground: '0 0% 55%', + accent: '190 55% 55%', + accentForeground: '20 35% 12%', + destructive: '355 60% 55%', + destructiveForeground: '0 0% 92%', + border: '20 25% 26%', + input: '20 25% 26%', + ring: '20 80% 55%' + }, + light: { + background: '20 30% 98%', + foreground: '0 0% 12%', + card: '0 0% 100%', + cardForeground: '0 0% 12%', + primary: '20 75% 50%', + primaryForeground: '0 0% 100%', + secondary: '20 20% 96%', + secondaryForeground: '0 0% 22%', + muted: '20 20% 96%', + mutedForeground: '0 0% 42%', + accent: '190 45% 42%', + accentForeground: '0 0% 100%', + destructive: '345 65% 48%', + destructiveForeground: '0 0% 100%', + border: '20 15% 90%', + input: '20 15% 90%', + ring: '20 75% 50%' + } + }, + { + name: 'orng', + label: 'Orange', + darkClass: 'orng-dark', + lightClass: 'orng-light', + dark: { + background: '0 0% 5%', + foreground: '0 0% 92%', + card: '0 0% 8%', + cardForeground: '0 0% 92%', + primary: '20 80% 55%', + primaryForeground: '0 0% 5%', + secondary: '20 25% 12%', + secondaryForeground: '0 0% 92%', + muted: '20 25% 12%', + mutedForeground: '0 0% 50%', + accent: '190 55% 55%', + accentForeground: '0 0% 5%', + destructive: '355 60% 55%', + destructiveForeground: '0 0% 92%', + border: '20 20% 15%', + input: '20 20% 15%', + ring: '20 80% 55%' + }, + light: { + background: '0 0% 100%', + foreground: '0 0% 12%', + card: '0 0% 100%', + cardForeground: '0 0% 12%', + primary: '20 75% 50%', + primaryForeground: '0 0% 100%', + secondary: '20 15% 96%', + secondaryForeground: '0 0% 22%', + muted: '20 15% 96%', + mutedForeground: '0 0% 42%', + accent: '190 45% 42%', + accentForeground: '0 0% 100%', + destructive: '345 65% 48%', + destructiveForeground: '0 0% 100%', + border: '20 10% 90%', + input: '20 10% 90%', + ring: '20 75% 50%' + } + }, + { + name: 'rosepine', + label: 'Rose Pine', + darkClass: 'rosepine-dark', + lightClass: 'rosepine-light', + dark: { + background: '290 25% 10%', + foreground: '290 15% 90%', + card: '290 22% 14%', + cardForeground: '290 15% 90%', + primary: '180 40% 65%', + primaryForeground: '290 25% 10%', + secondary: '290 18% 18%', + secondaryForeground: '290 15% 90%', + muted: '290 18% 18%', + mutedForeground: '290 10% 55%', + accent: '350 50% 70%', + accentForeground: '290 25% 10%', + destructive: '340 55% 60%', + destructiveForeground: '290 25% 10%', + border: '290 18% 24%', + input: '290 18% 24%', + ring: '180 40% 65%' + }, + light: { + background: '35 40% 96%', + foreground: '320 15% 22%', + card: '0 0% 100%', + cardForeground: '320 15% 22%', + primary: '190 40% 38%', + primaryForeground: '35 40% 96%', + secondary: '35 25% 92%', + secondaryForeground: '320 15% 30%', + muted: '35 25% 92%', + mutedForeground: '320 10% 48%', + accent: '350 45% 65%', + accentForeground: '35 40% 96%', + destructive: '340 55% 55%', + destructiveForeground: '35 40% 96%', + border: '35 20% 86%', + input: '35 20% 86%', + ring: '190 40% 38%' + } + }, + { + name: 'aura', + label: 'Aura', + darkClass: 'aura-dark', + lightClass: 'aura-light', + dark: { + background: '260 20% 10%', + foreground: '260 10% 92%', + card: '260 18% 14%', + cardForeground: '260 10% 92%', + primary: '260 65% 70%', + primaryForeground: '260 20% 10%', + secondary: '260 15% 18%', + secondaryForeground: '260 10% 92%', + muted: '260 15% 18%', + mutedForeground: '260 10% 58%', + accent: '0 65% 60%', + accentForeground: '260 20% 10%', + destructive: '0 70% 55%', + destructiveForeground: '260 20% 10%', + border: '260 15% 24%', + input: '260 15% 24%', + ring: '260 65% 70%' + }, + light: { + background: '270 30% 97%', + foreground: '270 15% 18%', + card: '0 0% 100%', + cardForeground: '270 15% 18%', + primary: '265 60% 65%', + primaryForeground: '270 30% 97%', + secondary: '270 20% 94%', + secondaryForeground: '270 15% 28%', + muted: '270 20% 94%', + mutedForeground: '270 12% 45%', + accent: '0 60% 62%', + accentForeground: '270 30% 97%', + destructive: '0 65% 55%', + destructiveForeground: '270 30% 97%', + border: '270 18% 88%', + input: '270 18% 88%', + ring: '265 60% 65%' + } + }, + { + name: 'opencode', + label: 'OpenCode', + darkClass: 'opencode-dark', + lightClass: 'opencode-light', + dark: { + background: '0 0% 5%', + foreground: '0 0% 92%', + card: '0 0% 8%', + cardForeground: '0 0% 92%', + primary: '30 80% 65%', + primaryForeground: '0 0% 5%', + secondary: '280 25% 15%', + secondaryForeground: '0 0% 92%', + muted: '280 25% 15%', + mutedForeground: '0 0% 50%', + accent: '280 55% 60%', + accentForeground: '0 0% 98%', + destructive: '355 55% 55%', + destructiveForeground: '0 0% 92%', + border: '280 20% 18%', + input: '280 20% 18%', + ring: '30 80% 65%' + }, + light: { + background: '0 0% 100%', + foreground: '0 0% 10%', + card: '0 0% 100%', + cardForeground: '0 0% 10%', + primary: '210 70% 50%', + primaryForeground: '0 0% 100%', + secondary: '35 30% 96%', + secondaryForeground: '0 0% 20%', + muted: '35 30% 96%', + mutedForeground: '0 0% 42%', + accent: '35 60% 48%', + accentForeground: '0 0% 100%', + destructive: '355 65% 48%', + destructiveForeground: '0 0% 100%', + border: '35 20% 92%', + input: '35 20% 92%', + ring: '210 70% 50%' + } + }, + { + name: 'onedarkpro', + label: 'One Dark Pro', + darkClass: 'onedarkpro-dark', + lightClass: 'onedarkpro-light', + dark: { + background: '220 20% 14%', + foreground: '220 10% 85%', + card: '220 18% 18%', + cardForeground: '220 10% 85%', + primary: '220 65% 65%', + primaryForeground: '220 20% 14%', + secondary: '220 15% 22%', + secondaryForeground: '220 10% 85%', + muted: '220 15% 22%', + mutedForeground: '220 10% 58%', + accent: '0 60% 60%', + accentForeground: '220 20% 14%', + destructive: '0 65% 58%', + destructiveForeground: '220 20% 14%', + border: '220 15% 28%', + input: '220 15% 28%', + ring: '220 65% 65%' + }, + light: { + background: '220 10% 97%', + foreground: '220 15% 18%', + card: '0 0% 100%', + cardForeground: '220 15% 18%', + primary: '220 70% 55%', + primaryForeground: '0 0% 100%', + secondary: '220 12% 94%', + secondaryForeground: '220 15% 26%', + muted: '220 12% 94%', + mutedForeground: '220 10% 42%', + accent: '345 55% 55%', + accentForeground: '0 0% 100%', + destructive: '0 60% 52%', + destructiveForeground: '0 0% 100%', + border: '220 12% 88%', + input: '220 12% 88%', + ring: '220 70% 55%' + } + }, + { + name: 'oc-2', + label: 'OC-2', + darkClass: 'oc-2-dark', + lightClass: 'oc-2-light', + dark: { + background: '0 0% 12%', + foreground: '30 15% 92%', + card: '0 0% 14%', + cardForeground: '30 15% 92%', + primary: '35 75% 68%', + primaryForeground: '0 0% 12%', + secondary: '0 0% 18%', + secondaryForeground: '30 15% 92%', + muted: '0 0% 18%', + mutedForeground: '0 0% 55%', + accent: '290 55% 72%', + accentForeground: '0 0% 98%', + destructive: '15 75% 55%', + destructiveForeground: '30 15% 92%', + border: '0 0% 22%', + input: '0 0% 22%', + ring: '35 75% 68%' + }, + light: { + background: '40 15% 96%', + foreground: '30 20% 12%', + card: '0 0% 100%', + cardForeground: '30 20% 12%', + primary: '65 55% 70%', + primaryForeground: '40 15% 96%', + secondary: '40 10% 96%', + secondaryForeground: '30 20% 22%', + muted: '40 10% 96%', + mutedForeground: '30 15% 42%', + accent: '290 45% 68%', + accentForeground: '40 15% 96%', + destructive: '15 70% 52%', + destructiveForeground: '40 15% 96%', + border: '40 10% 90%', + input: '40 10% 90%', + ring: '65 55% 70%' + } + }, + { + name: 'amoled', + label: 'AMOLED', + darkClass: 'amoled-dark', + lightClass: 'amoled-light', + dark: { + background: '0 0% 0%', + foreground: '0 0% 100%', + card: '0 0% 2%', + cardForeground: '0 0% 100%', + primary: '270 100% 75%', + primaryForeground: '0 0% 0%', + secondary: '0 0% 8%', + secondaryForeground: '0 0% 100%', + muted: '0 0% 8%', + mutedForeground: '0 0% 60%', + accent: '340 100% 60%', + accentForeground: '0 0% 0%', + destructive: '0 100% 55%', + destructiveForeground: '0 0% 100%', + border: '0 0% 12%', + input: '0 0% 12%', + ring: '270 100% 75%' + }, + light: { + background: '0 0% 95%', + foreground: '0 0% 5%', + card: '0 0% 100%', + cardForeground: '0 0% 5%', + primary: '270 100% 40%', + primaryForeground: '0 0% 100%', + secondary: '0 0% 96%', + secondaryForeground: '0 0% 15%', + muted: '0 0% 96%', + mutedForeground: '0 0% 42%', + accent: '340 100% 50%', + accentForeground: '0 0% 100%', + destructive: '0 100% 48%', + destructiveForeground: '0 0% 100%', + border: '0 0% 88%', + input: '0 0% 88%', + ring: '270 100% 40%' + } + }, + { + name: 'carbonfox', + label: 'Carbonfox', + darkClass: 'carbonfox-dark', + lightClass: 'carbonfox-light', + dark: { + background: '0 0% 25%', + foreground: '210 15% 95%', + card: '0 0% 28%', + cardForeground: '210 15% 95%', + primary: '205 100% 60%', + primaryForeground: '0 0% 25%', + secondary: '0 0% 32%', + secondaryForeground: '210 15% 95%', + muted: '0 0% 32%', + mutedForeground: '210 10% 60%', + accent: '350 80% 65%', + accentForeground: '0 0% 25%', + destructive: '355 75% 60%', + destructiveForeground: '0 0% 25%', + border: '0 0% 38%', + input: '0 0% 38%', + ring: '205 100% 60%' + }, + light: { + background: '0 0% 58%', + foreground: '210 20% 10%', + card: '0 0% 100%', + cardForeground: '210 20% 10%', + primary: '210 100% 40%', + primaryForeground: '0 0% 100%', + secondary: '0 0% 92%', + secondaryForeground: '210 20% 18%', + muted: '0 0% 92%', + mutedForeground: '0 0% 42%', + accent: '280 60% 55%', + accentForeground: '0 0% 100%', + destructive: '355 70% 50%', + destructiveForeground: '0 0% 100%', + border: '0 0% 86%', + input: '0 0% 86%', + ring: '210 100% 40%' + } + }, + { + name: 'vesper', + label: 'Vesper', + darkClass: 'vesper-dark', + lightClass: 'vesper-light', + dark: { + background: '40 15% 12%', + foreground: '60 20% 88%', + card: '40 12% 16%', + cardForeground: '60 20% 88%', + primary: '45 60% 60%', + primaryForeground: '40 15% 12%', + secondary: '40 10% 20%', + secondaryForeground: '60 20% 88%', + muted: '40 10% 20%', + mutedForeground: '60 15% 55%', + accent: '180 35% 50%', + accentForeground: '40 15% 12%', + destructive: '0 55% 55%', + destructiveForeground: '60 20% 88%', + border: '40 10% 26%', + input: '40 10% 26%', + ring: '45 60% 60%' + }, + light: { + background: '40 20% 96%', + foreground: '40 15% 15%', + card: '0 0% 100%', + cardForeground: '40 15% 15%', + primary: '45 55% 52%', + primaryForeground: '40 20% 96%', + secondary: '40 15% 90%', + secondaryForeground: '40 15% 25%', + muted: '40 15% 90%', + mutedForeground: '40 12% 40%', + accent: '180 30% 45%', + accentForeground: '40 20% 96%', + destructive: '0 50% 48%', + destructiveForeground: '40 20% 96%', + border: '40 12% 85%', + input: '40 12% 85%', + ring: '45 55% 52%' + } + }, + { + name: 'matrix', + label: 'Matrix', + darkClass: 'matrix-dark', + lightClass: 'matrix-light', + dark: { + background: '120 60% 5%', + foreground: '120 100% 85%', + card: '120 50% 8%', + cardForeground: '120 100% 85%', + primary: '120 100% 50%', + primaryForeground: '120 60% 5%', + secondary: '120 40% 12%', + secondaryForeground: '120 100% 85%', + muted: '120 40% 12%', + mutedForeground: '120 60% 55%', + accent: '180 100% 50%', + accentForeground: '120 60% 5%', + destructive: '0 100% 50%', + destructiveForeground: '120 100% 85%', + border: '120 35% 18%', + input: '120 35% 18%', + ring: '120 100% 50%' + }, + light: { + background: '120 30% 96%', + foreground: '120 40% 15%', + card: '0 0% 100%', + cardForeground: '120 40% 15%', + primary: '120 80% 40%', + primaryForeground: '120 30% 96%', + secondary: '120 20% 92%', + secondaryForeground: '120 40% 25%', + muted: '120 20% 92%', + mutedForeground: '120 30% 40%', + accent: '150 60% 42%', + accentForeground: '120 30% 96%', + destructive: '0 70% 50%', + destructiveForeground: '120 30% 96%', + border: '120 20% 86%', + input: '120 20% 86%', + ring: '120 80% 40%' + } } ];