//#region src/shared/parse-comments.ts
const RE_SPLIT_COMMENT = /(\s+\/\/)/;
const RE_V1_END_COMMENT_PREFIX = /(?:\/\/|["'#]|;{1,2}|%{1,2}|--)(\s*)$/;
const RE_V3_END_COMMENT_PREFIX = /(?:\/\/|#|;{1,2}|%{1,2}|--)(\s*)$/;
/**
* some comment formats have to be located at the end of line
* hence we can skip matching them for other tokens
*/
const matchers = [
	[/^(<!--)(.+)(-->)$/, false],
	[/^(\/\*)(.+)(\*\/)$/, false],
	[/^(\/\/|["'#]|;{1,2}|%{1,2}|--)(.*)$/, true],
	[/^(\*)(.+)$/, true]
];
/**
* @param lines line tokens
* @param jsx enable JSX parsing
* @param matchAlgorithm matching algorithm
*/
function parseComments(lines, jsx, matchAlgorithm) {
	const out = [];
	for (const line of lines) {
		if (matchAlgorithm === "v3") {
			const splittedElements = line.children.flatMap((element, idx) => {
				if (element.type !== "element") return element;
				const token = element.children[0];
				if (token.type !== "text") return element;
				const isLast = idx === line.children.length - 1;
				if (!matchToken(token.value, isLast)) return element;
				const rawSplits = token.value.split(RE_SPLIT_COMMENT);
				if (rawSplits.length <= 1) return element;
				let splits = [rawSplits[0]];
				for (let i = 1; i < rawSplits.length; i += 2) splits.push(rawSplits[i] + (rawSplits[i + 1] || ""));
				splits = splits.filter(Boolean);
				if (splits.length <= 1) return element;
				return splits.map((split) => {
					return {
						...element,
						children: [{
							type: "text",
							value: split
						}]
					};
				});
			});
			if (splittedElements.length !== line.children.length) line.children = splittedElements;
		}
		const elements = line.children;
		let start = elements.length - 1;
		if (matchAlgorithm === "v1") start = 0;
		else if (jsx) start = elements.length - 2;
		for (let i = Math.max(start, 0); i < elements.length; i++) {
			const token = elements[i];
			if (token.type !== "element") continue;
			const head = token.children.at(0);
			if (head?.type !== "text") continue;
			const isLast = i === elements.length - 1;
			let match = matchToken(head.value, isLast);
			let additionalTokens;
			if (!match && i > 0 && head.value.trim().startsWith("[!code")) {
				const prevToken = elements[i - 1];
				if (prevToken?.type === "element") {
					const prevHead = prevToken.children.at(0);
					if (prevHead?.type === "text" && prevHead.value.includes("//")) {
						const combinedMatch = matchToken(prevHead.value + head.value, isLast);
						if (combinedMatch) {
							match = combinedMatch;
							out.push({
								info: combinedMatch,
								line,
								token: prevToken,
								isLineCommentOnly: elements.length === 2 && prevToken.children.length === 1 && token.children.length === 1,
								isJsxStyle: false,
								additionalTokens: [token]
							});
							continue;
						}
					}
				}
			}
			if (!match) continue;
			if (jsx && !isLast && i !== 0) {
				const isJsxStyle = isValue(elements[i - 1], "{") && isValue(elements[i + 1], "}");
				out.push({
					info: match,
					line,
					token,
					isLineCommentOnly: elements.length === 3 && token.children.length === 1,
					isJsxStyle,
					additionalTokens
				});
			} else out.push({
				info: match,
				line,
				token,
				isLineCommentOnly: elements.length === 1 && token.children.length === 1,
				isJsxStyle: false,
				additionalTokens
			});
		}
	}
	return out;
}
function isValue(element, value) {
	if (element.type !== "element") return false;
	const text = element.children[0];
	if (text.type !== "text") return false;
	return text.value.trim() === value;
}
/**
* @param text text value of comment node
* @param isLast whether the token is located at the end of line
*/
function matchToken(text, isLast) {
	let trimmed = text.trimStart();
	const spaceFront = text.length - trimmed.length;
	trimmed = trimmed.trimEnd();
	const spaceEnd = text.length - trimmed.length - spaceFront;
	for (const [matcher, endOfLine] of matchers) {
		if (endOfLine && !isLast) continue;
		const result = matcher.exec(trimmed);
		if (!result) continue;
		return [
			" ".repeat(spaceFront) + result[1],
			result[2],
			result[3] ? result[3] + " ".repeat(spaceEnd) : void 0
		];
	}
}
/**
* Remove empty comment prefixes at line end, e.g. `// `
*
* For matchAlgorithm v1
*/
function v1ClearEndCommentPrefix(text) {
	const match = text.match(RE_V1_END_COMMENT_PREFIX);
	if (match && match[1].trim().length === 0) return text.slice(0, match.index);
	return text;
}
/**
* Remove empty comment prefixes at line end, e.g. `// `
*
* For matchAlgorithm v3
*/
function v3ClearEndCommentPrefix(text) {
	const match = text.match(RE_V3_END_COMMENT_PREFIX);
	if (match && match[1].trim().length === 0) return text.slice(0, match.index).trimEnd();
	return text;
}
//#endregion
//#region src/shared/notation-transformer.ts
function createCommentNotationTransformer(name, regex, onMatch, matchAlgorithm) {
	matchAlgorithm ??= "v3";
	return {
		name,
		code(code) {
			const lines = code.children.filter((i) => i.type === "element");
			const linesToRemove = [];
			code.data ??= {};
			const data = code.data;
			data._shiki_notation ??= parseComments(lines, ["jsx", "tsx"].includes(this.options.lang), matchAlgorithm);
			const parsed = data._shiki_notation;
			for (const comment of parsed) {
				if (comment.info[1].length === 0) continue;
				let lineIdx = lines.indexOf(comment.line);
				if (comment.isLineCommentOnly && matchAlgorithm !== "v1") lineIdx++;
				let replaced = false;
				comment.info[1] = comment.info[1].replace(regex, (...match) => {
					if (onMatch.call(this, match, comment.line, comment.token, lines, lineIdx)) {
						replaced = true;
						return "";
					}
					return match[0];
				});
				if (!replaced) continue;
				if (matchAlgorithm === "v1") comment.info[1] = v1ClearEndCommentPrefix(comment.info[1]);
				else if (matchAlgorithm === "v3") comment.info[1] = v3ClearEndCommentPrefix(comment.info[1]);
				const isEmpty = comment.info[1].trim().length === 0;
				if (isEmpty) comment.info[1] = "";
				if (isEmpty && comment.isLineCommentOnly) linesToRemove.push(comment.line);
				else if (isEmpty && comment.isJsxStyle) comment.line.children.splice(comment.line.children.indexOf(comment.token) - 1, 3);
				else if (isEmpty) {
					if (comment.additionalTokens) for (let j = comment.additionalTokens.length - 1; j >= 0; j--) {
						const additionalToken = comment.additionalTokens[j];
						const tokenIndex = comment.line.children.indexOf(additionalToken);
						if (tokenIndex !== -1) comment.line.children.splice(tokenIndex, 1);
					}
					comment.line.children.splice(comment.line.children.indexOf(comment.token), 1);
				} else {
					const head = comment.token.children[0];
					if (head.type === "text") {
						head.value = comment.info.join("");
						if (comment.additionalTokens) for (const additionalToken of comment.additionalTokens) {
							const additionalHead = additionalToken.children[0];
							if (additionalHead?.type === "text") additionalHead.value = "";
						}
					}
				}
			}
			for (const line of linesToRemove) {
				const index = code.children.indexOf(line);
				const nextLine = code.children[index + 1];
				let removeLength = 1;
				if (nextLine?.type === "text" && nextLine?.value === "\n") removeLength = 2;
				code.children.splice(index, removeLength);
			}
		}
	};
}
//#endregion
//#region src/transformers/compact-line-options.ts
/**
* Transformer for `shiki`'s legacy `lineOptions`
*/
function transformerCompactLineOptions(lineOptions = []) {
	return {
		name: "@shikijs/transformers:compact-line-options",
		line(node, line) {
			const lineOption = lineOptions.find((o) => o.line === line);
			if (lineOption?.classes) this.addClassToHast(node, lineOption.classes);
			return node;
		}
	};
}
//#endregion
//#region src/transformers/meta-highlight.ts
const RE_HIGHLIGHT_LINES = /\{([\d,-]+)\}/;
function parseMetaHighlightString(meta) {
	if (!meta) return null;
	const match = meta.match(RE_HIGHLIGHT_LINES);
	if (!match) return null;
	return match[1].split(",").flatMap((v) => {
		const range = v.split("-").map((n) => Number.parseInt(n, 10));
		return range.length === 1 ? [range[0]] : Array.from({ length: range[1] - range[0] + 1 }, (_, i) => range[0] + i);
	});
}
const symbol = Symbol("highlighted-lines");
/**
* Allow using `{1,3-5}` in the code snippet meta to mark highlighted lines.
*/
function transformerMetaHighlight(options = {}) {
	const { className = "highlighted", zeroIndexed = false } = options;
	return {
		name: "@shikijs/transformers:meta-highlight",
		line(node, lineNumber) {
			if (!this.options.meta?.__raw) return;
			const meta = this.meta;
			meta[symbol] ??= parseMetaHighlightString(this.options.meta.__raw);
			const highlightedLines = meta[symbol] ?? [];
			const effectiveLine = zeroIndexed ? lineNumber - 1 : lineNumber;
			if (highlightedLines.includes(effectiveLine)) this.addClassToHast(node, className);
			return node;
		}
	};
}
//#endregion
//#region src/transformers/meta-highlight-word.ts
const RE_WORD_MATCH = /\/((?:\\.|[^/])+)\//g;
const RE_ESCAPE_BACKSLASH = /\\(.)/g;
function parseMetaHighlightWords(meta) {
	if (!meta) return [];
	return [...meta.matchAll(RE_WORD_MATCH)].map((v) => v[1].replace(RE_ESCAPE_BACKSLASH, "$1"));
}
/**
* Allow using `/word/` in the code snippet meta to mark highlighted words.
*/
function transformerMetaWordHighlight(options = {}) {
	const { className = "highlighted-word" } = options;
	return {
		name: "@shikijs/transformers:meta-word-highlight",
		preprocess(code, options) {
			if (!this.options.meta?.__raw) return;
			const words = parseMetaHighlightWords(this.options.meta.__raw);
			options.decorations ||= [];
			for (const word of words) {
				const indexes = findAllSubstringIndexes(code, word);
				for (const index of indexes) options.decorations.push({
					start: index,
					end: index + word.length,
					properties: { class: className }
				});
			}
		}
	};
}
function findAllSubstringIndexes(str, substr) {
	const indexes = [];
	let cursor = 0;
	while (true) {
		const index = str.indexOf(substr, cursor);
		if (index === -1 || index >= str.length) break;
		if (index < cursor) break;
		indexes.push(index);
		cursor = index + substr.length;
	}
	return indexes;
}
//#endregion
//#region src/transformers/notation-map.ts
const RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
function escapeRegExp(str) {
	return str.replace(RE_ESCAPE_SPECIAL, "\\$&");
}
function transformerNotationMap(options = {}, name = "@shikijs/transformers:notation-map") {
	const { classMap = {}, classActivePre = void 0, classActiveCode = void 0 } = options;
	return createCommentNotationTransformer(name, new RegExp(`#?\\s*\\[!code (${Object.keys(classMap).map(escapeRegExp).join("|")})(:\\d+)?\\]`, "gi"), function([_, match, range = ":1"], _line, _comment, lines, index) {
		const lineNum = Number.parseInt(range.slice(1), 10);
		for (let i = index; i < Math.min(index + lineNum, lines.length); i++) this.addClassToHast(lines[i], classMap[match]);
		if (classActivePre) this.addClassToHast(this.pre, classActivePre);
		if (classActiveCode) this.addClassToHast(this.code, classActiveCode);
		return true;
	}, options.matchAlgorithm);
}
//#endregion
//#region src/transformers/notation-diff.ts
/**
* Use `[!code ++]` and `[!code --]` to mark added and removed lines.
*/
function transformerNotationDiff(options = {}) {
	const { classLineAdd = "diff add", classLineRemove = "diff remove", classActivePre = "has-diff", classActiveCode } = options;
	return transformerNotationMap({
		classMap: {
			"++": classLineAdd,
			"--": classLineRemove
		},
		classActivePre,
		classActiveCode,
		matchAlgorithm: options.matchAlgorithm
	}, "@shikijs/transformers:notation-diff");
}
//#endregion
//#region src/transformers/notation-error-level.ts
/**
* Allow using `[!code error]` `[!code warning]` notation in code to mark highlighted lines.
*/
function transformerNotationErrorLevel(options = {}) {
	const { classMap = {
		error: ["highlighted", "error"],
		warning: ["highlighted", "warning"],
		info: ["highlighted", "info"]
	}, classActivePre = "has-highlighted", classActiveCode } = options;
	return transformerNotationMap({
		classMap,
		classActivePre,
		classActiveCode,
		matchAlgorithm: options.matchAlgorithm
	}, "@shikijs/transformers:notation-error-level");
}
//#endregion
//#region src/transformers/notation-focus.ts
/**
* Allow using `[!code focus]` notation in code to mark focused lines.
*/
function transformerNotationFocus(options = {}) {
	const { classActiveLine = "focused", classActivePre = "has-focused", classActiveCode } = options;
	return transformerNotationMap({
		classMap: { focus: classActiveLine },
		classActivePre,
		classActiveCode,
		matchAlgorithm: options.matchAlgorithm
	}, "@shikijs/transformers:notation-focus");
}
//#endregion
//#region src/transformers/notation-highlight.ts
/**
* Allow using `[!code highlight]` notation in code to mark highlighted lines.
*/
function transformerNotationHighlight(options = {}) {
	const { classActiveLine = "highlighted", classActivePre = "has-highlighted", classActiveCode } = options;
	return transformerNotationMap({
		classMap: {
			highlight: classActiveLine,
			hl: classActiveLine
		},
		classActivePre,
		classActiveCode,
		matchAlgorithm: options.matchAlgorithm
	}, "@shikijs/transformers:notation-highlight");
}
//#endregion
//#region src/shared/highlight-word.ts
function highlightWordInLine(line, ignoredElement, word, className) {
	const content = getTextContent(line);
	let index = content.indexOf(word);
	while (index !== -1) {
		highlightRange.call(this, line.children, ignoredElement, index, word.length, className);
		index = content.indexOf(word, index + 1);
	}
}
function getTextContent(element) {
	if (element.type === "text") return element.value;
	if (element.type === "element" && element.tagName === "span") return element.children.map(getTextContent).join("");
	return "";
}
/**
* @param elements
* @param ignoredElement
* @param index highlight beginning index
* @param len highlight length
* @param className class name to add to highlighted nodes
*/
function highlightRange(elements, ignoredElement, index, len, className) {
	let currentIdx = 0;
	for (let i = 0; i < elements.length; i++) {
		const element = elements[i];
		if (element.type !== "element" || element.tagName !== "span" || element === ignoredElement) continue;
		const textNode = element.children[0];
		if (textNode.type !== "text") continue;
		if (hasOverlap([currentIdx, currentIdx + textNode.value.length - 1], [index, index + len])) {
			const start = Math.max(0, index - currentIdx);
			const length = len - Math.max(0, currentIdx - index);
			if (length === 0) continue;
			const separated = separateToken(element, textNode, start, length);
			this.addClassToHast(separated[1], className);
			const output = separated.filter(Boolean);
			elements.splice(i, 1, ...output);
			i += output.length - 1;
		}
		currentIdx += textNode.value.length;
	}
}
function hasOverlap(range1, range2) {
	return range1[0] <= range2[1] && range1[1] >= range2[0];
}
function separateToken(span, textNode, index, len) {
	const text = textNode.value;
	const createNode = (value) => inheritElement(span, { children: [{
		type: "text",
		value
	}] });
	return [
		index > 0 ? createNode(text.slice(0, index)) : void 0,
		createNode(text.slice(index, index + len)),
		index + len < text.length ? createNode(text.slice(index + len)) : void 0
	];
}
function inheritElement(original, overrides) {
	return {
		...original,
		properties: { ...original.properties },
		...overrides
	};
}
//#endregion
//#region src/transformers/notation-highlight-word.ts
const RE_CODE_WORD = /\s*\[!code word:((?:\\.|[^:\]])+)(:\d+)?\]/;
const RE_UNESCAPE = /\\(.)/g;
function transformerNotationWordHighlight(options = {}) {
	const { classActiveWord = "highlighted-word", classActivePre = void 0 } = options;
	return createCommentNotationTransformer("@shikijs/transformers:notation-highlight-word", RE_CODE_WORD, function([_, word, range], _line, comment, lines, index) {
		const lineNum = range ? Number.parseInt(range.slice(1), 10) : lines.length;
		word = word.replace(RE_UNESCAPE, "$1");
		for (let i = index; i < Math.min(index + lineNum, lines.length); i++) highlightWordInLine.call(this, lines[i], comment, word, classActiveWord);
		if (classActivePre) this.addClassToHast(this.pre, classActivePre);
		return true;
	}, options.matchAlgorithm);
}
//#endregion
//#region src/transformers/remove-comments.ts
/**
* Remove comments from the code.
*/
function transformerRemoveComments(options = {}) {
	const { removeEmptyLines = true } = options;
	return {
		name: "@shikijs/transformers:remove-comments",
		preprocess(_code, options) {
			if (options.includeExplanation !== true && options.includeExplanation !== "scopeName") throw new Error("`transformerRemoveComments` requires `includeExplanation` to be set to `true` or `'scopeName'`");
		},
		tokens(tokens) {
			const result = [];
			for (const line of tokens) {
				const filteredLine = [];
				let hasComment = false;
				for (const token of line) if (token.explanation?.some((exp) => exp.scopes.some((s) => s.scopeName.startsWith("comment")))) hasComment = true;
				else filteredLine.push(token);
				if (removeEmptyLines && hasComment) {
					if (filteredLine.every((token) => !token.content.trim())) continue;
				}
				result.push(filteredLine);
			}
			return result;
		}
	};
}
//#endregion
//#region src/transformers/remove-line-breaks.ts
/**
* Remove line breaks between lines.
* Useful when you override `display: block` to `.line` in CSS.
*/
function transformerRemoveLineBreak() {
	return {
		name: "@shikijs/transformers:remove-line-break",
		code(code) {
			code.children = code.children.filter((line) => !(line.type === "text" && line.value === "\n"));
		}
	};
}
//#endregion
//#region src/transformers/remove-notation-escape.ts
/**
* Remove notation escapes.
* Useful when you want to write `// [!code` in markdown.
* If you process `// [\!code ...]` expression, you can get `// [!code ...]` in the output.
*/
function transformerRemoveNotationEscape() {
	return {
		name: "@shikijs/transformers:remove-notation-escape",
		code(hast) {
			function replace(node) {
				if (node.type === "text") node.value = node.value.replace("[\\!code", "[!code");
				else if ("children" in node) for (const child of node.children) replace(child);
			}
			replace(hast);
			return hast;
		}
	};
}
//#endregion
//#region src/transformers/render-indent-guides.ts
const RE_INDENT_META = /\{indent:(\d+|false)\}/;
const RE_NON_BLANK = /[^ \t]/;
/**
* Render indentations as separate tokens.
* Apply with CSS, it can be used to render indent guides visually.
*/
function transformerRenderIndentGuides(options = {}) {
	return {
		name: "@shikijs/transformers:render-indent-guides",
		code(hast) {
			const indent = Number(this.options.meta?.indent ?? this.options.meta?.__raw?.match(RE_INDENT_META)?.[1] ?? options.indent ?? 2);
			if (Number.isNaN(indent) || indent <= 0) return hast;
			const indentRegex = new RegExp(` {${indent}}| {0,${indent - 1}}\t| {1,}$`, "g");
			const emptyLines = [];
			let level = 0;
			for (const line of hast.children) {
				if (line.type !== "element") continue;
				const first = line.children[0];
				if (first?.type !== "element" || first?.children[0]?.type !== "text") {
					emptyLines.push([line, level]);
					continue;
				}
				const text = first.children[0];
				const blanks = text.value.split(RE_NON_BLANK, 1)[0];
				const ranges = [];
				for (const match of blanks.matchAll(indentRegex)) {
					const start = match.index;
					const end = start + match[0].length;
					ranges.push([start, end]);
				}
				for (const [line, level] of emptyLines) line.children.unshift(...Array.from({ length: Math.min(ranges.length, level + 1) }, (_, i) => ({
					type: "element",
					tagName: "span",
					properties: {
						class: "indent",
						style: `--indent-offset: ${i * indent}ch;`
					},
					children: []
				})));
				emptyLines.length = 0;
				level = ranges.length;
				if (ranges.length) {
					line.children.unshift(...ranges.map(([start, end]) => ({
						type: "element",
						tagName: "span",
						properties: { class: "indent" },
						children: [{
							type: "text",
							value: text.value.slice(start, end)
						}]
					})));
					text.value = text.value.slice(ranges.at(-1)[1]);
				}
			}
			return hast;
		}
	};
}
//#endregion
//#region src/shared/utils.ts
function isTab(part) {
	return part === "	";
}
function isSpace(part) {
	return part === " " || part === "	";
}
function separateContinuousSpaces(inputs) {
	const result = [];
	let current = "";
	function bump() {
		if (current.length) result.push(current);
		current = "";
	}
	inputs.forEach((part, idx) => {
		if (isTab(part)) {
			bump();
			result.push(part);
		} else if (isSpace(part) && (isSpace(inputs[idx - 1]) || isSpace(inputs[idx + 1]))) {
			bump();
			result.push(part);
		} else current += part;
	});
	bump();
	return result;
}
function splitSpaces(parts, type, renderContinuousSpaces = true) {
	if (type === "all") return parts;
	let leftCount = 0;
	let rightCount = 0;
	if (type === "boundary" || type === "leading") for (let i = 0; i < parts.length; i++) if (isSpace(parts[i])) leftCount++;
	else break;
	if (type === "boundary" || type === "trailing") for (let i = parts.length - 1; i >= 0; i--) if (isSpace(parts[i])) rightCount++;
	else break;
	const middle = parts.slice(leftCount, parts.length - rightCount);
	return [
		...parts.slice(0, leftCount),
		...renderContinuousSpaces ? separateContinuousSpaces(middle) : [middle.join("")],
		...parts.slice(parts.length - rightCount)
	];
}
//#endregion
//#region src/transformers/render-whitespace.ts
const RE_SPACE_OR_TAB = /([ \t])/;
/**
* Render whitespaces as separate tokens.
* Apply with CSS, it can be used to render tabs and spaces visually.
*/
function transformerRenderWhitespace(options = {}) {
	const classMap = {
		" ": options.classSpace ?? "space",
		"	": options.classTab ?? "tab"
	};
	const position = options.position ?? "all";
	const keys = Object.keys(classMap);
	return {
		name: "@shikijs/transformers:render-whitespace",
		root(root) {
			const pre = root.children[0];
			(pre.tagName === "pre" ? pre.children[0] : { children: [root] }).children.forEach((line) => {
				if (line.type !== "element" && line.type !== "root") return;
				const elements = line.children.filter((token) => token.type === "element");
				const last = elements.length - 1;
				line.children = line.children.flatMap((token) => {
					if (token.type !== "element") return token;
					const index = elements.indexOf(token);
					if (position === "boundary" && index !== 0 && index !== last) return token;
					if (position === "trailing" && index !== last) return token;
					if (position === "leading" && index !== 0) return token;
					const node = token.children[0];
					if (node.type !== "text" || !node.value) return token;
					const parts = splitSpaces(node.value.split(RE_SPACE_OR_TAB).filter((i) => i.length), position === "boundary" && index === last && last !== 0 ? "trailing" : position, position !== "trailing" && position !== "leading");
					if (parts.length <= 1) return token;
					return parts.map((part) => {
						const clone = {
							...token,
							properties: { ...token.properties }
						};
						clone.children = [{
							type: "text",
							value: part
						}];
						if (keys.includes(part)) {
							this.addClassToHast(clone, classMap[part]);
							delete clone.properties.style;
						}
						return clone;
					});
				});
			});
		}
	};
}
//#endregion
//#region src/transformers/style-to-class.ts
/**
* Remove line breaks between lines.
* Useful when you override `display: block` to `.line` in CSS.
*/
function transformerStyleToClass(options = {}) {
	const { classPrefix = "__shiki_", classSuffix = "", classReplacer = (className) => className } = options;
	const classToStyle = /* @__PURE__ */ new Map();
	function stringifyStyle(style) {
		return Object.entries(style).map(([key, value]) => `${key}:${value}`).join(";");
	}
	function registerStyle(style) {
		const str = typeof style === "string" ? style : stringifyStyle(style);
		let className = classPrefix + cyrb53(str) + classSuffix;
		className = classReplacer(className);
		if (!classToStyle.has(className)) classToStyle.set(className, typeof style === "string" ? style : { ...style });
		return className;
	}
	return {
		name: "@shikijs/transformers:style-to-class",
		pre(t) {
			if (!t.properties.style) return;
			const className = registerStyle(t.properties.style);
			delete t.properties.style;
			this.addClassToHast(t, className);
		},
		tokens(lines) {
			for (const line of lines) for (const token of line) {
				if (!token.htmlStyle) continue;
				const className = registerStyle(token.htmlStyle);
				token.htmlStyle = {};
				token.htmlAttrs ||= {};
				if (!token.htmlAttrs.class) token.htmlAttrs.class = className;
				else token.htmlAttrs.class += ` ${className}`;
			}
		},
		getClassRegistry() {
			return classToStyle;
		},
		getCSS() {
			let css = "";
			for (const [className, style] of classToStyle.entries()) css += `.${className}{${typeof style === "string" ? style : stringifyStyle(style)}}`;
			return css;
		},
		clearRegistry() {
			classToStyle.clear();
		}
	};
}
/**
* A simple hash function.
*
* @see https://stackoverflow.com/a/52171480
*/
function cyrb53(str, seed = 0) {
	let h1 = 3735928559 ^ seed;
	let h2 = 1103547991 ^ seed;
	for (let i = 0, ch; i < str.length; i++) {
		ch = str.charCodeAt(i);
		h1 = Math.imul(h1 ^ ch, 2654435761);
		h2 = Math.imul(h2 ^ ch, 1597334677);
	}
	h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
	h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
	h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
	h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
	return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36).slice(0, 6);
}
//#endregion
export { createCommentNotationTransformer, findAllSubstringIndexes, parseMetaHighlightString, parseMetaHighlightWords, transformerCompactLineOptions, transformerMetaHighlight, transformerMetaWordHighlight, transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight, transformerNotationMap, transformerNotationWordHighlight, transformerRemoveComments, transformerRemoveLineBreak, transformerRemoveNotationEscape, transformerRenderIndentGuides, transformerRenderWhitespace, transformerStyleToClass };
