UNPKG

@lobehub/ui

Version:

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

1 lines 7.67 kB
{"version":3,"file":"remarkColor.mjs","names":[],"sources":["../../../src/Markdown/plugins/remarkColor.ts"],"sourcesContent":["import { visit } from 'unist-util-visit';\n\ninterface RemarkColorOptions {\n /**\n * 自定义颜色验证函数\n */\n colorValidator?: (colorString: string) => boolean;\n}\n\n/**\n * Remark plugin to handle color syntax in markdown code spans\n * Supports GitHub-style color visualization for HEX, RGB, and HSL colors\n *\n * @example\n * `#FF0000` -> renders with red color preview\n * `rgb(255, 0, 0)` -> renders with red color preview\n * `hsl(0, 100%, 50%)` -> renders with red color preview\n */\nexport const remarkColor = (options: RemarkColorOptions = {}) => {\n const { colorValidator } = options;\n\n /**\n * 验证并标准化颜色值\n */\n const validateAndNormalizeColor = (colorString: string): string | null => {\n const trimmed = colorString.trim();\n\n // 如果有自定义验证函数,使用它\n if (colorValidator && !colorValidator(trimmed)) {\n return null;\n }\n\n // HEX 颜色: #RRGGBB 或 #RGB\n const hexPattern = /^#([\\dA-Fa-f]{6}|[\\dA-Fa-f]{3})$/;\n if (hexPattern.test(trimmed)) {\n // 标准化为 6 位 HEX\n if (trimmed.length === 4) {\n const [, r, g, b] = trimmed;\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return trimmed.toUpperCase();\n }\n\n // RGB 颜色: rgb(r, g, b)\n const rgbPattern = /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i;\n const rgbMatch = trimmed.match(rgbPattern);\n if (rgbMatch) {\n const [, r, g, b] = rgbMatch;\n const rNum = parseInt(r, 10);\n const gNum = parseInt(g, 10);\n const bNum = parseInt(b, 10);\n\n // 验证 RGB 值范围\n if (rNum >= 0 && rNum <= 255 && gNum >= 0 && gNum <= 255 && bNum >= 0 && bNum <= 255) {\n return `rgb(${rNum}, ${gNum}, ${bNum})`;\n }\n }\n\n // HSL 颜色: hsl(h, s%, l%)\n const hslPattern = /^hsl\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)%\\s*,\\s*(\\d+)%\\s*\\)$/i;\n const hslMatch = trimmed.match(hslPattern);\n if (hslMatch) {\n const [, h, s, l] = hslMatch;\n const hNum = parseInt(h, 10);\n const sNum = parseInt(s, 10);\n const lNum = parseInt(l, 10);\n\n // 验证 HSL 值范围\n if (hNum >= 0 && hNum <= 360 && sNum >= 0 && sNum <= 100 && lNum >= 0 && lNum <= 100) {\n return `hsl(${hNum}, ${sNum}%, ${lNum}%)`;\n }\n }\n\n return null;\n };\n\n return (tree: any) => {\n // 处理 inlineCode 节点(反引号包围的代码)\n visit(tree, 'inlineCode', (node, index = 0, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n const colorValue = validateAndNormalizeColor(node.value);\n\n if (colorValue) {\n // 创建自定义颜色节点\n const colorNode = {\n children: [{ type: 'text', value: node.value }],\n color: colorValue,\n data: {\n hName: 'code',\n hProperties: {\n 'className': 'color-preview',\n 'data-color': colorValue,\n 'data-original': node.value,\n 'style': `--color-preview-color: ${colorValue}`,\n },\n },\n type: 'colorPreview',\n value: node.value,\n };\n\n // 替换 inlineCode 节点\n parent.children.splice(index, 1, colorNode);\n return index;\n }\n });\n\n // 处理文本节点中的颜色语法(作为备用,处理可能的边缘情况)\n visit(tree, 'text', (node, index = 0, parent) => {\n if (!node.value || typeof node.value !== 'string') return;\n\n // 查找反引号包围的颜色值\n const colorPattern = /`([^`]+)`/g;\n const text = node.value;\n\n let hasColorMatch = false;\n const newNodes = [];\n let lastIndex = 0;\n let match;\n\n while ((match = colorPattern.exec(text)) !== null) {\n const [fullMatch, colorCandidate] = match;\n const colorValue = validateAndNormalizeColor(colorCandidate);\n\n if (colorValue) {\n hasColorMatch = true;\n const startIndex = match.index;\n\n // 添加匹配前的文本\n if (startIndex > lastIndex) {\n newNodes.push({\n type: 'text',\n value: text.slice(lastIndex, startIndex),\n });\n }\n\n // 添加颜色节点\n newNodes.push({\n children: [{ type: 'text', value: colorCandidate }],\n color: colorValue,\n data: {\n hName: 'code',\n hProperties: {\n 'className': 'color-preview',\n 'data-color': colorValue,\n 'data-original': colorCandidate,\n 'style': `--color-preview-color: ${colorValue}`,\n },\n },\n type: 'colorPreview',\n value: colorCandidate,\n });\n\n lastIndex = startIndex + fullMatch.length;\n }\n }\n\n if (hasColorMatch) {\n // 添加剩余文本\n if (lastIndex < text.length) {\n newNodes.push({\n type: 'text',\n value: text.slice(lastIndex),\n });\n }\n\n // 替换当前节点\n if (newNodes.length > 0 && parent) {\n parent.children.splice(index, 1, ...newNodes);\n return index + newNodes.length - 1;\n }\n }\n });\n };\n};\n"],"mappings":"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"}