textarea {
- overflow-y: hidden;
- outline: none;
-
- color: transparent;
- }
-
.carta-highlight {
- display: none;
position: absolute;
left: 0;
right: 0;
@@ -153,11 +171,7 @@
word-break: break-word;
}
- .mounted > .carta-highlight {
- display: block;
- }
-
- :global(.carta-highlight) {
+ :global(.carta-highlight .shiki) {
margin: 0;
tab-size: 4;
background-color: transparent !important;
diff --git a/packages/carta-md/src/lib/internal/components/Renderer.svelte b/packages/carta-md/src/lib/internal/components/Renderer.svelte
index 2c375f4..1141749 100644
--- a/packages/carta-md/src/lib/internal/components/Renderer.svelte
+++ b/packages/carta-md/src/lib/internal/components/Renderer.svelte
@@ -6,7 +6,6 @@
export let carta: Carta;
export let value: string;
export let elem: HTMLDivElement;
- export let renderCls = 'prose';
export let handleScroll: (e: UIEvent) => void;
let mounted = false;
@@ -26,7 +25,7 @@
onMount(() => (mounted = true));
-
+
{@html renderedHtml}
{#if mounted}
diff --git a/packages/carta-md/src/lib/internal/highlight.ts b/packages/carta-md/src/lib/internal/highlight.ts
index 3263c0f..16cfddf 100644
--- a/packages/carta-md/src/lib/internal/highlight.ts
+++ b/packages/carta-md/src/lib/internal/highlight.ts
@@ -1,14 +1,268 @@
-// @ts-expect-error no type definitions
-import PrismImport from 'prismjs/components/prism-core';
-import type PrismType from 'prismjs';
+import {
+ getHighlighter,
+ type BundledTheme,
+ type ThemeInput,
+ type StringLiteralUnion,
+ type BundledLanguage,
+ type SpecialLanguage,
+ type LanguageInput,
+ type LanguageRegistration,
+ type HighlighterGeneric,
+ bundledLanguages,
+ bundledThemes,
+ type ThemeRegistration
+} from 'shiki';
+import type { Intellisense } from './utils';
-const Prism: typeof PrismType = PrismImport;
+/**
+ * Custom TextMate grammar rule for the highlighter.
+ */
+export type GrammarRule = {
+ name: string;
+ type: 'block' | 'inline';
+ definition: LanguageRegistration['repository'][string];
+};
-globalThis.Prism = Prism;
-import 'prismjs/components/prism-markup';
-import prismMarkdown from './prism-markdown';
-prismMarkdown(Prism);
+/**
+ * Custom TextMate highlighting rule for the highlighter.
+ */
+export type HighlightingRule = {
+ light: NonNullable[number];
+ dark: NonNullable[number];
+};
-const MARKDOWN = Prism.languages['md'];
+/**
+ * Shiki options for the highlighter.
+ */
+export type ShikiOptions = {
+ themes?: Array>;
+ langs?: (LanguageInput | StringLiteralUnion | SpecialLanguage)[];
+};
-export { Prism, MARKDOWN };
+type CustomMarkdownLangName = Awaited<(typeof import('./assets/markdown'))['default']['name']>;
+type DefaultLightThemeName = Awaited<(typeof import('./assets/theme-light'))['default']['name']>;
+type DefaultDarkThemeName = Awaited<(typeof import('./assets/theme-dark'))['default']['name']>;
+export const customMarkdownLangName: CustomMarkdownLangName = 'cartamd';
+export const defaultLightThemeName: DefaultLightThemeName = 'carta-light';
+export const defaultDarkThemeName: DefaultDarkThemeName = 'carta-dark';
+export const loadDefaultTheme = async (): Promise<{
+ light: ThemeRegistration;
+ dark: ThemeRegistration;
+}> => ({
+ light: structuredClone((await import('./assets/theme-light')).default),
+ dark: structuredClone((await import('./assets/theme-dark')).default)
+});
+
+/**
+ * Language for the highlighter.
+ */
+export type Language = Intellisense;
+/**
+ * Theme name for the highlighter.
+ */
+export type ThemeName = Intellisense;
+/**
+ * Theme for the highlighter.
+ */
+export type Theme = ThemeName | ThemeRegistration;
+/**
+ * Dual theme for light and dark mode.
+ */
+export type DualTheme = {
+ light: Theme;
+ dark: Theme;
+};
+
+/**
+ * Options for the highlighter.
+ */
+export type HighlighterOptions = {
+ grammarRules: GrammarRule[];
+ highlightingRules: HighlightingRule[];
+ theme: Theme | DualTheme;
+ shiki?: ShikiOptions;
+};
+
+/**
+ * Loads the highlighter instance, with custom rules and options. Uses Shiki under the hood.
+ * @param rules Custom rules for the highlighter, from plugins.
+ * @param options Custom options for the highlighter.
+ * @returns The highlighter instance.
+ */
+export async function loadHighlighter({
+ grammarRules,
+ highlightingRules,
+ theme,
+ shiki
+}: HighlighterOptions): Promise {
+ // Inject rules into the custom markdown language
+ const injectGrammarRules = (
+ lang: Awaited<(typeof import('./assets/markdown'))['default']>,
+ rules: GrammarRule[]
+ ) => {
+ lang.repository = {
+ ...langDefinition.repository,
+ ...Object.fromEntries(rules.map(({ name, definition }) => [name, definition]))
+ };
+ for (const rule of rules) {
+ if (rule.type === 'block') {
+ lang.repository.block.patterns.unshift({ include: `#${rule.name}` });
+ } else {
+ lang.repository.inline.patterns.unshift({ include: `#${rule.name}` });
+ }
+ }
+ };
+
+ const injectHighlightRules = (theme: ThemeRegistration, rules: HighlightingRule[]) => {
+ if (theme.type === 'light') {
+ theme.tokenColors ||= [];
+ theme.tokenColors.unshift(...rules.map(({ light }) => light));
+ } else {
+ theme.tokenColors ||= [];
+ theme.tokenColors.unshift(...rules.map(({ dark }) => dark));
+ }
+ };
+
+ // Additional themes and languages provided by the user
+ const themes = shiki?.themes ?? [];
+ const langs = shiki?.langs ?? [];
+
+ const highlighter: HighlighterGeneric = await getHighlighter({
+ themes,
+ langs
+ });
+
+ // Custom markdown language
+ const langDefinition = (await import('./assets/markdown')).default;
+ injectGrammarRules(langDefinition, grammarRules);
+ await highlighter.loadLanguage(langDefinition);
+
+ // Custom themes
+ if (isSingleTheme(theme)) {
+ let registration: ThemeRegistration;
+ if (isThemeRegistration(theme)) {
+ registration = theme;
+ } else {
+ registration = (await bundledThemes[theme as BundledTheme]()).default;
+ }
+
+ injectHighlightRules(registration, highlightingRules);
+
+ await highlighter.loadTheme(registration);
+ } else {
+ const { light, dark } = theme;
+
+ let lightRegistration: ThemeRegistration;
+ let darkRegistration: ThemeRegistration;
+
+ if (isThemeRegistration(light)) {
+ lightRegistration = light;
+ } else {
+ lightRegistration = (await bundledThemes[light as BundledTheme]()).default;
+ }
+
+ if (isThemeRegistration(dark)) {
+ darkRegistration = dark;
+ } else {
+ darkRegistration = (await bundledThemes[dark as BundledTheme]()).default;
+ }
+
+ injectHighlightRules(lightRegistration, highlightingRules);
+ injectHighlightRules(darkRegistration, highlightingRules);
+
+ await highlighter.loadTheme(lightRegistration);
+ await highlighter.loadTheme(darkRegistration);
+ }
+
+ return {
+ theme,
+ lang: customMarkdownLangName,
+ ...highlighter
+ };
+}
+export interface Highlighter extends HighlighterGeneric {
+ /**
+ * The language specified for the highlighter.
+ */
+ theme: Theme | DualTheme;
+ /**
+ * The theme specified for the highlighter.
+ */
+ lang: Language;
+}
+
+/**
+ * Checks if a language is a bundled language.
+ * @param lang The language to check.
+ * @returns Whether the language is a bundled language.
+ */
+export const isBundleLanguage = (lang: string): lang is BundledLanguage =>
+ Object.keys(bundledLanguages).includes(lang);
+/**
+ * Checks if a theme is a bundled theme.
+ * @param theme The theme to check.
+ * @returns Whether the theme is a bundled theme.
+ */
+export const isBundleTheme = (theme: string): theme is BundledTheme =>
+ Object.keys(bundledThemes).includes(theme);
+/**
+ * Checks if a theme is a dual theme.
+ * @param theme The theme to check.
+ * @returns Whether the theme is a dual theme.
+ */
+export const isDualTheme = (theme: Theme | DualTheme): theme is DualTheme =>
+ typeof theme == 'object' && 'light' in theme && 'dark' in theme;
+/**
+ * Checks if a theme is a single theme.
+ * @param theme The theme to check.
+ * @returns Whether the theme is a single theme.
+ */
+export const isSingleTheme = (theme: Theme | DualTheme): theme is Theme => !isDualTheme(theme);
+/**
+ * Checks if a theme is a theme registration.
+ * @param theme The theme to check.
+ * @returns Whether the theme is a theme registration.
+ */
+export const isThemeRegistration = (theme: Theme): theme is ThemeRegistration =>
+ typeof theme == 'object';
+
+/**
+ * Find all nested languages in the markdown text and load them into the highlighter.
+ * @param text Markdown text to parse for nested languages.
+ * @returns The set of nested languages found in the text.
+ */
+const findNestedLanguages = (text: string) => {
+ const languages = new Set();
+
+ const regex = /```([a-z]+)\n([\s\S]+?)\n```/g;
+ let match: RegExpExecArray | null;
+ while ((match = regex.exec(text))) {
+ languages.add(match[1]);
+ }
+ return languages;
+};
+
+/**
+ * Load all nested languages found in the markdown text into the highlighter.
+ * @param highlighter The highlighter instance.
+ * @param text The text to parse for nested languages.
+ * @returns Whether the highlighter was updated with new languages.
+ */
+export const loadNestedLanguages = async (highlighter: Highlighter, text: string) => {
+ text = text.replaceAll('\r\n', '\n'); // Normalize line endings
+
+ const languages = findNestedLanguages(text);
+ const loadedLanguages = highlighter.getLoadedLanguages();
+ let updated = false;
+ for (const lang of languages) {
+ if (isBundleLanguage(lang) && !loadedLanguages.includes(lang)) {
+ await highlighter.loadLanguage(lang);
+ loadedLanguages.push(lang);
+ updated = true;
+ }
+ }
+
+ return {
+ updated
+ };
+};
diff --git a/packages/carta-md/src/lib/internal/prism-markdown.js b/packages/carta-md/src/lib/internal/prism-markdown.js
deleted file mode 100644
index 3ffc7b7..0000000
--- a/packages/carta-md/src/lib/internal/prism-markdown.js
+++ /dev/null
@@ -1,439 +0,0 @@
-// Original source: https://github.com/PrismJS/prism/blob/master/components/prism-markdown.js
-export default function (Prism) {
- // Allow only one line break
- const inner = /(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;
-
- /**
- * This function is intended for the creation of the bold or italic pattern.
- *
- * This also adds a lookbehind group to the given pattern to ensure that the pattern is not backslash-escaped.
- *
- * _Note:_ Keep in mind that this adds a capturing group.
- *
- * @param {string} pattern
- * @returns {RegExp}
- */
- function createInline(pattern) {
- pattern = pattern.replace(//g, function () {
- return inner;
- });
- return RegExp(/((?:^|[^\\])(?:\\{2})*)/.source + '(?:' + pattern + ')');
- }
-
- const tableCell = /(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source;
- const tableRow = /\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(
- /__/g,
- function () {
- return tableCell;
- }
- );
- const tableLine = /\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;
-
- Prism.languages.markdown = Prism.languages.extend('markup', {});
- Prism.languages.insertBefore('markdown', 'prolog', {
- 'front-matter-block': {
- pattern: /(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,
- lookbehind: true,
- greedy: true,
- inside: {
- punctuation: /^---|---$/,
- 'front-matter': {
- pattern: /\S+(?:\s+\S+)*/,
- alias: ['yaml', 'language-yaml'],
- inside: Prism.languages.yaml
- }
- }
- },
- blockquote: {
- // > ...
- pattern: /^>(?:[\t ]*>)*/m,
- alias: 'punctuation'
- },
- table: {
- pattern: RegExp('^' + tableRow + tableLine + '(?:' + tableRow + ')*', 'm'),
- inside: {
- 'table-data-rows': {
- pattern: RegExp('^(' + tableRow + tableLine + ')(?:' + tableRow + ')*$'),
- lookbehind: true,
- inside: {
- 'table-data': {
- pattern: RegExp(tableCell),
- inside: Prism.languages.markdown
- },
- punctuation: /\|/
- }
- },
- 'table-line': {
- pattern: RegExp('^(' + tableRow + ')' + tableLine + '$'),
- lookbehind: true,
- inside: {
- punctuation: /\||:?-{3,}:?/
- }
- },
- 'table-header-row': {
- pattern: RegExp('^' + tableRow + '$'),
- inside: {
- 'table-header': {
- pattern: RegExp(tableCell),
- alias: 'important',
- inside: Prism.languages.markdown
- },
- punctuation: /\|/
- }
- }
- }
- },
- code: [
- {
- // Prefixed by 4 spaces or 1 tab and preceded by an empty line
- pattern:
- /((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,
- lookbehind: true,
- alias: 'keyword'
- },
- {
- // ```optional language
- // code block
- // ```
- pattern: /^```[\s\S]*?^```$/m,
- greedy: true,
- inside: {
- 'code-block': {
- pattern: /^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,
- lookbehind: true
- },
- 'code-language': {
- pattern: /^(```).+/,
- lookbehind: true
- },
- punctuation: /```/
- }
- }
- ],
- title: [
- {
- // title 1
- // =======
-
- // title 2
- // -------
- pattern: /\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,
- alias: 'important',
- inside: {
- punctuation: /==+$|--+$/
- }
- },
- {
- // # title 1
- // ###### title 6
- pattern: /(^\s*)#.+/m,
- lookbehind: true,
- alias: 'important',
- inside: {
- punctuation: /^#+|#+$/
- }
- }
- ],
- hr: {
- // ***
- // ---
- // * * *
- // -----------
- pattern: /(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,
- lookbehind: true,
- alias: 'punctuation'
- },
- list: {
- // * item
- // + item
- // - item
- // 1. item
- pattern: /(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,
- lookbehind: true,
- alias: 'punctuation'
- },
- 'url-reference': {
- // [id]: http://example.com "Optional title"
- // [id]: http://example.com 'Optional title'
- // [id]: http://example.com (Optional title)
- // [id]: "Optional title"
- pattern:
- /!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,
- inside: {
- variable: {
- pattern: /^(!?\[)[^\]]+/,
- lookbehind: true
- },
- string: /(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,
- punctuation: /^[[\]!:]|[<>]/
- },
- alias: 'url'
- },
- bold: {
- // **strong**
- // __strong__
-
- // allow one nested instance of italic text using the same delimiter
- pattern: createInline(
- /\b__(?:(?!_)|_(?:(?!_))+_)+__\b|\*\*(?:(?!\*)|\*(?:(?!\*))+\*)+\*\*/
- .source
- ),
- lookbehind: true,
- greedy: true,
- inside: {
- content: {
- pattern: /(^..)[\s\S]+(?=..$)/,
- lookbehind: true,
- inside: {} // see below
- },
- punctuation: /\*\*|__/
- }
- },
- italic: {
- // *em*
- // _em_
-
- // allow one nested instance of bold text using the same delimiter
- pattern: createInline(
- /\b_(?:(?!_)|__(?:(?!_))+__)+_\b|\*(?:(?!\*)|\*\*(?:(?!\*))+\*\*)+\*/
- .source
- ),
- lookbehind: true,
- greedy: true,
- inside: {
- content: {
- pattern: /(^.)[\s\S]+(?=.$)/,
- lookbehind: true,
- inside: {} // see below
- },
- punctuation: /[*_]/
- }
- },
- strike: {
- // ~~strike through~~
- // ~strike~
- pattern: createInline(/(~~?)(?:(?!~))+\2/.source),
- lookbehind: true,
- greedy: true,
- inside: {
- content: {
- pattern: /(^~~?)[\s\S]+(?=\1$)/,
- lookbehind: true,
- inside: {} // see below
- },
- punctuation: /~~?/
- }
- },
- 'code-snippet': {
- // `code`
- // ``code``
- pattern: /(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,
- lookbehind: true,
- greedy: true,
- alias: ['code', 'keyword']
- },
- url: {
- // [example](http://example.com "Optional title")
- // [example][id]
- // [example] [id]
- pattern: createInline(
- /!?\[(?:(?!\]))+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\]))+\])/
- .source
- ),
- lookbehind: true,
- greedy: true,
- inside: {
- operator: /^!/,
- content: {
- pattern: /(^\[)[^\]]+(?=\])/,
- lookbehind: true,
- inside: {} // see below
- },
- variable: {
- pattern: /(^\][ \t]?\[)[^\]]+(?=\]$)/,
- lookbehind: true
- },
- url: {
- pattern: /(^\]\()[^\s)]+/,
- lookbehind: true
- },
- string: {
- pattern: /(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,
- lookbehind: true
- }
- }
- },
- 'url-autolink': {
- pattern: /https?:\/\/(?:[^\\\n\r >])+/,
- lookbehind: true,
- greedy: true,
- alias: 'url'
- }
- });
-
- ['url', 'bold', 'italic', 'strike'].forEach(function (token) {
- ['url', 'bold', 'italic', 'strike', 'code-snippet'].forEach(function (inside) {
- if (token !== inside) {
- Prism.languages.markdown[token].inside.content.inside[inside] =
- Prism.languages.markdown[inside];
- }
- });
- });
-
- Prism.hooks.add('after-tokenize', function (env) {
- if (env.language !== 'markdown' && env.language !== 'md') {
- return;
- }
-
- function walkTokens(tokens) {
- if (!tokens || typeof tokens === 'string') {
- return;
- }
-
- for (let i = 0, l = tokens.length; i < l; i++) {
- const token = tokens[i];
-
- if (token.type !== 'code') {
- walkTokens(token.content);
- continue;
- }
-
- /*
- * Add the correct `language-xxxx` class to this code block. Keep in mind that the `code-language` token
- * is optional. But the grammar is defined so that there is only one case we have to handle:
- *
- * token.content = [
- * ```,
- * xxxx,
- * '\n', // exactly one new lines (\r or \n or \r\n)
- * ...,
- * '\n', // exactly one new lines again
- * ```
- * ];
- */
-
- const codeLang = token.content[1];
- const codeBlock = token.content[3];
-
- if (
- codeLang &&
- codeBlock &&
- codeLang.type === 'code-language' &&
- codeBlock.type === 'code-block' &&
- typeof codeLang.content === 'string'
- ) {
- // this might be a language that Prism does not support
-
- // do some replacements to support C++, C#, and F#
- let lang = codeLang.content.replace(/\b#/g, 'sharp').replace(/\b\+\+/g, 'pp');
- // only use the first word
- lang = (/[a-z][\w-]*/i.exec(lang) || [''])[0].toLowerCase();
- const alias = 'language-' + lang;
-
- // add alias
- if (!codeBlock.alias) {
- codeBlock.alias = [alias];
- } else if (typeof codeBlock.alias === 'string') {
- codeBlock.alias = [codeBlock.alias, alias];
- } else {
- codeBlock.alias.push(alias);
- }
- }
- }
- }
-
- walkTokens(env.tokens);
- });
-
- Prism.hooks.add('wrap', function (env) {
- if (env.type !== 'code-block') {
- return;
- }
-
- let codeLang = '';
- for (let i = 0, l = env.classes.length; i < l; i++) {
- const cls = env.classes[i];
- const match = /language-(.+)/.exec(cls);
- if (match) {
- codeLang = match[1];
- break;
- }
- }
-
- const grammar = Prism.languages[codeLang];
-
- if (!grammar) {
- if (codeLang && codeLang !== 'none' && Prism.plugins.autoloader) {
- const id = 'md-' + new Date().valueOf() + '-' + Math.floor(Math.random() * 1e16);
- env.attributes['id'] = id;
-
- Prism.plugins.autoloader.loadLanguages(codeLang, function () {
- const ele = document.getElementById(id);
- if (ele) {
- ele.innerHTML = Prism.highlight(ele.textContent, Prism.languages[codeLang], codeLang);
- }
- });
- }
- } else {
- env.content = Prism.highlight(textContent(env.content), grammar, codeLang);
- }
- });
-
- const tagPattern = RegExp(Prism.languages.markup.tag.pattern.source, 'gi');
-
- /**
- * A list of known entity names.
- *
- * This will always be incomplete to save space. The current list is the one used by lowdash's unescape function.
- *
- * @see {@link https://github.com/lodash/lodash/blob/2da024c3b4f9947a48517639de7560457cd4ec6c/unescape.js#L2}
- */
- const KNOWN_ENTITY_NAMES = {
- amp: '&',
- lt: '<',
- gt: '>',
- quot: '"'
- };
-
- // IE 11 doesn't support `String.fromCodePoint`
- const fromCodePoint = String.fromCodePoint || String.fromCharCode;
-
- /**
- * Returns the text content of a given HTML source code string.
- *
- * @param {string} html
- * @returns {string}
- */
- function textContent(html) {
- // remove all tags
- let text = html.replace(tagPattern, '');
-
- // decode known entities
- text = text.replace(/&(\w{1,8}|#x?[\da-f]{1,8});/gi, function (m, code) {
- code = code.toLowerCase();
-
- if (code[0] === '#') {
- let value;
- if (code[1] === 'x') {
- value = parseInt(code.slice(2), 16);
- } else {
- value = Number(code.slice(1));
- }
-
- return fromCodePoint(value);
- } else {
- const known = KNOWN_ENTITY_NAMES[code];
- if (known) {
- return known;
- }
-
- // unable to decode
- return m;
- }
- });
-
- return text;
- }
-
- Prism.languages.md = Prism.languages.markdown;
-}
diff --git a/packages/carta-md/src/routes/+page.svelte b/packages/carta-md/src/routes/+page.svelte
index 7799cde..a8ff175 100644
--- a/packages/carta-md/src/routes/+page.svelte
+++ b/packages/carta-md/src/routes/+page.svelte
@@ -4,7 +4,6 @@
import ToggleTheme from './ToggleTheme.svelte';
import sampleText from './sample.md?raw';
import '$lib/default.css';
- import '$lib/highlight.css';
const carta = new Carta();
diff --git a/packages/carta-md/src/routes/ToggleTheme.svelte b/packages/carta-md/src/routes/ToggleTheme.svelte
index 2598e49..8f369a4 100644
--- a/packages/carta-md/src/routes/ToggleTheme.svelte
+++ b/packages/carta-md/src/routes/ToggleTheme.svelte
@@ -50,7 +50,7 @@
background: #1b1b1f;
}
- :global(html.dark .prose) {
+ :global(html.dark .markdown-body) {
color: #fff;
}
@@ -67,8 +67,8 @@
/* Code dark mode */
- :global(html.dark .carta-highlight),
- :global(html.dark .carta-highlight span) {
- color: var(--hl-dark) !important;
+ :global(html.dark .shiki),
+ :global(html.dark .shiki span) {
+ color: var(--shiki-dark) !important;
}
diff --git a/packages/plugin-anchor/README.md b/packages/plugin-anchor/README.md
index 4b661ec..9dafc99 100644
--- a/packages/plugin-anchor/README.md
+++ b/packages/plugin-anchor/README.md
@@ -20,7 +20,7 @@ import '@cartamd/plugin-anchor/default.css';
```svelte