From 69b99c8e030fced2cd2aaad83fb24f7c9d372f40 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 14 Aug 2023 01:02:00 +0200 Subject: [PATCH] feat: support no default color for dual themes, #6 --- src/core/index.ts | 9 ++- src/core/renderer-html-dual-themes.ts | 11 +++- src/core/renderer-html.ts | 15 ++--- src/types.ts | 21 ++++++- test/core.test.ts | 4 +- test/dual-themes.test.ts | 75 ++++++++++++++++++++++++ test/index.test.ts | 4 +- test/out/dual-themes.html | 2 +- test/out/multiple-themes-no-default.html | 56 ++++++++++++++++++ test/out/multiple-themes.html | 2 +- test/singleton.test.ts | 4 +- 11 files changed, 180 insertions(+), 23 deletions(-) create mode 100644 test/out/multiple-themes-no-default.html diff --git a/src/core/index.ts b/src/core/index.ts index 0f5333b69..2c803af0c 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -128,9 +128,12 @@ export async function getHighlighterCore(options: HighlighterCoreOptions) { getTheme(theme)._theme, ] as [string, ThemedToken[][], ThemeRegisteration]) - return renderToHtmlDualThemes(tokens, cssVariablePrefix, { - lineOptions: options?.lineOptions, - }) + return renderToHtmlDualThemes( + tokens, + cssVariablePrefix, + defaultColor !== false, + options, + ) } async function loadLanguage(...langs: LanguageInput[]) { diff --git a/src/core/renderer-html-dual-themes.ts b/src/core/renderer-html-dual-themes.ts index f16e6b704..469f06591 100644 --- a/src/core/renderer-html-dual-themes.ts +++ b/src/core/renderer-html-dual-themes.ts @@ -57,6 +57,7 @@ export function _syncThemedTokens(...themes: ThemedToken[][][]) { export function renderToHtmlDualThemes( themes: [string, ThemedToken[][], ThemeRegisteration][], cssVariablePrefix = '--shiki-', + defaultColor = true, options: HtmlRendererOptions = {}, ) { const synced = _syncThemedTokens(...themes.map(t => t[1])) @@ -68,18 +69,22 @@ export function renderToHtmlDualThemes( merged.push(lineout) for (let j = 0; j < lines[0].length; j++) { const tokens = lines.map(t => t[j]) - const colors = tokens.map((t, idx) => `${idx === 0 ? '' : `${cssVariablePrefix + themes[idx][0]}:`}${t.color || 'inherit'}`).join(';') + const colors = tokens.map((t, idx) => `${idx === 0 && defaultColor ? '' : `${cssVariablePrefix + themes[idx][0]}:`}${t.color || 'inherit'}`).join(';') lineout.push({ ...tokens[0], color: colors, + htmlStyle: defaultColor ? undefined : colors, }) } } + const fg = themes.map((t, idx) => (idx === 0 && defaultColor ? '' : `${cssVariablePrefix + t[0]}:`) + t[2].fg).join(';') + const bg = themes.map((t, idx) => (idx === 0 && defaultColor ? '' : `${cssVariablePrefix + t[0]}-bg:`) + t[2].bg).join(';') return renderToHtml(merged, { ...options, - fg: themes.map((t, idx) => (idx === 0 ? '' : `${cssVariablePrefix + t[0]}:`) + t[2].fg).join(';'), - bg: themes.map((t, idx) => (idx === 0 ? '' : `${cssVariablePrefix + t[0]}-bg:`) + t[2].bg).join(';'), + fg, + bg, themeName: `shiki-dual-themes ${themes.map(t => t[2].name).join(' ')}`, + rootStyle: defaultColor ? undefined : [fg, bg].join(';'), }) } diff --git a/src/core/renderer-html.ts b/src/core/renderer-html.ts index 6b1221b65..2143342bc 100644 --- a/src/core/renderer-html.ts +++ b/src/core/renderer-html.ts @@ -39,7 +39,10 @@ export function renderToHtml(lines: ThemedToken[][], options: HtmlRendererOption return h( 'pre', - { className: `shiki ${options.themeName || ''}`, style: `background-color: ${bg}; color: ${fg}` }, + { + className: `shiki ${options.themeName || ''}`, + style: options.rootStyle || `background-color:${bg};color:${fg}`, + }, [ options.langId ? `
${options.langId}
` : '', h( @@ -58,16 +61,14 @@ export function renderToHtml(lines: ThemedToken[][], options: HtmlRendererOption index, }, line.map((token, index) => { - const cssDeclarations = [`color: ${token.color || options.fg}`] + const cssDeclarations = [token.htmlStyle || `color:${token.color || options.fg}`] if (token.fontStyle) { if (token.fontStyle & FontStyle.Italic) - cssDeclarations.push('font-style: italic') - + cssDeclarations.push('font-style:italic') if (token.fontStyle & FontStyle.Bold) - cssDeclarations.push('font-weight: bold') - + cssDeclarations.push('font-weight:bold') if (token.fontStyle & FontStyle.Underline) - cssDeclarations.push('text-decoration: underline') + cssDeclarations.push('text-decoration:underline') } return h( diff --git a/src/types.ts b/src/types.ts index 70e7c0565..12e1ab2e7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -75,15 +75,22 @@ export interface CodeToHtmlDualThemesOptionscode * ``` * + * When set to `false`, no default styles will be applied, and totally up to users to apply the styles: + * + * ```html + * code + * ``` + * + * * @default 'light' */ - defaultColor?: StringLiteralUnion<'light' | 'dark'> + defaultColor?: StringLiteralUnion<'light' | 'dark'> | false /** * Prefix of CSS variables used to store the color of the other theme. @@ -163,6 +170,11 @@ export interface HtmlRendererOptions { lineOptions?: LineOption[] elements?: ElementsOptions themeName?: string + /** + * Custom style string to be applied to the root `
` element.
+   * When specified, `fg` and `bg` will be ignored.
+   */
+  rootStyle?: string
 }
 
 export interface ElementsOptions {
@@ -270,6 +282,11 @@ export interface ThemedToken {
    * 6 or 8 digit hex code representation of the token's color
    */
   color?: string
+  /**
+   * Override with custom inline style for HTML renderer.
+   * When specified, `color` will be ignored.
+   */
+  htmlStyle?: string
   /**
    * Font style of token. Can be None/Italic/Bold/Underline
    */
diff --git a/test/core.test.ts b/test/core.test.ts
index 213988046..a0511d8c1 100644
--- a/test/core.test.ts
+++ b/test/core.test.ts
@@ -18,7 +18,7 @@ describe('should', () => {
     })
 
     expect(shiki.codeToHtml('console.log("Hi")', { lang: 'javascript' }))
-      .toMatchInlineSnapshot('"
console.log("Hi")
"') + .toMatchInlineSnapshot('"
console.log("Hi")
"') }) it('dynamic load theme and lang', async () => { @@ -55,7 +55,7 @@ describe('should', () => { `) expect(shiki.codeToHtml('print 1', { lang: 'python', theme: 'vitesse-light' })) - .toMatchInlineSnapshot('"
print 1
"') + .toMatchInlineSnapshot('"
print 1
"') }) it('requires nested lang', async () => { diff --git a/test/dual-themes.test.ts b/test/dual-themes.test.ts index a49214ff7..e7ec03eed 100644 --- a/test/dual-themes.test.ts +++ b/test/dual-themes.test.ts @@ -140,4 +140,79 @@ function toggleTheme() { expect(snippet + code) .toMatchFileSnapshot('./out/multiple-themes.html') }) + + it('multiple themes without default', async () => { + const code = await codeToHtmlDualThemes('console.log("hello")', { + lang: 'js', + themes: { + 'light': 'vitesse-light', + 'dark': 'vitesse-dark', + 'nord': 'nord', + 'min-dark': 'min-dark', + 'min-light': 'min-light', + }, + defaultColor: false, + cssVariablePrefix: '--s-', + }) + + const snippet = ` + + + +` + + expect(snippet + code) + .toMatchFileSnapshot('./out/multiple-themes-no-default.html') + }) }) diff --git a/test/index.test.ts b/test/index.test.ts index 3c089d2d2..4998726d1 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -9,7 +9,7 @@ describe('should', () => { }) expect(shiki.codeToHtml('console.log', { lang: 'js' })) - .toMatchInlineSnapshot('"
console.log
"') + .toMatchInlineSnapshot('"
console.log
"') }) it('dynamic load theme and lang', async () => { @@ -41,7 +41,7 @@ describe('should', () => { `) expect(shiki.codeToHtml('print 1', { lang: 'python', theme: 'min-dark' })) - .toMatchInlineSnapshot('"
print 1
"') + .toMatchInlineSnapshot('"
print 1
"') }) it('requires nested lang', async () => { diff --git a/test/out/dual-themes.html b/test/out/dual-themes.html index e3e9061fb..bbfcc3484 100644 --- a/test/out/dual-themes.html +++ b/test/out/dual-themes.html @@ -9,4 +9,4 @@ } -
console.log("hello")
\ No newline at end of file +
console.log("hello")
\ No newline at end of file diff --git a/test/out/multiple-themes-no-default.html b/test/out/multiple-themes-no-default.html new file mode 100644 index 000000000..d14f2a8b4 --- /dev/null +++ b/test/out/multiple-themes-no-default.html @@ -0,0 +1,56 @@ + + + + +
console.log("hello")
\ No newline at end of file diff --git a/test/out/multiple-themes.html b/test/out/multiple-themes.html index c6b4a5316..be1d11f92 100644 --- a/test/out/multiple-themes.html +++ b/test/out/multiple-themes.html @@ -45,4 +45,4 @@ } -
console.log("hello")
\ No newline at end of file +
console.log("hello")
\ No newline at end of file diff --git a/test/singleton.test.ts b/test/singleton.test.ts index 884ee5083..5743e6c62 100644 --- a/test/singleton.test.ts +++ b/test/singleton.test.ts @@ -4,10 +4,10 @@ import { codeToHtml, codeToThemedTokens } from '../src' describe('should', () => { it('codeToHtml', async () => { expect(await codeToHtml('console.log("hello")', { lang: 'js', theme: 'vitesse-light' })) - .toMatchInlineSnapshot('"
console.log("hello")
"') + .toMatchInlineSnapshot('"
console.log("hello")
"') expect(await codeToHtml('
bar
', { lang: 'html', theme: 'min-dark' })) - .toMatchInlineSnapshot('"
<div class="foo">bar</div>
"') + .toMatchInlineSnapshot('"
<div class="foo">bar</div>
"') }) it('codeToThemedTokens', async () => {