fix(cli): handle 3-digit hex codes in getLuminance and refactor color parsing

This commit is contained in:
Abhijit Balaji
2026-01-30 13:08:54 -08:00
parent 24ca1bd6f2
commit 27647bceb5
4 changed files with 16 additions and 33 deletions

View File

@@ -6,7 +6,7 @@
import { useEffect } from 'react';
import { useStdout } from 'ink';
import { parseX11Rgb, getLuminance } from '../themes/color-utils.js';
import { getLuminance, parseColor } from '../themes/color-utils.js';
import { themeManager, DEFAULT_THEME } from '../themes/theme-manager.js';
import { DefaultLight } from '../themes/default-light.js';
import { useSettings } from '../contexts/SettingsContext.js';
@@ -47,7 +47,7 @@ export function useTerminalTheme(
);
if (!match) return;
const hexColor = parseX11Rgb(match[1], match[2], match[3]);
const hexColor = parseColor(match[1], match[2], match[3]);
const luminance = getLuminance(hexColor);
config.setTerminalBackground(hexColor);

View File

@@ -12,8 +12,8 @@ import {
CSS_NAME_TO_HEX_MAP,
INK_SUPPORTED_NAMES,
getThemeTypeFromBackgroundColor,
parseX11Rgb,
getLuminance,
parseColor,
} from './color-utils.js';
describe('Color Utils', () => {
@@ -310,33 +310,33 @@ describe('Color Utils', () => {
});
});
describe('parseX11Rgb', () => {
describe('parseColor', () => {
it('should parse 1-digit components', () => {
// F/F/F => #ffffff
expect(parseX11Rgb('f', 'f', 'f')).toBe('#ffffff');
expect(parseColor('f', 'f', 'f')).toBe('#ffffff');
// 0/0/0 => #000000
expect(parseX11Rgb('0', '0', '0')).toBe('#000000');
expect(parseColor('0', '0', '0')).toBe('#000000');
});
it('should parse 2-digit components', () => {
// ff/ff/ff => #ffffff
expect(parseX11Rgb('ff', 'ff', 'ff')).toBe('#ffffff');
expect(parseColor('ff', 'ff', 'ff')).toBe('#ffffff');
// 80/80/80 => #808080
expect(parseX11Rgb('80', '80', '80')).toBe('#808080');
expect(parseColor('80', '80', '80')).toBe('#808080');
});
it('should parse 4-digit components (standard X11)', () => {
// ffff/ffff/ffff => #ffffff (65535/65535 * 255 = 255)
expect(parseX11Rgb('ffff', 'ffff', 'ffff')).toBe('#ffffff');
expect(parseColor('ffff', 'ffff', 'ffff')).toBe('#ffffff');
// 0000/0000/0000 => #000000
expect(parseX11Rgb('0000', '0000', '0000')).toBe('#000000');
expect(parseColor('0000', '0000', '0000')).toBe('#000000');
// 7fff/7fff/7fff => approx #7f7f7f (32767/65535 * 255 = 127.498... -> 127 -> 7f)
expect(parseX11Rgb('7fff', '7fff', '7fff')).toBe('#7f7f7f');
expect(parseColor('7fff', '7fff', '7fff')).toBe('#7f7f7f');
});
it('should handle mixed case', () => {
expect(parseX11Rgb('FFFF', 'FFFF', 'FFFF')).toBe('#ffffff');
expect(parseX11Rgb('Ffff', 'fFFF', 'ffFF')).toBe('#ffffff');
expect(parseColor('FFFF', 'FFFF', 'FFFF')).toBe('#ffffff');
expect(parseColor('Ffff', 'fFFF', 'ffFF')).toBe('#ffffff');
});
});
});

View File

@@ -318,7 +318,7 @@ export function getLuminance(backgroundColor: string): number {
* @param bHex Blue component as hex string
* @returns Hex color string (e.g. #RRGGBB)
*/
export function parseX11Rgb(rHex: string, gHex: string, bHex: string): string {
export function parseColor(rHex: string, gHex: string, bHex: string): string {
const parseComponent = (hex: string) => {
const val = parseInt(hex, 16);
if (hex.length === 1) return (val / 15) * 255;

View File

@@ -14,6 +14,7 @@ import {
enableBracketedPasteMode,
disableBracketedPasteMode,
} from '@google/gemini-cli-core';
import { parseColor } from '../themes/color-utils.js';
export type TerminalBackgroundColor = string | undefined;
@@ -129,7 +130,7 @@ export class TerminalCapabilityManager {
const match = buffer.match(TerminalCapabilityManager.OSC_11_REGEX);
if (match) {
bgReceived = true;
this.terminalBackgroundColor = this.parseColor(
this.terminalBackgroundColor = parseColor(
match[1],
match[2],
match[3],
@@ -234,24 +235,6 @@ export class TerminalCapabilityManager {
isKittyProtocolEnabled(): boolean {
return this.kittyEnabled;
}
private parseColor(rHex: string, gHex: string, bHex: string): string {
const parseComponent = (hex: string) => {
const val = parseInt(hex, 16);
if (hex.length === 1) return (val / 15) * 255;
if (hex.length === 2) return val;
if (hex.length === 3) return (val / 4095) * 255;
if (hex.length === 4) return (val / 65535) * 255;
return val;
};
const r = parseComponent(rHex);
const g = parseComponent(gHex);
const b = parseComponent(bHex);
const toHex = (c: number) => Math.round(c).toString(16).padStart(2, '0');
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}
}
export const terminalCapabilityManager =