UNPKG

@lobehub/ui

Version:

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

1 lines 7.72 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-f]{6}|[\\da-f]{3})$/i;\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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"}