From c2b531daf448222ac035490449eced365863292c Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 20 Feb 2026 02:40:12 +0000 Subject: [PATCH] feat(core): add specific check and warning for 256-color support --- .../scripts/fetch-pr-info.js | 3 +- packages/core/src/utils/compatibility.test.ts | 88 ++++++++++++++++--- packages/core/src/utils/compatibility.ts | 76 +++++++++++++--- 3 files changed, 142 insertions(+), 25 deletions(-) diff --git a/.gemini/skills/pr-address-comments/scripts/fetch-pr-info.js b/.gemini/skills/pr-address-comments/scripts/fetch-pr-info.js index de99def0ce..772f8d18a4 100755 --- a/.gemini/skills/pr-address-comments/scripts/fetch-pr-info.js +++ b/.gemini/skills/pr-address-comments/scripts/fetch-pr-info.js @@ -20,8 +20,7 @@ async function run(cmd) { stdio: ['pipe', 'pipe', 'ignore'], }); return stdout.trim(); - } catch (_e) { - // eslint-disable-line @typescript-eslint/no-unused-vars + } catch (_e) { // eslint-disable-line @typescript-eslint/no-unused-vars return null; } } diff --git a/packages/core/src/utils/compatibility.test.ts b/packages/core/src/utils/compatibility.test.ts index 8db512292a..c7819578f1 100644 --- a/packages/core/src/utils/compatibility.test.ts +++ b/packages/core/src/utils/compatibility.test.ts @@ -9,6 +9,7 @@ import os from 'node:os'; import { isWindows10, isJetBrainsTerminal, + supports256Colors, supportsTrueColor, getCompatibilityWarnings, } from './compatibility.js'; @@ -66,6 +67,25 @@ describe('compatibility', () => { }); }); + describe('supports256Colors', () => { + it('should return true when getColorDepth returns >= 8', () => { + process.stdout.getColorDepth = vi.fn().mockReturnValue(8); + expect(supports256Colors()).toBe(true); + }); + + it('should return true when TERM contains 256color', () => { + process.stdout.getColorDepth = vi.fn().mockReturnValue(4); + vi.stubEnv('TERM', 'xterm-256color'); + expect(supports256Colors()).toBe(true); + }); + + it('should return false when 256 colors are not supported', () => { + process.stdout.getColorDepth = vi.fn().mockReturnValue(4); + vi.stubEnv('TERM', 'xterm'); + expect(supports256Colors()).toBe(false); + }); + }); + describe('supportsTrueColor', () => { it('should return true when COLORTERM is truecolor', () => { vi.stubEnv('COLORTERM', 'truecolor'); @@ -103,8 +123,11 @@ describe('compatibility', () => { vi.stubEnv('TERMINAL_EMULATOR', ''); const warnings = getCompatibilityWarnings(); - expect(warnings).toContain( - 'Warning: Windows 10 detected. Some UI features like smooth scrolling may be degraded. Windows 11 is recommended for the best experience.', + expect(warnings).toContainEqual( + expect.objectContaining({ + id: 'windows-10', + message: expect.stringContaining('Windows 10 detected'), + }), ); }); @@ -113,35 +136,78 @@ describe('compatibility', () => { vi.stubEnv('TERMINAL_EMULATOR', 'JetBrains-JediTerm'); const warnings = getCompatibilityWarnings(); - expect(warnings).toContain( - 'Warning: JetBrains terminal detected. You may experience rendering or scrolling issues. Using an external terminal (e.g., Windows Terminal, iTerm2) is recommended.', + expect(warnings).toContainEqual( + expect.objectContaining({ + id: 'jetbrains-terminal', + message: expect.stringContaining('JetBrains terminal detected'), + }), ); }); - it('should return true color warning when not supported', () => { - vi.mocked(os.platform).mockReturnValue('darwin'); + it('should return 256-color warning when 256 colors are not supported', () => { + vi.mocked(os.platform).mockReturnValue('linux'); vi.stubEnv('TERMINAL_EMULATOR', ''); vi.stubEnv('COLORTERM', ''); + vi.stubEnv('TERM', 'xterm'); + process.stdout.getColorDepth = vi.fn().mockReturnValue(4); + + const warnings = getCompatibilityWarnings(); + expect(warnings).toContainEqual( + expect.objectContaining({ + id: '256-color', + message: expect.stringContaining('256-color support not detected'), + priority: 'high', + }), + ); + // Should NOT show true-color warning if 256-color warning is shown + expect(warnings.find((w) => w.id === 'true-color')).toBeUndefined(); + }); + + it('should return true color warning when 256 colors are supported but true color is not, and not Apple Terminal', () => { + vi.mocked(os.platform).mockReturnValue('linux'); + vi.stubEnv('TERMINAL_EMULATOR', ''); + vi.stubEnv('COLORTERM', ''); + vi.stubEnv('TERM_PROGRAM', 'xterm'); process.stdout.getColorDepth = vi.fn().mockReturnValue(8); const warnings = getCompatibilityWarnings(); - expect(warnings).toContain( - 'Warning: True color (24-bit) support not detected. Using a terminal with true color enabled will result in a better visual experience.', + expect(warnings).toContainEqual( + expect.objectContaining({ + id: 'true-color', + message: expect.stringContaining( + 'True color (24-bit) support not detected', + ), + priority: 'low', + }), ); }); + it('should NOT return true color warning for Apple Terminal', () => { + vi.mocked(os.platform).mockReturnValue('darwin'); + vi.stubEnv('TERMINAL_EMULATOR', ''); + vi.stubEnv('COLORTERM', ''); + vi.stubEnv('TERM_PROGRAM', 'Apple_Terminal'); + process.stdout.getColorDepth = vi.fn().mockReturnValue(8); + + const warnings = getCompatibilityWarnings(); + expect(warnings.find((w) => w.id === 'true-color')).toBeUndefined(); + }); + it('should return all warnings when all are detected', () => { vi.mocked(os.platform).mockReturnValue('win32'); vi.mocked(os.release).mockReturnValue('10.0.19041'); vi.stubEnv('TERMINAL_EMULATOR', 'JetBrains-JediTerm'); vi.stubEnv('COLORTERM', ''); + vi.stubEnv('TERM_PROGRAM', 'xterm'); process.stdout.getColorDepth = vi.fn().mockReturnValue(8); const warnings = getCompatibilityWarnings(); expect(warnings).toHaveLength(3); - expect(warnings[0]).toContain('Windows 10 detected'); - expect(warnings[1]).toContain('JetBrains terminal detected'); - expect(warnings[2]).toContain('True color (24-bit) support not detected'); + expect(warnings[0].message).toContain('Windows 10 detected'); + expect(warnings[1].message).toContain('JetBrains terminal detected'); + expect(warnings[2].message).toContain( + 'True color (24-bit) support not detected', + ); }); it('should return no warnings in a standard environment with true color', () => { diff --git a/packages/core/src/utils/compatibility.ts b/packages/core/src/utils/compatibility.ts index b1b6240a65..8099351ad0 100644 --- a/packages/core/src/utils/compatibility.ts +++ b/packages/core/src/utils/compatibility.ts @@ -30,6 +30,31 @@ export function isJetBrainsTerminal(): boolean { return process.env['TERMINAL_EMULATOR'] === 'JetBrains-JediTerm'; } +/** + * Detects if the current terminal is the default Apple Terminal.app. + */ +export function isAppleTerminal(): boolean { + return process.env['TERM_PROGRAM'] === 'Apple_Terminal'; +} + +/** + * Detects if the current terminal supports 256 colors (8-bit). + */ +export function supports256Colors(): boolean { + // Check if stdout supports at least 8-bit color depth + if (process.stdout.getColorDepth && process.stdout.getColorDepth() >= 8) { + return true; + } + + // Check TERM environment variable + const term = process.env['TERM'] || ''; + if (term.includes('256color')) { + return true; + } + + return false; +} + /** * Detects if the current terminal supports true color (24-bit). */ @@ -53,25 +78,52 @@ export function supportsTrueColor(): boolean { /** * Returns a list of compatibility warnings based on the current environment. */ -export function getCompatibilityWarnings(): string[] { - const warnings: string[] = []; +export enum WarningPriority { + Low = 'low', + High = 'high', +} + +export interface StartupWarning { + id: string; + message: string; + priority: WarningPriority; +} + +export function getCompatibilityWarnings(): StartupWarning[] { + const warnings: StartupWarning[] = []; if (isWindows10()) { - warnings.push( - 'Warning: Windows 10 detected. Some UI features like smooth scrolling may be degraded. Windows 11 is recommended for the best experience.', - ); + warnings.push({ + id: 'windows-10', + message: + 'Warning: Windows 10 detected. Some UI features like smooth scrolling may be degraded. Windows 11 is recommended for the best experience.', + priority: WarningPriority.High, + }); } if (isJetBrainsTerminal()) { - warnings.push( - 'Warning: JetBrains terminal detected. You may experience rendering or scrolling issues. Using an external terminal (e.g., Windows Terminal, iTerm2) is recommended.', - ); + warnings.push({ + id: 'jetbrains-terminal', + message: + 'Warning: JetBrains terminal detected. You may experience rendering or scrolling issues. Using an external terminal (e.g., Windows Terminal, iTerm2) is recommended.', + priority: WarningPriority.High, + }); } - if (!supportsTrueColor()) { - warnings.push( - 'Warning: True color (24-bit) support not detected. Using a terminal with true color enabled will result in a better visual experience.', - ); + if (!supports256Colors()) { + warnings.push({ + id: '256-color', + message: + 'Warning: 256-color support not detected. Using a terminal with at least 256-color support is recommended for a better visual experience.', + priority: WarningPriority.High, + }); + } else if (!supportsTrueColor() && !isAppleTerminal()) { + warnings.push({ + id: 'true-color', + message: + 'Warning: True color (24-bit) support not detected. Using a terminal with true color enabled will result in a better visual experience.', + priority: WarningPriority.Low, + }); } return warnings;