From a2f03636a50ade818f311d82565560dd8d1daf8d Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 4 Jun 2025 10:41:03 -0700 Subject: [PATCH] Update light themes (#726) --- .../cli/src/ui/components/ThemeDialog.tsx | 14 +- packages/cli/src/ui/themes/ayu-light.ts | 119 ++++++++++++++++ packages/cli/src/ui/themes/ayu.ts | 93 +++++++++++++ packages/cli/src/ui/themes/github-dark.ts | 127 ++++++++++++++++++ .../ui/themes/{github.ts => github-light.ts} | 6 +- packages/cli/src/ui/themes/theme-manager.ts | 12 +- packages/cli/src/ui/themes/theme.ts | 8 +- packages/cli/src/ui/utils/CodeColorizer.tsx | 10 +- 8 files changed, 369 insertions(+), 20 deletions(-) create mode 100644 packages/cli/src/ui/themes/ayu-light.ts create mode 100644 packages/cli/src/ui/themes/ayu.ts create mode 100644 packages/cli/src/ui/themes/github-dark.ts rename packages/cli/src/ui/themes/{github.ts => github-light.ts} (96%) diff --git a/packages/cli/src/ui/components/ThemeDialog.tsx b/packages/cli/src/ui/components/ThemeDialog.tsx index 58bfeffa..81a34ff4 100644 --- a/packages/cli/src/ui/components/ThemeDialog.tsx +++ b/packages/cli/src/ui/components/ThemeDialog.tsx @@ -103,7 +103,7 @@ export function ThemeDialog({ width="100%" > {/* Left Column: Selection */} - + {focusedSection === 'theme' ? '> ' : ' '}Select Theme{' '} {otherScopeModifiedMessage} @@ -133,13 +133,13 @@ export function ThemeDialog({ - (Use ↑/↓ arrows and Enter to select, Tab to change focus) + (Use Enter to select, Tab to change focus) {/* Right Column: Preview */} - + Preview {colorizeCode( - `# Source code -print("Hello, World!") -`, + `def fibonacci(n): + a, b = 0, 1 + for _ in range(n): + a, b = b, a + b + return a`, 'python', )} diff --git a/packages/cli/src/ui/themes/ayu-light.ts b/packages/cli/src/ui/themes/ayu-light.ts new file mode 100644 index 00000000..b2a1c60d --- /dev/null +++ b/packages/cli/src/ui/themes/ayu-light.ts @@ -0,0 +1,119 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { lightTheme, Theme } from './theme.js'; + +export const AyuLight: Theme = new Theme( + 'Ayu Light', + 'light', + { + hljs: { + display: 'block', + overflowX: 'auto', + padding: '0.5em', + background: '#f8f9fa', + color: '#5c6166', + }, + 'hljs-comment': { + color: '#787b80', + fontStyle: 'italic', + }, + 'hljs-quote': { + color: '#4cbf99', + fontStyle: 'italic', + }, + 'hljs-string': { + color: '#86b300', + }, + 'hljs-constant': { + color: '#4cbf99', + }, + 'hljs-number': { + color: '#a37acc', + }, + 'hljs-keyword': { + color: '#fa8d3e', + }, + 'hljs-selector-tag': { + color: '#fa8d3e', + }, + 'hljs-attribute': { + color: '#f2ae49', + }, + 'hljs-variable': { + color: '#5c6166', + }, + 'hljs-variable.language': { + color: '#55b4d4', + fontStyle: 'italic', + }, + 'hljs-title': { + color: '#399ee6', + }, + 'hljs-section': { + color: '#86b300', + fontWeight: 'bold', + }, + 'hljs-type': { + color: '#55b4d4', + }, + 'hljs-class .hljs-title': { + color: '#399ee6', + }, + 'hljs-tag': { + color: '#55b4d4', + }, + 'hljs-name': { + color: '#399ee6', + }, + 'hljs-builtin-name': { + color: '#f2ae49', + }, + 'hljs-meta': { + color: '#e6ba7e', + }, + 'hljs-symbol': { + color: '#f07171', + }, + 'hljs-bullet': { + color: '#f2ae49', + }, + 'hljs-regexp': { + color: '#4cbf99', + }, + 'hljs-link': { + color: '#55b4d4', + }, + 'hljs-deletion': { + color: '#ff7383', + }, + 'hljs-addition': { + color: '#6cbf43', + }, + 'hljs-emphasis': { + fontStyle: 'italic', + }, + 'hljs-strong': { + fontWeight: 'bold', + }, + 'hljs-literal': { + color: '#4cbf99', + }, + 'hljs-built_in': { + color: '#f07171', + }, + 'hljs-doctag': { + color: '#d14', + }, + 'hljs-template-variable': { + color: '#008080', + }, + 'hljs-selector-id': { + color: '#900', + }, + }, + lightTheme, +); diff --git a/packages/cli/src/ui/themes/ayu.ts b/packages/cli/src/ui/themes/ayu.ts new file mode 100644 index 00000000..a87a1ee3 --- /dev/null +++ b/packages/cli/src/ui/themes/ayu.ts @@ -0,0 +1,93 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { darkTheme, Theme } from './theme.js'; + +export const AyuDark: Theme = new Theme( + 'Ayu', + 'dark', + { + hljs: { + display: 'block', + overflowX: 'auto', + padding: '0.5em', + background: '#0b0e14', + color: '#bfbdb6', + }, + 'hljs-keyword': { + color: '#FF8F40', + }, + 'hljs-literal': { + color: '#D2A6FF', + }, + 'hljs-symbol': { + color: '#95E6CB', + }, + 'hljs-name': { + color: '#59C2FF', + }, + 'hljs-link': { + color: '#39BAE6', + }, + 'hljs-function .hljs-keyword': { + color: '#FFB454', + }, + 'hljs-subst': { + color: '#BFBDB6', + }, + 'hljs-string': { + color: '#AAD94C', + }, + 'hljs-title': { + color: '#FFB454', + }, + 'hljs-type': { + color: '#39BAE6', + }, + 'hljs-attribute': { + color: '#FFB454', + }, + 'hljs-bullet': { + color: '#FFB454', + }, + 'hljs-addition': { + color: '#7FD962', + }, + 'hljs-variable': { + color: '#BFBDB6', + }, + 'hljs-template-tag': { + color: '#FF8F40', + }, + 'hljs-template-variable': { + color: '#FF8F40', + }, + 'hljs-comment': { + color: '#ACB6BF8C', + fontStyle: 'italic', + }, + 'hljs-quote': { + color: '#95E6CB', + fontStyle: 'italic', + }, + 'hljs-deletion': { + color: '#F26D78', + }, + 'hljs-meta': { + color: '#E6B673', + }, + 'hljs-doctag': { + fontWeight: 'bold', + }, + 'hljs-strong': { + fontWeight: 'bold', + }, + 'hljs-emphasis': { + fontStyle: 'italic', + }, + }, + darkTheme, +); diff --git a/packages/cli/src/ui/themes/github-dark.ts b/packages/cli/src/ui/themes/github-dark.ts new file mode 100644 index 00000000..42f36db1 --- /dev/null +++ b/packages/cli/src/ui/themes/github-dark.ts @@ -0,0 +1,127 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { darkTheme, Theme } from './theme.js'; + +export const GitHubDark: Theme = new Theme( + 'GitHub', + 'dark', + { + hljs: { + display: 'block', + overflowX: 'auto', + padding: '0.5em', + color: '#d1d5da', + background: '#24292e', + }, + 'hljs-comment': { + color: '#6A737D', + fontStyle: 'italic', + }, + 'hljs-quote': { + color: '#6A737D', + fontStyle: 'italic', + }, + 'hljs-keyword': { + color: '#F97583', + fontWeight: 'bold', + }, + 'hljs-selector-tag': { + color: '#F97583', + fontWeight: 'bold', + }, + 'hljs-subst': { + color: '#e1e4e8', + }, + 'hljs-number': { + color: '#79B8FF', + }, + 'hljs-literal': { + color: '#79B8FF', + }, + 'hljs-variable': { + color: '#FFAB70', + }, + 'hljs-template-variable': { + color: '#FFAB70', + }, + 'hljs-tag .hljs-attr': { + color: '#FFAB70', + }, + 'hljs-string': { + color: '#9ECBFF', + }, + 'hljs-doctag': { + color: '#9ECBFF', + }, + 'hljs-title': { + color: '#B392F0', + fontWeight: 'bold', + }, + 'hljs-section': { + color: '#B392F0', + fontWeight: 'bold', + }, + 'hljs-selector-id': { + color: '#B392F0', + fontWeight: 'bold', + }, + 'hljs-type': { + color: '#85E89D', + fontWeight: 'bold', + }, + 'hljs-class .hljs-title': { + color: '#85E89D', + fontWeight: 'bold', + }, + 'hljs-tag': { + color: '#85E89D', + }, + 'hljs-name': { + color: '#85E89D', + }, + 'hljs-attribute': { + color: '#79B8FF', + }, + 'hljs-regexp': { + color: '#DBEDFF', + }, + 'hljs-link': { + color: '#DBEDFF', + }, + 'hljs-symbol': { + color: '#990073', + }, + 'hljs-bullet': { + color: '#990073', + }, + 'hljs-built_in': { + color: '#79B8FF', + }, + 'hljs-builtin-name': { + color: '#79B8FF', + }, + 'hljs-meta': { + color: '#79B8FF', + fontWeight: 'bold', + }, + 'hljs-deletion': { + background: '#86181D', + color: '#FDAEB7', + }, + 'hljs-addition': { + background: '#144620', + color: '#85E89D', + }, + 'hljs-emphasis': { + fontStyle: 'italic', + }, + 'hljs-strong': { + fontWeight: 'bold', + }, + }, + darkTheme, +); diff --git a/packages/cli/src/ui/themes/github.ts b/packages/cli/src/ui/themes/github-light.ts similarity index 96% rename from packages/cli/src/ui/themes/github.ts rename to packages/cli/src/ui/themes/github-light.ts index 2a5533bb..f66f8558 100644 --- a/packages/cli/src/ui/themes/github.ts +++ b/packages/cli/src/ui/themes/github-light.ts @@ -6,15 +6,15 @@ import { lightTheme, Theme } from './theme.js'; -export const GitHub: Theme = new Theme( - 'GitHub', +export const GitHubLight: Theme = new Theme( + 'GitHub Light', 'light', { hljs: { display: 'block', overflowX: 'auto', padding: '0.5em', - color: '#333', + color: '#24292E', background: '#f8f8f8', }, 'hljs-comment': { diff --git a/packages/cli/src/ui/themes/theme-manager.ts b/packages/cli/src/ui/themes/theme-manager.ts index 2b12a4c1..30f1a62c 100644 --- a/packages/cli/src/ui/themes/theme-manager.ts +++ b/packages/cli/src/ui/themes/theme-manager.ts @@ -4,9 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { AyuDark } from './ayu.js'; +import { AyuLight } from './ayu-light.js'; import { AtomOneDark } from './atom-one-dark.js'; import { Dracula } from './dracula.js'; -import { GitHub } from './github.js'; +import { GitHubDark } from './github-dark.js'; +import { GitHubLight } from './github-light.js'; import { GoogleCode } from './googlecode.js'; import { DefaultLight } from './default-light.js'; import { DefaultDark } from './default.js'; @@ -28,11 +31,14 @@ class ThemeManager { constructor() { this.availableThemes = [ + AyuDark, + AyuLight, AtomOneDark, Dracula, - DefaultLight, // Light mode. + DefaultLight, DefaultDark, - GitHub, + GitHubDark, + GitHubLight, GoogleCode, XCode, ANSI, diff --git a/packages/cli/src/ui/themes/theme.ts b/packages/cli/src/ui/themes/theme.ts index abe9b101..4b280ec4 100644 --- a/packages/cli/src/ui/themes/theme.ts +++ b/packages/cli/src/ui/themes/theme.ts @@ -28,13 +28,13 @@ export const lightTheme: ColorsTheme = { type: 'light', Background: '#FAFAFA', Foreground: '#3C3C43', - LightBlue: '#ADD8E6', + LightBlue: '#89BDCD', AccentBlue: '#3B82F6', AccentPurple: '#8B5CF6', AccentCyan: '#06B6D4', - AccentGreen: '#22C55E', - AccentYellow: '#EAB308', - AccentRed: '#EF4444', + AccentGreen: '#3CA84B', + AccentYellow: '#D5A40A', + AccentRed: '#DD4C4C', SubtleComment: '#9CA3AF', Gray: 'gray', GradientColors: ['#4796E4', '#847ACE', '#C3677F'], diff --git a/packages/cli/src/ui/utils/CodeColorizer.tsx b/packages/cli/src/ui/utils/CodeColorizer.tsx index 8f905498..b56c83f4 100644 --- a/packages/cli/src/ui/utils/CodeColorizer.tsx +++ b/packages/cli/src/ui/utils/CodeColorizer.tsx @@ -97,6 +97,7 @@ export function colorizeCode( // Render the HAST tree using the adapted theme // Apply the theme's default foreground color to the top-level Text element const lines = codeToHighlight.split('\n'); + const padWidth = String(lines.length).length; // Calculate padding width based on number of lines const getHighlightedLines = (line: string) => !language || !lowlight.registered(language) ? lowlight.highlightAuto(line) @@ -107,7 +108,7 @@ export function colorizeCode( {lines.map((line, index) => ( - {`${String(index + 1).padStart(3, ' ')} `} + {`${String(index + 1).padStart(padWidth, ' ')} `} {renderHastNode( @@ -129,14 +130,15 @@ export function colorizeCode( // Fallback to plain text with default color on error // Also display line numbers in fallback const lines = codeToHighlight.split('\n'); + const padWidth = String(lines.length).length; // Calculate padding width based on number of lines return ( {lines.map((line, index) => ( - - {`${String(index + 1).padStart(3, ' ')} `} + + {`${String(index + 1).padStart(padWidth, ' ')} `} - {line} + {line} {index < lines.length - 1 && '\n'} ))}