Compare commits

...

3 Commits

Author SHA1 Message Date
dribble-njr
0375954bfc feat: support GFM styles to follow fonts changes & optimize custom style logic 2024-11-22 15:01:17 +08:00
dribble-njr
2c26d59d7c feat: GFM alerts support custom css 2024-11-22 13:15:08 +08:00
dribble-njr
53a8cd5cd4 style: adjust caution alert margin right 2024-11-22 12:53:05 +08:00
6 changed files with 131 additions and 74 deletions

View File

@ -33,29 +33,53 @@ blockquote {
/* 引用段落样式 */
blockquote_p {
}
/* GFM 警告块 */
markdown-alert {
/* GFM note 样式 */
blockquote_note {
}
/* GFM 警告块标题 */
markdown-alert-title {
/* GFM tip 样式 */
blockquote_tip {
}
/* GFM 警告块内容,抵消 p 默认的 margin */
markdown-alert-content-wrapper {
/* GFM important 样式 */
blockquote_important {
}
/* GFM note */
markdown-alert-title-note {
/* GFM warning 样式 */
blockquote_warning {
}
/* GFM tip */
markdown-alert-title-tip {
/* GFM caution 样式 */
blockquote_caution {
}
/* GFM important */
markdown-alert-title-important {
/* GFM 通用标题 */
blockquote_title {
}
/* GFM warning */
markdown-alert-title-warning {
/* GFM note 标题 */
blockquote_title_note {
}
/* GFM caution */
markdown-alert-title-caution {
/* GFM tip 标题 */
blockquote_title_tip {
}
/* GFM important 标题 */
blockquote_title_important {
}
/* GFM warning 标题 */
blockquote_title_warning {
}
/* GFM caution 标题 */
blockquote_title_caution {
}
/* GFM note 段落样式 */
blockquote_p_note {
}
/* GFM tip 段落样式 */
blockquote_p_tip {
}
/* GFM important 段落样式 */
blockquote_p_important {
}
/* GFM warning 段落样式 */
blockquote_p_warning {
}
/* GFM caution 段落样式 */
blockquote_p_caution {
}
/* 段落样式 */
p {

View File

@ -10,7 +10,7 @@ const defaultTheme: Theme = {
},
block: {
// 一级标题
'h1': {
h1: {
'display': `table`,
'padding': `0 1em`,
'border-bottom': `2px solid var(--md-primary-color)`,
@ -22,7 +22,7 @@ const defaultTheme: Theme = {
},
// 二级标题
'h2': {
h2: {
'display': `table`,
'padding': `0 0.2em`,
'margin': `4em auto 2em`,
@ -34,7 +34,7 @@ const defaultTheme: Theme = {
},
// 三级标题
'h3': {
h3: {
'padding-left': `8px`,
'border-left': `3px solid var(--md-primary-color)`,
'margin': `2em 8px 0.75em 0`,
@ -45,7 +45,7 @@ const defaultTheme: Theme = {
},
// 四级标题
'h4': {
h4: {
'margin': `2em 8px 0.5em`,
'color': `var(--md-primary-color)`,
'font-size': `1em`,
@ -53,7 +53,7 @@ const defaultTheme: Theme = {
},
// 五级标题
'h5': {
h5: {
'margin': `1.5em 8px 0.5em`,
'color': `var(--md-primary-color)`,
'font-size': `1em`,
@ -61,14 +61,14 @@ const defaultTheme: Theme = {
},
// 六级标题
'h6': {
h6: {
'margin': `1.5em 8px 0.5em`,
'font-size': `1em`,
'color': `var(--md-primary-color)`,
},
// 段落
'p': {
p: {
'margin': `1.5em 8px`,
'letter-spacing': `0.1em`,
'color': `var(--el-text-color-regular)`,
@ -76,7 +76,7 @@ const defaultTheme: Theme = {
},
// 引用
'blockquote': {
blockquote: {
'font-style': `normal`,
'border-left': `none`,
'padding': `1em`,
@ -87,57 +87,73 @@ const defaultTheme: Theme = {
},
// 引用内容
'blockquote_p': {
blockquote_p: {
'display': `block`,
'font-size': `1em`,
'letter-spacing': `0.1em`,
'color': `var(--el-text-color-regular)`,
},
// GFM 警告块
'markdown-alert': {
'font-style': `normal`,
'border-left': `none`,
'padding': `1em`,
'border-radius': `8px`,
'background': `var(--blockquote-background)`,
'margin': `2em 8px`,
blockquote_note: {
},
blockquote_tip: {
},
blockquote_important: {
},
blockquote_warning: {
},
blockquote_caution: {
},
// GFM 警告块标题
'markdown-alert-title': {
blockquote_title: {
'display': `flex`,
'align-items': `center`,
'gap': `0.5em`,
'margin-bottom': `0.5em`,
},
// GFM 警告块内容,抵消 p 默认的 margin
'markdown-alert-content-wrapper': {
margin: `-1em -8px -1.5em;`,
},
'markdown-alert-title-note': {
blockquote_title_note: {
color: `#478be6`,
},
'markdown-alert-title-tip': {
blockquote_title_tip: {
color: `#57ab5a`,
},
'markdown-alert-title-important': {
blockquote_title_important: {
color: `#986ee2`,
},
'markdown-alert-title-warning': {
blockquote_title_warning: {
color: `#c69026`,
},
'markdown-alert-title-caution': {
blockquote_title_caution: {
color: `#e5534b`,
},
blockquote_p_note: {
},
blockquote_p_tip: {
},
blockquote_p_important: {
},
blockquote_p_warning: {
},
blockquote_p_caution: {
},
// 代码块
'code_pre': {
code_pre: {
'font-size': `14px`,
'overflow-x': `auto`,
'border-radius': `8px`,
@ -147,14 +163,14 @@ const defaultTheme: Theme = {
},
// 行内代码
'code': {
code: {
'margin': 0,
'white-space': `nowrap`,
'font-family': `Menlo, Operator Mono, Consolas, Monaco, monospace`,
},
// 图片
'image': {
image: {
'display': `block`,
'width': `100% !important`,
'margin': `0.1em auto 0.5em`,
@ -162,32 +178,32 @@ const defaultTheme: Theme = {
},
// 有序列表
'ol': {
ol: {
'padding-left': `1em`,
'margin-left': `0`,
'color': `var(--el-text-color-regular)`,
},
// 无序列表
'ul': {
ul: {
'list-style': `circle`,
'padding-left': `1em`,
'margin-left': `0`,
'color': `var(--el-text-color-regular)`,
},
'footnotes': {
footnotes: {
'margin': `0.5em 8px`,
'font-size': `80%`,
'color': `var(--el-text-color-regular)`,
},
'figure': {
figure: {
margin: `1.5em 8px`,
color: `var(--el-text-color-regular)`,
},
'hr': {
hr: {
'border-style': `solid`,
'border-width': `1px 0 0`,
'border-color': `rgba(0,0,0,0.1)`,

View File

@ -2,7 +2,8 @@ import type { PropertiesHyphen } from 'csstype'
import type { Token } from 'marked'
export type Block = `h1` | `h2` | `h3` | `h4` | `h5` | `h6` | `p` | `blockquote` | `blockquote_p` | `code_pre` | `code` | `image` | `ol` | `ul` | `footnotes` | `figure` | `hr`
type GFMBlock = `blockquote_note` | `blockquote_tip` | `blockquote_important` | `blockquote_warning` | `blockquote_caution` | `blockquote_title` | `blockquote_title_note` | `blockquote_title_tip` | `blockquote_title_important` | `blockquote_title_warning` | `blockquote_title_caution` | `blockquote_p` | `blockquote_p_note` | `blockquote_p_tip` | `blockquote_p_important` | `blockquote_p_warning` | `blockquote_p_caution`
export type Block = `h1` | `h2` | `h3` | `h4` | `h5` | `h6` | `p` | `blockquote` | `blockquote_p` | `code_pre` | `code` | `image` | `ol` | `ul` | `footnotes` | `figure` | `hr` | GFMBlock
export type Inline = `listitem` | `codespan` | `link` | `wx_link` | `strong` | `table` | `thead` | `td` | `footnote` | `figcaption` | `em`
interface CustomCSSProperties {
@ -14,8 +15,8 @@ export type ExtendedProperties = PropertiesHyphen & CustomCSSProperties
export interface Theme {
base: ExtendedProperties
block: Record<Block | string, ExtendedProperties>
inline: Record<Inline | string, ExtendedProperties>
block: Record<Block, ExtendedProperties>
inline: Record<Inline, ExtendedProperties>
}
export interface IOpts {
@ -41,7 +42,7 @@ export interface IConfigOption<VT = string> {
export interface AlertOptions {
className?: string
variants?: AlertVariantItem[]
theme?: Theme
styles?: ThemeStyles
}
/**

View File

@ -29,6 +29,8 @@ export default function markedAlert(options: AlertOptions = {}): MarkedExtension
} = matchedVariant
const typeRegexp = new RegExp(createSyntaxPattern(variantType), `i`)
const { styles } = options
Object.assign(token, {
type: `alert`,
meta: {
@ -37,25 +39,21 @@ export default function markedAlert(options: AlertOptions = {}): MarkedExtension
icon,
title,
titleClassName,
style: {
...options.theme?.block[className],
...options.theme?.block[`${className}-${variantType}`],
wrapperStyle: {
...styles?.blockquote,
...styles?.[`blockquote_${variantType}` as keyof typeof styles],
},
titleStyle: {
...options.theme?.block[titleClassName],
...options.theme?.block[`${titleClassName}-${variantType}`],
...styles?.blockquote_title,
...styles?.[`blockquote_title_${variantType}` as keyof typeof styles],
},
contentWrapperStyle: {
margin: options.theme?.block[`${className}-content-wrapper`]?.margin,
contentStyle: {
...styles?.blockquote_p,
...styles?.[`blockquote_p_${variantType}` as keyof typeof styles],
},
},
})
console.log({
...options.theme?.block[className],
...options.theme?.block[`${className}-${variantType}`],
}, `style`)
const firstLine = token.tokens?.[0] as Tokens.Paragraph
const firstLineText = firstLine.raw?.replace(typeRegexp, ``).trim()
@ -81,15 +79,17 @@ export default function markedAlert(options: AlertOptions = {}): MarkedExtension
name: `alert`,
level: `block`,
renderer({ meta, tokens = [] }) {
let tmpl = `<blockquote class="${meta.className} ${meta.className}-${meta.variant}" style='${getStyleString(meta.style)}'>\n`
tmpl += `<p class="${meta.titleClassName}" style='${getStyleString(meta.titleStyle)}'>`
let text = this.parser.parse(tokens)
text = text.replace(/<p .*?>/g, `<p style="${getStyleString(meta.contentStyle)}">`)
let tmpl = `<blockquote class="${meta.className} ${meta.className}-${meta.variant}" style="${getStyleString(meta.wrapperStyle)}">\n`
tmpl += `<p class="${meta.titleClassName}" style="${getStyleString(meta.titleStyle)}">`
tmpl += meta.icon.replace(
`<svg`,
`<svg style="fill: ${meta.titleStyle?.color ?? `inherit`}"`,
)
tmpl += meta.title
tmpl += `</p>\n`
tmpl += `<span style="${`${getStyleString(meta.contentWrapperStyle)} display: block;`}">${this.parser.parse(tokens)}</span>`
tmpl += text
tmpl += `</blockquote>\n`
return tmpl
@ -121,7 +121,7 @@ const defaultAlertVariant: AlertVariantItem[] = [
},
{
type: `caution`,
icon: `<svg class="octicon octicon-stop mr-2" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>`,
icon: `<svg class="octicon octicon-stop" style="margin-right: 0.25em;" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>`,
},
]

View File

@ -34,7 +34,7 @@ export function customizeTheme(theme: Theme, options: {
export function customCssWithTemplate(jsonString: Partial<Record<Block | Inline, PropertiesHyphen>>, color: string, theme: Theme) {
const newTheme = customizeTheme(theme, { color })
const mergeProperties = <T extends Block | Inline = Block>(target: Record<T, PropertiesHyphen>, source: Partial<Record<Block | Inline, PropertiesHyphen>>, keys: T[]) => {
const mergeProperties = <T extends Block | Inline = Block>(target: Record<T, PropertiesHyphen>, source: Partial<Record<Block | Inline | string, PropertiesHyphen>>, keys: T[]) => {
keys.forEach((key) => {
if (source[key]) {
target[key] = Object.assign(target[key] || {}, source[key])
@ -54,7 +54,23 @@ export function customCssWithTemplate(jsonString: Partial<Record<Block | Inline,
`p`,
`hr`,
`blockquote`,
`blockquote_note`,
`blockquote_tip`,
`blockquote_important`,
`blockquote_warning`,
`blockquote_caution`,
`blockquote_p`,
`blockquote_p_note`,
`blockquote_p_tip`,
`blockquote_p_important`,
`blockquote_p_warning`,
`blockquote_p_caution`,
`blockquote_title`,
`blockquote_title_note`,
`blockquote_title_tip`,
`blockquote_title_important`,
`blockquote_title_warning`,
`blockquote_title_caution`,
`image`,
`ul`,
`ol`,

View File

@ -126,7 +126,7 @@ export function initRenderer(opts: IOpts) {
function setOptions(newOpts: Partial<IOpts>): void {
opts = { ...opts, ...newOpts }
styleMapping = buildTheme(opts)
marked.use(markedAlert({ theme: opts.theme }))
marked.use(markedAlert({ styles: styleMapping }))
}
const buildFootnotes = () => {