diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index aca503e2..fd6bf492 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -25,6 +25,7 @@ import attrsPlugin from 'markdown-it-attrs' import { full as emojiPlugin } from 'markdown-it-emoji' import type { BuiltinLanguage, BuiltinTheme, Highlighter } from 'shiki' import type { Logger } from 'vite' +import type { Awaitable } from '../shared' import { containerPlugin, type ContainerOptions } from './plugins/containers' import { gitHubAlertsPlugin } from './plugins/githubAlerts' import { highlight as createHighlighter } from './plugins/highlight' @@ -52,11 +53,11 @@ export interface MarkdownOptions extends Options { /** * Setup markdown-it instance before applying plugins */ - preConfig?: (md: MarkdownItAsync) => Awaited + preConfig?: (md: MarkdownItAsync) => Awaitable /** * Setup markdown-it instance */ - config?: (md: MarkdownItAsync) => Awaited + config?: (md: MarkdownItAsync) => Awaitable /** * Disable cache (experimental) */ @@ -245,8 +246,13 @@ export async function createMarkdownRenderer( ) .use(lineNumberPlugin, options.lineNumbers) + const tableOpen = md.renderer.rules.table_open md.renderer.rules.table_open = function (tokens, idx, options, env, self) { - return '\n' + const token = tokens[idx] + if (token.attrIndex('tabindex') < 0) token.attrPush(['tabindex', '0']) + return tableOpen + ? tableOpen(tokens, idx, options, env, self) + : self.renderToken(tokens, idx, options) } if (options.gfmAlerts !== false) { @@ -268,22 +274,31 @@ export async function createMarkdownRenderer( .map((t) => t.content) .join('') }, - permalink: anchorPlugin.permalink.linkInsideHeader({ - symbol: '​', - renderAttrs: (slug, state) => { - // Find `heading_open` with the id identical to slug - const idx = state.tokens.findIndex((token) => { - const attrs = token.attrs - const id = attrs?.find((attr) => attr[0] === 'id') - return id && slug === id[1] - }) - // Get the actual heading content - const title = state.tokens[idx + 1].content - return { - 'aria-label': `Permalink to "${title}"` - } - } - }), + permalink: (slug, _, state, idx) => { + const title = + state.tokens[idx + 1]?.children + ?.filter((token) => ['text', 'code_inline'].includes(token.type)) + .reduce((acc, t) => acc + t.content, '') + .trim() || '' + + const linkTokens = [ + Object.assign(new state.Token('text', '', 0), { content: ' ' }), + Object.assign(new state.Token('link_open', 'a', 1), { + attrs: [ + ['class', 'header-anchor'], + ['href', `#${slug}`], + ['aria-label', `Permalink to “${title}”`] + ] + }), + Object.assign(new state.Token('html_inline', '', 0), { + content: '​', + meta: { isPermalinkSymbol: true } + }), + new state.Token('link_close', 'a', -1) + ] + + state.tokens[idx + 1].children?.push(...linkTokens) + }, ...options.anchor } as anchorPlugin.AnchorOptions).use(frontmatterPlugin, { ...options.frontmatter diff --git a/src/node/markdown/plugins/containers.ts b/src/node/markdown/plugins/containers.ts index 6b08fc3d..073fb38d 100644 --- a/src/node/markdown/plugins/containers.ts +++ b/src/node/markdown/plugins/containers.ts @@ -62,15 +62,16 @@ function createContainer( { render(tokens, idx, _options, env: MarkdownEnv & { references?: any }) { const token = tokens[idx] - const info = token.info.trim().slice(klass.length).trim() - const attrs = md.renderer.renderAttrs(token) if (token.nesting === 1) { + token.attrJoin('class', `${klass} custom-block`) + const attrs = md.renderer.renderAttrs(token) + const info = token.info.trim().slice(klass.length).trim() const title = md.renderInline(info || defaultTitle, { references: env.references }) if (klass === 'details') - return `
${title}\n` - return `

${title}

\n` + return `
${title}\n` + return `

${title}

\n` } else return klass === 'details' ? `
\n` : `
\n` } }