From 8c1c143b5eebb09b9fae5801c56317723b943401 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Thu, 7 May 2026 13:37:20 +0800 Subject: [PATCH] fix: render GitHub alert syntax in update notes --- src/components/setting/mods/update-viewer.tsx | 134 +++++++++++++++++- 1 file changed, 132 insertions(+), 2 deletions(-) diff --git a/src/components/setting/mods/update-viewer.tsx b/src/components/setting/mods/update-viewer.tsx index 56f833a29..74db7fe12 100644 --- a/src/components/setting/mods/update-viewer.tsx +++ b/src/components/setting/mods/update-viewer.tsx @@ -1,4 +1,4 @@ -import { Box, Button, LinearProgress } from '@mui/material' +import { alpha, Box, Button, LinearProgress } from '@mui/material' import { relaunch } from '@tauri-apps/plugin-process' import { open as openUrl } from '@tauri-apps/plugin-shell' import type { DownloadEvent } from '@tauri-apps/plugin-updater' @@ -15,6 +15,95 @@ import { portableFlag } from '@/pages/_layout' import { showNotice } from '@/services/notice-service' import { useSetUpdateState, useUpdateState } from '@/services/states' +type MarkdownNode = { + type: string + value?: string + children?: MarkdownNode[] + data?: { + hProperties?: Record + } +} + +const GITHUB_ALERTS = { + note: { label: 'Note', color: '#0969da' }, + tip: { label: 'Tip', color: '#1a7f37' }, + important: { label: 'Important', color: '#8250df' }, + warning: { label: 'Warning', color: '#9a6700' }, + caution: { label: 'Caution', color: '#cf222e' }, +} as const + +type GitHubAlertType = keyof typeof GITHUB_ALERTS + +const GITHUB_ALERT_PATTERN = + /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][\t ]*\n?/i +const GITHUB_ALERT_CLASS_PATTERN = + /markdown-alert-(note|tip|important|warning|caution)/ + +const getAlertTypeFromClassName = ( + className: unknown, +): GitHubAlertType | null => { + const value = Array.isArray(className) + ? className.join(' ') + : typeof className === 'string' + ? className + : '' + const match = value.match(GITHUB_ALERT_CLASS_PATTERN) + return match?.[1] as GitHubAlertType | null +} + +const findFirstTextNode = (node: MarkdownNode): MarkdownNode | null => { + if (node.type === 'text') return node + for (const child of node.children ?? []) { + const result = findFirstTextNode(child) + if (result) return result + } + return null +} + +const remarkGitHubAlerts = () => { + const visit = (node: MarkdownNode) => { + for (const child of node.children ?? []) { + visit(child) + } + + if (node.type !== 'blockquote') return + + const firstTextNode = findFirstTextNode(node) + const match = firstTextNode?.value?.match(GITHUB_ALERT_PATTERN) + if (!firstTextNode?.value || !match) return + + const alertType = match[1].toLowerCase() as GitHubAlertType + firstTextNode.value = firstTextNode.value + .replace(GITHUB_ALERT_PATTERN, '') + .replace(/^\n+/, '') + + node.data = { + ...(node.data ?? {}), + hProperties: { + ...(node.data?.hProperties ?? {}), + className: ['markdown-alert', `markdown-alert-${alertType}`], + }, + } + + node.children?.unshift({ + type: 'paragraph', + data: { + hProperties: { + className: 'markdown-alert-title', + }, + }, + children: [ + { + type: 'text', + value: GITHUB_ALERTS[alertType].label, + }, + ], + }) + } + + return visit +} + export function UpdateViewer({ ref }: { ref?: Ref }) { const { t } = useTranslation() @@ -140,16 +229,57 @@ export function UpdateViewer({ ref }: { ref?: Ref }) { > { const { children } = props return ( - + {children} ) }, + blockquote: ({ className, children }) => { + const alertType = getAlertTypeFromClassName(className) + + if (!alertType) { + return
{children}
+ } + + return ( + { + const color = GITHUB_ALERTS[alertType].color + return { + m: '12px 0 18px', + px: 2, + py: 1, + borderLeft: `4px solid ${color}`, + borderRadius: 1, + bgcolor: alpha( + color, + theme.palette.mode === 'dark' ? 0.16 : 0.08, + ), + '& p': { + my: 0.75, + }, + '& .markdown-alert-title': { + display: 'flex', + alignItems: 'center', + gap: 0.75, + fontWeight: 700, + lineHeight: 1.4, + }, + } + }} + > + {children} + + ) + }, }} > {markdownContent}