From ab8969a62b1893136bc97c40fc37531e21f7b900 Mon Sep 17 00:00:00 2001 From: seaHi Date: Wed, 16 Sep 2026 12:00:12 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E5=8A=A8=E6=80=81=E7=94=9F=E6=88=90=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E5=B9=B6=E7=A1=AC=E7=BC=96=E7=A0=81=20Markdown=20=E5=86=85?= =?UTF-8?q?=E8=81=94=20SVG=20=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/components/MarkdownRenderer.svelte | 57 ++++--- src/lib/markdown/icons.ts | 164 --------------------- src/lib/markdown/rendering.ts | 20 +-- 3 files changed, 37 insertions(+), 204 deletions(-) delete mode 100644 src/lib/markdown/icons.ts diff --git a/src/lib/components/MarkdownRenderer.svelte b/src/lib/components/MarkdownRenderer.svelte index aaf3d5c..b40b616 100644 --- a/src/lib/components/MarkdownRenderer.svelte +++ b/src/lib/components/MarkdownRenderer.svelte @@ -9,7 +9,6 @@ replaceGithubAdmonitions, sanitizeMarkdownHtml } from '$lib/markdown/rendering'; - import { markdownIcon } from '$lib/markdown/icons'; // 按需引入及注册主流核心前端、后端、运维语法包,替代沉重的 190+ 语言全集捆绑 import bash from 'highlight.js/lib/languages/bash'; @@ -257,12 +256,18 @@ }) .join('\n'); - const chevronRight = markdownIcon('chevron-right', 'chevron-icon'); - const listOlIcon = markdownIcon('list-ordered'); - const barsIcon = markdownIcon('text-wrap'); - const copyIcon = markdownIcon('copy'); - const checkIcon = markdownIcon('check'); - const ellipsisIcon = markdownIcon('ellipsis'); + const chevronRight = + ''; + const listOlIcon = + ''; + const barsIcon = + ''; + const copyIcon = + ''; + const checkIcon = + ''; + const ellipsisIcon = + ''; return `
@@ -306,20 +311,30 @@ function parseAdmonitions(markdown: string) { const icons = { - note: markdownIcon('pencil'), - abstract: markdownIcon('text-align-start'), - info: markdownIcon('info'), - tip: markdownIcon('lightbulb'), - success: markdownIcon('circle-check'), - question: markdownIcon('circle-question-mark'), - warning: markdownIcon('triangle-alert'), - failure: markdownIcon('circle-x'), - danger: markdownIcon('zap'), - bug: markdownIcon('bug'), - example: markdownIcon('flask-conical'), - quote: markdownIcon('quote'), - important: markdownIcon('circle-alert'), - caution: markdownIcon('octagon-alert') + note: '', + abstract: + '', + info: '', + tip: '', + success: + '', + question: + '', + warning: + '', + failure: + '', + danger: + '', + bug: '', + example: + '', + quote: + '', + important: + '', + caution: + '' }; let html = replaceGithubAdmonitions(markdown, (type, title, content) => { diff --git a/src/lib/markdown/icons.ts b/src/lib/markdown/icons.ts deleted file mode 100644 index 481c3e6..0000000 --- a/src/lib/markdown/icons.ts +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Markdown 正文里的图标需要以 HTML 字符串输出(通过 {@html} 渲染), - * 无法直接挂载 lucide-svelte 的 Svelte 组件,因此这里把所需的 lucide 图标 - * 序列化成等价的 SVG 标记,保证全站图标风格统一。 - * - * 图标数据取自 lucide(https://lucide.dev),仅保留用到的子集。 - */ -type IconNode = [tag: string, attrs: Record][]; - -const ICON_NODES = { - pencil: [ - [ - 'path', - { - d: 'M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z' - } - ], - ['path', { d: 'm15 5 4 4' }] - ], - 'text-align-start': [ - ['path', { d: 'M21 5H3' }], - ['path', { d: 'M15 12H3' }], - ['path', { d: 'M17 19H3' }] - ], - info: [ - ['circle', { cx: '12', cy: '12', r: '10' }], - ['path', { d: 'M12 16v-4' }], - ['path', { d: 'M12 8h.01' }] - ], - lightbulb: [ - [ - 'path', - { - d: 'M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5' - } - ], - ['path', { d: 'M9 18h6' }], - ['path', { d: 'M10 22h4' }] - ], - 'circle-check': [ - ['circle', { cx: '12', cy: '12', r: '10' }], - ['path', { d: 'm9 12 2 2 4-4' }] - ], - 'circle-question-mark': [ - ['circle', { cx: '12', cy: '12', r: '10' }], - ['path', { d: 'M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3' }], - ['path', { d: 'M12 17h.01' }] - ], - 'triangle-alert': [ - ['path', { d: 'm21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3' }], - ['path', { d: 'M12 9v4' }], - ['path', { d: 'M12 17h.01' }] - ], - 'circle-x': [ - ['circle', { cx: '12', cy: '12', r: '10' }], - ['path', { d: 'm15 9-6 6' }], - ['path', { d: 'm9 9 6 6' }] - ], - zap: [ - [ - 'path', - { - d: 'M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z' - } - ] - ], - bug: [ - ['path', { d: 'M12 20v-9' }], - ['path', { d: 'M14 7a4 4 0 0 1 4 4v3a6 6 0 0 1-12 0v-3a4 4 0 0 1 4-4z' }], - ['path', { d: 'M14.12 3.88 16 2' }], - ['path', { d: 'M21 21a4 4 0 0 0-3.81-4' }], - ['path', { d: 'M21 5a4 4 0 0 1-3.55 3.97' }], - ['path', { d: 'M22 13h-4' }], - ['path', { d: 'M3 21a4 4 0 0 1 3.81-4' }], - ['path', { d: 'M3 5a4 4 0 0 0 3.55 3.97' }], - ['path', { d: 'M6 13H2' }], - ['path', { d: 'm8 2 1.88 1.88' }], - ['path', { d: 'M9 7.13V6a3 3 0 1 1 6 0v1.13' }] - ], - 'flask-conical': [ - [ - 'path', - { - d: 'M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2' - } - ], - ['path', { d: 'M6.453 15h11.094' }], - ['path', { d: 'M8.5 2h7' }] - ], - quote: [ - [ - 'path', - { - d: 'M16 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z' - } - ], - [ - 'path', - { - d: 'M5 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z' - } - ] - ], - 'circle-alert': [ - ['circle', { cx: '12', cy: '12', r: '10' }], - ['line', { x1: '12', x2: '12', y1: '8', y2: '12' }], - ['line', { x1: '12', x2: '12.01', y1: '16', y2: '16' }] - ], - 'octagon-alert': [ - ['path', { d: 'M12 16h.01' }], - ['path', { d: 'M12 8v4' }], - [ - 'path', - { - d: 'M15.312 2a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586l-4.688-4.688A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2z' - } - ] - ], - 'chevron-right': [['path', { d: 'm9 18 6-6-6-6' }]], - 'list-ordered': [ - ['path', { d: 'M11 5h10' }], - ['path', { d: 'M11 12h10' }], - ['path', { d: 'M11 19h10' }], - ['path', { d: 'M4 4h1v5' }], - ['path', { d: 'M4 9h2' }], - ['path', { d: 'M6.5 20H3.4c0-1 2.6-1.925 2.6-3.5a1.5 1.5 0 0 0-2.6-1.02' }] - ], - 'text-wrap': [ - ['path', { d: 'm16 16-3 3 3 3' }], - ['path', { d: 'M3 12h14.5a1 1 0 0 1 0 7H13' }], - ['path', { d: 'M3 19h6' }], - ['path', { d: 'M3 5h18' }] - ], - copy: [ - ['rect', { width: '14', height: '14', x: '8', y: '8', rx: '2', ry: '2' }], - ['path', { d: 'M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2' }] - ], - check: [['path', { d: 'M20 6 9 17l-5-5' }]], - ellipsis: [ - ['circle', { cx: '12', cy: '12', r: '1' }], - ['circle', { cx: '19', cy: '12', r: '1' }], - ['circle', { cx: '5', cy: '12', r: '1' }] - ] -} satisfies Record; - -export type MarkdownIconName = keyof typeof ICON_NODES; - -const SVG_ATTRIBUTES = - 'viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + - 'stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"'; - -function renderNode([tag, attrs]: IconNode[number]): string { - const attributes = Object.entries(attrs) - .map(([key, value]) => ` ${key}="${value}"`) - .join(''); - return `<${tag}${attributes}>`; -} - -/** 渲染一个 lucide 图标的 SVG 字符串,className 用于挂载主题样式。 */ -export function markdownIcon(name: MarkdownIconName, className = ''): string { - const children = ICON_NODES[name].map(renderNode).join(''); - const classAttribute = className ? ` class="${className}"` : ''; - return `${children}`; -} diff --git a/src/lib/markdown/rendering.ts b/src/lib/markdown/rendering.ts index 647ac60..d9f086d 100644 --- a/src/lib/markdown/rendering.ts +++ b/src/lib/markdown/rendering.ts @@ -64,9 +64,6 @@ export function sanitizeMarkdownHtml(html: string): string { 'time', 'svg', 'path', - 'circle', - 'line', - 'rect', 'button', 'span', 'pre', @@ -78,23 +75,8 @@ export function sanitizeMarkdownHtml(html: string): string { a: ['href', 'target', 'rel', 'data-fancybox', 'data-caption'], img: ['src', 'alt', 'title', 'loading', 'width', 'height'], button: ['type', 'title', 'data-code', 'aria-hidden'], - svg: [ - 'viewBox', - 'viewbox', - 'fill', - 'width', - 'height', - 'xmlns', - 'focusable', - 'stroke', - 'stroke-width', - 'stroke-linecap', - 'stroke-linejoin' - ], + svg: ['viewBox', 'viewbox', 'fill', 'width', 'height', 'xmlns', 'focusable'], path: ['d', 'fill'], - circle: ['cx', 'cy', 'r'], - line: ['x1', 'y1', 'x2', 'y2'], - rect: ['x', 'y', 'width', 'height', 'rx', 'ry'], time: ['datetime'] }, allowedSchemes: ['http', 'https', 'mailto', 'tel'],