feat(ui): add Tokyo Night theme (#24054)

Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
This commit is contained in:
Daniel R. Neal
2026-03-31 18:08:28 -07:00
committed by GitHub
parent 1b265f343f
commit 8ae5b56b5b
7 changed files with 182 additions and 16 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -19,6 +19,7 @@ using the `/theme` command within Gemini CLI:
- `Holiday`
- `Shades Of Purple`
- `Solarized Dark`
- `Tokyo Night`
- **Light themes:**
- `ANSI Light`
- `Ayu Light`
@@ -252,6 +253,10 @@ identify their source, for example: `shades-of-green (green-extension)`.
<img src="/docs/assets/theme-solarized-dark.png" alt="Solarized Dark theme" width="600">
### Tokyo Night
<img src="/docs/assets/theme-tokyonight-dark.png" alt="Tokyo Night theme" width="600">
## Light themes
### ANSI Light

View File

@@ -27,6 +27,10 @@ import {
} from '../hooks/useConfirmingTool.js';
// Mock dependencies
vi.mock('ink-spinner', () => ({
default: () => <Text></Text>,
}));
const mockUseSettings = vi.fn().mockReturnValue({
merged: {
ui: {

View File

@@ -4,7 +4,7 @@ exports[`MainContent > MainContent Tool Output Height Logic > 'ASB mode - Focuse
"ScrollableList
AppHeader(full)
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
Shell Command Running a long command... │
Shell Command Running a long command... │
│ │
│ Line 11 │
│ Line 12 │
@@ -24,7 +24,7 @@ exports[`MainContent > MainContent Tool Output Height Logic > 'ASB mode - Unfocu
"ScrollableList
AppHeader(full)
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
Shell Command Running a long command... │
Shell Command Running a long command... │
│ │
│ Line 11 │
│ Line 12 │
@@ -43,7 +43,7 @@ AppHeader(full)
exports[`MainContent > MainContent Tool Output Height Logic > 'Normal mode - Constrained height' 1`] = `
"AppHeader(full)
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
Shell Command Running a long command... │
Shell Command Running a long command... │
│ │
│ ... first 11 lines hidden (Ctrl+O to show) ... │
│ Line 12 │
@@ -62,7 +62,7 @@ exports[`MainContent > MainContent Tool Output Height Logic > 'Normal mode - Con
exports[`MainContent > MainContent Tool Output Height Logic > 'Normal mode - Unconstrained height' 1`] = `
"AppHeader(full)
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
Shell Command Running a long command... │
Shell Command Running a long command... │
│ │
│ Line 1 │
│ Line 2 │

View File

@@ -14,9 +14,9 @@ exports[`Initial Theme Selection > should default to a dark theme when terminal
│ 7. Holiday Dark │ 6 return a │ │
│ 8. Shades Of Purple Dark │ │ │
│ 9. Solarized Dark │ 1 - print("Hello, " + name) │ │
│ 10. ANSI Light │ 1 + print(f"Hello, {name}!") │ │
│ 11. Ayu Light │ │ │
│ 12. Default Light └─────────────────────────────────────────────────┘ │
│ 10. Tokyo Night Dark │ 1 + print(f"Hello, {name}!") │ │
│ 11. ANSI Light │ │ │
│ 12. Ayu Light └─────────────────────────────────────────────────┘ │
│ ▼ │
│ │
│ (Use Enter to select, Tab to configure scope, Esc to close) │
@@ -64,9 +64,9 @@ exports[`Initial Theme Selection > should use the theme from settings even if te
│ 7. Holiday Dark │ 6 return a │ │
│ 8. Shades Of Purple Dark │ │ │
│ 9. Solarized Dark │ 1 - print("Hello, " + name) │ │
│ 10. ANSI Light │ 1 + print(f"Hello, {name}!") │ │
│ 11. Ayu Light │ │ │
│ 12. Default Light └─────────────────────────────────────────────────┘ │
│ 10. Tokyo Night Dark │ 1 + print(f"Hello, {name}!") │ │
│ 11. ANSI Light │ │ │
│ 12. Ayu Light └─────────────────────────────────────────────────┘ │
│ ▼ │
│ │
│ (Use Enter to select, Tab to configure scope, Esc to close) │
@@ -103,9 +103,9 @@ exports[`ThemeDialog Snapshots > should render correctly in theme selection mode
│ 7. Holiday Dark │ 6 return a │ │
│ 8. Shades Of Purple Dark │ │ │
│ 9. Solarized Dark │ 1 - print("Hello, " + name) │ │
│ 10. ANSI Light │ 1 + print(f"Hello, {name}!") │ │
│ 11. Ayu Light │ │ │
│ 12. Default Light └─────────────────────────────────────────────────┘ │
│ 10. Tokyo Night Dark │ 1 + print(f"Hello, {name}!") │ │
│ 11. ANSI Light │ │ │
│ 12. Ayu Light └─────────────────────────────────────────────────┘ │
│ ▼ │
│ │
│ (Use Enter to select, Tab to configure scope, Esc to close) │
@@ -128,9 +128,9 @@ exports[`ThemeDialog Snapshots > should render correctly in theme selection mode
│ 7. Holiday Dark │ 6 return a │ │
│ 8. Shades Of Purple Dark │ │ │
│ 9. Solarized Dark │ 1 - print("Hello, " + name) │ │
│ 10. ANSI Light │ 1 + print(f"Hello, {name}!") │ │
│ 11. Ayu Light │ │ │
│ 12. Default Light └─────────────────────────────────────────────────┘ │
│ 10. Tokyo Night Dark │ 1 + print(f"Hello, {name}!") │ │
│ 11. ANSI Light │ │ │
│ 12. Ayu Light └─────────────────────────────────────────────────┘ │
│ ▼ │
│ ╭─────────────────────────────────────────────────╮ │
│ │ DEVELOPER TOOLS (Not visible to users) │ │

View File

@@ -0,0 +1,155 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { type ColorsTheme, Theme } from '../../theme.js';
import { interpolateColor } from '../../color-utils.js';
const palette = {
bg: '#1a1b26',
bg_dark: '#16161e',
bg_dark1: '#0C0E14',
bg_highlight: '#292e42',
blue: '#7aa2f7',
blue0: '#3d59a1',
blue1: '#2ac3de',
blue2: '#0db9d7',
blue5: '#89ddff',
blue6: '#b4f9f8',
blue7: '#394b70',
comment: '#565f89',
cyan: '#7dcfff',
fg: '#c0caf5',
fg_dark: '#a9b1d6',
fg_gutter: '#3b4261',
green: '#9ece6a',
green1: '#73daca',
green2: '#41a6b5',
magenta: '#bb9af7',
magenta2: '#ff007c',
orange: '#ff9e64',
purple: '#9d7cd8',
red: '#f7768e',
red1: '#db4b4b',
teal: '#1abc9c',
yellow: '#e0af68',
diff: {
add: '#243e4a',
change: '#1f2231',
delete: '#4a272f',
},
};
const tokyoNightColors: ColorsTheme = {
type: 'dark',
Background: palette.bg,
Foreground: palette.fg,
LightBlue: palette.purple,
AccentBlue: palette.magenta,
AccentPurple: palette.blue,
AccentCyan: palette.cyan,
AccentGreen: palette.teal,
AccentYellow: palette.yellow,
AccentRed: palette.red1,
DiffAdded: palette.diff.add,
DiffRemoved: palette.diff.delete,
Comment: palette.comment,
Gray: palette.fg_dark,
DarkGray: palette.fg_gutter,
FocusColor: palette.blue,
GradientColors: [palette.blue, palette.magenta, palette.cyan],
};
export const TokyoNight: Theme = new Theme(
'Tokyo Night',
'dark',
{
hljs: {
display: 'block',
overflowX: 'auto',
padding: '0.5em',
background: palette.bg,
color: palette.fg,
},
'hljs-addition': { background: palette.diff.add },
'hljs-attr': { color: palette.green1 },
'hljs-attribute': { color: palette.green1 },
'hljs-brace': { color: palette.fg_dark },
'hljs-built_in': { color: palette.blue1 },
'hljs-builtin-symbol': { color: palette.blue1 },
'hljs-bullet': {
color: palette.orange,
fontWeight: 'bold',
},
'hljs-char': { color: palette.green },
'hljs-char-escape': { color: palette.magenta },
'hljs-character': { color: palette.green },
'hljs-class': { color: palette.blue1 },
'hljs-class-title': { color: palette.blue1 },
'hljs-code': { color: palette.green },
'hljs-comment': {
color: palette.comment,
fontStyle: 'italic',
},
'hljs-computation-expression': { color: palette.cyan },
'hljs-deletion': { background: palette.diff.delete },
'hljs-doctag': { color: palette.yellow },
'hljs-emphasis': { fontStyle: 'italic' },
'hljs-function': { color: palette.blue },
'hljs-function-dispatch': { color: palette.blue },
'hljs-keyword': {
color: palette.magenta,
fontStyle: 'italic',
},
'hljs-label': { color: palette.blue },
'hljs-link': { color: palette.teal },
'hljs-literal': { color: palette.orange },
'hljs-message-name': { color: palette.blue },
'hljs-meta': { color: palette.cyan },
'hljs-meta-prompt': { color: palette.fg_dark },
'hljs-name': { color: palette.magenta },
'hljs-named-character': { color: palette.blue1 },
'hljs-number': { color: palette.orange },
'hljs-operator': { color: palette.blue5 },
'hljs-params': { color: palette.yellow },
'hljs-property': { color: palette.green1 },
'hljs-punctuation': { color: palette.fg_dark },
'hljs-quote': {
color: palette.comment,
fontStyle: 'italic',
},
'hljs-regex': { color: palette.blue6 },
'hljs-regexp': { color: palette.blue6 },
'hljs-rest_arg': {
color: interpolateColor(palette.yellow, palette.fg, 0.8),
},
'hljs-section': {
color: palette.blue,
fontWeight: 'bold',
},
'hljs-selector-attr': { color: palette.cyan },
'hljs-selector-class': { color: palette.green1 },
'hljs-selector-id': { color: palette.green1 },
'hljs-selector-pseudo': { color: palette.cyan },
'hljs-selector-tag': { color: palette.magenta },
'hljs-string': { color: palette.green },
'hljs-strong': { fontWeight: 'bold' },
'hljs-subst': { color: palette.blue5 },
'hljs-symbol': { color: palette.magenta },
'hljs-tag': { color: palette.blue1 },
'hljs-template-tag': { color: palette.blue5 },
'hljs-template-variable': { color: palette.fg },
'hljs-title': { color: palette.blue },
'hljs-title-class': { color: palette.blue1 },
'hljs-title-class-inherited': { color: palette.blue1 },
'hljs-title-function': { color: palette.blue },
'hljs-title-function-invoke': { color: palette.blue },
'hljs-type': { color: palette.blue1 },
'hljs-variable': { color: palette.fg },
'hljs-variable-constant': { color: palette.orange },
'hljs-variable-language': { color: palette.red },
},
tokyoNightColors,
);

View File

@@ -18,6 +18,7 @@ import { ShadesOfPurple } from './builtin/dark/shades-of-purple-dark.js';
import { SolarizedDark } from './builtin/dark/solarized-dark.js';
import { SolarizedLight } from './builtin/light/solarized-light.js';
import { XCode } from './builtin/light/xcode-light.js';
import { TokyoNight } from './builtin/dark/tokyonight-dark.js';
import * as fs from 'node:fs';
import * as path from 'node:path';
import type { Theme, ThemeType, ColorsTheme, CustomTheme } from './theme.js';
@@ -84,6 +85,7 @@ class ThemeManager {
SolarizedDark,
SolarizedLight,
XCode,
TokyoNight,
ANSI,
ANSILight,
];