UNPKG

@lobehub/ui

Version:

Lobe UI is an open-source UI component library for building AIGC web apps

128 lines (126 loc) 3.9 kB
import { visit } from "../../node_modules/unist-util-visit/lib/index.mjs"; //#region src/Markdown/plugins/remarkColor.ts /** * Remark plugin to handle color syntax in markdown code spans * Supports GitHub-style color visualization for HEX, RGB, and HSL colors * * @example * `#FF0000` -> renders with red color preview * `rgb(255, 0, 0)` -> renders with red color preview * `hsl(0, 100%, 50%)` -> renders with red color preview */ const remarkColor = (options = {}) => { const { colorValidator } = options; /** * 验证并标准化颜色值 */ const validateAndNormalizeColor = (colorString) => { const trimmed = colorString.trim(); if (colorValidator && !colorValidator(trimmed)) return null; if (/^#([\dA-Fa-f]{6}|[\dA-Fa-f]{3})$/.test(trimmed)) { if (trimmed.length === 4) { const [, r, g, b] = trimmed; return `#${r}${r}${g}${g}${b}${b}`; } return trimmed.toUpperCase(); } const rgbMatch = trimmed.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i); if (rgbMatch) { const [, r, g, b] = rgbMatch; const rNum = parseInt(r, 10); const gNum = parseInt(g, 10); const bNum = parseInt(b, 10); if (rNum >= 0 && rNum <= 255 && gNum >= 0 && gNum <= 255 && bNum >= 0 && bNum <= 255) return `rgb(${rNum}, ${gNum}, ${bNum})`; } const hslMatch = trimmed.match(/^hsl\s*\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$/i); if (hslMatch) { const [, h, s, l] = hslMatch; const hNum = parseInt(h, 10); const sNum = parseInt(s, 10); const lNum = parseInt(l, 10); if (hNum >= 0 && hNum <= 360 && sNum >= 0 && sNum <= 100 && lNum >= 0 && lNum <= 100) return `hsl(${hNum}, ${sNum}%, ${lNum}%)`; } return null; }; return (tree) => { visit(tree, "inlineCode", (node, index = 0, parent) => { if (!node.value || typeof node.value !== "string") return; const colorValue = validateAndNormalizeColor(node.value); if (colorValue) { const colorNode = { children: [{ type: "text", value: node.value }], color: colorValue, data: { hName: "code", hProperties: { "className": "color-preview", "data-color": colorValue, "data-original": node.value, "style": `--color-preview-color: ${colorValue}` } }, type: "colorPreview", value: node.value }; parent.children.splice(index, 1, colorNode); return index; } }); visit(tree, "text", (node, index = 0, parent) => { if (!node.value || typeof node.value !== "string") return; const colorPattern = /`([^`]+)`/g; const text = node.value; let hasColorMatch = false; const newNodes = []; let lastIndex = 0; let match; while ((match = colorPattern.exec(text)) !== null) { const [fullMatch, colorCandidate] = match; const colorValue = validateAndNormalizeColor(colorCandidate); if (colorValue) { hasColorMatch = true; const startIndex = match.index; if (startIndex > lastIndex) newNodes.push({ type: "text", value: text.slice(lastIndex, startIndex) }); newNodes.push({ children: [{ type: "text", value: colorCandidate }], color: colorValue, data: { hName: "code", hProperties: { "className": "color-preview", "data-color": colorValue, "data-original": colorCandidate, "style": `--color-preview-color: ${colorValue}` } }, type: "colorPreview", value: colorCandidate }); lastIndex = startIndex + fullMatch.length; } } if (hasColorMatch) { if (lastIndex < text.length) newNodes.push({ type: "text", value: text.slice(lastIndex) }); if (newNodes.length > 0 && parent) { parent.children.splice(index, 1, ...newNodes); return index + newNodes.length - 1; } } }); }; }; //#endregion export { remarkColor }; //# sourceMappingURL=remarkColor.mjs.map