diff --git a/packages/monaco/src/index.ts b/packages/monaco/src/index.ts index 6c7517d06..05866138c 100644 --- a/packages/monaco/src/index.ts +++ b/packages/monaco/src/index.ts @@ -20,9 +20,17 @@ export interface ShikiToMonacoOptions { * @default 500 */ tokenizeTimeLimit?: number + /** + * A map of theme names to their Monaco theme options. + * + * This is useful for specifying the base theme for high contrast themes. + */ + themes?: Record } -export function textmateThemeToMonacoTheme(theme: ThemeRegistrationResolved): MonacoTheme { +export function textmateThemeToMonacoTheme(theme: ThemeRegistrationResolved, options?: ShikiToMonacoOptions['themes']): MonacoTheme { let rules = 'rules' in theme ? theme.rules as MonacoTheme['rules'] : undefined @@ -54,8 +62,19 @@ export function textmateThemeToMonacoTheme(theme: ThemeRegistrationResolved): Mo .map(([key, value]) => [key, `#${normalizeColor(value)}`]), ) + let base = theme.type === 'light' ? 'vs' : 'vs-dark' + if (options?.[theme.name]?.type) { + const type = options[theme.name].type + if (type === 'hc' || type === 'hc-light') + base = type === 'hc' ? 'hc-black' : 'hc-light' + else if (type === 'light') + base = 'vs' + else if (type === 'dark') + base = 'vs-dark' + } + return { - base: theme.type === 'light' ? 'vs' : 'vs-dark', + base: base as any, inherit: false, colors, rules, @@ -72,7 +91,7 @@ export function shikiToMonaco( const themeIds = highlighter.getLoadedThemes() for (const themeId of themeIds) { const tmTheme = highlighter.getTheme(themeId) - const monacoTheme = textmateThemeToMonacoTheme(tmTheme) + const monacoTheme = textmateThemeToMonacoTheme(tmTheme, options.themes) themeMap.set(themeId, monacoTheme) monaco.editor.defineTheme(themeId, monacoTheme) } diff --git a/packages/monaco/test/high-contrast.test.ts b/packages/monaco/test/high-contrast.test.ts new file mode 100644 index 000000000..ac5d727d6 --- /dev/null +++ b/packages/monaco/test/high-contrast.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it, vi } from 'vitest' +import { shikiToMonaco } from '../src/index' + +describe('shikiToMonaco high contrast', () => { + it('registers high contrast theme with correct base', () => { + const highlighter = { + getLoadedThemes: () => ['hc-theme'], + getTheme: (_id: string) => ({ + name: 'hc-theme', + type: 'dark', + colors: { 'editor.foreground': '#ffffff' }, + rules: [], + }), + setTheme: () => ({ colorMap: [] }), + getLoadedLanguages: () => [], + } as any + + const monaco = { + editor: { + defineTheme: vi.fn(), + setTheme: vi.fn(), + create: vi.fn(), + }, + languages: { + register: vi.fn(), + getLanguages: () => [], + setTokensProvider: vi.fn(), + }, + } as any + + shikiToMonaco(highlighter, monaco, { + themes: { + 'hc-theme': { + type: 'hc', + }, + }, + }) + + const defineThemeCall = monaco.editor.defineTheme.mock.calls[0] + const themeData = defineThemeCall[1] + + // This is what we want to fail currently. + // It should be 'vs-dark' currently, but we want it to be 'hc-black' eventually. + // For reproduction, we assert what happens NOW, or we assert what SHOULD happen and expect failure. + // I'll assert what SHOULD happen so it fails. + expect(themeData.base).toBe('hc-black') + }) +})