mirror of
https://github.com/sern-handler/website
synced 2026-06-10 09:52:22 +00:00
4777 lines
161 KiB
JavaScript
4777 lines
161 KiB
JavaScript
// 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 `<script type="module">` elements.
|
||
*/
|
||
async getJsModules() {
|
||
const jsModules = /* @__PURE__ */ new Set();
|
||
for (const plugin of this.plugins) {
|
||
const pluginModules = typeof plugin.jsModules === "function" ? await plugin.jsModules(this.getResolverContext()) : plugin.jsModules;
|
||
pluginModules?.forEach((moduleCode) => {
|
||
moduleCode = moduleCode.trim();
|
||
if (moduleCode)
|
||
jsModules.add(moduleCode);
|
||
});
|
||
}
|
||
return [...jsModules];
|
||
}
|
||
cssVar(styleSetting, fallbackValue) {
|
||
return `var(${getCssVarName(styleSetting)}${fallbackValue ? `, ${fallbackValue}` : ""})`;
|
||
}
|
||
getResolverContext() {
|
||
return {
|
||
cssVar: (styleSetting, fallbackValue) => this.cssVar(styleSetting, fallbackValue),
|
||
cssVarName: getCssVarName,
|
||
styleVariants: this.styleVariants
|
||
};
|
||
}
|
||
themes;
|
||
minSyntaxHighlightingColorContrast;
|
||
useDarkModeMediaQuery;
|
||
themeCssRoot;
|
||
themeCssSelector;
|
||
cascadeLayer;
|
||
useStyleReset;
|
||
customizeTheme;
|
||
useThemedScrollbars;
|
||
useThemedSelectionColors;
|
||
styleOverrides;
|
||
styleVariants;
|
||
defaultLocale;
|
||
defaultProps;
|
||
plugins;
|
||
logger;
|
||
};
|
||
|
||
// src/common/plugin-data.ts
|
||
var AttachedPluginData = class {
|
||
dataStorage = /* @__PURE__ */ new WeakMap();
|
||
getInitialValueFn;
|
||
constructor(getInitialValueFn) {
|
||
this.getInitialValueFn = getInitialValueFn;
|
||
}
|
||
getOrCreateFor(target) {
|
||
let data = this.dataStorage.get(target);
|
||
if (data === void 0) {
|
||
data = this.getInitialValueFn();
|
||
this.dataStorage.set(target, data);
|
||
}
|
||
return data;
|
||
}
|
||
setFor(target, data) {
|
||
this.dataStorage.set(target, data);
|
||
}
|
||
};
|
||
|
||
// src/common/plugin-texts.ts
|
||
var PluginTexts = class {
|
||
defaultTexts;
|
||
localizedTexts = /* @__PURE__ */ new Map();
|
||
overridesByLocale = /* @__PURE__ */ new Map();
|
||
constructor(defaultTexts) {
|
||
this.defaultTexts = defaultTexts;
|
||
}
|
||
/**
|
||
* Adds localized texts for a specific locale. You must provide a full set of localized texts
|
||
* for the given locale.
|
||
*
|
||
* It is recommended to use two-letter language codes (e.g. `de`, `fr`, `es`) without region
|
||
* codes to make your localized texts available to all users speaking the same language.
|
||
* Region codes should only be added if regional differences must be taken into account.
|
||
*
|
||
* Plugin authors can use this to provide localized versions of their texts.
|
||
* Users can also call this function to provide their own localizations.
|
||
*
|
||
* If you only want to customize a few texts of an existing localization,
|
||
* have a look at `overrideTexts` instead.
|
||
*/
|
||
addLocale(locale, localizedTexts) {
|
||
locale = this.parseLocale(locale).locale;
|
||
this.localizedTexts.set(locale, localizedTexts);
|
||
}
|
||
/**
|
||
* Allows you to override any defined texts. This is useful if you want to customize a few
|
||
* selected texts without having to provide a full set of localized texts.
|
||
*
|
||
* You can either override texts for a specific `locale`, or override the default texts
|
||
* by setting `locale` to `undefined`.
|
||
*
|
||
* It is recommended to use two-letter language codes (e.g. `de`, `fr`, `es`) without region
|
||
* codes to apply your overrides to all users speaking the same language.
|
||
* Region codes should only be added if regional differences must be taken into account.
|
||
*/
|
||
overrideTexts(locale, localeTextOverrides) {
|
||
locale = locale && this.parseLocale(locale).locale;
|
||
const localeOverrides = this.overridesByLocale.get(locale) || this.overridesByLocale.set(locale, {}).get(locale);
|
||
Object.assign(localeOverrides, localeTextOverrides);
|
||
}
|
||
/**
|
||
* Returns the best matching texts for the requested locale,
|
||
* taking any available localized texts and overrides into account.
|
||
*
|
||
* Example for locale `de-DE`:
|
||
* - If localized texts for `de-DE` are available, these will be returned.
|
||
* - If `de-DE` is not available, but `de` is, these will be returned.
|
||
* - As the final fallback, the default texts will be returned.
|
||
*/
|
||
get(locale) {
|
||
const { acceptedLocales } = this.parseLocale(locale);
|
||
const localizedTexts = this.getLocalizedTexts(acceptedLocales);
|
||
return this.applyOverrides(localizedTexts, acceptedLocales);
|
||
}
|
||
parseLocale(locale) {
|
||
const parts = locale.trim().toLowerCase().split(/[-_]/);
|
||
const language = parts[0];
|
||
const region = parts[1];
|
||
const normalizedLocale = region ? `${language}-${region}` : language;
|
||
const acceptedLocales = [];
|
||
acceptedLocales.push(language);
|
||
if (region)
|
||
acceptedLocales.push(normalizedLocale);
|
||
return {
|
||
language,
|
||
region,
|
||
locale: normalizedLocale,
|
||
acceptedLocales
|
||
};
|
||
}
|
||
getLocalizedTexts(acceptedLocales) {
|
||
for (const acceptedLocale of acceptedLocales) {
|
||
const localizedTexts = this.localizedTexts.get(acceptedLocale);
|
||
if (localizedTexts) {
|
||
return localizedTexts;
|
||
}
|
||
}
|
||
return this.defaultTexts;
|
||
}
|
||
applyOverrides(texts, acceptedLocales) {
|
||
const result = { ...texts };
|
||
const overrides = [...acceptedLocales, void 0].map((locale) => this.overridesByLocale.get(locale)).filter((x) => x);
|
||
if (overrides.length) {
|
||
const keys = Object.keys(texts);
|
||
keys.forEach((key) => {
|
||
for (const override of overrides) {
|
||
const overrideValue = override?.[key];
|
||
if (overrideValue) {
|
||
result[key] = overrideValue;
|
||
return;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
return result;
|
||
}
|
||
};
|
||
|
||
// src/common/plugin.ts
|
||
function definePlugin(plugin) {
|
||
return plugin;
|
||
}
|
||
|
||
// src/helpers/ast.ts
|
||
var addClassName2 = addClassName;
|
||
var getClassNames2 = getClassNames;
|
||
var getInlineStyles2 = getInlineStyles;
|
||
var removeClassName2 = removeClassName;
|
||
var setInlineStyle2 = setInlineStyle;
|
||
var setInlineStyles2 = setInlineStyles;
|
||
var setProperty2 = setProperty;
|
||
|
||
// src/helpers/i18n.ts
|
||
function formatTemplate(template, variables) {
|
||
const getReplacement = (varName, ...choices) => {
|
||
const value = variables[varName];
|
||
if (value === void 0)
|
||
throw new Error(`Unknown variable name "${varName}" found in string template "${template}". Available variables: ${JSON.stringify(Object.keys(variables))}`);
|
||
if (!choices.length)
|
||
return value.toString();
|
||
const parsedChoices = choices.map((choice) => {
|
||
const condition = choice.match(/^\s*(<|>|)\s*(-?[0-9.]+?)\s*=\s?/);
|
||
if (!condition)
|
||
return { text: choice.replace(/^\s/, "") };
|
||
const [fullMatch, operator, conditionValue] = condition;
|
||
const number = Number.parseFloat(conditionValue);
|
||
if (isNaN(number))
|
||
throw new Error(`Expected condition value "${conditionValue}" to be a number in string template "${template}".`);
|
||
if (typeof value !== "number")
|
||
throw new Error(
|
||
`Condition "${operator}${conditionValue}" in string template "${template}" requires variable "${varName}" to be a number, but it's ${JSON.stringify(value)}.`
|
||
);
|
||
return {
|
||
condition: {
|
||
operator: operator || "=",
|
||
number
|
||
},
|
||
text: choice.slice(fullMatch.length)
|
||
};
|
||
});
|
||
const catchAllCount = parsedChoices.filter((choice) => !choice.condition).length;
|
||
if (catchAllCount !== 1)
|
||
throw new Error(`Expected exactly 1 catch-all choice for variable "${varName}", but found ${catchAllCount} in string template "${template}".`);
|
||
for (const { condition, text } of parsedChoices) {
|
||
if (!condition)
|
||
return text;
|
||
if (typeof value !== "number")
|
||
continue;
|
||
const conditionIsMatching = (
|
||
// Less than
|
||
condition.operator === "<" && value < condition.number || // Greater than
|
||
condition.operator === ">" && value > condition.number || // Equals
|
||
condition.operator === "=" && value === condition.number
|
||
);
|
||
if (conditionIsMatching)
|
||
return text;
|
||
}
|
||
return "";
|
||
};
|
||
let result = template;
|
||
result = result.replace(/(?<!\\)\\{/g, "\f(").replace(/(?<!\\)\\}/g, "\f)");
|
||
result = result.replace(/\\(\\[{}])/g, "$1");
|
||
const innermostPlaceholderRegex = /\{([^{]*?)\}/g;
|
||
let keepGoing = true;
|
||
while (keepGoing) {
|
||
keepGoing = false;
|
||
result = result.replace(innermostPlaceholderRegex, (match, contents) => {
|
||
keepGoing = true;
|
||
const [varName, ...choices] = contents.split(";");
|
||
return getReplacement(varName, ...choices).replace(/{/g, "\f(").replace(/}/g, "\f)");
|
||
});
|
||
}
|
||
result = result.replace(/\f\(/g, "{").replace(/\f\)/g, "}");
|
||
return result;
|
||
}
|
||
|
||
// ../../../node_modules/.pnpm/djb2a@2.0.0/node_modules/djb2a/index.js
|
||
var MAGIC_CONSTANT = 5381;
|
||
function djb2a(string) {
|
||
let hash = MAGIC_CONSTANT;
|
||
for (let index = 0; index < string.length; index++) {
|
||
hash = (hash << 5) + hash ^ string.charCodeAt(index);
|
||
}
|
||
return hash >>> 0;
|
||
}
|
||
|
||
// src/helpers/objects.ts
|
||
function stableStringify(obj, options = {}) {
|
||
const { includeFunctionContents = false } = options;
|
||
const visited = /* @__PURE__ */ new WeakSet();
|
||
const toJson = (value) => {
|
||
if (typeof value === "object" && value !== null) {
|
||
if (visited.has(value)) {
|
||
return "[Circular]";
|
||
}
|
||
visited.add(value);
|
||
let result;
|
||
if (Array.isArray(value)) {
|
||
result = value.map(toJson);
|
||
} else {
|
||
const objValue = value;
|
||
const sortedKeys = Object.keys(objValue).sort();
|
||
const sortedObj = {};
|
||
for (const key of sortedKeys) {
|
||
sortedObj[key] = toJson(objValue[key]);
|
||
}
|
||
result = sortedObj;
|
||
}
|
||
visited.delete(value);
|
||
return result;
|
||
}
|
||
if (typeof value === "function") {
|
||
return includeFunctionContents ? value.toString() : "[Function]";
|
||
}
|
||
return value;
|
||
};
|
||
if (obj === void 0)
|
||
return "undefined";
|
||
return JSON.stringify(toJson(obj));
|
||
}
|
||
function getStableObjectHash(obj, options = {}) {
|
||
const { includeFunctionContents = false, hashLength = 5 } = options;
|
||
const numericHash = djb2a(stableStringify(obj, { includeFunctionContents }));
|
||
const padding = "0".repeat(hashLength);
|
||
return (padding + numericHash.toString(36)).slice(-hashLength);
|
||
}
|
||
export {
|
||
AnnotationRenderPhaseOrder,
|
||
AttachedPluginData,
|
||
ExpressiveCodeAnnotation,
|
||
ExpressiveCodeBlock,
|
||
ExpressiveCodeEngine,
|
||
ExpressiveCodeLine,
|
||
ExpressiveCodeTheme,
|
||
InlineStyleAnnotation,
|
||
MetaOptions,
|
||
PluginStyleSettings,
|
||
PluginTexts,
|
||
addClassName2 as addClassName,
|
||
changeAlphaToReachColorContrast,
|
||
changeLuminanceToReachColorContrast,
|
||
chromaticRecolor,
|
||
codeLineClass,
|
||
cssVarReplacements,
|
||
darken,
|
||
definePlugin,
|
||
ensureColorContrastOnBackground,
|
||
formatTemplate,
|
||
getClassNames2 as getClassNames,
|
||
getColorContrast,
|
||
getColorContrastOnBackground,
|
||
getCssVarName,
|
||
getFirstStaticColor,
|
||
getInlineStyles2 as getInlineStyles,
|
||
getLuminance,
|
||
getStableObjectHash,
|
||
getStaticBackgroundColor,
|
||
isInlineStyleAnnotation,
|
||
lighten,
|
||
mix,
|
||
multiplyAlpha,
|
||
onBackground,
|
||
removeClassName2 as removeClassName,
|
||
resolveStyleVariants,
|
||
runHooks,
|
||
setAlpha,
|
||
setInlineStyle2 as setInlineStyle,
|
||
setInlineStyles2 as setInlineStyles,
|
||
setLuminance,
|
||
setProperty2 as setProperty,
|
||
stableStringify,
|
||
toHexColor,
|
||
toRgbaString,
|
||
validateExpressiveCodeAnnotation
|
||
};
|
||
//# sourceMappingURL=index.js.map
|