From 4c2a5045a0d209cfda5723a4dc84fe59670b558e Mon Sep 17 00:00:00 2001 From: Taylor Mullen Date: Tue, 22 Apr 2025 18:57:47 -0700 Subject: [PATCH] Add theming support. - Added a number of common themes to our support matrix: - AtomOneDark - Dracula - VS - GitHub - GoogleCode - XCode - ... Admittedly these all were randomly picked, we could probably curate these better... - Added a new `ThemeDialog` UI that can be accessed via `/theme`. It shows your currentlyt available themes and allows you to change them freely. It does **not**: - Save the theme between sessions - Allow you to hit escape - Show a preview prior to selection. - These themes are from reacts highlight js library. Fixes https://b.corp.google.com/issues/412797985 --- packages/cli/src/ui/App.tsx | 64 ++++++--- packages/cli/src/ui/components/Header.tsx | 2 - .../cli/src/ui/components/ThemeDialog.tsx | 49 +++++++ packages/cli/src/ui/hooks/useThemeCommand.ts | 40 ++++++ packages/cli/src/ui/themes/atom-one-dark.ts | 122 +++++++++++++++++ packages/cli/src/ui/themes/dracula.ts | 99 ++++++++++++++ packages/cli/src/ui/themes/github.ts | 124 +++++++++++++++++ packages/cli/src/ui/themes/googlecode.ts | 121 ++++++++++++++++ packages/cli/src/ui/themes/theme-manager.ts | 47 ++++++- packages/cli/src/ui/themes/vs.ts | 101 ++++++++++++++ packages/cli/src/ui/themes/xcode.ts | 129 ++++++++++++++++++ 11 files changed, 876 insertions(+), 22 deletions(-) create mode 100644 packages/cli/src/ui/components/ThemeDialog.tsx create mode 100644 packages/cli/src/ui/hooks/useThemeCommand.ts create mode 100644 packages/cli/src/ui/themes/atom-one-dark.ts create mode 100644 packages/cli/src/ui/themes/dracula.ts create mode 100644 packages/cli/src/ui/themes/github.ts create mode 100644 packages/cli/src/ui/themes/googlecode.ts create mode 100644 packages/cli/src/ui/themes/vs.ts create mode 100644 packages/cli/src/ui/themes/xcode.ts diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx index 43a1d1e6..48184cea 100644 --- a/packages/cli/src/ui/App.tsx +++ b/packages/cli/src/ui/App.tsx @@ -4,17 +4,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo, useCallback } from 'react'; import { Box, Text } from 'ink'; import { StreamingState, type HistoryItem } from './types.js'; import { useGeminiStream } from './hooks/useGeminiStream.js'; import { useLoadingIndicator } from './hooks/useLoadingIndicator.js'; import { useInputHistory } from './hooks/useInputHistory.js'; +import { useThemeCommand } from './hooks/useThemeCommand.js'; import { Header } from './components/Header.js'; import { HistoryDisplay } from './components/HistoryDisplay.js'; import { LoadingIndicator } from './components/LoadingIndicator.js'; import { InputPrompt } from './components/InputPrompt.js'; import { Footer } from './components/Footer.js'; +import { ThemeDialog } from './components/ThemeDialog.js'; import { ITermDetectionWarning } from './utils/itermDetection.js'; import { useStartupWarnings, @@ -22,13 +24,13 @@ import { } from './hooks/useAppEffects.js'; import { shortenPath, type Config } from '@gemini-code/server'; import { Colors } from './colors.js'; +import { Tips } from './components/Tips.js'; interface AppProps { config: Config; } export const App = ({ config }: AppProps) => { - // Destructured prop const [history, setHistory] = useState([]); const [startupWarnings, setStartupWarnings] = useState([]); const { streamingState, submitQuery, initError, debugMessage } = @@ -36,9 +38,24 @@ export const App = ({ config }: AppProps) => { const { elapsedTime, currentLoadingPhrase } = useLoadingIndicator(streamingState); + const { isThemeDialogOpen, openThemeDialog, handleThemeSelect } = + useThemeCommand(); + useStartupWarnings(setStartupWarnings); useInitializationErrorEffect(initError, history, setHistory); + const handleFinalSubmit = useCallback( + (submittedValue: string) => { + const trimmedValue = submittedValue.trim(); + if (trimmedValue === '/theme') { + openThemeDialog(); + } else if (trimmedValue.length > 0) { + submitQuery(submittedValue); + } + }, + [openThemeDialog, submitQuery], + ); + const userMessages = useMemo( () => history @@ -56,13 +73,16 @@ export const App = ({ config }: AppProps) => { const { query, handleSubmit: handleHistorySubmit } = useInputHistory({ userMessages, - onSubmit: submitQuery, + onSubmit: handleFinalSubmit, isActive: isInputActive, }); + // --- Render Logic --- + return (
+ {startupWarnings.length > 0 && ( { )} - - - - - - {isInputActive && ( + {isThemeDialogOpen ? ( + + ) : ( <> - - cwd: - - {shortenPath(config.getTargetDir(), /*maxLength*/ 70)} - + + + - + {isInputActive && ( + <> + + cwd: + + {shortenPath(config.getTargetDir(), /*maxLength*/ 70)} + + + + + + )} )} diff --git a/packages/cli/src/ui/components/Header.tsx b/packages/cli/src/ui/components/Header.tsx index 8861389b..5706cee6 100644 --- a/packages/cli/src/ui/components/Header.tsx +++ b/packages/cli/src/ui/components/Header.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { Box, Text } from 'ink'; import Gradient from 'ink-gradient'; -import { Tips } from './Tips.js'; const gradientColors = ['#4796E4', '#847ACE', '#C3677F']; @@ -32,6 +31,5 @@ export const Header: React.FC = () => ( `} - ); diff --git a/packages/cli/src/ui/components/ThemeDialog.tsx b/packages/cli/src/ui/components/ThemeDialog.tsx new file mode 100644 index 00000000..b3e4f063 --- /dev/null +++ b/packages/cli/src/ui/components/ThemeDialog.tsx @@ -0,0 +1,49 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { Box, Text } from 'ink'; +import { Colors } from '../colors.js'; +import { themeManager } from '../themes/theme-manager.js'; +import { RadioButtonSelect } from './shared/RadioButtonSelect.js'; + +interface ThemeDialogProps { + /** Callback function when a theme is selected */ + onSelect: (themeName: string) => void; +} + +export function ThemeDialog({ onSelect }: ThemeDialogProps): React.JSX.Element { + const themeItems = themeManager.getAvailableThemes().map((theme) => ({ + label: theme.active ? `${theme.name} (Active)` : theme.name, + value: theme.name, + })); + const initialIndex = themeItems.findIndex( + (item) => item.value === themeManager.getActiveTheme().name, + ); + return ( + + + Select Theme + + + + + (Use ↑/↓ arrows and Enter to select) + + + + ); +} diff --git a/packages/cli/src/ui/hooks/useThemeCommand.ts b/packages/cli/src/ui/hooks/useThemeCommand.ts new file mode 100644 index 00000000..85bd4906 --- /dev/null +++ b/packages/cli/src/ui/hooks/useThemeCommand.ts @@ -0,0 +1,40 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { useState, useCallback } from 'react'; +import { themeManager } from '../themes/theme-manager.js'; + +interface UseThemeCommandReturn { + isThemeDialogOpen: boolean; + openThemeDialog: () => void; + handleThemeSelect: (themeName: string) => void; +} + +export const useThemeCommand = (): UseThemeCommandReturn => { + const [isThemeDialogOpen, setIsThemeDialogOpen] = useState(false); + const [, setForceRender] = useState(0); + + const openThemeDialog = useCallback(() => { + setIsThemeDialogOpen(true); + }, []); + + const handleThemeSelect = useCallback((themeName: string) => { + try { + themeManager.setActiveTheme(themeName); + setForceRender((v) => v + 1); // Trigger potential re-render + } catch (error) { + console.error(`Error setting theme: ${error}`); + } finally { + setIsThemeDialogOpen(false); // Close the dialog + } + }, []); + + return { + isThemeDialogOpen, + openThemeDialog, + handleThemeSelect, + }; +}; diff --git a/packages/cli/src/ui/themes/atom-one-dark.ts b/packages/cli/src/ui/themes/atom-one-dark.ts new file mode 100644 index 00000000..316b9048 --- /dev/null +++ b/packages/cli/src/ui/themes/atom-one-dark.ts @@ -0,0 +1,122 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Theme } from './theme.js'; + +export const AtomOneDark: Theme = new Theme('Atom One Dark', { + hljs: { + display: 'block', + overflowX: 'auto', + padding: '0.5em', + color: '#abb2bf', + background: '#282c34', + }, + 'hljs-comment': { + color: '#5c6370', + fontStyle: 'italic', + }, + 'hljs-quote': { + color: '#5c6370', + fontStyle: 'italic', + }, + 'hljs-doctag': { + color: '#c678dd', + }, + 'hljs-keyword': { + color: '#c678dd', + }, + 'hljs-formula': { + color: '#c678dd', + }, + 'hljs-section': { + color: '#e06c75', + }, + 'hljs-name': { + color: '#e06c75', + }, + 'hljs-selector-tag': { + color: '#e06c75', + }, + 'hljs-deletion': { + color: '#e06c75', + }, + 'hljs-subst': { + color: '#e06c75', + }, + 'hljs-literal': { + color: '#56b6c2', + }, + 'hljs-string': { + color: '#98c379', + }, + 'hljs-regexp': { + color: '#98c379', + }, + 'hljs-addition': { + color: '#98c379', + }, + 'hljs-attribute': { + color: '#98c379', + }, + 'hljs-meta-string': { + color: '#98c379', + }, + 'hljs-built_in': { + color: '#e6c07b', + }, + 'hljs-class .hljs-title': { + color: '#e6c07b', + }, + 'hljs-attr': { + color: '#d19a66', + }, + 'hljs-variable': { + color: '#d19a66', + }, + 'hljs-template-variable': { + color: '#d19a66', + }, + 'hljs-type': { + color: '#d19a66', + }, + 'hljs-selector-class': { + color: '#d19a66', + }, + 'hljs-selector-attr': { + color: '#d19a66', + }, + 'hljs-selector-pseudo': { + color: '#d19a66', + }, + 'hljs-number': { + color: '#d19a66', + }, + 'hljs-symbol': { + color: '#61aeee', + }, + 'hljs-bullet': { + color: '#61aeee', + }, + 'hljs-link': { + color: '#61aeee', + textDecoration: 'underline', + }, + 'hljs-meta': { + color: '#61aeee', + }, + 'hljs-selector-id': { + color: '#61aeee', + }, + 'hljs-title': { + color: '#61aeee', + }, + 'hljs-emphasis': { + fontStyle: 'italic', + }, + 'hljs-strong': { + fontWeight: 'bold', + }, +}); diff --git a/packages/cli/src/ui/themes/dracula.ts b/packages/cli/src/ui/themes/dracula.ts new file mode 100644 index 00000000..0b55435a --- /dev/null +++ b/packages/cli/src/ui/themes/dracula.ts @@ -0,0 +1,99 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Theme } from './theme.js'; + +export const Dracula: Theme = new Theme('Dracula', { + hljs: { + display: 'block', + overflowX: 'auto', + padding: '0.5em', + background: '#282a36', + color: '#f8f8f2', + }, + 'hljs-keyword': { + color: '#8be9fd', + fontWeight: 'bold', + }, + 'hljs-selector-tag': { + color: '#8be9fd', + fontWeight: 'bold', + }, + 'hljs-literal': { + color: '#8be9fd', + fontWeight: 'bold', + }, + 'hljs-section': { + color: '#8be9fd', + fontWeight: 'bold', + }, + 'hljs-link': { + color: '#8be9fd', + }, + 'hljs-function .hljs-keyword': { + color: '#ff79c6', + }, + 'hljs-subst': { + color: '#f8f8f2', + }, + 'hljs-string': { + color: '#f1fa8c', + }, + 'hljs-title': { + color: '#f1fa8c', + fontWeight: 'bold', + }, + 'hljs-name': { + color: '#f1fa8c', + fontWeight: 'bold', + }, + 'hljs-type': { + color: '#f1fa8c', + fontWeight: 'bold', + }, + 'hljs-attribute': { + color: '#f1fa8c', + }, + 'hljs-symbol': { + color: '#f1fa8c', + }, + 'hljs-bullet': { + color: '#f1fa8c', + }, + 'hljs-addition': { + color: '#f1fa8c', + }, + 'hljs-variable': { + color: '#f1fa8c', + }, + 'hljs-template-tag': { + color: '#f1fa8c', + }, + 'hljs-template-variable': { + color: '#f1fa8c', + }, + 'hljs-comment': { + color: '#6272a4', + }, + 'hljs-quote': { + color: '#6272a4', + }, + 'hljs-deletion': { + color: '#6272a4', + }, + 'hljs-meta': { + color: '#6272a4', + }, + 'hljs-doctag': { + fontWeight: 'bold', + }, + 'hljs-strong': { + fontWeight: 'bold', + }, + 'hljs-emphasis': { + fontStyle: 'italic', + }, +}); diff --git a/packages/cli/src/ui/themes/github.ts b/packages/cli/src/ui/themes/github.ts new file mode 100644 index 00000000..fb4080e6 --- /dev/null +++ b/packages/cli/src/ui/themes/github.ts @@ -0,0 +1,124 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Theme } from './theme.js'; + +export const GitHub: Theme = new Theme('GitHub', { + hljs: { + display: 'block', + overflowX: 'auto', + padding: '0.5em', + color: '#333', + background: '#f8f8f8', + }, + 'hljs-comment': { + color: '#998', + fontStyle: 'italic', + }, + 'hljs-quote': { + color: '#998', + fontStyle: 'italic', + }, + 'hljs-keyword': { + color: '#333', + fontWeight: 'bold', + }, + 'hljs-selector-tag': { + color: '#333', + fontWeight: 'bold', + }, + 'hljs-subst': { + color: '#333', + fontWeight: 'normal', + }, + 'hljs-number': { + color: '#008080', + }, + 'hljs-literal': { + color: '#008080', + }, + 'hljs-variable': { + color: '#008080', + }, + 'hljs-template-variable': { + color: '#008080', + }, + 'hljs-tag .hljs-attr': { + color: '#008080', + }, + 'hljs-string': { + color: '#d14', + }, + 'hljs-doctag': { + color: '#d14', + }, + 'hljs-title': { + color: '#900', + fontWeight: 'bold', + }, + 'hljs-section': { + color: '#900', + fontWeight: 'bold', + }, + 'hljs-selector-id': { + color: '#900', + fontWeight: 'bold', + }, + 'hljs-type': { + color: '#458', + fontWeight: 'bold', + }, + 'hljs-class .hljs-title': { + color: '#458', + fontWeight: 'bold', + }, + 'hljs-tag': { + color: '#000080', + fontWeight: 'normal', + }, + 'hljs-name': { + color: '#000080', + fontWeight: 'normal', + }, + 'hljs-attribute': { + color: '#000080', + fontWeight: 'normal', + }, + 'hljs-regexp': { + color: '#009926', + }, + 'hljs-link': { + color: '#009926', + }, + 'hljs-symbol': { + color: '#990073', + }, + 'hljs-bullet': { + color: '#990073', + }, + 'hljs-built_in': { + color: '#0086b3', + }, + 'hljs-builtin-name': { + color: '#0086b3', + }, + 'hljs-meta': { + color: '#999', + fontWeight: 'bold', + }, + 'hljs-deletion': { + background: '#fdd', + }, + 'hljs-addition': { + background: '#dfd', + }, + 'hljs-emphasis': { + fontStyle: 'italic', + }, + 'hljs-strong': { + fontWeight: 'bold', + }, +}); diff --git a/packages/cli/src/ui/themes/googlecode.ts b/packages/cli/src/ui/themes/googlecode.ts new file mode 100644 index 00000000..ff01d8b6 --- /dev/null +++ b/packages/cli/src/ui/themes/googlecode.ts @@ -0,0 +1,121 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Theme } from './theme.js'; + +export const GoogleCode: Theme = new Theme('Google Code', { + hljs: { + display: 'block', + overflowX: 'auto', + padding: '0.5em', + background: 'white', + color: 'black', + }, + 'hljs-comment': { + color: '#800', + }, + 'hljs-quote': { + color: '#800', + }, + 'hljs-keyword': { + color: '#008', + }, + 'hljs-selector-tag': { + color: '#008', + }, + 'hljs-section': { + color: '#008', + }, + 'hljs-title': { + color: '#606', + }, + 'hljs-name': { + color: '#008', + }, + 'hljs-variable': { + color: '#660', + }, + 'hljs-template-variable': { + color: '#660', + }, + 'hljs-string': { + color: '#080', + }, + 'hljs-selector-attr': { + color: '#080', + }, + 'hljs-selector-pseudo': { + color: '#080', + }, + 'hljs-regexp': { + color: '#080', + }, + 'hljs-literal': { + color: '#066', + }, + 'hljs-symbol': { + color: '#066', + }, + 'hljs-bullet': { + color: '#066', + }, + 'hljs-meta': { + color: '#066', + }, + 'hljs-number': { + color: '#066', + }, + 'hljs-link': { + color: '#066', + }, + 'hljs-doctag': { + color: '#606', + fontWeight: 'bold', + }, + 'hljs-type': { + color: '#606', + }, + 'hljs-attr': { + color: '#606', + }, + 'hljs-built_in': { + color: '#606', + }, + 'hljs-builtin-name': { + color: '#606', + }, + 'hljs-params': { + color: '#606', + }, + 'hljs-attribute': { + color: '#000', + }, + 'hljs-subst': { + color: '#000', + }, + 'hljs-formula': { + backgroundColor: '#eee', + fontStyle: 'italic', + }, + 'hljs-selector-id': { + color: '#9B703F', + }, + 'hljs-selector-class': { + color: '#9B703F', + }, + 'hljs-addition': { + backgroundColor: '#baeeba', + }, + 'hljs-deletion': { + backgroundColor: '#ffc8bd', + }, + 'hljs-strong': { + fontWeight: 'bold', + }, + 'hljs-emphasis': { + fontStyle: 'italic', + }, +}); diff --git a/packages/cli/src/ui/themes/theme-manager.ts b/packages/cli/src/ui/themes/theme-manager.ts index e083575c..7e375574 100644 --- a/packages/cli/src/ui/themes/theme-manager.ts +++ b/packages/cli/src/ui/themes/theme-manager.ts @@ -4,19 +4,64 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { AtomOneDark } from './atom-one-dark.js'; +import { Dracula } from './dracula.js'; +import { GitHub } from './github.js'; +import { GoogleCode } from './googlecode.js'; +import { VS } from './vs.js'; import { VS2015 } from './vs2015.js'; +import { XCode } from './xcode.js'; import { Theme } from './theme.js'; +export interface ThemeDisplay { + name: string; + active: boolean; +} + class ThemeManager { private static readonly DEFAULT_THEME: Theme = VS2015; private readonly availableThemes: Theme[]; private activeTheme: Theme; constructor() { - this.availableThemes = [VS2015]; + this.availableThemes = [ + AtomOneDark, + Dracula, + VS, + VS2015, + GitHub, + GoogleCode, + XCode, + ]; this.activeTheme = ThemeManager.DEFAULT_THEME; } + /** + * Returns a list of available theme names. + */ + getAvailableThemes(): ThemeDisplay[] { + return this.availableThemes.map((theme) => ({ + name: theme.name, + active: theme === this.activeTheme, + })); + } + + /** + * Sets the active theme. + * @param themeName The name of the theme to activate. + */ + setActiveTheme(themeName: string): void { + const foundTheme = this.availableThemes.find( + (theme) => theme.name === themeName, + ); + + if (foundTheme) { + this.activeTheme = foundTheme; + } else { + throw new Error(`Theme "${themeName}" not found.`); + } + } + /** * Returns the currently active theme object. */ diff --git a/packages/cli/src/ui/themes/vs.ts b/packages/cli/src/ui/themes/vs.ts new file mode 100644 index 00000000..a87e9e80 --- /dev/null +++ b/packages/cli/src/ui/themes/vs.ts @@ -0,0 +1,101 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Theme } from './theme.js'; + +export const VS: Theme = new Theme('VS', { + hljs: { + display: 'block', + overflowX: 'auto', + padding: '0.5em', + background: 'white', + color: 'black', + }, + 'hljs-comment': { + color: '#008000', + }, + 'hljs-quote': { + color: '#008000', + }, + 'hljs-variable': { + color: '#008000', + }, + 'hljs-keyword': { + color: '#00f', + }, + 'hljs-selector-tag': { + color: '#00f', + }, + 'hljs-built_in': { + color: '#00f', + }, + 'hljs-name': { + color: '#00f', + }, + 'hljs-tag': { + color: '#00f', + }, + 'hljs-string': { + color: '#a31515', + }, + 'hljs-title': { + color: '#a31515', + }, + 'hljs-section': { + color: '#a31515', + }, + 'hljs-attribute': { + color: '#a31515', + }, + 'hljs-literal': { + color: '#a31515', + }, + 'hljs-template-tag': { + color: '#a31515', + }, + 'hljs-template-variable': { + color: '#a31515', + }, + 'hljs-type': { + color: '#a31515', + }, + 'hljs-addition': { + color: '#a31515', + }, + 'hljs-deletion': { + color: '#2b91af', + }, + 'hljs-selector-attr': { + color: '#2b91af', + }, + 'hljs-selector-pseudo': { + color: '#2b91af', + }, + 'hljs-meta': { + color: '#2b91af', + }, + 'hljs-doctag': { + color: '#808080', + }, + 'hljs-attr': { + color: '#f00', + }, + 'hljs-symbol': { + color: '#00b0e8', + }, + 'hljs-bullet': { + color: '#00b0e8', + }, + 'hljs-link': { + color: '#00b0e8', + }, + 'hljs-emphasis': { + fontStyle: 'italic', + }, + 'hljs-strong': { + fontWeight: 'bold', + }, +}); diff --git a/packages/cli/src/ui/themes/xcode.ts b/packages/cli/src/ui/themes/xcode.ts new file mode 100644 index 00000000..ff2fb852 --- /dev/null +++ b/packages/cli/src/ui/themes/xcode.ts @@ -0,0 +1,129 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Theme } from './theme.js'; + +export const XCode: Theme = new Theme('XCode', { + hljs: { + display: 'block', + overflowX: 'auto', + padding: '0.5em', + background: '#fff', + color: 'black', + }, + 'xml .hljs-meta': { + color: '#c0c0c0', + }, + 'hljs-comment': { + color: '#007400', + }, + 'hljs-quote': { + color: '#007400', + }, + 'hljs-tag': { + color: '#aa0d91', + }, + 'hljs-attribute': { + color: '#aa0d91', + }, + 'hljs-keyword': { + color: '#aa0d91', + }, + 'hljs-selector-tag': { + color: '#aa0d91', + }, + 'hljs-literal': { + color: '#aa0d91', + }, + 'hljs-name': { + color: '#aa0d91', + }, + 'hljs-variable': { + color: '#3F6E74', + }, + 'hljs-template-variable': { + color: '#3F6E74', + }, + 'hljs-code': { + color: '#c41a16', + }, + 'hljs-string': { + color: '#c41a16', + }, + 'hljs-meta-string': { + color: '#c41a16', + }, + 'hljs-regexp': { + color: '#0E0EFF', + }, + 'hljs-link': { + color: '#0E0EFF', + }, + 'hljs-title': { + color: '#1c00cf', + }, + 'hljs-symbol': { + color: '#1c00cf', + }, + 'hljs-bullet': { + color: '#1c00cf', + }, + 'hljs-number': { + color: '#1c00cf', + }, + 'hljs-section': { + color: '#643820', + }, + 'hljs-meta': { + color: '#643820', + }, + 'hljs-class .hljs-title': { + color: '#5c2699', + }, + 'hljs-type': { + color: '#5c2699', + }, + 'hljs-built_in': { + color: '#5c2699', + }, + 'hljs-builtin-name': { + color: '#5c2699', + }, + 'hljs-params': { + color: '#5c2699', + }, + 'hljs-attr': { + color: '#836C28', + }, + 'hljs-subst': { + color: '#000', + }, + 'hljs-formula': { + backgroundColor: '#eee', + fontStyle: 'italic', + }, + 'hljs-addition': { + backgroundColor: '#baeeba', + }, + 'hljs-deletion': { + backgroundColor: '#ffc8bd', + }, + 'hljs-selector-id': { + color: '#9b703f', + }, + 'hljs-selector-class': { + color: '#9b703f', + }, + 'hljs-doctag': { + fontWeight: 'bold', + }, + 'hljs-strong': { + fontWeight: 'bold', + }, + 'hljs-emphasis': { + fontStyle: 'italic', + }, +});