diff --git a/frontend/src/components/model/ModelQuickSelect.test.tsx b/frontend/src/components/model/ModelQuickSelect.test.tsx new file mode 100644 index 000000000..73b3b1344 --- /dev/null +++ b/frontend/src/components/model/ModelQuickSelect.test.tsx @@ -0,0 +1,90 @@ +import { describe, it, expect, vi } from 'vitest' +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { ModelQuickSelect } from './ModelQuickSelect' + +vi.mock('@/hooks/useModelSelection', () => ({ + useModelSelection: () => ({ + model: { providerID: 'anthropic', modelID: 'claude-sonnet-4' }, + modelString: 'anthropic/claude-sonnet-4', + recentModels: [], + favoriteModels: [], + setModel: vi.fn(), + toggleFavorite: vi.fn(), + removeRecentModel: vi.fn(), + }), +})) + +vi.mock('@/hooks/useVariants', () => ({ + useVariants: () => ({ + availableVariants: [], + currentVariant: null, + setVariant: vi.fn(), + clearVariant: vi.fn(), + hasVariants: false, + }), +})) + +vi.mock('@/hooks/useProviders', () => ({ + useProviders: () => ({ + data: { + providers: [ + { + id: 'anthropic', + name: 'Anthropic', + isConnected: true, + models: { + 'claude-sonnet-4': { + id: 'claude-sonnet-4', + name: 'Claude Sonnet 4', + limit: { context: 200000, output: 64000 }, + }, + }, + }, + ], + }, + }), +})) + +const DARK_ONLY_CLASS = /text-white|bg-zinc-950|bg-white\/|border-white\// + +function darkOnlyClassNames(root: HTMLElement): string[] { + return Array.from(root.querySelectorAll('*')) + .map((element) => (typeof element.className === 'string' ? element.className : '')) + .filter((className) => DARK_ONLY_CLASS.test(className)) +} + +async function openModelSheet() { + const user = userEvent.setup() + render( + + Select model + , + ) + await user.click(screen.getByText('Select model')) + const dialog = await screen.findByRole('dialog', { name: 'Select model' }) + return { user, dialog } +} + +describe('ModelQuickSelect theming', () => { + it('renders the quick model sheet with theme-aware surfaces', async () => { + const { dialog } = await openModelSheet() + + expect(dialog.className).toContain('bg-popover') + expect(dialog.className).not.toContain('bg-zinc-950') + expect(darkOnlyClassNames(dialog)).toEqual([]) + }) + + it('renders the More models action and provider list with theme-aware colors', async () => { + const { user } = await openModelSheet() + + const moreModels = screen.getByRole('button', { name: /More models/ }) + expect(moreModels.className).toContain('text-foreground') + expect(moreModels.className).not.toMatch(DARK_ONLY_CLASS) + + await user.click(moreModels) + + const dialog = screen.getByRole('dialog', { name: 'Select model' }) + expect(darkOnlyClassNames(dialog)).toEqual([]) + }) +}) diff --git a/frontend/src/components/model/ModelQuickSelect.tsx b/frontend/src/components/model/ModelQuickSelect.tsx index 1bbc6cc92..85b6d4039 100644 --- a/frontend/src/components/model/ModelQuickSelect.tsx +++ b/frontend/src/components/model/ModelQuickSelect.tsx @@ -158,7 +158,7 @@ function VirtualizedList({ className={['h-full overflow-y-auto', className].filter(Boolean).join(' ')} > {items.length === 0 ? ( -
{emptyLabel}
+
{emptyLabel}
) : (
{visibleItems.map((item, index) => ( @@ -444,17 +444,17 @@ export function ModelQuickSelect({ return (
@@ -465,7 +465,7 @@ export function ModelQuickSelect({ event.stopPropagation() removeRecentModel({ providerID: item.providerID, modelID: item.modelID }) }} - className="rounded-full p-1.5 text-white/30 hover:bg-white/10 hover:text-white/70" + className="rounded-full p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground" aria-label="Remove from recent" > @@ -477,7 +477,7 @@ export function ModelQuickSelect({ event.stopPropagation() toggleFavorite({ providerID: item.providerID, modelID: item.modelID }) }} - className="rounded-full p-1.5 text-white/50 transition-opacity hover:bg-white/10 hover:text-white" + className="rounded-full p-1.5 text-muted-foreground transition-opacity hover:bg-accent hover:text-foreground" aria-label={isFavorite ? 'Remove from favorites' : 'Add to favorites'} > @@ -495,13 +495,13 @@ export function ModelQuickSelect({ key={provider.id} type="button" onClick={() => handleProviderSelect(provider.id)} - className="flex w-full items-center gap-3 rounded-xl py-2.5 text-left transition-colors hover:bg-white/5" + className="flex w-full items-center gap-3 rounded-xl py-2.5 text-left transition-colors hover:bg-accent" > - {provider.label} - {provider.count} {provider.count === 1 ? 'model' : 'models'} + {provider.label} + {provider.count} {provider.count === 1 ? 'model' : 'models'} - + ) } @@ -550,7 +550,7 @@ export function ModelQuickSelect({ isOpen={isOpen} onClose={() => handleOpenChange(false)} heightClass="h-[70dvh] max-h-[720px]" - className="z-[300] border-white/10 bg-zinc-950 text-white shadow-2xl md:mx-auto md:max-w-lg" + className="z-[300] border-border bg-popover text-foreground shadow-2xl md:mx-auto md:max-w-lg" ariaLabel="Select model" >
@@ -559,18 +559,18 @@ export function ModelQuickSelect({
- + setSearchQuery(event.target.value)} placeholder={selectedProviderId ? 'Search models...' : 'Search providers...'} - className="h-9 border-white/10 bg-white/5 pl-9 text-sm text-white placeholder:text-white/40" + className="h-9 border-border bg-muted pl-9 text-sm text-foreground placeholder:text-muted-foreground" autoComplete="off" name="model-search" /> @@ -581,7 +581,7 @@ export function ModelQuickSelect({
@@ -601,11 +601,11 @@ export function ModelQuickSelect({ @@ -620,11 +620,11 @@ export function ModelQuickSelect({ {showAllModels ? (
{/* Provider sidebar — desktop only */} -
+
{connectedProviderItems.length > 0 && ( <> -

Connected

+

Connected

{connectedProviderItems.map(provider => ( ))} - {availableProviderItems.length > 0 &&
} + {availableProviderItems.length > 0 &&
} )} {availableProviderItems.length > 0 && ( <> -

Available

+

Available

{availableProviderItems.map(provider => ( ))} @@ -698,20 +698,20 @@ export function ModelQuickSelect({
{connectedProviderItems.length > 0 && ( <> -

Connected

+

Connected

{connectedProviderItems.map(renderProviderOption)} - {availableProviderItems.length > 0 &&
} + {availableProviderItems.length > 0 &&
} )} {availableProviderItems.length > 0 && ( <> -

Available

+

Available

{availableProviderItems.map(renderProviderOption)} )}
{filteredProviderItems.length === 0 && ( -
No providers found
+
No providers found
)}
)} @@ -723,7 +723,7 @@ export function ModelQuickSelect({
{quickSections.map(section => (
-

+

{section.icon} {section.title}

@@ -733,14 +733,14 @@ export function ModelQuickSelect({
))}
-
+