11import { describe , expect , it } from 'vitest' ;
22
3- import { sessionAccentHex } from '#/tui/utils/session-accent' ;
3+ import { accentHexForHue , sessionAccentHex } from '#/tui/utils/session-accent' ;
44
55function channelSum ( hex : string ) : number {
66 return [ 1 , 3 , 5 ] . reduce ( ( sum , start ) => sum + Number . parseInt ( hex . slice ( start , start + 2 ) , 16 ) , 0 ) ;
77}
88
9+ function relativeLuminance ( hex : string ) : number {
10+ const channels = [ 1 , 3 , 5 ] . map ( ( start ) => Number . parseInt ( hex . slice ( start , start + 2 ) , 16 ) / 255 ) ;
11+ const [ red , green , blue ] = channels . map ( ( channel ) =>
12+ channel <= 0.03928 ? channel / 12.92 : ( ( channel + 0.055 ) / 1.055 ) ** 2.4 ,
13+ ) ;
14+ return 0.2126 * red ! + 0.7152 * green ! + 0.0722 * blue ! ;
15+ }
16+
917describe ( 'sessionAccentHex' , ( ) => {
1018 it ( 'returns a stable six-digit hex color for each key' , ( ) => {
1119 const accent = sessionAccentHex ( 'session-alpha' , 'dark' ) ;
@@ -25,4 +33,15 @@ describe('sessionAccentHex', () => {
2533 channelSum ( sessionAccentHex ( 'session-alpha' , 'dark' ) ) ,
2634 ) ;
2735 } ) ;
36+
37+ it ( 'keeps every light-theme hue above the chrome contrast floor' , ( ) => {
38+ for ( let hue = 0 ; hue < 360 ; hue ++ ) {
39+ const contrast = 1.05 / ( relativeLuminance ( accentHexForHue ( hue , 'light' ) ) + 0.05 ) ;
40+ expect ( contrast , `hue ${ String ( hue ) } ` ) . toBeGreaterThanOrEqual ( 3 ) ;
41+ }
42+ } ) ;
43+
44+ it ( 'keeps the dark-theme hue mapping unchanged' , ( ) => {
45+ expect ( accentHexForHue ( 60 , 'dark' ) ) . toBe ( '#F8F877' ) ;
46+ } ) ;
2847} ) ;
0 commit comments