fix(desktop): keep quick-entry options readable in dark themes
This commit is contained in:
55
apps/desktop/src/app/quick-entry/quick-entry-app.test.tsx
Normal file
55
apps/desktop/src/app/quick-entry/quick-entry-app.test.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
/** Regression coverage for #77429: native picker options must paint a readable surface. */
|
||||
|
||||
import { act, cleanup, render, screen } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { QuickEntryStatePush } from '@/store/quick-entry'
|
||||
|
||||
import { QuickEntryApp } from './quick-entry-app'
|
||||
|
||||
const initialHermesDesktop = window.hermesDesktop
|
||||
|
||||
describe('QuickEntryApp', () => {
|
||||
let pushState: ((payload: QuickEntryStatePush) => void) | undefined
|
||||
|
||||
beforeEach(() => {
|
||||
pushState = undefined
|
||||
window.hermesDesktop = {
|
||||
quickEntry: {
|
||||
dismiss: vi.fn(),
|
||||
onShown: vi.fn(() => vi.fn()),
|
||||
onState: vi.fn(callback => {
|
||||
pushState = callback
|
||||
|
||||
return vi.fn()
|
||||
}),
|
||||
submit: vi.fn()
|
||||
}
|
||||
} as never
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
window.hermesDesktop = initialHermesDesktop
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('paints every native target option with matching theme foreground and background tokens', () => {
|
||||
render(<QuickEntryApp />)
|
||||
|
||||
act(() => {
|
||||
pushState?.({
|
||||
connected: true,
|
||||
sessions: [{ id: 'session-1', title: 'A recent session' }]
|
||||
})
|
||||
})
|
||||
|
||||
const options = screen.getAllByRole('option') as HTMLOptionElement[]
|
||||
expect(options).toHaveLength(3)
|
||||
|
||||
for (const option of options) {
|
||||
expect(option.style.color).toBe('var(--ui-text-primary, var(--foreground))')
|
||||
expect(option.style.backgroundColor).toBe('var(--ui-bg-elevated, var(--background))')
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -10,6 +10,14 @@ import {
|
||||
type QuickComposerState
|
||||
} from '@/store/quick-entry'
|
||||
|
||||
// Native select popups do not reliably inherit the closed control's colors.
|
||||
// Paint both sides of the contrast pair on every option so Chromium cannot
|
||||
// combine a dark-theme foreground with an OS-provided light popup surface.
|
||||
const QUICK_TARGET_OPTION_STYLE = {
|
||||
backgroundColor: 'var(--ui-bg-elevated, var(--background))',
|
||||
color: 'var(--ui-text-primary, var(--foreground))'
|
||||
}
|
||||
|
||||
/**
|
||||
* The Quick Entry composer — the whole renderer surface of the global-hotkey
|
||||
* mini window. Deliberately one input plus a session-target picker and nothing
|
||||
@@ -187,10 +195,14 @@ export function QuickEntryApp() {
|
||||
}}
|
||||
value={state.target}
|
||||
>
|
||||
<option value={QUICK_TARGET_CURRENT}>Current chat</option>
|
||||
<option value={QUICK_TARGET_NEW}>New session</option>
|
||||
<option style={QUICK_TARGET_OPTION_STYLE} value={QUICK_TARGET_CURRENT}>
|
||||
Current chat
|
||||
</option>
|
||||
<option style={QUICK_TARGET_OPTION_STYLE} value={QUICK_TARGET_NEW}>
|
||||
New session
|
||||
</option>
|
||||
{state.sessions.map(session => (
|
||||
<option key={session.id} value={session.id}>
|
||||
<option key={session.id} style={QUICK_TARGET_OPTION_STYLE} value={session.id}>
|
||||
{session.title}
|
||||
</option>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user