feat: add title render support for code block (#522)

This commit is contained in:
pengzhanbo 2025-03-12 23:38:43 +08:00 committed by GitHub
parent 79d3ee6565
commit 3d9361e2f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 67 additions and 11 deletions

View File

@ -5,11 +5,17 @@ import { isPlainObject } from '@vuepress/helper'
import { definitions, getFileIconName, getFileIconTypeFromExtension } from '../fileIcons/index.js'
import { stringifyProp } from '../utils/stringifyProp.js'
export const codeTabs: PluginWithOptions<CodeTabsOptions> = (md, options: CodeTabsOptions = {}) => {
const getIcon = (filename: string): string | void => {
if (options.icon === false)
return undefined
const { named, extensions } = isPlainObject(options.icon) ? options.icon : {}
export function createCodeTabIconGetter(
options: CodeTabsOptions = {},
): (filename: string) => string | void {
const noop = () => undefined
if (options.icon === false)
return noop
const { named, extensions } = isPlainObject(options.icon) ? options.icon : {}
return function getIcon(filename: string): string | void {
if (named === false && definitions.named[filename])
return undefined
if (extensions === false && getFileIconTypeFromExtension(filename)) {
@ -26,6 +32,10 @@ export const codeTabs: PluginWithOptions<CodeTabsOptions> = (md, options: CodeTa
}
return getFileIconName(filename)
}
}
export const codeTabs: PluginWithOptions<CodeTabsOptions> = (md, options: CodeTabsOptions = {}) => {
const getIcon = createCodeTabIconGetter(options)
tab(md, {
name: 'code-tabs',

View File

@ -1,3 +1,4 @@
export * from '../shared/index.js'
export { createCodeTabIconGetter } from './container/codeTabs.js'
export { resolveImageSize } from './enhance/imageSize.js'
export * from './plugin.js'

View File

@ -42,7 +42,7 @@ html:not([data-theme="dark"]) .vp-code span {
z-index: 3;
font-size: 0.75rem;
color: var(--vp-code-line-number-color);
content: attr(data-title);
content: attr(data-ext);
transition: color var(--vp-t-color);
}
@ -236,8 +236,9 @@ html:not([data-theme="dark"]) .vp-code span {
content: "+";
}
/**
* Copy Code Button
/*
Copy Code Button
--------------------------------------------------------------------------
*/
.vp-copy-code-button {
--copy-code-c-text: var(--vp-code-block-color);
@ -280,3 +281,33 @@ html:not([data-theme="dark"]) .vp-code span {
.vp-doc div[class*="language-"].has-collapsed-lines:not(.collapsed) .collapsed-lines:hover {
--vp-collapsed-lines-bg: transparent;
}
/*
Code Block Title
---------------------------------------------------------------
*/
:root {
--code-title-c-bg: var(--vp-code-block-bg);
--code-title-divider: var(--vp-c-divider);
--code-title-c-text: var(--vp-c-text-1);
}
/* stylelint-disable-next-line no-descending-specificity */
.vp-doc .code-block-title div[class*="language-"] {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.code-block-title .code-block-title-bar {
margin-bottom: -16px;
}
.code-block-title .code-block-title-bar .title {
padding: 10px 12px;
}
.code-block-title .code-block-title-bar .title .vp-icon {
width: 18px;
height: 18px;
margin-left: 0;
}

View File

@ -24,7 +24,7 @@ import { shikiPlugin } from '@vuepress/plugin-shiki'
import { sitemapPlugin } from '@vuepress/plugin-sitemap'
import { watermarkPlugin } from '@vuepress/plugin-watermark'
import { mdEnhancePlugin } from 'vuepress-plugin-md-enhance'
import { markdownPowerPlugin } from 'vuepress-plugin-md-power'
import { createCodeTabIconGetter, markdownPowerPlugin } from 'vuepress-plugin-md-power'
import { getThemeConfig } from '../loadConfig/index.js'
export interface SetupPluginOptions {
@ -118,10 +118,16 @@ export function getPlugins({
const shikiTheme = shikiOptions && 'theme' in shikiOptions ? shikiOptions.theme : shikiOptions && 'themes' in shikiOptions ? shikiOptions.themes : { light: 'vitesse-light', dark: 'vitesse-dark' }
if (shikiOptions !== false) {
const { twoslash, ...restShikiOptions } = isPlainObject(shikiOptions) ? shikiOptions : {}
const { twoslash, codeBlockTitle: _, langs = [], ...restShikiOptions } = isPlainObject(shikiOptions) ? shikiOptions : {}
const twoslashOptions = twoslash === true ? {} : twoslash
const markdownPower = isPlainObject(pluginOptions.markdownPower) ? pluginOptions.markdownPower : {}
const getIcon = createCodeTabIconGetter(markdownPower.codeTabs)
plugins.push(shikiPlugin({
// enable some default features
langs: [
...twoslash ? ['js', 'ts', 'vue'] : [],
...langs,
],
notationDiff: true,
notationErrorLevel: true,
notationFocus: true,
@ -129,6 +135,15 @@ export function getPlugins({
notationWordHighlight: true,
highlightLines: true,
collapsedLines: false,
codeBlockTitle: (title, code) => {
const icon = getIcon(title)
return `<div class="code-block-title">
<div class="code-block-title-bar">
<span class="title">${icon ? `<VPIcon name="${icon}"/>` : ''}${title}</span>
</div>
${code}
</div>`
},
twoslash: isPlainObject(twoslashOptions)
? {
...twoslashOptions,
@ -171,7 +186,6 @@ export function getPlugins({
if (pluginOptions.watermark) {
plugins.push(watermarkPlugin({
delay: 300,
enabled: true,
...isPlainObject(pluginOptions.watermark) ? pluginOptions.watermark : {},
}))