mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-10 20:32:21 +08:00
fix: render GitHub alert syntax in update notes
This commit is contained in:
@@ -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<string, unknown>
|
||||
}
|
||||
}
|
||||
|
||||
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<DialogRef> }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -140,16 +229,57 @@ export function UpdateViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
>
|
||||
<Box sx={{ height: 'calc(100% - 10px)', overflow: 'auto' }}>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGitHubAlerts]}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
components={{
|
||||
a: ({ ...props }) => {
|
||||
const { children } = props
|
||||
return (
|
||||
<a {...props} target="_blank">
|
||||
<a {...props} target="_blank" rel="noreferrer">
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
},
|
||||
blockquote: ({ className, children }) => {
|
||||
const alertType = getAlertTypeFromClassName(className)
|
||||
|
||||
if (!alertType) {
|
||||
return <blockquote className={className}>{children}</blockquote>
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
component="blockquote"
|
||||
className={className}
|
||||
sx={(theme) => {
|
||||
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}
|
||||
</Box>
|
||||
)
|
||||
},
|
||||
}}
|
||||
>
|
||||
{markdownContent}
|
||||
|
||||
Reference in New Issue
Block a user