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

@ -132,6 +132,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",
@ -215,6 +216,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')

@ -745,6 +745,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.mjs b/dist/index.mjs
index 7146607f6eb82fe3e58634d0be5acfaa929cf526..a1cdfa79be761a0efc9880a35ec12016b7aea837 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -200,7 +200,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;
@@ -232,7 +232,8 @@ const HTTP_URL_RE = /^https?:\/\//;
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;

@ -8,6 +8,7 @@ overrides:
esbuild: '-'
patchedDependencies:
vitepress-plugin-group-icons: c379c3737bf09a62f52fdff21e0d71647ab2bdd813a34ac32a4d8aa1edca0e7f
vue-sfc-transformer: 06dbce7d98fac77faf5e66a94aa6e04f34c0cc7ec0910ec10848e5f2e08b8b68
importers:
@ -308,7 +309,7 @@ importers:
version: link:..
vitepress-plugin-group-icons:
specifier: ^1.7.6
version: 1.7.6(vite@8.2.1(@types/node@26.2.0)(jiti@1.21.7)(yaml@2.9.0))
version: 1.7.6(patch_hash=c379c3737bf09a62f52fdff21e0d71647ab2bdd813a34ac32a4d8aa1edca0e7f)(vite@8.2.1(@types/node@26.2.0)(jiti@1.21.7)(yaml@2.9.0))
vitepress-plugin-llms:
specifier: ^1.13.4
version: 1.13.4(supports-color@7.2.0)
@ -5082,7 +5083,7 @@ snapshots:
jiti: 1.21.7
yaml: 2.9.0
vitepress-plugin-group-icons@1.7.6(vite@8.2.1(@types/node@26.2.0)(jiti@1.21.7)(yaml@2.9.0)):
vitepress-plugin-group-icons@1.7.6(patch_hash=c379c3737bf09a62f52fdff21e0d71647ab2bdd813a34ac32a4d8aa1edca0e7f)(vite@8.2.1(@types/node@26.2.0)(jiti@1.21.7)(yaml@2.9.0)):
dependencies:
'@iconify-json/logos': 1.2.12
'@iconify-json/vscode-icons': 1.2.70

@ -17,6 +17,7 @@ overrides:
patchedDependencies:
vue-sfc-transformer: patches/vue-sfc-transformer.patch
vitepress-plugin-group-icons: patches/vitepress-plugin-group-icons.patch
shellEmulator: true

@ -495,6 +495,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*/
@ -521,6 +547,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;
@ -581,6 +611,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;

@ -367,6 +367,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);

@ -172,7 +172,7 @@ function createCodeGroupOpenRender(md: MarkdownItAsync): RenderRule {
if ((tokens[i].type === 'fence' && tokens[i].tag === 'code') || isHtml) {
const title = extractTitle(
isHtml ? tokens[i].content : tokens[i].info,
isHtml
{ html: isHtml, fallbackToLang: true }
)
if (title) {

@ -1,5 +1,5 @@
import type { MarkdownItAsync } from 'markdown-it-async'
import type Token from 'markdown-it/lib/token.mjs'
import type { MarkdownEnv, MarkdownLocaleOptions } from '../../shared'
export interface Options {
@ -22,7 +22,12 @@ export function preWrapperPlugin(md: MarkdownItAsync, options: Options) {
const [tokens, idx, , env] = 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' : ''
@ -43,6 +48,11 @@ 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="${tooltipText}" data-copied="${copiedText}" class="copy"></button>` +
`<span class="lang">${label}</span>` +
fence(...args) +
@ -51,13 +61,21 @@ export function preWrapperPlugin(md: MarkdownItAsync, options: Options) {
}
}
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): string {
@ -69,3 +87,18 @@ function extractLang(info: string): string {
.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