pull/5007/merge
Bjorn Lu 3 weeks ago committed by GitHub
commit 0676d468e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -120,6 +120,12 @@ const line1 = 'This is line 1'
const line2 = 'This is line 2'
```
## Title Bar
```js [main.js]
console.log('Hello, VitePress!')
```
## Import Code Snippets
### Basic Code Snippet

@ -84,6 +84,7 @@ describe('Table of Contents', () => {
"Multiple single lines, ranges",
"Comment Highlight",
"Line Numbers",
"Title Bar",
"Import Code Snippets",
"Basic Code Snippet",
"Specify Region",
@ -213,6 +214,16 @@ describe('Line Numbers', () => {
})
})
describe('Title bar', () => {
test('render title bar', async () => {
const div = page.locator('#title-bar + div')
const titleBar = div.locator('.title-bar')
expect(titleBar).toBeTruthy()
const titleText = titleBar.locator('.title-text')
expect(await titleText.textContent()).toBe('main.js')
})
})
describe('Import Code Snippets', () => {
test('basic', async () => {
const lines = page.locator('#basic-code-snippet + div code > span')

@ -619,6 +619,24 @@ const line3 = 'This is line 3'
const line4 = 'This is line 4'
```
## Title Bar
You can add a title bar to your code blocks by adding `[title]` in the fenced code block:
**Input**
````md
```js [main.js]
console.log('Hello, VitePress!')
```
````
**Output**
```js [main.js]
console.log('Hello, VitePress!')
```
## Import Code Snippets
You can import code snippets from existing files via following syntax:

@ -0,0 +1,23 @@
diff --git a/dist/index.js b/dist/index.js
index d607382406f19def4a6a6472c6143ff56f1db205..ed525461280b730d40be93291d17fa05477eb24b 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -101,7 +101,7 @@ function groupIconMdPlugin(md, options) {
});
};
const fenceRule = md.renderer.rules.fence;
- if (fenceRule) md.renderer.rules.fence = (...args) => {
+ if (false) md.renderer.rules.fence = (...args) => {
const [tokens, idx] = args;
const token = tokens[idx];
let isOnCodeGroup = false;
@@ -133,7 +133,8 @@ function groupIconMdPlugin(md, options) {
async function generateCSS(labels, options) {
const baseCSS = `
.vp-code-block-title [data-title]::before,
-.vp-code-group [data-title]::before {
+.vp-code-group [data-title]::before,
+.vp-doc [class*='language-'] [data-title]::before {
display: inline-block;
width: 1em;
height: 1em;

@ -15,6 +15,9 @@ patchedDependencies:
markdown-it-anchor@9.2.0:
hash: cdc28e7c329be30688ad192126ba505446611fbe526ad51483e4b1287aa35cf9
path: patches/markdown-it-anchor@9.2.0.patch
vitepress-plugin-group-icons:
hash: 771e35a6827c2547da230d52d3b7032b1d1d75926162c95384f77e5528659991
path: patches/vitepress-plugin-group-icons.patch
importers:
@ -335,7 +338,7 @@ importers:
version: link:..
vitepress-plugin-group-icons:
specifier: ^1.6.3
version: 1.6.3(@types/node@24.3.0)(esbuild@0.25.9)(jiti@1.21.7)(markdown-it@14.1.0)(yaml@2.8.1)
version: 1.6.3(patch_hash=771e35a6827c2547da230d52d3b7032b1d1d75926162c95384f77e5528659991)(@types/node@24.3.0)(esbuild@0.25.9)(jiti@1.21.7)(markdown-it@14.1.0)(yaml@2.8.1)
vitepress-plugin-llms:
specifier: ^1.7.3
version: 1.7.3
@ -6011,7 +6014,7 @@ snapshots:
- tsx
- yaml
vitepress-plugin-group-icons@1.6.3(@types/node@24.3.0)(esbuild@0.25.9)(jiti@1.21.7)(markdown-it@14.1.0)(yaml@2.8.1):
vitepress-plugin-group-icons@1.6.3(patch_hash=771e35a6827c2547da230d52d3b7032b1d1d75926162c95384f77e5528659991)(@types/node@24.3.0)(esbuild@0.25.9)(jiti@1.21.7)(markdown-it@14.1.0)(yaml@2.8.1):
dependencies:
'@iconify-json/logos': 1.2.9
'@iconify-json/vscode-icons': 1.2.30

@ -14,6 +14,7 @@ overrides:
patchedDependencies:
'@types/mdurl@2.0.0': patches/@types__mdurl@2.0.0.patch
markdown-it-anchor@9.2.0: patches/markdown-it-anchor@9.2.0.patch
vitepress-plugin-group-icons: patches/vitepress-plugin-group-icons.patch
autoInstallPeers: false
shellEmulator: true

@ -426,6 +426,32 @@
border-color 0.5s,
color 0.5s;
}
.vp-doc [class*='language-'] > .title-bar {
position: relative;
display: flex;
margin-right: -24px;
margin-left: -24px;
padding: 0 12px;
background-color: var(--vp-code-block-title-bg);
box-shadow: inset 0 -1px var(--vp-code-block-divider-color);
}
@media (min-width: 640px) {
.vp-doc [class*='language-'] > .title-bar {
margin-right: 0;
margin-left: 0;
border-radius: 8px 8px 0 0;
}
}
.vp-doc [class*='language-'] > .title-bar > .title-text {
padding: 0 12px;
line-height: 48px;
font-size: 14px;
font-weight: 500;
color: var(--vp-code-block-title-color);
white-space: nowrap;
}
.vp-doc [class*='language-'] > button.copy {
/*rtl:ignore*/
@ -452,6 +478,10 @@
opacity 0.25s;
}
.vp-doc [class*='language-'] > .title-bar ~ button.copy {
top: 60px;
}
.vp-doc [class*='language-']:hover > button.copy,
.vp-doc [class*='language-'] > button.copy:focus {
opacity: 1;
@ -512,6 +542,10 @@
opacity 0.4s;
}
.vp-doc [class*='language-'] > .title-bar ~ span.lang {
top: 50px;
}
.vp-doc [class*='language-']:hover > button.copy + span.lang,
.vp-doc [class*='language-'] > button.copy:focus + span.lang {
opacity: 0;

@ -339,6 +339,8 @@
--vp-code-block-color: var(--vp-c-text-2);
--vp-code-block-bg: var(--vp-c-bg-alt);
--vp-code-block-divider-color: var(--vp-c-gutter);
--vp-code-block-title-color: var(--vp-c-text-2);
--vp-code-block-title-bg: var(--vp-code-block-bg);
--vp-code-lang-color: var(--vp-c-text-2);

@ -83,7 +83,7 @@ function createCodeGroup(md: MarkdownItAsync): ContainerArgs {
) {
const title = extractTitle(
isHtml ? tokens[i].content : tokens[i].info,
isHtml
{ html: isHtml, fallbackToLang: true }
)
if (title) {

@ -1,4 +1,5 @@
import type { MarkdownItAsync } from 'markdown-it-async'
import type Token from 'markdown-it/lib/token.mjs'
export interface Options {
codeCopyButtonTitle: string
@ -16,7 +17,12 @@ export function preWrapperPlugin(md: MarkdownItAsync, options: Options) {
const [tokens, idx] = args
const token = tokens[idx]
// remove title from info
// @ts-ignore
const isFromSnippet = !!token.src
const title =
isFromSnippet || isInCodeGroup(tokens, idx)
? ''
: extractTitle(token.info)
token.info = token.info.replace(/\[.*\]/, '')
const active = / active( |$)/.test(token.info) ? ' active' : ''
@ -27,21 +33,34 @@ export function preWrapperPlugin(md: MarkdownItAsync, options: Options) {
return (
`<div class="language-${lang}${active}">` +
(title
? `<div class="title-bar">` +
`<span class="title-text" data-title="${md.utils.escapeHtml(title)}">${title}</span>` +
`</div>`
: '') +
`<button title="${options.codeCopyButtonTitle}" class="copy"></button>` +
`<span class="lang">${label}</span>` +
fence(...args) +
'</div>'
`</div>`
)
}
}
export function extractTitle(info: string, html = false) {
if (html) {
export interface ExtractTitleOptions {
html?: boolean
fallbackToLang?: boolean
}
export function extractTitle(info: string, options?: ExtractTitleOptions) {
if (options?.html) {
return (
info.replace(/<!--[^]*?-->/g, '').match(/data-title="(.*?)"/)?.[1] || ''
)
}
return info.match(/\[(.*)\]/)?.[1] || extractLang(info) || 'txt'
return (
info.match(/\[(.*)\]/)?.[1] ||
(options?.fallbackToLang ? extractLang(info) || 'txt' : '')
)
}
function extractLang(info: string) {
@ -53,3 +72,18 @@ function extractLang(info: string) {
.replace(/^vue-html$/, 'template')
.replace(/^ansi$/, '')
}
/**
* Whether the `idx` within `tokens` is inside a code-group container
*/
function isInCodeGroup(tokens: Token[], idx: number): boolean {
for (let i = idx - 1; i >= 0; --i) {
if (tokens[i].type === 'container_code-group_open') {
return true
}
if (tokens[i].type === 'container_code-group_close') {
return false
}
}
return false
}

Loading…
Cancel
Save