// src/hast.ts
import { toHtml } from "hast-util-to-html";
import { toText } from "hast-util-to-text";
import { matches, select, selectAll } from "hast-util-select";
import { visit } from "unist-util-visit";
import { visitParents, CONTINUE, EXIT, SKIP } from "unist-util-visit-parents";
import { h, s } from "hastscript";
import postcss, { Declaration } from "postcss";
// src/internal/escaping.ts
function escapeRegExp(input) {
return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function serializeCssStringValue(value, quoteStyle = "single") {
const quote = quoteStyle === "single" ? "'" : '"';
const escapedValue = Array.from(value).map((char) => {
const code = char.charCodeAt(0);
switch (true) {
case code === 0:
return "\uFFFD";
case (code >= 1 && code <= 31 || code === 127):
return `\\${code.toString(16)} `;
case (char === quote || char === "\\"):
return `\\${char}`;
default:
return char;
}
}).join("");
return `${quote}${escapedValue}${quote}`;
}
// src/hast.ts
function setProperty(node, propertyName, value) {
const properties = node.properties || {};
node.properties = properties;
if (value !== null) {
properties[propertyName] = value;
} else {
delete properties[propertyName];
}
}
function getClassNames(node) {
const stringOrArr = node.properties?.className;
if (!stringOrArr || stringOrArr === true)
return [];
if (Array.isArray(stringOrArr))
return stringOrArr.map((className) => className.toString());
return stringOrArr.toString().split(" ");
}
function addClassName(node, className) {
const classNames = getClassNames(node);
if (classNames.indexOf(className) === -1)
classNames.push(className);
setProperty(node, "className", classNames);
}
function removeClassName(node, className) {
const classNames = getClassNames(node);
const index = classNames.indexOf(className);
if (index === -1)
return;
classNames.splice(index, 1);
setProperty(node, "className", classNames);
}
function getInlineStyles(node) {
const styles = /* @__PURE__ */ new Map();
const styleString = node.properties?.style?.toString().trim() || "";
if (!styleString)
return styles;
const postCssOptions = { from: void 0 };
try {
const root = postcss.parse(styleString, postCssOptions);
root.each((node2) => {
if (node2.type === "decl")
styles.set(node2.prop, node2.value);
});
} catch (error) {
}
return styles;
}
function setInlineStyles(node, styles) {
const styleString = [...styles].map(
([prop, value]) => new Declaration({
prop,
value,
raws: {
between: ":"
}
}).toString()
).join(";");
setProperty(node, "style", styleString);
}
function setInlineStyle(node, cssProperty, value, valueFormat = "raw") {
const styles = getInlineStyles(node);
if (value !== null) {
styles.set(cssProperty, valueFormat === "string" ? serializeCssStringValue(value) : value);
} else {
styles.delete(cssProperty);
}
setInlineStyles(node, styles);
}
// src/common/annotation.ts
var AnnotationRenderPhaseOrder = ["earliest", "earlier", "normal", "later", "latest"];
var ExpressiveCodeAnnotation = class {
constructor({ inlineRange, renderPhase }) {
this.inlineRange = inlineRange;
this.renderPhase = renderPhase;
}
/**
* An optional name for the annotation. This can be used for debugging or logging purposes,
* or to allow other plugins to identify the annotation.
*/
name;
/**
* An optional range of columns within the line that this annotation applies to.
* If not provided, the annotation will apply to the entire line.
*/
inlineRange;
/**
* Determines the phase in which this annotation should be rendered.
* Rendering is done in phases, from `earliest` to `latest`.
* Annotations with the same phase are rendered in the order they were added.
*
* The earlier an annotation is rendered, the more likely it is to be split, modified
* or wrapped by later annotations. Syntax highlighting is rendered in the `earliest` phase
* to allow other annotations to wrap and modify the highlighted code.
*
* The default phase is `normal`.
*/
renderPhase;
};
var InlineStyleAnnotation = class extends ExpressiveCodeAnnotation {
name;
color;
italic;
bold;
underline;
styleVariantIndex;
constructor({ color, italic = false, bold = false, underline = false, styleVariantIndex, ...baseOptions }) {
super(baseOptions);
this.name = "Inline style";
this.color = color;
this.italic = italic;
this.bold = bold;
this.underline = underline;
this.styleVariantIndex = styleVariantIndex;
}
render({ nodesToTransform, styleVariants }) {
const newStyles = /* @__PURE__ */ new Map();
const addStylesForVariantIndex = (variantIndex) => {
const varPrefix = `--${variantIndex}`;
if (this.color)
newStyles.set(varPrefix, this.color);
if (this.italic)
newStyles.set(`${varPrefix}fs`, "italic");
if (this.bold)
newStyles.set(`${varPrefix}fw`, "bold");
if (this.underline)
newStyles.set(`${varPrefix}td`, "underline");
};
const variantIndices = this.styleVariantIndex !== void 0 ? [this.styleVariantIndex] : styleVariants.map((_, i) => i);
variantIndices.forEach(addStylesForVariantIndex);
if (newStyles.size === 0)
return nodesToTransform;
const buildStyleString = (styles) => {
return [...styles].map(([key, value]) => `${key}:${value}`).join(";");
};
const isInlineStyleNode = (node) => node.tagName === "span" && // Our inline style nodes have no class names
!getClassNames(node).length && // Our inline style nodes contain CSS variable declarations
node.properties?.style?.toString().startsWith("--");
const modifyExistingStyles = (node, remove = false) => {
const existingStyles = (node.properties?.style?.toString() || "").split(";").map((style) => {
const declParts = style.split(":");
return [declParts[0], declParts.slice(1).join(":")];
});
const modifiedStylesMap = new Map(existingStyles);
newStyles.forEach((value, key) => {
if (remove) {
modifiedStylesMap.delete(key);
} else {
modifiedStylesMap.set(key, value);
}
});
const modifiedStyles = buildStyleString(modifiedStylesMap);
if (modifiedStyles) {
setProperty(node, "style", modifiedStyles);
} else if (node.properties?.style) {
delete node.properties.style;
}
return modifiedStyles;
};
const removeNestedConflictingStyles = (node) => {
for (let childIdx = node.children?.length - 1; childIdx >= 0; childIdx--) {
const child = node.children[childIdx];
if (child.type === "element") {
if (isInlineStyleNode(child)) {
if (!modifyExistingStyles(child, true)) {
node.children.splice(childIdx, 1, ...child.children);
}
}
removeNestedConflictingStyles(child);
}
}
};
return nodesToTransform.map((node) => {
removeNestedConflictingStyles(node);
if (node.type === "element" && isInlineStyleNode(node)) {
modifyExistingStyles(node);
return node;
}
const transformedNode = h("span", { style: buildStyleString(newStyles) }, node);
return transformedNode;
});
}
};
function isInlineStyleAnnotation(annotation) {
return annotation instanceof InlineStyleAnnotation || annotation.name === "Inline style";
}
// src/helpers/meta-options.ts
var MetaOptions = class {
constructor(input) {
const { options, errors } = parseOptions(input);
this.#parsedOptions = options;
this.#errors = errors.length ? errors : void 0;
}
#parsedOptions;
#errors;
/**
* A list of error messages that occurred when parsing the meta string,
* or `undefined` if no errors occurred.
*/
get errors() {
return this.#errors;
}
/**
* Returns a list of meta options, optionally filtered by their key and/or {@link MetaOptionKind}.
*
* @param keyOrKeys
* Allows to filter the options by key. An empty string will return options without a key.
* A non-empty string will return options with a matching key (case-insensitive).
* An array of strings will return options with any of the matching keys.
* If omitted, no key-based filtering will be applied.
*
* @param kind
* Allows to filter the options by {@link MetaOptionKind}.
* If omitted, no kind-based filtering will be applied.
*/
list(keyOrKeys, kind) {
const filtered = this.#parsedOptions.filter((option) => {
if (kind !== void 0 && option.kind !== kind)
return false;
if (keyOrKeys === void 0)
return true;
const keys = Array.isArray(keyOrKeys) ? keyOrKeys : [keyOrKeys];
return keys.some((key) => key === "" && !option.key || option.key?.toLowerCase() === key.toLowerCase());
});
return filtered;
}
value(key, kind) {
if (!key)
throw new Error("You must specify a non-empty key when using getString, getRange, getRegExp or getBoolean.");
return this.list(key, kind)?.pop()?.value;
}
/**
* Returns the last string value with the given key (case-insensitive),
* or without a key by passing an empty string.
*/
getString(key) {
return this.value(key, "string");
}
/**
* Returns an array of all string values with the given keys (case-insensitive),
* or without a key by passing an empty string.
*/
getStrings(keyOrKeys) {
return this.list(keyOrKeys, "string").map((option) => option.value);
}
/**
* Returns the last range value (`{value}`) with the given key (case-insensitive),
* or without a key by passing an empty string.
*/
getRange(key) {
return this.value(key, "range");
}
/**
* Returns an array of all range values (`{value}`) with the given keys (case-insensitive),
* or without a key by passing an empty string.
*/
getRanges(keyOrKeys) {
return this.list(keyOrKeys, "range").map((option) => option.value);
}
/**
* Returns the last integer value with the given key (case-insensitive),
* or without a key by passing an empty string.
*/
getInteger(key) {
return this.getIntegers(key).pop();
}
/**
* Returns an array of all integer values with the given keys (case-insensitive),
* or without a key by passing an empty string.
*/
getIntegers(keyOrKeys) {
return this.list(keyOrKeys).map((option) => {
if (option.kind !== "string" && option.kind !== "range")
return NaN;
if (!/^-?\d+$/.test(option.value.trim()))
return NaN;
return parseInt(option.value, 10);
}).filter((value) => !isNaN(value));
}
/**
* Returns the last RegExp value (`/value/`) with the given key (case-insensitive),
* or without a key by passing an empty string.
*/
getRegExp(key) {
return this.value(key, "regexp");
}
/**
* Returns an array of all RegExp values (`/value/`) with the given keys (case-insensitive),
* or without a key by passing an empty string.
*/
getRegExps(keyOrKeys) {
return this.list(keyOrKeys, "regexp").map((option) => option.value);
}
/**
* Returns the last boolean value with the given key (case-insensitive).
*/
getBoolean(key) {
return this.value(key, "boolean");
}
};
function parseOptions(input, syntax = {
valueDelimiters: ["'", '"', "/", "{...}"],
keyValueSeparator: "="
}) {
const options = [];
const errors = [];
const delimitedValues = parseDelimitedValues(input, syntax);
let inputWithoutDelimited = input;
delimitedValues.forEach(({ index, fullMatch: raw, key, value, valueStartDelimiter, valueEndDelimiter }) => {
inputWithoutDelimited = inputWithoutDelimited.slice(0, index) + " ".repeat(raw.length) + inputWithoutDelimited.slice(index + raw.length);
if (valueStartDelimiter === "/") {
let regExp2;
try {
regExp2 = new RegExp(value, "gd");
} catch (_error) {
try {
regExp2 = new RegExp(value, "g");
} catch (error) {
const msg = error instanceof Error ? error.message : error;
errors.push(`Failed to parse option \`${raw.trim()}\`: ${msg}`);
return;
}
}
options.push({
index,
raw,
kind: "regexp",
key,
value: regExp2,
valueStartDelimiter,
valueEndDelimiter
});
return;
}
if (valueStartDelimiter === "{") {
options.push({
index,
raw,
kind: "range",
key,
value,
valueStartDelimiter,
valueEndDelimiter
});
return;
}
options.push({
index,
raw,
kind: "string",
key,
value,
valueStartDelimiter,
valueEndDelimiter
});
});
const escapedSeparator = escapeRegExp(syntax.keyValueSeparator).replace(/-/g, "\\-");
const regExp = new RegExp(`([^\\s${escapedSeparator}]+)(?:\\s*${escapedSeparator}\\s*(\\S+))?`, "g");
const simpleOptions = [...inputWithoutDelimited.matchAll(regExp)];
simpleOptions.forEach((match) => {
const index = match.index ?? 0;
const [raw, key, value] = match;
if (value === "true" || value === "false" || value === void 0) {
options.push({
index,
raw,
kind: "boolean",
key,
value: value !== "false",
valueStartDelimiter: "",
valueEndDelimiter: ""
});
} else {
options.push({
index,
raw,
kind: "string",
key,
value,
valueStartDelimiter: "",
valueEndDelimiter: ""
});
}
});
options.sort((a, b) => a.index - b.index);
return {
options,
errors
};
}
function parseDelimitedValues(input, syntax) {
const valueDelimiterPairs = syntax.valueDelimiters.map((valueDelimiter) => {
const parts = valueDelimiter.split("...");
const isPair = parts.length === 2;
return {
valueStartDelimiter: isPair ? parts[0] : valueDelimiter,
valueEndDelimiter: isPair ? parts[1] : valueDelimiter
};
});
const singleCharValueDelimiters = valueDelimiterPairs.map((pair) => pair.valueStartDelimiter).filter((delimiter) => delimiter.length === 1).join("");
const regExpParts = valueDelimiterPairs.map(({ valueStartDelimiter, valueEndDelimiter }) => {
const part = [
// Whitespace or start of string
`(?:\\s|^)`,
// Optional group for key name and key/value separator
[
// Start of non-capturing optional group
`(?:`,
// Key name (captured)
`([^\\s${escapeRegExp((singleCharValueDelimiters + syntax.keyValueSeparator).replace(/-/g, "\\-"))}]+)`,
// Optional whitespace
`\\s*`,
// Key/value separator (e.g. `=`)
escapeRegExp(syntax.keyValueSeparator),
// Optional whitespace
`\\s*`,
// End of non-capturing optional group
`)?`
],
// Value start delimiter
escapeRegExp(valueStartDelimiter),
// Value string (captured, can be an empty string),
// consisting of any of the following parts:
// - any character that is not a backslash
// - a backslash followed by any character
`((?:[^\\\\]|\\\\.)*?)`,
// Value end delimiter that is not escaped by a preceding `\`
`${escapeRegExp(valueEndDelimiter)}`,
// Whitespace or end of string
`(?=\\s|$)`
];
return part.flat().join("");
});
const regExp = new RegExp(regExpParts.join("|"), "g");
const matches2 = [...input.matchAll(regExp)];
return matches2.map((match) => {
const [fullMatch, ...keyValuePairs] = match;
const firstCaptureGroupIndex = keyValuePairs.findIndex((value2) => value2 !== void 0);
const delimiterPairIdx = Math.floor(firstCaptureGroupIndex / 2);
const { valueStartDelimiter, valueEndDelimiter } = valueDelimiterPairs[delimiterPairIdx];
const [key, escapedValue] = keyValuePairs.slice(delimiterPairIdx * 2, delimiterPairIdx * 2 + 2);
const escapedBackslashOrValueEndDelimiter = new RegExp(`\\\\(\\\\|${escapeRegExp(valueEndDelimiter)})`, "g");
const value = escapedValue.replace(escapedBackslashOrValueEndDelimiter, "$1");
return {
index: match.index ?? 0,
fullMatch,
key,
value,
valueStartDelimiter,
valueEndDelimiter
};
});
}
// src/common/logger.ts
var ExpressiveCodeLogger = class {
label;
logger;
constructor(logger = {}) {
this.label = logger.label ?? "expressive-code";
this.logger = logger;
}
debug(message) {
if (this.logger.debug) {
this.logger.debug(message);
} else {
console.debug(`[${this.label}] ${message}`);
}
}
info(message) {
if (this.logger.info) {
this.logger.info(message);
} else {
console.info(`[${this.label}] ${message}`);
}
}
warn(message) {
if (this.logger.warn) {
this.logger.warn(message);
} else {
console.warn(`[${this.label}] ${message}`);
}
}
error(message) {
if (this.logger.error) {
this.logger.error(message);
} else {
console.error(`[${this.label}] ${message}`);
}
}
};
function logErrorDetails(input) {
const pad = (lines) => lines.map((line) => ` ${line}`);
const getErrorDetails = (error2) => {
const lines = [];
const errMsgLines = error2.message.split(/\r?\n/);
lines.push(`${error2.name}: ${errMsgLines[0]}`, ...errMsgLines.slice(1));
if (error2.stack) {
lines.push(...error2.stack.split(/\r?\n/).slice(errMsgLines.length));
}
if (error2.cause instanceof Error) {
lines.push("Caused by:");
lines.push(...pad(getErrorDetails(error2.cause)));
}
return lines;
};
const error = input.error instanceof Error ? input.error : new Error(String(input.error));
const details = pad(getErrorDetails(error)).join("\n");
input.logger.error(`${input.prefix} Error details:
${details}
`);
}
// src/common/plugin-hooks.ts
async function runHooks(key, context, runner) {
const { plugins, config } = context;
for (const plugin of plugins) {
const hookFn = plugin.hooks?.[key];
if (!hookFn)
continue;
try {
await runner({ hookName: key, hookFn, plugin });
} catch (error) {
const msg = error instanceof Error ? error.message : error;
const prefix = `Plugin "${plugin.name}" caused an error in its "${key}" hook.`;
logErrorDetails({ logger: config.logger, prefix, error });
throw new Error(`${prefix} Error message: ${msg}`, { cause: error });
}
}
}
// src/common/style-settings.ts
var cssVarReplacements = /* @__PURE__ */ new Map([
["background", "bg"],
["foreground", "fg"],
["color", "col"],
["border", "brd"],
["padding", "pad"],
["margin", "marg"],
["radius", "rad"],
["opacity", "opa"],
["width", "wd"],
["height", "ht"],
["weight", "wg"],
["block", "blk"],
["inline", "inl"],
["bottom", "btm"],
["value", "val"],
["active", "act"],
["inactive", "inact"],
["highlight", "hl"],
["selection", "sel"],
["indicator", "ind"],
["shadow", "shd"],
["family", "fml"],
["transform", "trf"],
["decoration", "dec"],
["button", "btn"],
["editor", "ed"],
["terminal", "trm"],
["scrollbar", "sb"],
["toolbar", "tb"],
["gutter", "gtr"],
["titlebar", "ttb"],
["textMarkers", "tm"],
["frames", "frm"]
]);
function getCssVarName(styleSetting) {
let varName = styleSetting.replace(/\./g, "-");
const capitalize = (word) => word[0].toUpperCase() + word.slice(1);
cssVarReplacements.forEach((replacement, term) => {
const termRegExp = new RegExp(
[
// The lowercase term,
// preceded by a non-lowercase character or the beginning of the string,
// and followed by a non-lowercase character or the end of the string
`(?<=[^a-z]|^)${term}(?=[^a-z]|$)`,
// The capitalized term,
// preceded by a lowercase character or the beginning of the string,
// and followed by a non-lowercase character or the end of the string
`(?<=[a-z]|^)${capitalize(term)}(?=[^a-z]|$)`
].join("|"),
"g"
);
varName = varName.replace(termRegExp, (match) => match === term ? replacement : capitalize(replacement));
});
return `--ec-${varName}`;
}
var codeLineClass = "ec-line";
// src/internal/type-checks.ts
function isNumber(input) {
return typeof input === "number" && !isNaN(input);
}
function isString(input) {
return typeof input === "string";
}
function isBoolean(input) {
return typeof input === "boolean";
}
function isHastNode(node) {
return node?.type ? typeof node.type === "string" : false;
}
function isHastElement(node) {
return isHastNode(node) && node.type === "element";
}
function newTypeError(expectedTypeDescription, actualValue, fieldName) {
return new Error(`${fieldName ? `Invalid ${fieldName} value: ` : ""}Expected a valid ${expectedTypeDescription}, but got ${JSON.stringify(actualValue)}`);
}
// src/internal/render-line.ts
function splitLineAtAnnotationBoundaries(line) {
const textParts = [];
const partIndicesByAnnotation = /* @__PURE__ */ new Map();
const fullText = line.text;
const annotations = line.getAnnotations();
const annotationBoundaries = [
...new Set(
annotations.flatMap(({ inlineRange }) => {
if (!inlineRange)
return [];
return [inlineRange.columnStart, inlineRange.columnEnd];
})
)
].sort((a, b) => a - b);
let lastColumn = 0;
annotationBoundaries.forEach((column) => {
if (column === lastColumn)
return;
textParts.push(fullText.slice(lastColumn, column));
lastColumn = column;
});
if (lastColumn < fullText.length)
textParts.push(fullText.slice(lastColumn));
annotations.forEach((annotation) => {
if (!annotation.inlineRange)
return;
const { columnStart, columnEnd } = annotation.inlineRange;
const partIndices = [];
let partStart = 0;
textParts.forEach((part, partIndex) => {
const partEnd = partStart + part.length;
if (partStart >= columnStart && partEnd <= columnEnd) {
partIndices.push(partIndex);
}
partStart = partEnd;
});
partIndicesByAnnotation.set(annotation, partIndices);
});
return {
textParts,
partIndicesByAnnotation
};
}
function renderLineToAst({
line,
lineIndex,
gutterElements,
...restContext
}) {
const { textParts, partIndicesByAnnotation } = splitLineAtAnnotationBoundaries(line);
const partNodes = textParts.map((textPart) => h(null, [textPart]));
const annotations = [...line.getAnnotations()].sort(renderPhaseSortFn);
annotations.forEach((annotation, annotationIndex) => {
if (!annotation.inlineRange)
return;
const partIndices = partIndicesByAnnotation.get(annotation);
if (!partIndices)
throw new Error(`Failed to find inline annotation in part indices: ${JSON.stringify(annotation)}`);
if (partIndices.length > 1) {
const isPartiallyContainedInLaterAnnotations = annotations.slice(annotationIndex + 1).some((laterAnnotation) => {
if (!laterAnnotation.inlineRange)
return false;
const laterPartIndices = partIndicesByAnnotation.get(laterAnnotation);
if (!laterPartIndices)
return false;
const intersectingParts = laterPartIndices.filter((partIndex) => partIndices.includes(partIndex));
const isPartiallyContained = intersectingParts.length > 0 && intersectingParts.length < partIndices.length;
return isPartiallyContained;
});
if (!isPartiallyContainedInLaterAnnotations) {
const mergedNode = h(
null,
partIndices.map((partIndex) => partNodes[partIndex])
);
partNodes.splice(partIndices[0], partIndices.length, mergedNode);
const indicesToRemove = partIndices.length - 1;
const firstPartIndex = partIndices[0];
const lastPartIndex = partIndices[partIndices.length - 1];
partIndicesByAnnotation.forEach((partIndicesToCheck) => {
let anyChanges = false;
const updatedIndices = partIndicesToCheck.map((partIndex) => {
if (partIndex <= firstPartIndex)
return partIndex;
anyChanges = true;
if (partIndex > lastPartIndex)
return partIndex - indicesToRemove;
return NaN;
}).filter((partIndex) => !isNaN(partIndex));
if (anyChanges) {
partIndicesToCheck.splice(0, partIndicesToCheck.length, ...updatedIndices);
}
});
}
}
const renderInput = partIndices.map((partIndex) => partNodes[partIndex]);
const renderOutput = annotation.render({ nodesToTransform: [...renderInput], line, lineIndex, ...restContext });
validateAnnotationRenderOutput(renderOutput, renderInput.length);
partIndices.forEach((partIndex, index) => {
partNodes[partIndex] = renderOutput[index];
});
});
const sortedGutterElements = [...gutterElements].sort((a, b) => renderPhaseSortFn(a.gutterElement, b.gutterElement));
const renderedGutterElements = sortedGutterElements.map(({ pluginName, gutterElement }) => {
try {
const node = gutterElement.renderLine({ ...restContext, line, lineIndex });
if (!isHastElement(node))
throw new Error(`renderLine function did not return a valid HAST Element node: ${JSON.stringify(node)}`);
return node;
} catch (error) {
const msg = error instanceof Error ? error.message : error;
throw new Error(`Plugin "${pluginName}" failed to render a gutter element. Error message: ${msg}`, { cause: error });
}
});
let lineNode = h(`div.${codeLineClass}`);
if (renderedGutterElements.length) {
lineNode.children.push(h("div.gutter", renderedGutterElements));
}
lineNode.children.push(h("div.code", partNodes.length > 0 ? partNodes : h(null, "\n")));
annotations.forEach((annotation) => {
if (annotation.inlineRange)
return;
const renderOutput = annotation.render({ nodesToTransform: [lineNode], line, lineIndex, ...restContext });
validateAnnotationRenderOutput(renderOutput, 1);
lineNode = renderOutput[0];
if (!isHastElement(lineNode)) {
throw newTypeError("hast Element", lineNode, "line-level annotation render output");
}
});
return lineNode;
}
function getRenderEmptyLineFn(context) {
return () => {
const { gutterElements } = context;
const sortedGutterElements = [...gutterElements].sort((a, b) => renderPhaseSortFn(a.gutterElement, b.gutterElement));
const renderedGutterElements = sortedGutterElements.map(({ pluginName, gutterElement }) => {
try {
const node = gutterElement.renderPlaceholder();
if (!isHastElement(node))
throw new Error(`renderPlaceholder function did not return a valid HAST Element node: ${JSON.stringify(node)}`);
return node;
} catch (error) {
const msg = error instanceof Error ? error.message : error;
throw new Error(`Plugin "${pluginName}" failed to render a gutter element placeholder. Error message: ${msg}`, { cause: error });
}
});
const lineAst = h(`div.${codeLineClass}`);
const gutterWrapper = renderedGutterElements.length ? h("div.gutter", renderedGutterElements) : void 0;
if (gutterWrapper)
lineAst.children.push(gutterWrapper);
const codeWrapper = h("div.code");
lineAst.children.push(codeWrapper);
return {
lineAst,
gutterWrapper,
codeWrapper
};
};
}
function renderPhaseSortFn(a, b) {
const indexA = AnnotationRenderPhaseOrder.indexOf(a.renderPhase || "normal");
const indexB = AnnotationRenderPhaseOrder.indexOf(b.renderPhase || "normal");
return indexA - indexB;
}
function validateAnnotationRenderOutput(nodes, expectedLength) {
if (!Array.isArray(nodes) || nodes.length !== expectedLength)
throw new Error(`Expected annotation render function to return an array of ${expectedLength} node(s), but got ${JSON.stringify(nodes)}.`);
nodes.forEach((node, nodeIndex) => {
if (!node || !node.type)
throw new Error(`Annotation render function returned an invalid node at index ${nodeIndex}: ${JSON.stringify(node)}`);
});
}
// src/internal/render-block.ts
async function renderBlock({
codeBlock,
groupContents,
locale,
config,
plugins,
cssVar,
cssVarName,
styleVariants
}) {
const state = {
canEditAnnotations: true,
canEditCode: true,
canEditLanguage: true,
canEditMetadata: true
};
codeBlock.state = state;
const blockStyles = [];
const gutterElements = [];
const runHooksContext = {
plugins,
config
};
const baseContext = {
codeBlock,
groupContents,
locale,
config,
cssVar,
cssVarName,
styleVariants
};
const runBeforeRenderingHooks = async (key) => {
await runHooks(key, runHooksContext, async ({ hookFn, plugin }) => {
await hookFn({
...baseContext,
addStyles: (styles) => blockStyles.push({ pluginName: plugin.name, styles }),
addGutterElement: (gutterElement) => {
if (!gutterElement || typeof gutterElement !== "object")
throw newTypeError("object", gutterElement, "gutterElement");
if (typeof gutterElement.renderLine !== "function")
throw newTypeError('"function" type', typeof gutterElement.renderLine, "gutterElement.renderLine");
if (gutterElement.renderPhase && AnnotationRenderPhaseOrder.indexOf(gutterElement.renderPhase) === -1)
throw newTypeError("AnnotationRenderPhase", gutterElement.renderPhase, "gutterElement.renderPhase");
gutterElements.push({ pluginName: plugin.name, gutterElement });
}
});
});
};
state.canEditCode = false;
await runBeforeRenderingHooks("preprocessLanguage");
state.canEditLanguage = false;
applyDefaultProps(codeBlock, config);
await runBeforeRenderingHooks("preprocessMetadata");
state.canEditCode = true;
await runBeforeRenderingHooks("preprocessCode");
await runBeforeRenderingHooks("performSyntaxAnalysis");
await runBeforeRenderingHooks("postprocessAnalyzedCode");
state.canEditCode = false;
await runBeforeRenderingHooks("annotateCode");
await runBeforeRenderingHooks("postprocessAnnotations");
state.canEditMetadata = false;
state.canEditAnnotations = false;
const lines = codeBlock.getLines();
const renderedAstLines = [];
const renderEmptyLine = getRenderEmptyLineFn({ gutterElements, ...baseContext });
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
const line = lines[lineIndex];
const lineRenderData = {
lineAst: renderLineToAst({ line, lineIndex, gutterElements, ...baseContext })
};
if (codeBlock.props.wrap && codeBlock.props.preserveIndent !== false) {
const indent = line.text.match(/^\s*/)?.[0].length ?? 0;
if (indent > 0)
setInlineStyle(lineRenderData.lineAst, "--ecIndent", `${indent}ch`);
}
await runHooks("postprocessRenderedLine", runHooksContext, async ({ hookFn, plugin }) => {
await hookFn({
...baseContext,
addStyles: (styles) => blockStyles.push({ pluginName: plugin.name, styles }),
line,
lineIndex,
renderData: lineRenderData,
renderEmptyLine
});
if (!isHastElement(lineRenderData.lineAst)) {
throw newTypeError("hast Element", lineRenderData.lineAst, "lineAst");
}
});
renderedAstLines.push(lineRenderData.lineAst);
}
const blockRenderData = {
blockAst: buildCodeBlockAstFromRenderedLines(codeBlock, renderedAstLines)
};
await runHooks("postprocessRenderedBlock", runHooksContext, async ({ hookFn, plugin }) => {
await hookFn({
...baseContext,
addStyles: (styles) => blockStyles.push({ pluginName: plugin.name, styles }),
renderData: blockRenderData,
renderEmptyLine
});
if (!isHastElement(blockRenderData.blockAst)) {
throw newTypeError("hast Element", blockRenderData.blockAst, "blockAst");
}
});
return {
renderedBlockAst: blockRenderData.blockAst,
blockStyles
};
}
function buildCodeBlockAstFromRenderedLines(codeBlock, renderedLines) {
const preProperties = { dataLanguage: codeBlock.language || "plaintext" };
const preElement = h("pre", preProperties, h("code", renderedLines));
if (codeBlock.props.wrap) {
const maxLineLength = codeBlock.getLines().reduce((max, line) => Math.max(max, line.text.length), 0);
addClassName(preElement, "wrap");
setInlineStyle(preElement, "--ecMaxLine", `${maxLineLength}ch`);
}
return preElement;
}
function applyDefaultProps(codeBlock, config) {
const { overridesByLang = {}, ...baseDefaults } = config.defaultProps;
const mergedDefaults = { ...baseDefaults };
Object.keys(overridesByLang).forEach((key) => {
const langs = key.split(",").map((lang) => lang.trim());
if (langs.includes(codeBlock.language)) {
Object.assign(mergedDefaults, overridesByLang[key]);
}
});
const defaultKeys = Object.keys(mergedDefaults);
defaultKeys.forEach((key) => {
if (codeBlock.props[key] === void 0)
codeBlock.props[key] = mergedDefaults[key];
});
}
function validateExpressiveCodeProcessingState(state) {
const isValid = state && // Expect all properties to be defined and booleans
isBoolean(state.canEditCode) && isBoolean(state.canEditLanguage) && isBoolean(state.canEditMetadata) && isBoolean(state.canEditAnnotations);
if (!isValid)
throw newTypeError("ExpressiveCodeProcessingState", state);
}
// src/internal/ranges.ts
function getAbsoluteRange({
start,
end,
rangeMax
}) {
start = Math.min(start ?? 0, rangeMax);
end = Math.min(end ?? rangeMax, rangeMax);
if (start < 0)
start = Math.max(start + rangeMax, 0);
if (end < 0)
end = Math.max(end + rangeMax, 0);
return [start, end];
}
// src/common/line.ts
var ExpressiveCodeLine = class {
constructor(text) {
if (typeof text !== "string")
throw new Error(`Expected code line text to be a string, but got ${JSON.stringify(text)}.`);
this.#text = text;
}
#text;
get text() {
return this.#text;
}
#parent;
get parent() {
return this.#parent;
}
set parent(value) {
if (!(value instanceof ExpressiveCodeBlock))
throw new Error("When setting the parent of a code line, you must specify a valid code block instance.");
if (this.#parent) {
if (this.#parent === value)
return;
throw new Error(`You cannot change the parent of a code line after it has been added to a code block.`);
}
this.#parent = value;
}
#annotations = [];
getAnnotations() {
const matchingAnnotations = this.#annotations.filter((annotation) => !!annotation);
return Object.freeze(matchingAnnotations);
}
addAnnotation(annotation) {
validateExpressiveCodeAnnotation(annotation);
if (this.#parent?.state?.canEditAnnotations === false)
throw new Error("Cannot edit code line annotations in the current state.");
this.#annotations.push(annotation);
}
deleteAnnotation(annotation) {
validateExpressiveCodeAnnotation(annotation);
if (this.#parent?.state?.canEditAnnotations === false)
throw new Error("Cannot edit code line annotations in the current state.");
const index = this.#annotations.indexOf(annotation);
if (index === -1)
throw new Error(
`Failed to delete annotation as it was not found (name=${JSON.stringify(annotation.constructor.name)}, inlineRange=${JSON.stringify(annotation.inlineRange)})`
);
this.#annotations.splice(index, 1);
}
editText(columnStart, columnEnd, newText) {
if (columnStart !== void 0 && !isNumber(columnStart))
throw newTypeError("number", columnStart);
if (columnEnd !== void 0 && !isNumber(columnEnd))
throw newTypeError("number", columnEnd);
if (!isString(newText))
throw newTypeError("string", newText);
if (this.#parent?.state?.canEditCode === false)
throw new Error("Cannot edit code line text in the current state.");
const [editStart, editEnd] = getAbsoluteRange({ start: columnStart, end: columnEnd, rangeMax: this.#text.length });
const editDelta = newText.length - (editEnd - editStart);
for (let index = this.#annotations.length - 1; index >= 0; index--) {
const annotation = this.#annotations[index];
if (!annotation.inlineRange)
continue;
const { columnStart: annotationStart, columnEnd: annotationEnd } = annotation.inlineRange;
if (annotationEnd < editStart)
continue;
if (annotationStart > editEnd) {
annotation.inlineRange.columnStart += editDelta;
annotation.inlineRange.columnEnd += editDelta;
continue;
}
if (editStart >= annotationStart && editEnd <= annotationEnd) {
annotation.inlineRange.columnEnd += editDelta;
continue;
}
if (editStart <= annotationStart && editEnd >= annotationEnd) {
this.#annotations.splice(index, 1);
continue;
}
if (editStart > annotationStart) {
annotation.inlineRange.columnEnd = editStart;
} else {
annotation.inlineRange.columnStart = editEnd + editDelta;
annotation.inlineRange.columnEnd += editDelta;
}
}
this.#text = this.text.slice(0, editStart) + newText + this.text.slice(editEnd);
return this.text;
}
};
function validateExpressiveCodeInlineRange(inlineRange) {
if (!isNumber(inlineRange.columnStart) || !isNumber(inlineRange.columnEnd))
throw newTypeError("ExpressiveCodeAnnotation", inlineRange, "inlineRange");
}
function validateExpressiveCodeAnnotation(annotation) {
if (typeof annotation?.render !== "function")
throw newTypeError("ExpressiveCodeAnnotation", annotation?.render, "render");
if (annotation.inlineRange)
validateExpressiveCodeInlineRange(annotation.inlineRange);
}
// src/common/block.ts
var ExpressiveCodeBlock = class {
constructor(options) {
const { code, language, meta = "", props, locale, parentDocument } = options;
if (!isString(code) || !isString(language) || !isString(meta))
throw newTypeError("object of type ExpressiveCodeBlockOptions", options);
this.#lines = [];
this.#language = language;
this.#meta = meta;
this.#metaOptions = new MetaOptions(meta);
this.#props = props || {};
this.#locale = locale;
this.#parentDocument = parentDocument;
const lines = code.split(/\r?\n/).map((line) => line.trimEnd());
while (lines.length && !lines[0].length)
lines.shift();
while (lines.length && !lines[lines.length - 1].length)
lines.pop();
if (lines.length)
this.insertLines(0, lines);
this.props.wrap = this.metaOptions.getBoolean("wrap") ?? this.props.wrap;
this.props.preserveIndent = this.metaOptions.getBoolean("preserveIndent") ?? this.props.preserveIndent;
}
/**
* This field exists to ensure that only actual class instances are accepted
* as the type `ExpressiveCodeBlock` by TypeScript. Without this workaround,
* plain objects with the same structure would be accepted, but fail at runtime.
*/
_requireInstance = Symbol("ExpressiveCodeBlock");
#lines;
#language;
#meta;
#metaOptions;
#props;
#locale;
#parentDocument;
#state;
/**
* Provides read-only access to the code block's plaintext contents.
*/
get code() {
return this.#lines.map((line) => line.text).join("\n");
}
get language() {
return this.#language;
}
/**
* Allows getting and setting the code block's language.
*
* Setting this property may throw an error if not allowed in the current {@link state}.
*/
set language(value) {
if (this.#state?.canEditLanguage === false)
throw new Error('Cannot edit code block property "language" in the current state.');
this.#language = value;
}
get meta() {
return this.#meta;
}
/**
* Allows getting or setting the code block's meta string. In markdown or MDX documents,
* this is the part of the code block's opening fence that comes after the language name.
*
* Setting this property may throw an error if not allowed in the current {@link state}.
*/
set meta(value) {
if (this.#state?.canEditMetadata === false)
throw new Error('Cannot edit code block property "meta" in the current state.');
this.#meta = value;
this.#metaOptions = new MetaOptions(value);
}
/**
* Provides read-only access to the parsed version of the block's {@link meta} string.
*/
get metaOptions() {
return this.#metaOptions;
}
/**
* Provides access to the code block's props.
*
* To allow users to set these props through the meta string, plugins can use the
* `preprocessMetadata` hook to read `metaOptions` and update their props accordingly.
*
* Props can be modified until rendering starts and become read-only afterwards.
*/
get props() {
if (this.#state?.canEditMetadata === false) {
return Object.freeze({ ...this.#props });
}
return this.#props;
}
/**
* Allows getting the code block's locale (e.g. `en-US` or `de-DE`). It is used by plugins
* to display localized strings depending on the language of the containing page.
*
* Integrations like `rehype-expressive-code` support multi-language sites by allowing you
* to provide custom logic to determine a block's locale (e.g. based on its parent document).
*
* If no locale is defined here, `ExpressiveCodeEngine` will render the code block
* using the `defaultLocale` provided in its configuration.
*/
get locale() {
return this.#locale;
}
/**
* Provides read-only access to optional data about the parent document
* the code block is located in.
*
* Integrations like `rehype-expressive-code` can provide this information based on
* the source document being processed. There may be cases where no document is available,
* e.g. when the code block was created dynamically.
*/
get parentDocument() {
return this.#parentDocument;
}
/**
* Provides read-only access to the code block's processing state.
*
* The processing state controls which properties of the code block can be modified.
* The engine updates it automatically during rendering.
*/
get state() {
if (this.#state) {
const result = { ...this.#state };
Object.freeze(result);
return result;
}
}
/**
* @internal
*/
set state(value) {
validateExpressiveCodeProcessingState(value);
if (this.#state) {
if (this.#state === value)
return;
throw new Error(`You cannot change the state object of a code block after assigning it once.`);
}
this.#state = value;
}
/**
* Returns the line at the given index, or `undefined` if the index is out of range.
*/
getLine(index) {
if (!isNumber(index) || index < 0)
throw new Error("Line index must be a non-negative number.");
return this.getLines(index, index + 1)[0];
}
/**
* Returns a readonly array of lines starting at the given index and ending before
* the given index (exclusive). The indices support the same syntax as JavaScript’s
* `Array.slice` method.
*/
getLines(startIndex, endIndex) {
return Object.freeze(this.#lines.slice(startIndex, endIndex));
}
/**
* Deletes the line at the given index.
*
* May throw an error if not allowed in the current {@link state}.
*/
deleteLine(index) {
this.deleteLines([index]);
}
/**
* Deletes the lines at the given indices.
*
* This function automatically sorts the indices in descending order before deleting the lines,
* so you do not need to worry about indices shifting after deleting a line.
*
* May throw an error if not allowed in the current {@link state}.
*/
deleteLines(indices) {
if (!Array.isArray(indices) || indices.length === 0 || indices.some((index) => !isNumber(index) || index < 0))
throw newTypeError("non-empty non-negative number[]", indices);
if (this.#state?.canEditCode === false)
throw new Error("Cannot delete code block lines in the current state.");
const sorted = [...indices].sort((a, b) => b - a);
let lastIndex;
sorted.forEach((index) => {
if (lastIndex === index)
throw new Error(`A batch of lines to delete cannot contain the same index twice. Given indices: ${JSON.stringify(indices)}`);
lastIndex = index;
const isValidIndex = index >= 0 && index < this.#lines.length;
if (!isValidIndex)
throw new Error(`Cannot delete invalid index ${JSON.stringify(index)} from line array (length=${this.#lines.length}). Given indices: ${JSON.stringify(indices)}`);
this.#lines.splice(index, 1);
});
}
/**
* Inserts a new line at the given index.
*
* May throw an error if not allowed in the current {@link state}.
*/
insertLine(index, textLine) {
return this.insertLines(index, [textLine])[0];
}
/**
* Inserts multiple new lines at the given index.
*
* May throw an error if not allowed in the current {@link state}.
*/
insertLines(index, textLines) {
if (!isNumber(index) || index < 0)
throw newTypeError("non-negative number", index);
if (!Array.isArray(textLines) || textLines.length === 0 || textLines.some((textLine) => !isString(textLine)))
throw newTypeError("non-empty string[]", textLines);
if (this.#state?.canEditCode === false)
throw new Error("Cannot insert code block lines in the current state.");
const isValidIndex = index >= 0 && index <= this.#lines.length;
if (!isValidIndex)
throw new Error(`Cannot insert at invalid index ${JSON.stringify(index)} into line array (length=${this.#lines.length}).`);
const lineInstances = textLines.map((text) => {
const line = new ExpressiveCodeLine(text);
line.parent = this;
return line;
});
this.#lines.splice(index, 0, ...lineInstances);
return lineInstances;
}
};
// ../../../node_modules/.pnpm/shiki@1.1.7/node_modules/shiki/dist/themes/github-dark.mjs
var githubDark = Object.freeze({
"colors": {
"activityBar.activeBorder": "#f9826c",
"activityBar.background": "#24292e",
"activityBar.border": "#1b1f23",
"activityBar.foreground": "#e1e4e8",
"activityBar.inactiveForeground": "#6a737d",
"activityBarBadge.background": "#0366d6",
"activityBarBadge.foreground": "#fff",
"badge.background": "#044289",
"badge.foreground": "#c8e1ff",
"breadcrumb.activeSelectionForeground": "#d1d5da",
"breadcrumb.focusForeground": "#e1e4e8",
"breadcrumb.foreground": "#959da5",
"breadcrumbPicker.background": "#2b3036",
"button.background": "#176f2c",
"button.foreground": "#dcffe4",
"button.hoverBackground": "#22863a",
"button.secondaryBackground": "#444d56",
"button.secondaryForeground": "#fff",
"button.secondaryHoverBackground": "#586069",
"checkbox.background": "#444d56",
"checkbox.border": "#1b1f23",
"debugToolBar.background": "#2b3036",
"descriptionForeground": "#959da5",
"diffEditor.insertedTextBackground": "#28a74530",
"diffEditor.removedTextBackground": "#d73a4930",
"dropdown.background": "#2f363d",
"dropdown.border": "#1b1f23",
"dropdown.foreground": "#e1e4e8",
"dropdown.listBackground": "#24292e",
"editor.background": "#24292e",
"editor.findMatchBackground": "#ffd33d44",
"editor.findMatchHighlightBackground": "#ffd33d22",
"editor.focusedStackFrameHighlightBackground": "#2b6a3033",
"editor.foldBackground": "#58606915",
"editor.foreground": "#e1e4e8",
"editor.inactiveSelectionBackground": "#3392FF22",
"editor.lineHighlightBackground": "#2b3036",
"editor.linkedEditingBackground": "#3392FF22",
"editor.selectionBackground": "#3392FF44",
"editor.selectionHighlightBackground": "#17E5E633",
"editor.selectionHighlightBorder": "#17E5E600",
"editor.stackFrameHighlightBackground": "#C6902625",
"editor.wordHighlightBackground": "#17E5E600",
"editor.wordHighlightBorder": "#17E5E699",
"editor.wordHighlightStrongBackground": "#17E5E600",
"editor.wordHighlightStrongBorder": "#17E5E666",
"editorBracketHighlight.foreground1": "#79b8ff",
"editorBracketHighlight.foreground2": "#ffab70",
"editorBracketHighlight.foreground3": "#b392f0",
"editorBracketHighlight.foreground4": "#79b8ff",
"editorBracketHighlight.foreground5": "#ffab70",
"editorBracketHighlight.foreground6": "#b392f0",
"editorBracketMatch.background": "#17E5E650",
"editorBracketMatch.border": "#17E5E600",
"editorCursor.foreground": "#c8e1ff",
"editorError.foreground": "#f97583",
"editorGroup.border": "#1b1f23",
"editorGroupHeader.tabsBackground": "#1f2428",
"editorGroupHeader.tabsBorder": "#1b1f23",
"editorGutter.addedBackground": "#28a745",
"editorGutter.deletedBackground": "#ea4a5a",
"editorGutter.modifiedBackground": "#2188ff",
"editorIndentGuide.activeBackground": "#444d56",
"editorIndentGuide.background": "#2f363d",
"editorLineNumber.activeForeground": "#e1e4e8",
"editorLineNumber.foreground": "#444d56",
"editorOverviewRuler.border": "#1b1f23",
"editorWarning.foreground": "#ffea7f",
"editorWhitespace.foreground": "#444d56",
"editorWidget.background": "#1f2428",
"errorForeground": "#f97583",
"focusBorder": "#005cc5",
"foreground": "#d1d5da",
"gitDecoration.addedResourceForeground": "#34d058",
"gitDecoration.conflictingResourceForeground": "#ffab70",
"gitDecoration.deletedResourceForeground": "#ea4a5a",
"gitDecoration.ignoredResourceForeground": "#6a737d",
"gitDecoration.modifiedResourceForeground": "#79b8ff",
"gitDecoration.submoduleResourceForeground": "#6a737d",
"gitDecoration.untrackedResourceForeground": "#34d058",
"input.background": "#2f363d",
"input.border": "#1b1f23",
"input.foreground": "#e1e4e8",
"input.placeholderForeground": "#959da5",
"list.activeSelectionBackground": "#39414a",
"list.activeSelectionForeground": "#e1e4e8",
"list.focusBackground": "#044289",
"list.hoverBackground": "#282e34",
"list.hoverForeground": "#e1e4e8",
"list.inactiveFocusBackground": "#1d2d3e",
"list.inactiveSelectionBackground": "#282e34",
"list.inactiveSelectionForeground": "#e1e4e8",
"notificationCenterHeader.background": "#24292e",
"notificationCenterHeader.foreground": "#959da5",
"notifications.background": "#2f363d",
"notifications.border": "#1b1f23",
"notifications.foreground": "#e1e4e8",
"notificationsErrorIcon.foreground": "#ea4a5a",
"notificationsInfoIcon.foreground": "#79b8ff",
"notificationsWarningIcon.foreground": "#ffab70",
"panel.background": "#1f2428",
"panel.border": "#1b1f23",
"panelInput.border": "#2f363d",
"panelTitle.activeBorder": "#f9826c",
"panelTitle.activeForeground": "#e1e4e8",
"panelTitle.inactiveForeground": "#959da5",
"peekViewEditor.background": "#1f242888",
"peekViewEditor.matchHighlightBackground": "#ffd33d33",
"peekViewResult.background": "#1f2428",
"peekViewResult.matchHighlightBackground": "#ffd33d33",
"pickerGroup.border": "#444d56",
"pickerGroup.foreground": "#e1e4e8",
"progressBar.background": "#0366d6",
"quickInput.background": "#24292e",
"quickInput.foreground": "#e1e4e8",
"scrollbar.shadow": "#0008",
"scrollbarSlider.activeBackground": "#6a737d88",
"scrollbarSlider.background": "#6a737d33",
"scrollbarSlider.hoverBackground": "#6a737d44",
"settings.headerForeground": "#e1e4e8",
"settings.modifiedItemIndicator": "#0366d6",
"sideBar.background": "#1f2428",
"sideBar.border": "#1b1f23",
"sideBar.foreground": "#d1d5da",
"sideBarSectionHeader.background": "#1f2428",
"sideBarSectionHeader.border": "#1b1f23",
"sideBarSectionHeader.foreground": "#e1e4e8",
"sideBarTitle.foreground": "#e1e4e8",
"statusBar.background": "#24292e",
"statusBar.border": "#1b1f23",
"statusBar.debuggingBackground": "#931c06",
"statusBar.debuggingForeground": "#fff",
"statusBar.foreground": "#d1d5da",
"statusBar.noFolderBackground": "#24292e",
"statusBarItem.prominentBackground": "#282e34",
"statusBarItem.remoteBackground": "#24292e",
"statusBarItem.remoteForeground": "#d1d5da",
"tab.activeBackground": "#24292e",
"tab.activeBorder": "#24292e",
"tab.activeBorderTop": "#f9826c",
"tab.activeForeground": "#e1e4e8",
"tab.border": "#1b1f23",
"tab.hoverBackground": "#24292e",
"tab.inactiveBackground": "#1f2428",
"tab.inactiveForeground": "#959da5",
"tab.unfocusedActiveBorder": "#24292e",
"tab.unfocusedActiveBorderTop": "#1b1f23",
"tab.unfocusedHoverBackground": "#24292e",
"terminal.ansiBlack": "#586069",
"terminal.ansiBlue": "#2188ff",
"terminal.ansiBrightBlack": "#959da5",
"terminal.ansiBrightBlue": "#79b8ff",
"terminal.ansiBrightCyan": "#56d4dd",
"terminal.ansiBrightGreen": "#85e89d",
"terminal.ansiBrightMagenta": "#b392f0",
"terminal.ansiBrightRed": "#f97583",
"terminal.ansiBrightWhite": "#fafbfc",
"terminal.ansiBrightYellow": "#ffea7f",
"terminal.ansiCyan": "#39c5cf",
"terminal.ansiGreen": "#34d058",
"terminal.ansiMagenta": "#b392f0",
"terminal.ansiRed": "#ea4a5a",
"terminal.ansiWhite": "#d1d5da",
"terminal.ansiYellow": "#ffea7f",
"terminal.foreground": "#d1d5da",
"terminal.tab.activeBorder": "#f9826c",
"terminalCursor.background": "#586069",
"terminalCursor.foreground": "#79b8ff",
"textBlockQuote.background": "#24292e",
"textBlockQuote.border": "#444d56",
"textCodeBlock.background": "#2f363d",
"textLink.activeForeground": "#c8e1ff",
"textLink.foreground": "#79b8ff",
"textPreformat.foreground": "#d1d5da",
"textSeparator.foreground": "#586069",
"titleBar.activeBackground": "#24292e",
"titleBar.activeForeground": "#e1e4e8",
"titleBar.border": "#1b1f23",
"titleBar.inactiveBackground": "#1f2428",
"titleBar.inactiveForeground": "#959da5",
"tree.indentGuidesStroke": "#2f363d",
"welcomePage.buttonBackground": "#2f363d",
"welcomePage.buttonHoverBackground": "#444d56"
},
"displayName": "GitHub Dark",
"name": "github-dark",
"semanticHighlighting": true,
"tokenColors": [
{
"scope": [
"comment",
"punctuation.definition.comment",
"string.comment"
],
"settings": {
"foreground": "#6a737d"
}
},
{
"scope": [
"constant",
"entity.name.constant",
"variable.other.constant",
"variable.other.enummember",
"variable.language"
],
"settings": {
"foreground": "#79b8ff"
}
},
{
"scope": [
"entity",
"entity.name"
],
"settings": {
"foreground": "#b392f0"
}
},
{
"scope": "variable.parameter.function",
"settings": {
"foreground": "#e1e4e8"
}
},
{
"scope": "entity.name.tag",
"settings": {
"foreground": "#85e89d"
}
},
{
"scope": "keyword",
"settings": {
"foreground": "#f97583"
}
},
{
"scope": [
"storage",
"storage.type"
],
"settings": {
"foreground": "#f97583"
}
},
{
"scope": [
"storage.modifier.package",
"storage.modifier.import",
"storage.type.java"
],
"settings": {
"foreground": "#e1e4e8"
}
},
{
"scope": [
"string",
"punctuation.definition.string",
"string punctuation.section.embedded source"
],
"settings": {
"foreground": "#9ecbff"
}
},
{
"scope": "support",
"settings": {
"foreground": "#79b8ff"
}
},
{
"scope": "meta.property-name",
"settings": {
"foreground": "#79b8ff"
}
},
{
"scope": "variable",
"settings": {
"foreground": "#ffab70"
}
},
{
"scope": "variable.other",
"settings": {
"foreground": "#e1e4e8"
}
},
{
"scope": "invalid.broken",
"settings": {
"fontStyle": "italic",
"foreground": "#fdaeb7"
}
},
{
"scope": "invalid.deprecated",
"settings": {
"fontStyle": "italic",
"foreground": "#fdaeb7"
}
},
{
"scope": "invalid.illegal",
"settings": {
"fontStyle": "italic",
"foreground": "#fdaeb7"
}
},
{
"scope": "invalid.unimplemented",
"settings": {
"fontStyle": "italic",
"foreground": "#fdaeb7"
}
},
{
"scope": "carriage-return",
"settings": {
"background": "#f97583",
"content": "^M",
"fontStyle": "italic underline",
"foreground": "#24292e"
}
},
{
"scope": "message.error",
"settings": {
"foreground": "#fdaeb7"
}
},
{
"scope": "string variable",
"settings": {
"foreground": "#79b8ff"
}
},
{
"scope": [
"source.regexp",
"string.regexp"
],
"settings": {
"foreground": "#dbedff"
}
},
{
"scope": [
"string.regexp.character-class",
"string.regexp constant.character.escape",
"string.regexp source.ruby.embedded",
"string.regexp string.regexp.arbitrary-repitition"
],
"settings": {
"foreground": "#dbedff"
}
},
{
"scope": "string.regexp constant.character.escape",
"settings": {
"fontStyle": "bold",
"foreground": "#85e89d"
}
},
{
"scope": "support.constant",
"settings": {
"foreground": "#79b8ff"
}
},
{
"scope": "support.variable",
"settings": {
"foreground": "#79b8ff"
}
},
{
"scope": "meta.module-reference",
"settings": {
"foreground": "#79b8ff"
}
},
{
"scope": "punctuation.definition.list.begin.markdown",
"settings": {
"foreground": "#ffab70"
}
},
{
"scope": [
"markup.heading",
"markup.heading entity.name"
],
"settings": {
"fontStyle": "bold",
"foreground": "#79b8ff"
}
},
{
"scope": "markup.quote",
"settings": {
"foreground": "#85e89d"
}
},
{
"scope": "markup.italic",
"settings": {
"fontStyle": "italic",
"foreground": "#e1e4e8"
}
},
{
"scope": "markup.bold",
"settings": {
"fontStyle": "bold",
"foreground": "#e1e4e8"
}
},
{
"scope": [
"markup.underline"
],
"settings": {
"fontStyle": "underline"
}
},
{
"scope": [
"markup.strikethrough"
],
"settings": {
"fontStyle": "strikethrough"
}
},
{
"scope": "markup.inline.raw",
"settings": {
"foreground": "#79b8ff"
}
},
{
"scope": [
"markup.deleted",
"meta.diff.header.from-file",
"punctuation.definition.deleted"
],
"settings": {
"background": "#86181d",
"foreground": "#fdaeb7"
}
},
{
"scope": [
"markup.inserted",
"meta.diff.header.to-file",
"punctuation.definition.inserted"
],
"settings": {
"background": "#144620",
"foreground": "#85e89d"
}
},
{
"scope": [
"markup.changed",
"punctuation.definition.changed"
],
"settings": {
"background": "#c24e00",
"foreground": "#ffab70"
}
},
{
"scope": [
"markup.ignored",
"markup.untracked"
],
"settings": {
"background": "#79b8ff",
"foreground": "#2f363d"
}
},
{
"scope": "meta.diff.range",
"settings": {
"fontStyle": "bold",
"foreground": "#b392f0"
}
},
{
"scope": "meta.diff.header",
"settings": {
"foreground": "#79b8ff"
}
},
{
"scope": "meta.separator",
"settings": {
"fontStyle": "bold",
"foreground": "#79b8ff"
}
},
{
"scope": "meta.output",
"settings": {
"foreground": "#79b8ff"
}
},
{
"scope": [
"brackethighlighter.tag",
"brackethighlighter.curly",
"brackethighlighter.round",
"brackethighlighter.square",
"brackethighlighter.angle",
"brackethighlighter.quote"
],
"settings": {
"foreground": "#d1d5da"
}
},
{
"scope": "brackethighlighter.unmatched",
"settings": {
"foreground": "#fdaeb7"
}
},
{
"scope": [
"constant.other.reference.link",
"string.other.link"
],
"settings": {
"fontStyle": "underline",
"foreground": "#dbedff"
}
}
],
"type": "dark"
});
// ../../../node_modules/.pnpm/shiki@1.1.7/node_modules/shiki/dist/themes/github-light.mjs
var githubLight = Object.freeze({
"colors": {
"activityBar.activeBorder": "#f9826c",
"activityBar.background": "#fff",
"activityBar.border": "#e1e4e8",
"activityBar.foreground": "#2f363d",
"activityBar.inactiveForeground": "#959da5",
"activityBarBadge.background": "#2188ff",
"activityBarBadge.foreground": "#fff",
"badge.background": "#dbedff",
"badge.foreground": "#005cc5",
"breadcrumb.activeSelectionForeground": "#586069",
"breadcrumb.focusForeground": "#2f363d",
"breadcrumb.foreground": "#6a737d",
"breadcrumbPicker.background": "#fafbfc",
"button.background": "#159739",
"button.foreground": "#fff",
"button.hoverBackground": "#138934",
"button.secondaryBackground": "#e1e4e8",
"button.secondaryForeground": "#1b1f23",
"button.secondaryHoverBackground": "#d1d5da",
"checkbox.background": "#fafbfc",
"checkbox.border": "#d1d5da",
"debugToolBar.background": "#fff",
"descriptionForeground": "#6a737d",
"diffEditor.insertedTextBackground": "#34d05822",
"diffEditor.removedTextBackground": "#d73a4922",
"dropdown.background": "#fafbfc",
"dropdown.border": "#e1e4e8",
"dropdown.foreground": "#2f363d",
"dropdown.listBackground": "#fff",
"editor.background": "#fff",
"editor.findMatchBackground": "#ffdf5d",
"editor.findMatchHighlightBackground": "#ffdf5d66",
"editor.focusedStackFrameHighlightBackground": "#28a74525",
"editor.foldBackground": "#d1d5da11",
"editor.foreground": "#24292e",
"editor.inactiveSelectionBackground": "#0366d611",
"editor.lineHighlightBackground": "#f6f8fa",
"editor.linkedEditingBackground": "#0366d611",
"editor.selectionBackground": "#0366d625",
"editor.selectionHighlightBackground": "#34d05840",
"editor.selectionHighlightBorder": "#34d05800",
"editor.stackFrameHighlightBackground": "#ffd33d33",
"editor.wordHighlightBackground": "#34d05800",
"editor.wordHighlightBorder": "#24943e99",
"editor.wordHighlightStrongBackground": "#34d05800",
"editor.wordHighlightStrongBorder": "#24943e50",
"editorBracketHighlight.foreground1": "#005cc5",
"editorBracketHighlight.foreground2": "#e36209",
"editorBracketHighlight.foreground3": "#5a32a3",
"editorBracketHighlight.foreground4": "#005cc5",
"editorBracketHighlight.foreground5": "#e36209",
"editorBracketHighlight.foreground6": "#5a32a3",
"editorBracketMatch.background": "#34d05840",
"editorBracketMatch.border": "#34d05800",
"editorCursor.foreground": "#044289",
"editorError.foreground": "#cb2431",
"editorGroup.border": "#e1e4e8",
"editorGroupHeader.tabsBackground": "#f6f8fa",
"editorGroupHeader.tabsBorder": "#e1e4e8",
"editorGutter.addedBackground": "#28a745",
"editorGutter.deletedBackground": "#d73a49",
"editorGutter.modifiedBackground": "#2188ff",
"editorIndentGuide.activeBackground": "#d7dbe0",
"editorIndentGuide.background": "#eff2f6",
"editorLineNumber.activeForeground": "#24292e",
"editorLineNumber.foreground": "#1b1f234d",
"editorOverviewRuler.border": "#fff",
"editorWarning.foreground": "#f9c513",
"editorWhitespace.foreground": "#d1d5da",
"editorWidget.background": "#f6f8fa",
"errorForeground": "#cb2431",
"focusBorder": "#2188ff",
"foreground": "#444d56",
"gitDecoration.addedResourceForeground": "#28a745",
"gitDecoration.conflictingResourceForeground": "#e36209",
"gitDecoration.deletedResourceForeground": "#d73a49",
"gitDecoration.ignoredResourceForeground": "#959da5",
"gitDecoration.modifiedResourceForeground": "#005cc5",
"gitDecoration.submoduleResourceForeground": "#959da5",
"gitDecoration.untrackedResourceForeground": "#28a745",
"input.background": "#fafbfc",
"input.border": "#e1e4e8",
"input.foreground": "#2f363d",
"input.placeholderForeground": "#959da5",
"list.activeSelectionBackground": "#e2e5e9",
"list.activeSelectionForeground": "#2f363d",
"list.focusBackground": "#cce5ff",
"list.hoverBackground": "#ebf0f4",
"list.hoverForeground": "#2f363d",
"list.inactiveFocusBackground": "#dbedff",
"list.inactiveSelectionBackground": "#e8eaed",
"list.inactiveSelectionForeground": "#2f363d",
"notificationCenterHeader.background": "#e1e4e8",
"notificationCenterHeader.foreground": "#6a737d",
"notifications.background": "#fafbfc",
"notifications.border": "#e1e4e8",
"notifications.foreground": "#2f363d",
"notificationsErrorIcon.foreground": "#d73a49",
"notificationsInfoIcon.foreground": "#005cc5",
"notificationsWarningIcon.foreground": "#e36209",
"panel.background": "#f6f8fa",
"panel.border": "#e1e4e8",
"panelInput.border": "#e1e4e8",
"panelTitle.activeBorder": "#f9826c",
"panelTitle.activeForeground": "#2f363d",
"panelTitle.inactiveForeground": "#6a737d",
"pickerGroup.border": "#e1e4e8",
"pickerGroup.foreground": "#2f363d",
"progressBar.background": "#2188ff",
"quickInput.background": "#fafbfc",
"quickInput.foreground": "#2f363d",
"scrollbar.shadow": "#6a737d33",
"scrollbarSlider.activeBackground": "#959da588",
"scrollbarSlider.background": "#959da533",
"scrollbarSlider.hoverBackground": "#959da544",
"settings.headerForeground": "#2f363d",
"settings.modifiedItemIndicator": "#2188ff",
"sideBar.background": "#f6f8fa",
"sideBar.border": "#e1e4e8",
"sideBar.foreground": "#586069",
"sideBarSectionHeader.background": "#f6f8fa",
"sideBarSectionHeader.border": "#e1e4e8",
"sideBarSectionHeader.foreground": "#2f363d",
"sideBarTitle.foreground": "#2f363d",
"statusBar.background": "#fff",
"statusBar.border": "#e1e4e8",
"statusBar.debuggingBackground": "#f9826c",
"statusBar.debuggingForeground": "#fff",
"statusBar.foreground": "#586069",
"statusBar.noFolderBackground": "#fff",
"statusBarItem.prominentBackground": "#e8eaed",
"statusBarItem.remoteBackground": "#fff",
"statusBarItem.remoteForeground": "#586069",
"tab.activeBackground": "#fff",
"tab.activeBorder": "#fff",
"tab.activeBorderTop": "#f9826c",
"tab.activeForeground": "#2f363d",
"tab.border": "#e1e4e8",
"tab.hoverBackground": "#fff",
"tab.inactiveBackground": "#f6f8fa",
"tab.inactiveForeground": "#6a737d",
"tab.unfocusedActiveBorder": "#fff",
"tab.unfocusedActiveBorderTop": "#e1e4e8",
"tab.unfocusedHoverBackground": "#fff",
"terminal.ansiBlack": "#24292e",
"terminal.ansiBlue": "#0366d6",
"terminal.ansiBrightBlack": "#959da5",
"terminal.ansiBrightBlue": "#005cc5",
"terminal.ansiBrightCyan": "#3192aa",
"terminal.ansiBrightGreen": "#22863a",
"terminal.ansiBrightMagenta": "#5a32a3",
"terminal.ansiBrightRed": "#cb2431",
"terminal.ansiBrightWhite": "#d1d5da",
"terminal.ansiBrightYellow": "#b08800",
"terminal.ansiCyan": "#1b7c83",
"terminal.ansiGreen": "#28a745",
"terminal.ansiMagenta": "#5a32a3",
"terminal.ansiRed": "#d73a49",
"terminal.ansiWhite": "#6a737d",
"terminal.ansiYellow": "#dbab09",
"terminal.foreground": "#586069",
"terminal.tab.activeBorder": "#f9826c",
"terminalCursor.background": "#d1d5da",
"terminalCursor.foreground": "#005cc5",
"textBlockQuote.background": "#fafbfc",
"textBlockQuote.border": "#e1e4e8",
"textCodeBlock.background": "#f6f8fa",
"textLink.activeForeground": "#005cc5",
"textLink.foreground": "#0366d6",
"textPreformat.foreground": "#586069",
"textSeparator.foreground": "#d1d5da",
"titleBar.activeBackground": "#fff",
"titleBar.activeForeground": "#2f363d",
"titleBar.border": "#e1e4e8",
"titleBar.inactiveBackground": "#f6f8fa",
"titleBar.inactiveForeground": "#6a737d",
"tree.indentGuidesStroke": "#e1e4e8",
"welcomePage.buttonBackground": "#f6f8fa",
"welcomePage.buttonHoverBackground": "#e1e4e8"
},
"displayName": "GitHub Light",
"name": "github-light",
"semanticHighlighting": true,
"tokenColors": [
{
"scope": [
"comment",
"punctuation.definition.comment",
"string.comment"
],
"settings": {
"foreground": "#6a737d"
}
},
{
"scope": [
"constant",
"entity.name.constant",
"variable.other.constant",
"variable.other.enummember",
"variable.language"
],
"settings": {
"foreground": "#005cc5"
}
},
{
"scope": [
"entity",
"entity.name"
],
"settings": {
"foreground": "#6f42c1"
}
},
{
"scope": "variable.parameter.function",
"settings": {
"foreground": "#24292e"
}
},
{
"scope": "entity.name.tag",
"settings": {
"foreground": "#22863a"
}
},
{
"scope": "keyword",
"settings": {
"foreground": "#d73a49"
}
},
{
"scope": [
"storage",
"storage.type"
],
"settings": {
"foreground": "#d73a49"
}
},
{
"scope": [
"storage.modifier.package",
"storage.modifier.import",
"storage.type.java"
],
"settings": {
"foreground": "#24292e"
}
},
{
"scope": [
"string",
"punctuation.definition.string",
"string punctuation.section.embedded source"
],
"settings": {
"foreground": "#032f62"
}
},
{
"scope": "support",
"settings": {
"foreground": "#005cc5"
}
},
{
"scope": "meta.property-name",
"settings": {
"foreground": "#005cc5"
}
},
{
"scope": "variable",
"settings": {
"foreground": "#e36209"
}
},
{
"scope": "variable.other",
"settings": {
"foreground": "#24292e"
}
},
{
"scope": "invalid.broken",
"settings": {
"fontStyle": "italic",
"foreground": "#b31d28"
}
},
{
"scope": "invalid.deprecated",
"settings": {
"fontStyle": "italic",
"foreground": "#b31d28"
}
},
{
"scope": "invalid.illegal",
"settings": {
"fontStyle": "italic",
"foreground": "#b31d28"
}
},
{
"scope": "invalid.unimplemented",
"settings": {
"fontStyle": "italic",
"foreground": "#b31d28"
}
},
{
"scope": "carriage-return",
"settings": {
"background": "#d73a49",
"content": "^M",
"fontStyle": "italic underline",
"foreground": "#fafbfc"
}
},
{
"scope": "message.error",
"settings": {
"foreground": "#b31d28"
}
},
{
"scope": "string variable",
"settings": {
"foreground": "#005cc5"
}
},
{
"scope": [
"source.regexp",
"string.regexp"
],
"settings": {
"foreground": "#032f62"
}
},
{
"scope": [
"string.regexp.character-class",
"string.regexp constant.character.escape",
"string.regexp source.ruby.embedded",
"string.regexp string.regexp.arbitrary-repitition"
],
"settings": {
"foreground": "#032f62"
}
},
{
"scope": "string.regexp constant.character.escape",
"settings": {
"fontStyle": "bold",
"foreground": "#22863a"
}
},
{
"scope": "support.constant",
"settings": {
"foreground": "#005cc5"
}
},
{
"scope": "support.variable",
"settings": {
"foreground": "#005cc5"
}
},
{
"scope": "meta.module-reference",
"settings": {
"foreground": "#005cc5"
}
},
{
"scope": "punctuation.definition.list.begin.markdown",
"settings": {
"foreground": "#e36209"
}
},
{
"scope": [
"markup.heading",
"markup.heading entity.name"
],
"settings": {
"fontStyle": "bold",
"foreground": "#005cc5"
}
},
{
"scope": "markup.quote",
"settings": {
"foreground": "#22863a"
}
},
{
"scope": "markup.italic",
"settings": {
"fontStyle": "italic",
"foreground": "#24292e"
}
},
{
"scope": "markup.bold",
"settings": {
"fontStyle": "bold",
"foreground": "#24292e"
}
},
{
"scope": [
"markup.underline"
],
"settings": {
"fontStyle": "underline"
}
},
{
"scope": [
"markup.strikethrough"
],
"settings": {
"fontStyle": "strikethrough"
}
},
{
"scope": "markup.inline.raw",
"settings": {
"foreground": "#005cc5"
}
},
{
"scope": [
"markup.deleted",
"meta.diff.header.from-file",
"punctuation.definition.deleted"
],
"settings": {
"background": "#ffeef0",
"foreground": "#b31d28"
}
},
{
"scope": [
"markup.inserted",
"meta.diff.header.to-file",
"punctuation.definition.inserted"
],
"settings": {
"background": "#f0fff4",
"foreground": "#22863a"
}
},
{
"scope": [
"markup.changed",
"punctuation.definition.changed"
],
"settings": {
"background": "#ffebda",
"foreground": "#e36209"
}
},
{
"scope": [
"markup.ignored",
"markup.untracked"
],
"settings": {
"background": "#005cc5",
"foreground": "#f6f8fa"
}
},
{
"scope": "meta.diff.range",
"settings": {
"fontStyle": "bold",
"foreground": "#6f42c1"
}
},
{
"scope": "meta.diff.header",
"settings": {
"foreground": "#005cc5"
}
},
{
"scope": "meta.separator",
"settings": {
"fontStyle": "bold",
"foreground": "#005cc5"
}
},
{
"scope": "meta.output",
"settings": {
"foreground": "#005cc5"
}
},
{
"scope": [
"brackethighlighter.tag",
"brackethighlighter.curly",
"brackethighlighter.round",
"brackethighlighter.square",
"brackethighlighter.angle",
"brackethighlighter.quote"
],
"settings": {
"foreground": "#586069"
}
},
{
"scope": "brackethighlighter.unmatched",
"settings": {
"foreground": "#b31d28"
}
},
{
"scope": [
"constant.other.reference.link",
"string.other.link"
],
"settings": {
"fontStyle": "underline",
"foreground": "#032f62"
}
}
],
"type": "light"
});
// src/internal/css.ts
import postcss2 from "postcss";
import postcssNested from "postcss-nested";
var groupWrapperElement = "div";
var groupWrapperClassName = "expressive-code";
var preprocessor = postcss2([
// Prevent top-level selectors that are already scoped from being scoped twice
(root) => {
const groupWrapperScope = `.${groupWrapperClassName}`;
root.walkRules((rule) => {
if (rule.parent?.parent === root) {
rule.selectors = rule.selectors.map((selector) => {
if (selector.indexOf(groupWrapperScope) === 0) {
return selector.slice(groupWrapperScope.length).trim() || "&";
}
return selector;
});
}
});
},
// Parse SASS-like nested selectors
postcssNested()
]);
var processor = postcss2([
// Prevent selectors targeting the wrapper class name or top-level elements from being scoped
(root) => {
const groupWrapperScope = escapeRegExp(`.${groupWrapperClassName}`);
const regExpScopedTopLevel = new RegExp(`^${groupWrapperScope} .*(${groupWrapperScope}|:root|html|body)`, "g");
root.walkRules((rule) => {
rule.selectors = rule.selectors.map((selector) => selector.replace(regExpScopedTopLevel, "$1"));
});
},
// Apply some simple minifications
(root) => {
root.raws.after = "";
root.walkComments((comment) => {
comment.remove();
});
root.walkRules((rule) => {
rule.selector = rule.selectors.join(",");
rule.raws.before = rule.raws.before?.trim() || "";
rule.raws.between = "";
rule.raws.after = "";
rule.raws.semicolon = false;
});
root.walkAtRules((atRule) => {
atRule.raws.before = atRule.raws.before?.trim() || "";
atRule.raws.between = "";
atRule.raws.after = "";
});
root.walkDecls((decl) => {
decl.raws.before = decl.raws.before?.trim() || "";
decl.raws.between = decl.raws.between?.trim() || ":";
decl.raws.value = {
value: decl.value,
raw: decl.raws.value?.raw.trim() ?? decl.value.trim()
};
});
}
]);
async function scopeAndMinifyNestedCss(css) {
const postCssOptions = { from: void 0 };
const root = postcss2.parse(`.${groupWrapperClassName}{${css}}`, postCssOptions);
const preprocessedStyles = await preprocessor.process(root, postCssOptions);
const processedStyles = await processor.process(preprocessedStyles, postCssOptions);
return processedStyles.css;
}
var processedStylesCache = /* @__PURE__ */ new Map();
async function processPluginStyles(pluginStyles) {
const result = /* @__PURE__ */ new Set();
const seenStyles = /* @__PURE__ */ new Set();
for (const { pluginName, styles } of pluginStyles) {
if (seenStyles.has(styles))
continue;
seenStyles.add(styles);
const cacheKey = styles;
const cachedStyles = processedStylesCache.get(cacheKey);
if (cachedStyles !== void 0) {
result.add(cachedStyles);
continue;
}
try {
const processedCss = await scopeAndMinifyNestedCss(styles);
result.add(processedCss);
processedStylesCache.set(cacheKey, processedCss);
} catch (error) {
const msg = error instanceof Error ? error.message : error;
throw new Error(`Plugin "${pluginName}" added CSS styles that could not be processed (error=${JSON.stringify(msg)}). Styles="${styles}"`);
}
}
return result;
}
function wrapInCascadeLayer(css, cascadeLayerName) {
if (!cascadeLayerName || cascadeLayerName.trim() === "")
return css;
return `@layer ${cascadeLayerName.trim()}{${css}}`;
}
// src/internal/render-group.ts
async function renderGroup({
input,
options,
defaultLocale,
config,
plugins,
cssVar,
cssVarName,
styleVariants
}) {
const inputArray = Array.isArray(input) ? input : [input];
const groupContents = inputArray.map((blockOrOptions) => {
if (blockOrOptions instanceof ExpressiveCodeBlock) {
return { codeBlock: blockOrOptions };
} else {
return { codeBlock: new ExpressiveCodeBlock(blockOrOptions) };
}
});
Object.freeze(groupContents);
options?.onInitGroup?.(groupContents);
const renderedGroupContents = groupContents;
const pluginStyles = [];
for (const groupContent of renderedGroupContents) {
const { renderedBlockAst, blockStyles } = await renderBlock({
codeBlock: groupContent.codeBlock,
groupContents,
locale: groupContent.codeBlock.locale || defaultLocale,
config,
plugins,
cssVar,
cssVarName,
styleVariants
});
groupContent.renderedBlockAst = renderedBlockAst;
pluginStyles.push(...blockStyles);
}
const groupRenderData = {
groupAst: buildGroupAstFromRenderedBlocks(renderedGroupContents.map(({ renderedBlockAst }) => renderedBlockAst))
};
const runHooksContext = {
plugins,
config
};
await runHooks("postprocessRenderedBlockGroup", runHooksContext, async ({ hookFn, plugin }) => {
await hookFn({
renderedGroupContents,
pluginStyles,
addStyles: (styles) => pluginStyles.push({ pluginName: plugin.name, styles }),
renderData: groupRenderData
});
if (!isHastElement(groupRenderData.groupAst)) {
throw newTypeError("hast Element", groupRenderData.groupAst, "groupAst");
}
});
return {
renderedGroupAst: groupRenderData.groupAst,
renderedGroupContents,
styles: await processPluginStyles(pluginStyles)
};
}
function buildGroupAstFromRenderedBlocks(renderedBlocks) {
return h(`${groupWrapperElement}.${groupWrapperClassName}`, renderedBlocks);
}
// src/helpers/color-transforms.ts
import { TinyColor, readability } from "@ctrl/tinycolor";
// ../../../node_modules/.pnpm/culori@4.0.1/node_modules/culori/src/lrgb/convertRgbToLrgb.js
var fn = (c = 0) => {
const abs = Math.abs(c);
if (abs <= 0.04045) {
return c / 12.92;
}
return (Math.sign(c) || 1) * Math.pow((abs + 0.055) / 1.055, 2.4);
};
var convertRgbToLrgb = ({ r, g, b, alpha }) => {
let res = {
mode: "lrgb",
r: fn(r),
g: fn(g),
b: fn(b)
};
if (alpha !== void 0)
res.alpha = alpha;
return res;
};
var convertRgbToLrgb_default = convertRgbToLrgb;
// ../../../node_modules/.pnpm/culori@4.0.1/node_modules/culori/src/lrgb/convertLrgbToRgb.js
var fn2 = (c = 0) => {
const abs = Math.abs(c);
if (abs > 31308e-7) {
return (Math.sign(c) || 1) * (1.055 * Math.pow(abs, 1 / 2.4) - 0.055);
}
return c * 12.92;
};
var convertLrgbToRgb = ({ r, g, b, alpha }, mode = "rgb") => {
let res = {
mode,
r: fn2(r),
g: fn2(g),
b: fn2(b)
};
if (alpha !== void 0)
res.alpha = alpha;
return res;
};
var convertLrgbToRgb_default = convertLrgbToRgb;
// ../../../node_modules/.pnpm/culori@4.0.1/node_modules/culori/src/util/normalizeHue.js
var normalizeHue = (hue) => (hue = hue % 360) < 0 ? hue + 360 : hue;
var normalizeHue_default = normalizeHue;
// ../../../node_modules/.pnpm/culori@4.0.1/node_modules/culori/src/lch/convertLabToLch.js
var convertLabToLch = ({ l, a, b, alpha }, mode = "lch") => {
if (a === void 0)
a = 0;
if (b === void 0)
b = 0;
let c = Math.sqrt(a * a + b * b);
let res = { mode, l, c };
if (c)
res.h = normalizeHue_default(Math.atan2(b, a) * 180 / Math.PI);
if (alpha !== void 0)
res.alpha = alpha;
return res;
};
var convertLabToLch_default = convertLabToLch;
// ../../../node_modules/.pnpm/culori@4.0.1/node_modules/culori/src/lch/convertLchToLab.js
var convertLchToLab = ({ l, c, h: h2, alpha }, mode = "lab") => {
if (h2 === void 0)
h2 = 0;
let res = {
mode,
l,
a: c ? c * Math.cos(h2 / 180 * Math.PI) : 0,
b: c ? c * Math.sin(h2 / 180 * Math.PI) : 0
};
if (alpha !== void 0)
res.alpha = alpha;
return res;
};
var convertLchToLab_default = convertLchToLab;
// ../../../node_modules/.pnpm/culori@4.0.1/node_modules/culori/src/hsl/convertHslToRgb.js
function convertHslToRgb({ h: h2, s: s2, l, alpha }) {
h2 = normalizeHue_default(h2 !== void 0 ? h2 : 0);
if (s2 === void 0)
s2 = 0;
if (l === void 0)
l = 0;
let m1 = l + s2 * (l < 0.5 ? l : 1 - l);
let m2 = m1 - (m1 - l) * 2 * Math.abs(h2 / 60 % 2 - 1);
let res;
switch (Math.floor(h2 / 60)) {
case 0:
res = { r: m1, g: m2, b: 2 * l - m1 };
break;
case 1:
res = { r: m2, g: m1, b: 2 * l - m1 };
break;
case 2:
res = { r: 2 * l - m1, g: m1, b: m2 };
break;
case 3:
res = { r: 2 * l - m1, g: m2, b: m1 };
break;
case 4:
res = { r: m2, g: 2 * l - m1, b: m1 };
break;
case 5:
res = { r: m1, g: 2 * l - m1, b: m2 };
break;
default:
res = { r: 2 * l - m1, g: 2 * l - m1, b: 2 * l - m1 };
}
res.mode = "rgb";
if (alpha !== void 0)
res.alpha = alpha;
return res;
}
// ../../../node_modules/.pnpm/culori@4.0.1/node_modules/culori/src/oklab/convertLrgbToOklab.js
var convertLrgbToOklab = ({ r, g, b, alpha }) => {
if (r === void 0)
r = 0;
if (g === void 0)
g = 0;
if (b === void 0)
b = 0;
let L = Math.cbrt(
0.41222147079999993 * r + 0.5363325363 * g + 0.0514459929 * b
);
let M = Math.cbrt(
0.2119034981999999 * r + 0.6806995450999999 * g + 0.1073969566 * b
);
let S = Math.cbrt(
0.08830246189999998 * r + 0.2817188376 * g + 0.6299787005000002 * b
);
let res = {
mode: "oklab",
l: 0.2104542553 * L + 0.793617785 * M - 0.0040720468 * S,
a: 1.9779984951 * L - 2.428592205 * M + 0.4505937099 * S,
b: 0.0259040371 * L + 0.7827717662 * M - 0.808675766 * S
};
if (alpha !== void 0) {
res.alpha = alpha;
}
return res;
};
var convertLrgbToOklab_default = convertLrgbToOklab;
// ../../../node_modules/.pnpm/culori@4.0.1/node_modules/culori/src/oklab/convertRgbToOklab.js
var convertRgbToOklab = (rgb) => {
let res = convertLrgbToOklab_default(convertRgbToLrgb_default(rgb));
if (rgb.r === rgb.b && rgb.b === rgb.g) {
res.a = res.b = 0;
}
return res;
};
var convertRgbToOklab_default = convertRgbToOklab;
// ../../../node_modules/.pnpm/culori@4.0.1/node_modules/culori/src/oklab/convertOklabToLrgb.js
var convertOklabToLrgb = ({ l, a, b, alpha }) => {
if (l === void 0)
l = 0;
if (a === void 0)
a = 0;
if (b === void 0)
b = 0;
let L = Math.pow(
l * 0.9999999984505198 + 0.39633779217376786 * a + 0.2158037580607588 * b,
3
);
let M = Math.pow(
l * 1.0000000088817609 - 0.10556134232365635 * a - 0.06385417477170591 * b,
3
);
let S = Math.pow(
l * 1.0000000546724108 - 0.08948418209496575 * a - 1.2914855378640917 * b,
3
);
let res = {
mode: "lrgb",
r: 4.076741661347994 * L - 3.307711590408193 * M + 0.230969928729428 * S,
g: -1.2684380040921763 * L + 2.6097574006633715 * M - 0.3413193963102197 * S,
b: -0.004196086541837188 * L - 0.7034186144594493 * M + 1.7076147009309444 * S
};
if (alpha !== void 0) {
res.alpha = alpha;
}
return res;
};
var convertOklabToLrgb_default = convertOklabToLrgb;
// ../../../node_modules/.pnpm/culori@4.0.1/node_modules/culori/src/oklab/convertOklabToRgb.js
var convertOklabToRgb = (c) => convertLrgbToRgb_default(convertOklabToLrgb_default(c));
var convertOklabToRgb_default = convertOklabToRgb;
// src/internal/search-algorithms.ts
function binarySearch({
getValueFn,
targetValue,
preferHigher,
tolerance = 0.1,
low = 0,
high = 1,
minChangeFactor = 1e-3,
maxIterations = 25
}) {
const epsilon = minChangeFactor * Math.abs(high - low);
let iterations = 0;
let mid;
let lastMid;
while (mid = (low + high) / 2, iterations < maxIterations) {
const currentValue = getValueFn(mid);
const resultIsWithinTolerance = Math.abs(currentValue - targetValue) <= tolerance;
const resultIsInPreferredDirection = preferHigher === void 0 ? true : preferHigher ? currentValue > targetValue : currentValue < targetValue;
const midChangedLessThanEpsilon = lastMid !== void 0 && Math.abs(lastMid - mid) < epsilon;
if (resultIsInPreferredDirection && (resultIsWithinTolerance || midChangedLessThanEpsilon)) {
return mid;
} else if (currentValue < targetValue) {
low = mid;
} else {
high = mid;
}
iterations++;
lastMid = mid;
}
return mid;
}
function bisect({
checkFn,
low = 0,
high = 1,
/**
* If the midpoint changes less than `minChangeFactor * Math.abs(high - low)`
* between iterations, the search will stop and return the highest value
* that `checkFn` returned `true` for.
*/
minChangeFactor = 1e-3,
maxIterations = 25
}) {
const epsilon = minChangeFactor * Math.abs(high - low);
let iterations = 0;
let highestValid;
let mid;
let lastMid;
while (mid = (low + high) / 2, iterations < maxIterations) {
const isValid = checkFn(mid);
if (isValid) {
highestValid = mid;
low = mid;
} else {
high = mid;
}
const midChangedLessThanEpsilon = lastMid !== void 0 && Math.abs(lastMid - mid) < epsilon;
if (midChangedLessThanEpsilon)
return highestValid;
iterations++;
lastMid = mid;
}
return highestValid;
}
// src/internal/color-spaces.ts
var D65 = [0.3127 / 0.329, 1, (1 - 0.3127 - 0.329) / 0.329];
var D50 = [0.3457 / 0.3585, 1, (1 - 0.3457 - 0.3585) / 0.3585];
var m = [
[3.240969941904521, -1.537383177570093, -0.498610760293],
[-0.96924363628087, 1.87596750150772, 0.041555057407175],
[0.055630079696993, -0.20397695888897, 1.056971514242878]
];
function xyzToRgb(xyz) {
const [x, y, z] = [xyz.x, xyz.y, xyz.z].map((v) => v / 100);
const linearToSrgb = (v) => v > 31308e-7 ? 1.055 * Math.pow(v, 1 / 2.4) - 0.055 : 12.92 * v;
const r = x * m[0][0] + y * m[0][1] + z * m[0][2];
const g = x * m[1][0] + y * m[1][1] + z * m[1][2];
const b = x * m[2][0] + y * m[2][1] + z * m[2][2];
return {
r: linearToSrgb(r) * 255,
g: linearToSrgb(g) * 255,
b: linearToSrgb(b) * 255
};
}
function labToXyz(lab, illuminant = D65) {
const [l, a, b] = [lab.l, lab.a, lab.b].map((v) => v / 100);
const y = (l + 0.16) / 1.16;
const x = a / 5 + y;
const z = y - b / 2;
const transform = (v, whitepoint) => {
const pow = Math.pow(v, 3);
return (pow > 8856e-6 ? pow : (v - 16 / 116) / 7.787037) * whitepoint;
};
return {
x: transform(x, illuminant[0]) * 100,
y: transform(y, illuminant[1]) * 100,
z: transform(z, illuminant[2]) * 100
};
}
function lchabToLab(lch) {
return {
l: lch.l,
a: lch.c * Math.cos((lch.h ?? 0) * Math.PI / 180),
b: lch.c * Math.sin((lch.h ?? 0) * Math.PI / 180),
alpha: lch.alpha
};
}
function labToRgba(lab, illuminant = D65) {
const xyz = labToXyz(lab, illuminant);
return {
...xyzToRgb(xyz),
a: lab.alpha
};
}
function lchabToRgba(lch, illuminant = D65) {
return labToRgba(lchabToLab(lch), illuminant);
}
function rgbaToCulori(rgba) {
const { r, g, b, a } = rgba;
return {
mode: "rgb",
r: r / 255,
g: g / 255,
b: b / 255,
...a !== void 0 && { alpha: a }
};
}
function rgbaFromCulori(culoriRgb) {
const { r, g, b, alpha } = culoriRgb;
return {
r: r * 255,
g: g * 255,
b: b * 255,
a: alpha
};
}
function hslToRgba(input) {
return rgbaFromCulori(convertHslToRgb(input));
}
function rgbaToOklch(input) {
const oklab = convertRgbToOklab_default(rgbaToCulori(input));
return convertLabToLch_default(oklab, "oklch");
}
function oklchToRgba(input, clampChroma = true) {
const convert = (oklch) => {
const oklab = convertLchToLab_default(oklch, "oklab");
const rgb = convertOklabToRgb_default(oklab);
const minChannel = Math.min(rgb.r, rgb.g, rgb.b);
const maxChannel = Math.max(rgb.r, rgb.g, rgb.b);
const inGamut = minChannel >= 0 && maxChannel <= 1;
return {
rgb,
c: oklch.c,
inGamut
};
};
let result = convert(input);
if (!result.inGamut && clampChroma) {
result = convert({ ...input, c: 0 });
if (result.inGamut) {
const maxChromaInGamut = bisect({
checkFn: (c) => convert({ ...input, c }).inGamut,
low: 0,
high: input.c,
minChangeFactor: 1e-4
});
result = convert({ ...input, c: maxChromaInGamut ?? 0 });
}
}
return rgbaFromCulori(result.rgb);
}
function normalizeAngle(angle) {
angle %= 360;
return angle < 0 ? angle + 360 : angle;
}
function parseAngle(value) {
return normalizeAngle(parseFloat(value));
}
function parseCssLabColor(labString) {
const match = labString.match(/^lab\(\s*([\d.]+%?)\s+(-?[\d.]+%?)\s+(-?[\d.]+%?)(?:\s*\/\s*([\d.]+%?))?\s*\)$/i);
if (!match) {
return void 0;
}
const [, l, a, b, alpha] = match;
return {
l: parseCssValue(l, 0, 100),
a: parseCssValue(a, -125, 125),
b: parseCssValue(b, -125, 125),
alpha: alpha !== void 0 ? parseCssValue(alpha, 0, 1) : void 0
};
}
function parseCssLchColor(lchString) {
const match = lchString.match(/^lch\(\s*([\d.]+%?)\s+([\d.]+%?)\s+([\d.]+(?:deg)?)(?:\s*\/\s*([\d.]+%?))?\s*\)$/i);
if (!match) {
return void 0;
}
const [, l, c, h2, alpha] = match;
return {
l: parseCssValue(l, 0, 100),
c: parseCssValue(c, 0, 150),
h: parseAngle(h2),
alpha: alpha !== void 0 ? parseCssValue(alpha, 0, 1) : void 0
};
}
function parseCssOklchColor(oklchString) {
const match = oklchString.match(/^oklch\(\s*([\d.]+%?)\s+([\d.]+%?)\s+([\d.]+(?:deg)?)(?:\s*\/\s*([\d.]+%?))?\s*\)$/i);
if (!match) {
return void 0;
}
const [, l, c, h2, alpha] = match;
return {
mode: "oklch",
l: parseCssValue(l, 0, 1),
c: parseCssValue(c, 0, 0.5, 0.4),
h: parseAngle(h2),
...alpha !== void 0 && { alpha: parseCssValue(alpha, 0, 1) }
};
}
function parseCssValue(value, min, max, valueFor100Percent) {
const isPercentage = value.endsWith("%");
const floatValue = parseFloat(value);
const convertedValue = isPercentage ? floatValue * (valueFor100Percent ?? max) / 100 : floatValue;
return Math.max(min, Math.min(max, convertedValue));
}
// src/helpers/color-transforms.ts
function setAlpha(input, newAlpha) {
return withParsedColor(input, (color) => {
return toHexColor(color.setAlpha(newAlpha));
});
}
function multiplyAlpha(input, factor) {
return withParsedColor(input, (color) => {
return toHexColor(color.setAlpha(minMaxRounded(color.getAlpha() * factor)));
});
}
function getLuminance(input) {
return toTinyColor(input).getLuminance();
}
function setLuminance(input, targetLuminance) {
return withParsedColor(input, (color) => {
targetLuminance = minMaxRounded(targetLuminance);
const increasing = targetLuminance > color.getLuminance();
const mixColor = increasing ? "#fff" : "#000";
const mixAmount = binarySearch({
getValueFn: (amount) => {
return toTinyColor(color).mix(mixColor, amount * 100).getLuminance();
},
targetValue: targetLuminance,
preferHigher: targetLuminance > 0 && targetLuminance < 1 ? increasing : void 0,
tolerance: 1 / 256,
// Ensure that the binary search range matches the luminance target direction
low: increasing ? 0 : 1,
high: increasing ? 1 : 0
});
return toHexColor(color.mix(mixColor, mixAmount * 100));
});
}
function lighten(input, amount) {
return withParsedColor(input, (color) => {
const hsl = color.toHsl();
const l = minMaxRounded(hsl.l);
const { h: h2, s: s2, a: alpha } = hsl;
return toHexColor(toTinyColor({ mode: "hsl", h: h2, s: s2, l: minMaxRounded(l + l * amount), alpha }));
});
}
function darken(input, amount) {
return lighten(input, -amount);
}
function mix(input, mixinInput, amount) {
return withParsedColor(input, (color) => {
const mixinColor = toTinyColor(mixinInput);
const mixAmount = minMaxRounded(amount * 100, 0, 100);
return toHexColor(color.mix(mixinColor, mixAmount));
});
}
function onBackground(input, background) {
return withParsedColor(
input,
(color) => {
const backgroundColor = toTinyColor(background);
return toHexColor(color.onBackground(backgroundColor));
},
background
);
}
function getColorContrast(color1, color2) {
const color = toTinyColor(color1);
const backgroundColor = toTinyColor(color2);
return readability(color, backgroundColor);
}
function getColorContrastOnBackground(input, background) {
const color = toTinyColor(input);
const backgroundColor = toTinyColor(background);
return readability(color.onBackground(backgroundColor), backgroundColor);
}
function ensureColorContrastOnBackground(input, background, minContrast = 5.5, maxContrast = 22) {
return withParsedColor(input, (color) => {
return withParsedColor(
background,
(backgroundColor) => {
const hexBackgroundColor = toHexColor(backgroundColor);
let newColor = toTinyColor(color);
let curContrast = readability(newColor.onBackground(backgroundColor), backgroundColor);
if (curContrast < minContrast) {
const contrastWithoutAlpha = readability(newColor, backgroundColor);
if (contrastWithoutAlpha < minContrast) {
newColor = toTinyColor(changeLuminanceToReachColorContrast(toHexColor(newColor), hexBackgroundColor, minContrast));
curContrast = readability(newColor.onBackground(backgroundColor), backgroundColor);
}
}
if (curContrast < minContrast || curContrast > maxContrast) {
newColor = toTinyColor(changeAlphaToReachColorContrast(toHexColor(newColor), hexBackgroundColor, minContrast, maxContrast));
}
return toHexColor(newColor);
},
toHexColor(color)
);
});
}
function changeLuminanceToReachColorContrast(input1, input2, minContrast = 6) {
const color1 = toTinyColor(input1);
const color2 = toTinyColor(input2);
const oldContrast = readability(color1, color2);
if (oldContrast >= minContrast)
return toHexColor(color1);
const color1L = color1.getLuminance();
const color2L = color2.getLuminance();
const lightenTargetL = (color2L + 0.05) * minContrast - 0.05;
const darkenTargetL = (color2L + 0.05) / minContrast - 0.05;
const lightenedColor = setLuminance(input1, lightenTargetL);
const darkenedColor = setLuminance(input1, darkenTargetL);
const lightenedContrast = readability(lightenedColor, color2);
const darkenedContrast = readability(darkenedColor, color2);
if (lightenedContrast <= oldContrast && darkenedContrast <= oldContrast)
return toHexColor(color1);
if (color1L >= color2L && lightenedContrast >= minContrast)
return lightenedColor;
if (color1L < color2L && darkenedContrast >= minContrast)
return darkenedColor;
return lightenedContrast > darkenedContrast ? lightenedColor : darkenedColor;
}
function changeAlphaToReachColorContrast(input, background, minContrast = 6, maxContrast = 22) {
const color = toTinyColor(input);
const backgroundColor = toTinyColor(background);
const colorOnBackground = color.onBackground(backgroundColor);
const curContrast = readability(colorOnBackground, backgroundColor);
if (curContrast >= minContrast && curContrast <= maxContrast)
return toHexColor(color);
const newAlpha = binarySearch({
getValueFn: (alpha) => {
const newColor = toTinyColor(color).setAlpha(alpha);
const onBg = newColor.onBackground(backgroundColor);
const result = readability(onBg, backgroundColor);
return result;
},
targetValue: curContrast < minContrast ? minContrast : maxContrast,
preferHigher: curContrast < minContrast,
tolerance: 1 / 256,
low: 0.15,
high: 1
});
return setAlpha(toHexColor(color), newAlpha);
}
function getFirstStaticColor(...inputs) {
const extractFallbackFromCssVar = (input) => {
const match = input.match(/^\s*var\([^,]+,\s*(.+?)\s*\)\s*$/i);
return match ? match[1] : void 0;
};
const isValid = (input) => toTinyColor(input)?.isValid;
for (const input of inputs) {
if (!input)
continue;
if (isValid(input))
return input;
let cssVarFallback = extractFallbackFromCssVar(input);
while (cssVarFallback) {
if (isValid(cssVarFallback))
return cssVarFallback;
cssVarFallback = extractFallbackFromCssVar(cssVarFallback);
}
}
return void 0;
}
function getStaticBackgroundColor(styleVariant) {
const color = getFirstStaticColor(styleVariant.resolvedStyleSettings.get("codeBackground"), styleVariant.theme.bg);
return color ?? (styleVariant.theme.type === "dark" ? "#202020" : "#fff");
}
function chromaticRecolor(input, target) {
let targetHue;
let targetChroma;
let targetChromaMeasuredAtLightness;
if (typeof target === "string") {
const targetOklch = rgbaToOklch(toTinyColor(target));
targetHue = targetOklch.h ?? 0;
targetChroma = targetOklch.c;
targetChromaMeasuredAtLightness = targetOklch.l;
} else {
targetHue = target.hue;
targetChroma = target.chroma;
targetChromaMeasuredAtLightness = target.chromaMeasuredAtLightness;
}
return withParsedColor(input, (color) => {
const oklch = rgbaToOklch(color);
oklch.h = targetHue;
const maxChromaForInputLightness = rgbaToOklch(oklchToRgba({ ...oklch, c: 0.4 })).c;
let newChroma;
if (targetChromaMeasuredAtLightness !== void 0) {
const maxChromaForTargetLightness = rgbaToOklch(oklchToRgba({ ...oklch, c: 0.4, l: targetChromaMeasuredAtLightness })).c;
const relativeTargetChroma = Math.min(targetChroma, maxChromaForTargetLightness) / maxChromaForTargetLightness;
newChroma = maxChromaForInputLightness * relativeTargetChroma;
} else {
newChroma = Math.min(targetChroma, maxChromaForInputLightness);
}
const linearDecrease = (i, start, end) => Math.max(0, Math.min(1, 1 - (i - start) / (end - start)));
const highLightnessFactor = linearDecrease(oklch.l, 0.95, 0.99);
oklch.c = newChroma * highLightnessFactor;
return toHexColor(toTinyColor(oklchToRgba(oklch, true)));
});
}
function withParsedColor(input, transform, fallback) {
const color = input && toTinyColor(input);
if (!color || !color.isValid) {
const fallbackOrInput = fallback !== void 0 ? fallback : input;
return !fallbackOrInput || typeof fallbackOrInput === "string" ? fallbackOrInput : toHexColor(fallbackOrInput);
}
return transform(color);
}
function toTinyColor(input) {
if (input instanceof TinyColor) {
return new TinyColor(input.toRgb());
}
if (typeof input === "string") {
const labColor = parseCssLabColor(input);
if (labColor) {
return new TinyColor(labToRgba(labColor));
}
const lchColor = parseCssLchColor(input);
if (lchColor) {
return new TinyColor(lchabToRgba(lchColor));
}
const oklchColor = parseCssOklchColor(input);
if (oklchColor) {
return new TinyColor(oklchToRgba(oklchColor));
}
return new TinyColor(input);
}
if (typeof input === "object" && "mode" in input) {
if (input.mode === "hsl")
return new TinyColor(hslToRgba(input));
if (input.mode === "oklch")
return new TinyColor(oklchToRgba(input));
}
return new TinyColor(input);
}
function toHexColor(input) {
const color = input instanceof TinyColor ? input : toTinyColor(input);
return color.toHexShortString();
}
function toRgbaString(input) {
return toTinyColor(input).toRgbString().toLowerCase();
}
function roundFloat(number, decimalPoints) {
const decimal = Math.pow(10, decimalPoints);
return Math.round(number * decimal) / decimal;
}
function minMaxRounded(number, min = 0, max = 1, decimalPoints = 3) {
return Math.max(min, Math.min(max, roundFloat(number, decimalPoints)));
}
// src/internal/vscode-colors.ts
var groupedDefaultWorkbenchColorKeys = {
backgrounds: [
"editor.background",
"editorGroupHeader.tabsBackground",
"editorGroupHeader.tabsBorder",
"titleBar.activeBackground",
"titleBar.border",
"panel.background",
"tab.activeBackground",
"tab.activeBorderTop",
"tab.activeBorder",
"terminal.background",
"widget.shadow"
],
accents: [
"focusBorder",
"editor.selectionBackground",
"textBlockQuote.border",
"textLink.activeForeground",
"textLink.foreground",
"editorLink.activeForeground",
"tab.activeForeground",
"tab.inactiveForeground",
"tab.unfocusedActiveForeground",
"tab.unfocusedInactiveForeground"
]
};
var defaultEditorBackgroundColors = ["#1e1e1e", "#ffffff"];
var defaultEditorForegroundColors = ["#bbbbbb", "#333333"];
var defaultWorkbenchColors = {
// Base colors
focusBorder: ["#007fd4", "#0090f1"],
foreground: ["#cccccc", "#616161"],
disabledForeground: ["#cccccc80", "#61616180"],
descriptionForeground: [["transparent", "foreground", 0.7], "#717171"],
errorForeground: ["#f48771", "#a1260d"],
"icon.foreground": ["#c5c5c5", "#424242"],
// Contrast colors
contrastActiveBorder: null,
contrastBorder: null,
// Colors inside a text document, such as the welcome page
"textBlockQuote.background": ["#7f7f7f1a", "#7f7f7f1a"],
"textBlockQuote.border": ["#007acc80", "#007acc80"],
"textCodeBlock.background": ["#0a0a0a66", "#dcdcdc66"],
"textLink.activeForeground": ["#3794ff", "#006ab1"],
"textLink.foreground": ["#3794ff", "#006ab1"],
"textPreformat.foreground": ["#d7ba7d", "#a31515"],
"textSeparator.foreground": ["#ffffff2e", "#0000002e"],
// Editor colors
"editor.background": defaultEditorBackgroundColors,
"editor.foreground": defaultEditorForegroundColors,
"editorLineNumber.foreground": ["#858585", "#237893"],
"editorLineNumber.activeForeground": "editorActiveLineNumber.foreground",
"editorActiveLineNumber.foreground": ["#c6c6c6", "#0b216f"],
"editor.selectionBackground": ["#264f78", "#add6ff"],
"editor.inactiveSelectionBackground": ["transparent", "editor.selectionBackground", 0.5],
"editor.selectionHighlightBackground": ["lessProminent", "editor.selectionBackground", "editor.background", 0.3, 0.6],
// Editor status colors & icons
"editorError.foreground": ["#f14c4c", "#e51400"],
"editorWarning.foreground": ["#cca700", "#bf8803"],
"editorInfo.foreground": ["#3794ff", "#1a85ff"],
"editorHint.foreground": ["#eeeeeeb2", "#6c6c6c"],
"problemsErrorIcon.foreground": "editorError.foreground",
"problemsWarningIcon.foreground": "editorWarning.foreground",
"problemsInfoIcon.foreground": "editorInfo.foreground",
// Editor find matches
"editor.findMatchBackground": ["#515c6a", "#a8ac94"],
"editor.findMatchHighlightBackground": ["#ea5c0055", "#ea5c0055"],
"editor.findRangeHighlightBackground": ["#3a3d4166", "#b4b4b44d"],
// Editor links
"editorLink.activeForeground": ["#4e94ce", "#0000ff"],
// Editor lightbulb icons
"editorLightBulb.foreground": ["#ffcc00", "#ddb100"],
"editorLightBulbAutoFix.foreground": ["#75beff", "#007acc"],
// Editor diffs
"diffEditor.insertedTextBackground": ["#9ccc2c33", "#9ccc2c40"],
"diffEditor.insertedTextBorder": null,
"diffEditor.removedTextBackground": ["#ff000033", "#ff000033"],
"diffEditor.removedTextBorder": null,
"diffEditor.insertedLineBackground": ["#9bb95533", "#9bb95533"],
"diffEditor.removedLineBackground": ["#ff000033", "#ff000033"],
// Editor sticky scroll
"editorStickyScroll.background": "editor.background",
"editorStickyScrollHover.background": ["#2a2d2e", "#f0f0f0"],
// Editor inlays (hints displayed inside an editor line)
"editorInlayHint.background": [
["transparent", "badge.background", 0.8],
["transparent", "badge.background", 0.6]
],
"editorInlayHint.foreground": "badge.foreground",
"editorInlayHint.typeBackground": "editorInlayHint.background",
"editorInlayHint.typeForeground": "editorInlayHint.foreground",
"editorInlayHint.parameterBackground": "editorInlayHint.background",
"editorInlayHint.parameterForeground": "editorInlayHint.foreground",
// Editor groups & panes
"editorPane.background": ["editor.background", "editor.background"],
"editorGroup.emptyBackground": null,
"editorGroup.focusedEmptyBorder": null,
"editorGroupHeader.tabsBackground": ["#252526", "#f3f3f3"],
"editorGroupHeader.tabsBorder": null,
"editorGroupHeader.noTabsBackground": ["editor.background", "editor.background"],
"editorGroupHeader.border": null,
"editorGroup.border": ["#444444", "#e7e7e7"],
"editorGroup.dropBackground": ["#53595d80", "#2677cb2d"],
"editorGroup.dropIntoPromptForeground": ["editorWidget.foreground", "editorWidget.foreground"],
"editorGroup.dropIntoPromptBackground": ["editorWidget.background", "editorWidget.background"],
"editorGroup.dropIntoPromptBorder": null,
"sideBySideEditor.horizontalBorder": ["editorGroup.border", "editorGroup.border"],
"sideBySideEditor.verticalBorder": ["editorGroup.border", "editorGroup.border"],
// Scrollbars
"scrollbar.shadow": ["#000000", "#dddddd"],
"scrollbarSlider.background": ["#79797966", "#64646466"],
"scrollbarSlider.hoverBackground": ["#646464b2", "#646464b2"],
"scrollbarSlider.activeBackground": ["#bfbfbf66", "#00000099"],
// Panels
"panel.background": "editor.background",
"panel.border": "#80808059",
"panelTitle.activeBorder": "panelTitle.activeForeground",
"panelTitle.activeForeground": ["#e7e7e7", "#424242"],
"panelTitle.inactiveForeground": [
["transparent", "panelTitle.activeForeground", 0.6],
["transparent", "panelTitle.activeForeground", 0.75]
],
"panelSectionHeader.background": "#80808051",
"terminal.background": "panel.background",
// Widgets
"widget.shadow": ["#0000005b", "#00000028"],
"editorWidget.background": ["#252526", "#f3f3f3"],
"editorWidget.foreground": "foreground",
"editorWidget.border": ["#454545", "#c8c8c8"],
"quickInput.background": "editorWidget.background",
"quickInput.foreground": "editorWidget.foreground",
"quickInputTitle.background": ["#ffffff1a", "#0000000f"],
"pickerGroup.foreground": ["#3794ff", "#0066bf"],
"pickerGroup.border": ["#3f3f46", "#cccedb"],
"editor.hoverHighlightBackground": ["#264f7840", "#add6ff26"],
"editorHoverWidget.background": "editorWidget.background",
"editorHoverWidget.foreground": "editorWidget.foreground",
"editorHoverWidget.border": "editorWidget.border",
"editorHoverWidget.statusBarBackground": [
["lighten", "editorHoverWidget.background", 0.2],
["darken", "editorHoverWidget.background", 0.05]
],
// Title bar
"titleBar.activeBackground": ["#3c3c3c", "#dddddd"],
"titleBar.activeForeground": ["#cccccc", "#333333"],
"titleBar.inactiveBackground": ["transparent", "titleBar.activeBackground", 0.6],
"titleBar.inactiveForeground": ["transparent", "titleBar.activeForeground", 0.6],
"titleBar.border": null,
// Toolbars
"toolbar.hoverBackground": ["#5a5d5e50", "#b8b8b850"],
"toolbar.activeBackground": [
["lighten", "toolbar.hoverBackground", 0.1],
["darken", "toolbar.hoverBackground", 0.1]
],
// Tab background
"tab.activeBackground": ["editor.background", "editor.background"],
"tab.unfocusedActiveBackground": ["tab.activeBackground", "tab.activeBackground"],
"tab.inactiveBackground": ["#2d2d2d", "#ececec"],
"tab.unfocusedInactiveBackground": ["tab.inactiveBackground", "tab.inactiveBackground"],
// Tab foreground
"tab.activeForeground": ["#ffffff", "#333333"],
"tab.inactiveForeground": [
["transparent", "tab.activeForeground", 0.5],
["transparent", "tab.activeForeground", 0.7]
],
"tab.unfocusedActiveForeground": [
["transparent", "tab.activeForeground", 0.5],
["transparent", "tab.activeForeground", 0.7]
],
"tab.unfocusedInactiveForeground": [
["transparent", "tab.inactiveForeground", 0.5],
["transparent", "tab.inactiveForeground", 0.5]
],
// Tab hover foreground/background
"tab.hoverBackground": null,
"tab.unfocusedHoverBackground": [
["transparent", "tab.hoverBackground", 0.5],
["transparent", "tab.hoverBackground", 0.7]
],
"tab.hoverForeground": null,
"tab.unfocusedHoverForeground": [
["transparent", "tab.hoverForeground", 0.5],
["transparent", "tab.hoverForeground", 0.5]
],
// Tab borders
"tab.border": ["#252526", "#f3f3f3"],
"tab.lastPinnedBorder": ["tree.indentGuidesStroke", "tree.indentGuidesStroke"],
"tab.activeBorder": null,
"tab.unfocusedActiveBorder": [
["transparent", "tab.activeBorder", 0.5],
["transparent", "tab.activeBorder", 0.7]
],
"tab.activeBorderTop": null,
"tab.unfocusedActiveBorderTop": [
["transparent", "tab.activeBorderTop", 0.5],
["transparent", "tab.activeBorderTop", 0.7]
],
"tab.hoverBorder": null,
"tab.unfocusedHoverBorder": [
["transparent", "tab.hoverBorder", 0.5],
["transparent", "tab.hoverBorder", 0.7]
],
// Tab modified border
"tab.activeModifiedBorder": ["#3399cc", "#33aaee"],
"tab.inactiveModifiedBorder": [
["transparent", "tab.activeModifiedBorder", 0.5],
["transparent", "tab.activeModifiedBorder", 0.5]
],
"tab.unfocusedActiveModifiedBorder": [
["transparent", "tab.activeModifiedBorder", 0.5],
["transparent", "tab.activeModifiedBorder", 0.7]
],
"tab.unfocusedInactiveModifiedBorder": [
["transparent", "tab.inactiveModifiedBorder", 0.5],
["transparent", "tab.inactiveModifiedBorder", 0.5]
],
// Badges (small information labels, for example, search results count)
"badge.background": ["#4d4d4d", "#c4c4c4"],
"badge.foreground": ["#ffffff", "#333333"],
// Buttons
"button.background": ["#0e639c", "#007acc"],
"button.foreground": ["#ffffff", "#ffffff"],
"button.border": "contrastBorder",
"button.separator": ["transparent", "button.foreground", 0.4],
"button.hoverBackground": [
["lighten", "button.background", 0.2],
["darken", "button.background", 0.2]
],
"button.secondaryBackground": ["#3a3d41", "#5f6a79"],
"button.secondaryForeground": ["#ffffff", "#ffffff"],
"button.secondaryHoverBackground": [
["lighten", "button.secondaryBackground", 0.2],
["darken", "button.secondaryBackground", 0.2]
],
// Dropdowns (selects)
"dropdown.background": ["#3c3c3c", "#ffffff"],
"dropdown.foreground": ["#f0f0f0", "foreground"],
"dropdown.border": ["dropdown.background", "#cecece"],
// Lists
"list.activeSelectionBackground": ["#04395e", "#0060c0"],
"list.activeSelectionForeground": "#ffffff",
// Trees
"tree.indentGuidesStroke": ["#585858", "#a9a9a9"],
// Input fields
"input.background": ["#3c3c3c", "#ffffff"],
"input.foreground": "foreground",
"input.placeholderForeground": ["transparent", "foreground", 0.5],
"inputOption.activeBorder": ["#007acc", "#007acc"],
"inputOption.hoverBackground": ["#5a5d5e80", "#b8b8b850"],
"inputOption.activeBackground": [
["transparent", "focusBorder", 0.4],
["transparent", "focusBorder", 0.2]
],
"inputOption.activeForeground": ["#ffffff", "#000000"],
"inputValidation.infoBackground": ["#063b49", "#d6ecf2"],
"inputValidation.infoBorder": ["#007acc", "#007acc"],
"inputValidation.warningBackground": ["#352a05", "#f6f5d2"],
"inputValidation.warningBorder": ["#b89500", "#b89500"],
"inputValidation.errorBackground": ["#5a1d1d", "#f2dede"],
"inputValidation.errorBorder": ["#be1100", "#be1100"],
// Keybinding labels
"keybindingLabel.background": ["#8080802b", "#dddddd66"],
"keybindingLabel.foreground": ["#cccccc", "#555555"],
"keybindingLabel.border": ["#33333399", "#cccccc66"],
"keybindingLabel.bottomBorder": ["#44444499", "#bbbbbb66"],
// Menu colors
"menu.foreground": "dropdown.foreground",
"menu.background": "dropdown.background",
"menu.selectionForeground": "list.activeSelectionForeground",
"menu.selectionBackground": "list.activeSelectionBackground",
"menu.separatorBackground": ["#606060", "#d4d4d4"],
// Snippet placeholder colors
"editor.snippetTabstopHighlightBackground": ["#7c7c74c", "#0a326433"],
"editor.snippetFinalTabstopHighlightBorder": ["#525252", "#0a326480"],
// Terminal colors
"terminal.ansiBlack": "#000000",
"terminal.ansiRed": "#cd3131",
"terminal.ansiGreen": ["#0dbc79", "#00bc00"],
"terminal.ansiYellow": ["#e5e510", "#949800"],
"terminal.ansiBlue": ["#2472c8", "#0451a5"],
"terminal.ansiMagenta": ["#bc3fbc", "#bc05bc"],
"terminal.ansiCyan": ["#11a8cd", "#0598bc"],
"terminal.ansiWhite": ["#e5e5e5", "#555555"],
"terminal.ansiBrightBlack": "#666666",
"terminal.ansiBrightRed": ["#f14c4c", "#cd3131"],
"terminal.ansiBrightGreen": ["#23d18b", "#14ce14"],
"terminal.ansiBrightYellow": ["#f5f543", "#b5ba00"],
"terminal.ansiBrightBlue": ["#3b8eea", "#0451a5"],
"terminal.ansiBrightMagenta": ["#d670d6", "#bc05bc"],
"terminal.ansiBrightCyan": ["#29b8db", "#0598bc"],
"terminal.ansiBrightWhite": ["#e5e5e5", "#a5a5a5"]
};
function resolveVSCodeWorkbenchColors(colors, themeType) {
const typeIndex = themeType === "dark" ? 0 : 1;
const workbenchColors = {
...defaultWorkbenchColors,
...colors
};
const colorsStartedResolving = /* @__PURE__ */ new Set();
const colorsResolved = /* @__PURE__ */ new Map();
function applyColorTransform(unresolvedColor) {
if (unresolvedColor.length === 3) {
const [type, colorKey, amount] = unresolvedColor;
const hexColor = resolveColor(colorKey);
if (hexColor === null)
return null;
if (type === "transparent") {
return multiplyAlpha(hexColor, amount);
} else if (type === "lighten") {
return lighten(hexColor, amount);
} else if (type === "darken") {
return darken(hexColor, amount);
}
}
if (unresolvedColor.length === 5 && unresolvedColor[0] === "lessProminent") {
const [, colorKey, backgroundKey, factor, transparency] = unresolvedColor;
const hexFrom = resolveColor(colorKey);
if (hexFrom === null)
return null;
const hexBackground = resolveColor(backgroundKey);
if (hexBackground === null)
return multiplyAlpha(hexFrom, factor * transparency);
const fromLum = getLuminance(hexFrom);
const bgLum = getLuminance(hexBackground);
let combinedFactor = factor ? factor : 0.5;
if (fromLum < bgLum) {
combinedFactor *= (bgLum - fromLum) / bgLum;
const lightened = lighten(hexFrom, combinedFactor);
return multiplyAlpha(lightened, transparency);
} else {
combinedFactor *= (fromLum - bgLum) / fromLum;
const darkened = darken(hexFrom, combinedFactor);
return multiplyAlpha(darkened, transparency);
}
}
}
function resolveColor(unresolvedColor) {
if (unresolvedColor === null)
return null;
const alreadyResolvedColor = colorsResolved.get(unresolvedColor);
if (alreadyResolvedColor !== void 0)
return alreadyResolvedColor;
if (colorsStartedResolving.has(unresolvedColor))
throw new Error("Circular reference in default colors.");
colorsStartedResolving.add(unresolvedColor);
let resolved;
if (typeof unresolvedColor === "string") {
if (unresolvedColor.startsWith("#")) {
resolved = unresolvedColor.toLowerCase();
} else {
const referencedColor = workbenchColors[unresolvedColor];
if (referencedColor !== void 0)
resolved = resolveColor(referencedColor);
}
} else if (Array.isArray(unresolvedColor)) {
if (unresolvedColor.length === 2) {
resolved = resolveColor(unresolvedColor[typeIndex]);
} else {
resolved = applyColorTransform(unresolvedColor);
}
}
if (resolved === void 0)
throw new Error(`Invalid color value ${JSON.stringify(unresolvedColor)}, expected a hex color.`);
colorsResolved.set(unresolvedColor, resolved);
return resolved;
}
const keys = Object.keys(workbenchColors);
keys.forEach((key) => {
try {
workbenchColors[key] = resolveColor(workbenchColors[key]);
} catch (error) {
const msg = error instanceof Error ? error.message : error;
throw new Error(`Failed to resolve theme color for key ${key}: ${msg}`);
}
});
return workbenchColors;
}
function guessThemeTypeFromEditorColors(colors) {
const bgLuminance = getLuminance(colors?.["editor.background"] || defaultEditorBackgroundColors[0]);
const fgLuminance = getLuminance(colors?.["editor.foreground"] || defaultEditorForegroundColors[0]);
return bgLuminance < fgLuminance ? "dark" : "light";
}
// ../../../node_modules/.pnpm/strip-json-comments@5.0.1/node_modules/strip-json-comments/index.js
var singleComment = Symbol("singleComment");
var multiComment = Symbol("multiComment");
var stripWithoutWhitespace = () => "";
var stripWithWhitespace = (string, start, end) => string.slice(start, end).replace(/\S/g, " ");
var isEscaped = (jsonString, quotePosition) => {
let index = quotePosition - 1;
let backslashCount = 0;
while (jsonString[index] === "\\") {
index -= 1;
backslashCount += 1;
}
return Boolean(backslashCount % 2);
};
function stripJsonComments(jsonString, { whitespace = true, trailingCommas = false } = {}) {
if (typeof jsonString !== "string") {
throw new TypeError(`Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\``);
}
const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace;
let isInsideString = false;
let isInsideComment = false;
let offset = 0;
let buffer = "";
let result = "";
let commaIndex = -1;
for (let index = 0; index < jsonString.length; index++) {
const currentCharacter = jsonString[index];
const nextCharacter = jsonString[index + 1];
if (!isInsideComment && currentCharacter === '"') {
const escaped = isEscaped(jsonString, index);
if (!escaped) {
isInsideString = !isInsideString;
}
}
if (isInsideString) {
continue;
}
if (!isInsideComment && currentCharacter + nextCharacter === "//") {
buffer += jsonString.slice(offset, index);
offset = index;
isInsideComment = singleComment;
index++;
} else if (isInsideComment === singleComment && currentCharacter + nextCharacter === "\r\n") {
index++;
isInsideComment = false;
buffer += strip(jsonString, offset, index);
offset = index;
continue;
} else if (isInsideComment === singleComment && currentCharacter === "\n") {
isInsideComment = false;
buffer += strip(jsonString, offset, index);
offset = index;
} else if (!isInsideComment && currentCharacter + nextCharacter === "/*") {
buffer += jsonString.slice(offset, index);
offset = index;
isInsideComment = multiComment;
index++;
continue;
} else if (isInsideComment === multiComment && currentCharacter + nextCharacter === "*/") {
index++;
isInsideComment = false;
buffer += strip(jsonString, offset, index + 1);
offset = index + 1;
continue;
} else if (trailingCommas && !isInsideComment) {
if (commaIndex !== -1) {
if (currentCharacter === "}" || currentCharacter === "]") {
buffer += jsonString.slice(offset, index);
result += strip(buffer, 0, 1) + buffer.slice(1);
buffer = "";
offset = index;
commaIndex = -1;
} else if (currentCharacter !== " " && currentCharacter !== " " && currentCharacter !== "\r" && currentCharacter !== "\n") {
buffer += jsonString.slice(offset, index);
offset = index;
commaIndex = -1;
}
} else if (currentCharacter === ",") {
result += buffer + jsonString.slice(offset, index);
buffer = "";
offset = index;
commaIndex = index;
}
}
}
return result + buffer + (isInsideComment ? strip(jsonString.slice(offset)) : jsonString.slice(offset));
}
// src/common/theme.ts
var ExpressiveCodeTheme = class _ExpressiveCodeTheme {
name;
type;
colors;
fg;
bg;
semanticHighlighting;
settings;
styleOverrides;
/**
* Loads the given theme for use with Expressive Code. Supports both Shiki and VS Code themes.
*
* You can also pass an existing `ExpressiveCodeTheme` instance to create a copy of it.
*
* Note: To save on bundle size, this constructor does not support loading themes
* bundled with Shiki by name (e.g. `dracula`). Instead, import Shiki's `loadTheme`
* function yourself, use it to load its bundled theme (e.g. `themes/dracula.json`),
* and pass the result to this constructor.
*/
constructor(theme) {
let themeType = theme.type;
if (themeType === "css")
throw new Error('Theme type "css" is not supported.');
if (themeType !== "dark" && themeType !== "light") {
themeType = guessThemeTypeFromEditorColors(theme.colors);
}
const themeColors = { ...theme.colors };
for (const key in themeColors) {
if (typeof themeColors[key] !== "string" || !themeColors[key].trim().length)
delete themeColors[key];
}
this.name = theme.name || themeType;
this.type = themeType;
this.colors = resolveVSCodeWorkbenchColors(themeColors, this.type);
this.fg = theme.fg || this.colors["editor.foreground"];
this.bg = theme.bg || this.colors["editor.background"];
this.semanticHighlighting = theme.semanticHighlighting || false;
const premultiplyTable = [["editorGroupHeader.tabsBackground", "editor.background"]];
premultiplyTable.forEach(([colorKey, bgKey]) => {
this.colors[colorKey] = onBackground(this.colors[colorKey], this.colors[bgKey]);
});
const themeTokenSettings = theme.tokenColors || theme.settings;
this.settings = this.parseThemeSettings(themeTokenSettings);
this.styleOverrides = theme.styleOverrides ?? {};
}
/**
* Applies chromatic adjustments to entire groups of theme colors while keeping their
* relative lightness and alpha components intact. This can be used to quickly create
* theme variants that fit the color scheme of any website or brand.
*
* Adjustments can either be defined as hue and chroma values in the OKLCH color space
* (range 0–360 for hue, 0–0.4 for chroma), or these values can be extracted from
* hex color strings (e.g. `#3b82f6`).
*
* You can target predefined groups of theme colors (e.g. `backgrounds`, `accents`)
* and/or use the `custom` property to define your own groups of theme colors to be adjusted.
* Each custom group must contain a `themeColorKeys` property with an array of VS Code
* theme color keys (e.g. `['panel.background', 'panel.border']`) and a `targetHueAndChroma`
* property that accepts the same adjustment target values as `backgrounds` and `accents`.
* Custom groups will be applied in the order they are defined.
*
* Returns the same `ExpressiveCodeTheme` instance to allow chaining.
*/
applyHueAndChromaAdjustments(adjustments) {
const adjustedColors = {};
const adjustColors = (colors, target) => {
colors.forEach((color) => {
if (!this.colors[color])
return;
adjustedColors[color] = chromaticRecolor(this.colors[color], target);
});
};
if (adjustments.backgrounds) {
adjustColors(groupedDefaultWorkbenchColorKeys.backgrounds, adjustments.backgrounds);
}
if (adjustments.accents) {
adjustColors(groupedDefaultWorkbenchColorKeys.accents, adjustments.accents);
}
if (adjustments.custom) {
adjustments.custom.forEach((custom) => {
adjustColors(custom.themeColorKeys, custom.targetHueAndChroma);
});
}
Object.assign(this.colors, adjustedColors);
return this;
}
/**
* Processes the theme's syntax highlighting colors to ensure a minimum contrast ratio
* between foreground and background colors.
*
* The default value of 5.5 ensures optimal accessibility with a contrast ratio of 5.5:1.
*
* You can optionally pass a custom background color to use for the contrast checks.
* By default, the theme's background color will be used.
*
* Returns the same `ExpressiveCodeTheme` instance to allow chaining.
*/
ensureMinSyntaxHighlightingColorContrast(minContrast = 5.5, backgroundColor) {
const fixedContrastCache = /* @__PURE__ */ new Map();
const fixContrast = (color) => {
const cachedResult = fixedContrastCache.get(color);
if (cachedResult)
return cachedResult;
const newColor = ensureColorContrastOnBackground(color, backgroundColor || this.bg, minContrast);
fixedContrastCache.set(color, newColor);
return newColor;
};
this.colors["editor.foreground"] = fixContrast(this.colors["editor.foreground"]);
this.fg = fixContrast(this.colors["editor.foreground"]);
this.settings.forEach((s2) => {
if (!s2.settings.foreground)
return;
s2.settings.foreground = fixContrast(s2.settings.foreground);
});
return this;
}
/**
* Parses the given theme settings into a properly typed format
* that can be used by both Expressive Code and Shiki.
*
* As theme scopes can be defined as either a comma-separated string, or an array of strings,
* they will always be converted to their array form to simplify further processing.
*
* Also recreates known object properties to prevent accidental mutation
* of the original settings when copying a theme.
*/
parseThemeSettings(settings) {
if (!settings || !Array.isArray(settings))
return [];
return settings.map((unknownSetting) => {
const { name, scope: anyScope, settings: settings2, ...rest } = unknownSetting;
const scope = Array.isArray(anyScope) ? anyScope.slice() : typeof anyScope === "string" ? anyScope.split(/\s*,\s*/) : void 0;
return {
...name !== void 0 ? { name } : {},
...scope !== void 0 ? { scope } : {},
settings: { ...settings2 },
...rest
};
});
}
/**
* Attempts to parse the given JSON string as a theme.
*
* As some themes follow the JSONC format and may contain comments and trailing commas,
* this method will attempt to strip them before parsing the result.
*/
static fromJSONString(json) {
return new _ExpressiveCodeTheme(JSON.parse(stripJsonComments(json, { trailingCommas: true })));
}
};
// src/common/plugin-style-settings.ts
var PluginStyleSettings = class {
defaultValues;
cssVarExclusions;
constructor({ defaultValues, cssVarExclusions = [] }) {
this.defaultValues = defaultValues;
this.cssVarExclusions = cssVarExclusions;
}
};
// src/internal/core-styles.ts
var coreStyleSettings = new PluginStyleSettings({
defaultValues: {
// Outer container
borderRadius: "0.3rem",
borderWidth: "1.5px",
borderColor: ({ theme }) => theme.colors["titleBar.border"] || lighten(theme.colors["editor.background"], theme.type === "dark" ? 0.5 : -0.15) || "transparent",
// Code editor content
codeFontFamily: minifyFontFamily(`ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace`),
codeFontSize: "0.85rem",
codeFontWeight: "400",
codeLineHeight: "1.65",
codePaddingBlock: "1rem",
codePaddingInline: "1.35rem",
codeBackground: ({ theme }) => theme.colors["editor.background"],
codeForeground: ({ theme }) => theme.colors["editor.foreground"],
codeSelectionBackground: ({ theme }) => theme.colors["editor.selectionBackground"],
// Gutter
gutterBorderColor: ({ resolveSetting }) => setAlpha(resolveSetting("gutterForeground"), 0.2),
gutterBorderWidth: "1.5px",
gutterForeground: ({ theme, resolveSetting }) => ensureColorContrastOnBackground(theme.colors["editorLineNumber.foreground"] || resolveSetting("codeForeground"), resolveSetting("codeBackground"), 3.3, 3.6),
gutterHighlightForeground: ({ theme, resolveSetting }) => ensureColorContrastOnBackground(
theme.colors["editorLineNumber.activeForeground"] || theme.colors["editorLineNumber.foreground"] || resolveSetting("codeForeground"),
resolveSetting("codeBackground"),
4.5,
5
),
// UI elements
uiFontFamily: minifyFontFamily(
`ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'`
),
uiFontSize: "0.9rem",
uiFontWeight: "400",
uiLineHeight: "1.65",
uiPaddingBlock: "0.25rem",
uiPaddingInline: "1rem",
uiSelectionBackground: ({ theme }) => theme.colors["menu.selectionBackground"],
uiSelectionForeground: ({ theme }) => theme.colors["menu.selectionForeground"],
// Special colors
focusBorder: ({ theme }) => theme.colors["focusBorder"],
scrollbarThumbColor: ({ theme, resolveSetting }) => ensureColorContrastOnBackground(theme.colors["scrollbarSlider.background"], resolveSetting("codeBackground"), 1, 2),
scrollbarThumbHoverColor: ({ theme, resolveSetting }) => ensureColorContrastOnBackground(theme.colors["scrollbarSlider.hoverBackground"], resolveSetting("codeBackground"), 2.5, 3.5)
}
});
function getCoreBaseStyles({
cssVar,
useStyleReset,
useThemedScrollbars,
useThemedSelectionColors
}) {
const ifThemedScrollbars = (css) => useThemedScrollbars ? css : "";
const ifThemedSelectionColors = (css) => useThemedSelectionColors ? css : "";
return `
font-family: ${cssVar("uiFontFamily")};
font-size: ${cssVar("uiFontSize")};
font-weight: ${cssVar("uiFontWeight")};
line-height: ${cssVar("uiLineHeight")};
text-size-adjust: none;
-webkit-text-size-adjust: none;
*:not(path) {
${useStyleReset ? "all: revert;" : ""}
box-sizing: border-box;
}
${ifThemedSelectionColors(`::selection {
background: ${cssVar("uiSelectionBackground")};
color: ${cssVar("uiSelectionForeground")};
}`)}
pre {
display: flex;
margin: 0;
padding: 0;
border: ${cssVar("borderWidth")} solid ${cssVar("borderColor")};
border-radius: calc(${cssVar("borderRadius")} + ${cssVar("borderWidth")});
background: ${cssVar("codeBackground")};
&:focus-visible {
outline: 3px solid ${cssVar("focusBorder")};
outline-offset: -3px;
}
& > code {
all: unset;
display: block;
flex: 1 0 100%;
padding: ${cssVar("codePaddingBlock")} 0;
color: ${cssVar("codeForeground")};
font-family: ${cssVar("codeFontFamily")};
font-size: ${cssVar("codeFontSize")};
font-weight: ${cssVar("codeFontWeight")};
line-height: ${cssVar("codeLineHeight")};
}
${ifThemedSelectionColors(`::selection {
background: ${cssVar("codeSelectionBackground")};
color: inherit;
}`)}
/* Show horizontal scrollbar if required */
overflow-x: auto;
/* Enable word wrapping on demand */
&.wrap .${codeLineClass} .code {
white-space: pre-wrap;
overflow-wrap: break-word;
min-width: min(20ch, var(--ecMaxLine, 20ch));
& span.indent {
white-space: pre;
}
}
${ifThemedScrollbars(`
&::-webkit-scrollbar,
&::-webkit-scrollbar-track {
background-color: inherit;
border-radius: calc(${cssVar("borderRadius")} + ${cssVar("borderWidth")});
border-top-left-radius: 0;
border-top-right-radius: 0;
}
&::-webkit-scrollbar-thumb {
background-color: ${cssVar("scrollbarThumbColor")};
border: 4px solid transparent;
background-clip: content-box;
border-radius: 10px;
}
&::-webkit-scrollbar-thumb:hover {
background-color: ${cssVar("scrollbarThumbHoverColor")};
}
`)}
}
/* Code lines */
.${codeLineClass} {
/* RTL support: Code is always LTR */
direction: ltr;
unicode-bidi: isolate;
/* Prepare grid layout for optional gutter */
display: grid;
grid-template-areas: 'gutter code';
grid-template-columns: auto 1fr;
position: relative;
.gutter {
grid-area: gutter;
color: ${cssVar("gutterForeground")};
/* Make all gutter elements non-interactive by default */
& > * {
pointer-events: none;
user-select: none;
-webkit-user-select: none;
}
/* Apply conditional styles if a gutter is present */
& ~ .code {
--ecLineBrdCol: ${cssVar("gutterBorderColor")};
}
}
&.highlight .gutter {
color: ${cssVar("gutterHighlightForeground")};
}
.code {
grid-area: code;
position: relative;
box-sizing: content-box;
padding-inline-start: calc(var(--ecIndent, 0ch) + ${cssVar("codePaddingInline")} - var(--ecGtrBrdWd));
padding-inline-end: ${cssVar("codePaddingInline")};
text-indent: calc(var(--ecIndent, 0ch) * -1);
&::before,
&::after,
& :where(*) {
text-indent: 0;
}
/* Support a colorful border on the start of the code line */
--ecGtrBrdWd: ${cssVar("gutterBorderWidth")};
border-inline-start: var(--ecGtrBrdWd) solid var(--ecLineBrdCol, transparent);
}
}
/* Increase end padding of the first line for the copy button */
:nth-child(1 of .${codeLineClass}) .code {
padding-inline-end: calc(2rem + ${cssVar("codePaddingInline")});
}
/* Common style to hide elements from screen readers */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
`;
}
function getCoreThemeStyles(styleVariantIndex) {
return `
/* Theme-dependent styles for InlineStyleAnnotation */
.${codeLineClass} :where(span[style^='--']:not([class])) {
color: var(--${styleVariantIndex}, inherit);
font-style: var(--${styleVariantIndex}fs, inherit);
font-weight: var(--${styleVariantIndex}fw, inherit);
text-decoration: var(--${styleVariantIndex}td, inherit);
}
`;
}
function minifyFontFamily(fontFamily) {
return fontFamily.split(",").map((font) => font.trim()).join(",");
}
// src/internal/style-resolving.ts
function resolveStyleSettings({
theme,
styleVariantIndex,
plugins,
styleOverrides
}) {
const attemptedToResolve = /* @__PURE__ */ new Set();
const resolvedByPath = /* @__PURE__ */ new Map();
const resolverArgs = { theme, styleVariantIndex, resolveSetting };
const unresolvedByPath = getStyleSettingsByPath(coreStyleSettings.defaultValues);
plugins.forEach((plugin) => {
if (!plugin.styleSettings)
return;
applyStyleSettings(unresolvedByPath, getStyleSettingsByPath(plugin.styleSettings.defaultValues));
});
applyStyleSettings(unresolvedByPath, getStyleSettingsByPath(styleOverrides ?? {}));
applyStyleSettings(unresolvedByPath, getStyleSettingsByPath(theme.styleOverrides ?? {}));
function resolveSetting(settingPath) {
let result = resolvedByPath.get(settingPath);
if (result === void 0 && !resolvedByPath.has(settingPath)) {
if (attemptedToResolve.has(settingPath))
throw new Error(`Circular dependency detected while resolving style setting '${settingPath}'`);
attemptedToResolve.add(settingPath);
const valueOrResolver = unresolvedByPath.get(settingPath);
const resolvedDefinition = typeof valueOrResolver === "function" ? valueOrResolver(resolverArgs) : valueOrResolver;
result = Array.isArray(resolvedDefinition) ? resolvedDefinition[theme.type === "dark" ? 0 : 1] : resolvedDefinition;
resolvedByPath.set(settingPath, result);
}
if (result === void 0)
throw new Error(
`Failed to resolve style setting '${settingPath}' for theme '${theme.name}': The resolved value was undefined. This could be caused by your plugins or styleOverrides.`
);
return result;
}
unresolvedByPath.forEach((_, settingPath) => resolveSetting(settingPath));
return resolvedByPath;
}
function getCssVarDeclarations({
resolvedStyleSettings,
plugins,
cssVarName
}) {
const cssVarDeclarations = /* @__PURE__ */ new Map();
const excludedPaths = /* @__PURE__ */ new Set();
plugins.forEach((plugin) => {
plugin.styleSettings?.cssVarExclusions.forEach((path) => excludedPaths.add(path));
});
resolvedStyleSettings.forEach((value, path) => {
if (excludedPaths.has(path))
return;
cssVarDeclarations.set(cssVarName(path), value);
});
return cssVarDeclarations;
}
function getStyleSettingsByPath(styleSettings) {
const result = /* @__PURE__ */ new Map();
for (const [key, value] of Object.entries(styleSettings)) {
if (typeof value === "object" && !Array.isArray(value)) {
Object.entries(value).forEach(([subKey, subValue]) => {
result.set(`${key}.${subKey}`, subValue);
});
} else {
result.set(key, value);
}
}
return result;
}
function applyStyleSettings(target, source) {
source.forEach((value, path) => value !== void 0 && target.set(path, value));
}
// src/common/style-variants.ts
function resolveStyleVariants({
themes,
plugins,
styleOverrides,
cssVarName
}) {
return themes.map((theme, styleVariantIndex) => {
const resolvedStyleSettings = resolveStyleSettings({ theme, styleVariantIndex, plugins, styleOverrides });
const cssVarDeclarations = getCssVarDeclarations({ resolvedStyleSettings, plugins, cssVarName });
return {
theme,
resolvedStyleSettings,
cssVarDeclarations
};
});
}
// src/internal/tabindex-js-module.min.ts
var tabindex_js_module_min_default = 'try{(()=>{function i(e){if(!e)return;let r=e.getAttribute("tabindex")!==null,t=e.scrollWidth>e.clientWidth;t&&!r?e.setAttribute("tabindex","0"):!t&&r&&e.removeAttribute("tabindex")}function a(e){let r=new Set,t;return new ResizeObserver(u=>{u.forEach(o=>r.add(o.target)),t&&clearTimeout(t),t=setTimeout(()=>{t=void 0,r.forEach(o=>e(o)),r.clear()},250)})}function s(e,r){e.querySelectorAll?.(".expressive-code pre > code").forEach(t=>{let n=t.parentElement;n&&(i(n),r.observe(n))})}var d=a(i);s(document,d);var c=new MutationObserver(e=>e.forEach(r=>r.addedNodes.forEach(t=>{s(t,d)})));c.observe(document.body,{childList:!0,subtree:!0});document.addEventListener("astro:page-load",()=>{s(document,d)});})();}catch(e){console.error("[EC] tabindex-js-module failed:",e)}';
// src/internal/core-plugins.ts
var corePlugins = [
{
name: "Indent wrapper",
hooks: {
postprocessAnnotations: ({ codeBlock }) => {
codeBlock.getLines().forEach((line) => {
const indent = line.text.match(/^\s+/)?.[0].length ?? 0;
if (indent > 0) {
line.getAnnotations().forEach((annotation) => {
const { inlineRange } = annotation;
if (!inlineRange || !isInlineStyleAnnotation(annotation))
return;
if (inlineRange.columnStart >= 0 && inlineRange?.columnEnd <= indent) {
line.deleteAnnotation(annotation);
}
});
line.addAnnotation(
new IndentAnnotation({
inlineRange: { columnStart: 0, columnEnd: indent },
renderPhase: "earlier"
})
);
}
});
}
}
},
{
name: "Scrollable block tabindex",
jsModules: [tabindex_js_module_min_default]
}
];
var IndentAnnotation = class extends ExpressiveCodeAnnotation {
render({ nodesToTransform }) {
return nodesToTransform.map((node) => h("span.indent", node));
}
};
// src/common/engine.ts
var ExpressiveCodeEngine = class {
/**
* Creates a new instance of the Expressive Code engine.
*
* To minimize overhead caused by loading plugins, you can create a single instance
* for your application and keep using it to render all your code blocks.
*/
constructor(config) {
const deprecatedConfig = config;
if (deprecatedConfig.theme && !config.themes) {
config.themes = Array.isArray(deprecatedConfig.theme) ? deprecatedConfig.theme : [deprecatedConfig.theme];
delete deprecatedConfig.theme;
}
this.themes = Array.isArray(config.themes) ? [...config.themes] : config.themes ? [config.themes] : [new ExpressiveCodeTheme(githubDark), new ExpressiveCodeTheme(githubLight)];
this.minSyntaxHighlightingColorContrast = config.minSyntaxHighlightingColorContrast ?? 5.5;
this.useDarkModeMediaQuery = config.useDarkModeMediaQuery ?? (this.themes.length === 2 && this.themes[0].type !== this.themes[1].type);
this.themeCssRoot = config.themeCssRoot ?? ":root";
this.themeCssSelector = config.themeCssSelector ?? ((theme) => `[data-theme='${theme.name}']`);
this.cascadeLayer = config.cascadeLayer ?? "";
this.useStyleReset = config.useStyleReset ?? true;
this.customizeTheme = config.customizeTheme;
this.useThemedScrollbars = config.useThemedScrollbars ?? true;
this.useThemedSelectionColors = config.useThemedSelectionColors ?? false;
this.styleOverrides = { ...config.styleOverrides };
this.defaultLocale = config.defaultLocale || "en-US";
this.defaultProps = config.defaultProps || {};
this.plugins = [...corePlugins, ...config.plugins?.flat() || []];
this.logger = new ExpressiveCodeLogger(config.logger);
this.themes = this.themes.map((theme, styleVariantIndex) => {
if (this.customizeTheme) {
theme = this.customizeTheme(theme) ?? theme;
}
if (this.minSyntaxHighlightingColorContrast > 0) {
const themeStyleSettings = resolveStyleSettings({
theme,
styleVariantIndex,
plugins: this.plugins,
styleOverrides: this.styleOverrides
});
const codeBg = getFirstStaticColor(themeStyleSettings.get("codeBackground"));
theme.ensureMinSyntaxHighlightingColorContrast(this.minSyntaxHighlightingColorContrast, codeBg);
}
return theme;
});
this.styleVariants = resolveStyleVariants({
themes: this.themes,
styleOverrides: this.styleOverrides,
plugins: this.plugins,
cssVarName: getCssVarName
});
}
/**
* Renders the given code block(s) and returns the rendered group & block ASTs,
* the rendered code block contents after all transformations have been applied,
* and a set of non-global CSS styles required by the rendered code blocks.
*
* In Expressive Code, all processing of your code blocks and their metadata
* is performed by plugins. To render markup around lines or inline ranges of characters,
* the `render` method calls the hook functions registered by all added plugins.
*
* @param input
* The code block(s) to render. Can either be an `ExpressiveCodeBlockOptions` object
* containing the properties required to create a new `ExpressiveCodeBlock` internally,
* an existing `ExpressiveCodeBlock`, or an array containing any combination of these.
*
* @param options
* Optional configuration options for the rendering process.
*/
async render(input, options) {
return await renderGroup({
input,
options,
defaultLocale: this.defaultLocale,
config: {
...this
},
plugins: this.plugins,
// Also pass resolved style variants in case plugins need them
...this.getResolverContext()
});
}
/**
* Returns a string containing all CSS styles that should be added to every page
* using Expressive Code. These styles are static base styles which do not depend
* on the configured theme(s).
*
* The calling code must take care of actually adding the returned styles to the page.
*
* Please note that the styles contain references to CSS variables, which must also
* be added to the page. These can be obtained by calling {@link getThemeStyles}.
*/
async getBaseStyles() {
const pluginStyles = [];
const resolverContext = this.getResolverContext();
pluginStyles.push({
pluginName: "core",
styles: getCoreBaseStyles({
...resolverContext,
useStyleReset: this.useStyleReset,
useThemedScrollbars: this.useThemedScrollbars,
useThemedSelectionColors: this.useThemedSelectionColors
})
});
for (const plugin of this.plugins) {
if (!plugin.baseStyles)
continue;
const resolvedStyles = typeof plugin.baseStyles === "function" ? await plugin.baseStyles(resolverContext) : plugin.baseStyles;
if (!resolvedStyles)
continue;
pluginStyles.push({
pluginName: plugin.name,
styles: resolvedStyles
});
}
const processedStyles = await processPluginStyles(pluginStyles);
return wrapInCascadeLayer([...processedStyles].join(""), this.cascadeLayer);
}
/**
* Returns a string containing theme-dependent styles that should be added to every page
* using Expressive Code. These styles contain CSS variable declarations that are generated
* automatically based on the configured {@link ExpressiveCodeEngineConfig.themes themes},
* {@link ExpressiveCodeEngineConfig.useDarkModeMediaQuery useDarkModeMediaQuery} and
* {@link ExpressiveCodeEngineConfig.themeCssSelector themeCssSelector} config options.
*
* The first theme defined in the `themes` option is considered the "base theme",
* for which a full set of CSS variables is declared and scoped to the selector
* defined by the `themeCssRoot` option (defaults to `:root`).
*
* For all alternate themes, a differential set of CSS variables is declared for cases where
* their values differ from the base theme, and scoped to theme-specific selectors that are
* generated by combining `themeCssRoot` with the theme selector specified by this option.
*
* The calling code must take care of actually adding the returned styles to the page.
*
* Please note that these styles must be added to the page together with the base styles
* returned by {@link getBaseStyles}.
*/
async getThemeStyles() {
const themeStyles = [];
const renderDeclarations = (declarations) => [...declarations].map(([varName, varValue]) => `${varName}:${varValue}`).join(";");
const { cssVarDeclarations: baseVars, theme: baseTheme } = this.styleVariants[0];
const baseThemeSelector = this.themeCssSelector && this.themeCssSelector(baseTheme, { styleVariants: this.styleVariants });
const notBaseThemeSelector = baseThemeSelector ? `:not(${baseThemeSelector})` : "";
const baseThemeBlockInsideAlternateThemeRoot = notBaseThemeSelector && `${this.themeCssRoot}${notBaseThemeSelector} &${baseThemeSelector}`;
const baseVarSelectors = [
// Root selector without any specific theme selectors
this.themeCssRoot,
// Code blocks with base theme selector inside root with non-base theme selector
baseThemeBlockInsideAlternateThemeRoot
].filter((selector) => selector).join(",");
const baseThemeStyleSelectors = [
// Code blocks with no specific theme selector
"&",
// Code blocks with base theme selector inside root with non-base theme selector
baseThemeBlockInsideAlternateThemeRoot
].filter((selector) => selector).join(",");
themeStyles.push(
await scopeAndMinifyNestedCss(`
${baseVarSelectors} {
${renderDeclarations(baseVars)}
}
${baseThemeStyleSelectors} {
${getCoreThemeStyles(0)}
}
`)
);
const alternateVariants = [];
for (let styleVariantIndex = 1; styleVariantIndex < this.styleVariants.length; styleVariantIndex++) {
const styleVariant = this.styleVariants[styleVariantIndex];
const diffVars = /* @__PURE__ */ new Map();
styleVariant.cssVarDeclarations.forEach((varValue, varName) => {
if (baseVars.get(varName) !== varValue) {
diffVars.set(varName, varValue);
}
});
alternateVariants.push({
theme: styleVariant.theme,
cssVars: renderDeclarations(diffVars),
coreStyles: getCoreThemeStyles(styleVariantIndex)
});
}
if (this.useDarkModeMediaQuery) {
const baseTheme2 = this.styleVariants[0].theme;
const altType = baseTheme2.type === "dark" ? "light" : "dark";
const firstAltVariant = alternateVariants.find((variant) => variant.theme.type === altType);
if (!firstAltVariant)
throw new Error(
[
`The config option "useDarkModeMediaQuery: true" requires at least`,
`one dark and one light theme, but the following themes were given:`,
this.themes.map((theme) => `${theme.name} (${theme.type})`).join(", ")
].join(" ")
);
const darkModeMediaQuery = await scopeAndMinifyNestedCss(`
@media (prefers-color-scheme: ${altType}) {
${this.themeCssRoot}${notBaseThemeSelector} {
${firstAltVariant.cssVars}
}
${this.themeCssRoot}${notBaseThemeSelector} & {
${firstAltVariant.coreStyles}
}
}
`);
themeStyles.push(darkModeMediaQuery);
}
if (this.themeCssSelector !== false) {
for (const { theme, cssVars, coreStyles } of alternateVariants) {
const themeSelector = this.themeCssSelector && this.themeCssSelector(theme, { styleVariants: this.styleVariants });
if (!themeSelector)
continue;
themeStyles.push(
await scopeAndMinifyNestedCss(`
${this.themeCssRoot}${themeSelector} &${notBaseThemeSelector}, &${themeSelector} {
${cssVars};
${coreStyles}
}
`)
);
}
}
return wrapInCascadeLayer(themeStyles.join(""), this.cascadeLayer);
}
/**
* Returns an array of JavaScript modules (pure code without any wrapping `script` tags)
* that should be added to every page containing code blocks.
*
* The contents are collected from the `jsModules` property of all registered plugins.
* Any duplicates are removed.
*
* The calling code must take care of actually adding the collected scripts to the page.
* For example, it could create site-wide JavaScript files from the returned modules
* and refer to them in a script tag with `type="module"`, or it could insert them
* into inline `