pull/5012/merge
Tri Rahmat Gunadi 5 days ago committed by GitHub
commit e21d5631bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -173,6 +173,76 @@ export default config
::: :::
### Group Name Basic
::: code-group name=installs
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
:::
### Group Name Second Instance (Same Name for Sync Test)
::: code-group name=installs
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
:::
### Group Name with Hyphens and Underscores
::: code-group name=install_methods-v2
```bash [npm]
npm install vitepress@next
```
```bash [pnpm]
pnpm add vitepress@next
```
:::
### Group Name with Spaces (Should be Rejected)
::: code-group name="install methods"
```bash [npm]
npm install vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
### Group Name with Invalid Characters (Should be Rejected)
::: code-group name=install@methods!
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
:::
## Markdown File Inclusion ## Markdown File Inclusion
<!--@include: ./foo.md--> <!--@include: ./foo.md-->

@ -91,6 +91,11 @@ describe('Table of Contents', () => {
"Code Groups", "Code Groups",
"Basic Code Group", "Basic Code Group",
"With Other Features", "With Other Features",
"Group Name Basic",
"Group Name Second Instance (Same Name for Sync Test)",
"Group Name with Hyphens and Underscores",
"Group Name with Spaces (Should be Rejected)",
"Group Name with Invalid Characters (Should be Rejected)",
"Markdown File Inclusion", "Markdown File Inclusion",
"Region", "Region",
"Markdown At File Inclusion", "Markdown At File Inclusion",
@ -277,6 +282,91 @@ describe('Code Groups', () => {
await getClassList(blocks.nth(1).locator('code > span').nth(0)) await getClassList(blocks.nth(1).locator('code > span').nth(0))
).toContain('highlighted') ).toContain('highlighted')
}) })
test('group-name basic', async () => {
const div = page.locator('#group-name-basic + div')
// Verify data attribute exists
const groupName = await div.getAttribute('data-group-name')
expect(groupName).toBe('installs')
// Verify tabs still work
const labels = div.locator('.tabs > label')
expect(await labels.count()).toBe(2)
// Verify clicking still switches tabs
await labels.nth(1).click()
const blocks = div.locator('.blocks > div')
expect(await getClassList(blocks.nth(1))).toContain('active')
})
test('group-name synchronization across groups', async () => {
// Clear localStorage to ensure clean test state
await page.evaluate(() => localStorage.clear())
await page.reload()
await page.waitForSelector('#group-name-basic + div')
const div1 = page.locator('#group-name-basic + div')
const div2 = page.locator(
'#group-name-second-instance-same-name-for-sync-test + div'
)
// Both groups should have the same group-name
expect(await div1.getAttribute('data-group-name')).toBe('installs')
expect(await div2.getAttribute('data-group-name')).toBe('installs')
// Initially, both should have first tab active
expect(await getClassList(div1.locator('.blocks > div').nth(0))).toContain(
'active'
)
expect(await getClassList(div2.locator('.blocks > div').nth(0))).toContain(
'active'
)
// Click second tab in first group
await div1.locator('.tabs > label').nth(1).click()
// Both groups should now have second tab active (synced)
expect(await getClassList(div1.locator('.blocks > div').nth(1))).toContain(
'active'
)
expect(await getClassList(div2.locator('.blocks > div').nth(1))).toContain(
'active'
)
// Click first tab in second group
await div2.locator('.tabs > label').nth(0).click()
// Both groups should now have first tab active again (synced back)
expect(await getClassList(div1.locator('.blocks > div').nth(0))).toContain(
'active'
)
expect(await getClassList(div2.locator('.blocks > div').nth(0))).toContain(
'active'
)
})
test('group-name with hyphens and underscores', async () => {
const div = page.locator('#group-name-with-hyphens-and-underscores + div')
const groupName = await div.getAttribute('data-group-name')
expect(groupName).toBe('install_methods-v2')
})
test('group-name with spaces should be rejected', async () => {
const div = page.locator('#group-name-with-spaces-should-be-rejected + div')
const groupName = await div.getAttribute('data-group-name')
// Quoted names with spaces should be rejected
expect(groupName).toBeNull()
})
test('group-name with invalid characters should be rejected', async () => {
const div = page.locator(
'#group-name-with-invalid-characters-should-be-rejected + div'
)
const groupName = await div.getAttribute('data-group-name')
// Should be rejected due to invalid characters
expect(groupName).toBeNull()
})
}) })
describe('Markdown File Inclusion', () => { describe('Markdown File Inclusion', () => {

@ -776,6 +776,102 @@ You can also [import snippets](#import-code-snippets) in code groups:
::: :::
### Named Code Groups
You can name code groups to synchronize tab selections across multiple groups. When you have multiple code groups with the same name, selecting a tab in one will automatically select the corresponding tab in all other groups with the same name.
**Input**
````md
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
<!-- Later in the same domain: -->
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
````
When you click on a tab (e.g., "pnpm") in one group, all other groups with `name=package-managers` will automatically switch to the same tab.
**Output**
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
Try clicking different tabs above! Notice how both code groups switch together because they share the same `name`.
::: info
Your tab selection is automatically saved to localStorage. When you return to the page, your preferred tab for each name will be automatically selected.
:::
The `name` parameter accepts only alphanumeric characters, hyphens, and underscores. No whitespace is allowed.
Valid examples:
- `name=installs`
- `name=install-methods`
- `name=install_methods`
- `name=installMethods`
::: tip
This feature is especially useful in documentation where you show the same tool (like package managers or programming languages) in multiple places, providing a consistent experience for users.
:::
## Markdown File Inclusion ## Markdown File Inclusion
You can include a markdown file in another markdown file, even nested. You can include a markdown file in another markdown file, even nested.

@ -756,6 +756,104 @@ También puede [importar _snippets_ de código](#import-code-snippets) en grupos
::: :::
<!-- TODO: Translate to Spanish -->
### Named Code Groups
You can name code groups to synchronize tab selections across multiple groups. When you have multiple code groups with the same name, selecting a tab in one will automatically select the corresponding tab in all other groups with the same name.
**Input**
````md
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
<!-- Later in the same domain: -->
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
````
When you click on a tab (e.g., "pnpm") in one group, all other groups with `name=package-managers` will automatically switch to the same tab.
**Output**
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
Try clicking different tabs above! Notice how both code groups switch together because they share the same `name`.
::: info
Your tab selection is automatically saved to localStorage. When you return to the page, your preferred tab for each name will be automatically selected.
:::
The `name` parameter accepts only alphanumeric characters, hyphens, and underscores. No whitespace is allowed.
Valid examples:
- `name=installs`
- `name=install-methods`
- `name=install_methods`
- `name=installMethods`
::: tip
This feature is especially useful in documentation where you show the same tool (like package managers or programming languages) in multiple places, providing a consistent experience for users.
:::
<!-- END TODO: Translate to Spanish -->
## Inclusión de Archivo Markdown {#markdown-file-inclusion} ## Inclusión de Archivo Markdown {#markdown-file-inclusion}
Puede incluir un archivo markdown en otro archvo markdown, incluso anidado. Puede incluir un archivo markdown en otro archvo markdown, incluso anidado.

@ -707,6 +707,104 @@ export default config
<<< @/snippets/snippet-with-region.js#snippet{1,2 ts:line-numbers} [قطعه با منطقه] <<< @/snippets/snippet-with-region.js#snippet{1,2 ts:line-numbers} [قطعه با منطقه]
::: :::
<!-- TODO: Translate to Farsi -->
### Named Code Groups
You can name code groups to synchronize tab selections across multiple groups. When you have multiple code groups with the same name, selecting a tab in one will automatically select the corresponding tab in all other groups with the same name.
**Input**
````md
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
<!-- Later in the same domain: -->
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
````
When you click on a tab (e.g., "pnpm") in one group, all other groups with `name=package-managers` will automatically switch to the same tab.
**Output**
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
Try clicking different tabs above! Notice how both code groups switch together because they share the same `name`.
::: info
Your tab selection is automatically saved to localStorage. When you return to the page, your preferred tab for each name will be automatically selected.
:::
The `name` parameter accepts only alphanumeric characters, hyphens, and underscores. No whitespace is allowed.
Valid examples:
- `name=installs`
- `name=install-methods`
- `name=install_methods`
- `name=installMethods`
::: tip
This feature is especially useful in documentation where you show the same tool (like package managers or programming languages) in multiple places, providing a consistent experience for users.
:::
<!-- END TODO: Translate to Farsi -->
## ادغام فایل‌های Markdown {#markdown-file-inclusion} ## ادغام فایل‌های Markdown {#markdown-file-inclusion}
می‌توانید یک فایل Markdown را در یک فایل Markdown دیگر، حتی در صورت وجود تو در تو، وارد کنید. می‌توانید یک فایل Markdown را در یک فایل Markdown دیگر، حتی در صورت وجود تو در تو، وارد کنید.

@ -781,6 +781,104 @@ export default {
::: :::
<!-- TODO: Translate to Japanese -->
### Named Code Groups
You can name code groups to synchronize tab selections across multiple groups. When you have multiple code groups with the same name, selecting a tab in one will automatically select the corresponding tab in all other groups with the same name.
**Input**
````md
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
<!-- Later in the same domain: -->
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
````
When you click on a tab (e.g., "pnpm") in one group, all other groups with `name=package-managers` will automatically switch to the same tab.
**Output**
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
Try clicking different tabs above! Notice how both code groups switch together because they share the same `name`.
::: info
Your tab selection is automatically saved to localStorage. When you return to the page, your preferred tab for each name will be automatically selected.
:::
The `name` parameter accepts only alphanumeric characters, hyphens, and underscores. No whitespace is allowed.
Valid examples:
- `name=installs`
- `name=install-methods`
- `name=install_methods`
- `name=installMethods`
::: tip
This feature is especially useful in documentation where you show the same tool (like package managers or programming languages) in multiple places, providing a consistent experience for users.
:::
<!-- END TODO: Translate to Japanese -->
## Markdown ファイルのインクルード {#markdown-file-inclusion} ## Markdown ファイルのインクルード {#markdown-file-inclusion}
ある Markdown ファイルの中に、別の Markdown ファイルを取り込めます(入れ子も可能)。 ある Markdown ファイルの中に、別の Markdown ファイルを取り込めます(入れ子も可能)。

@ -754,6 +754,104 @@ export default config
::: :::
<!-- TODO: Translate to Korean -->
### Named Code Groups
You can name code groups to synchronize tab selections across multiple groups. When you have multiple code groups with the same name, selecting a tab in one will automatically select the corresponding tab in all other groups with the same name.
**Input**
````md
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
<!-- Later in the same domain: -->
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
````
When you click on a tab (e.g., "pnpm") in one group, all other groups with `name=package-managers` will automatically switch to the same tab.
**Output**
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
Try clicking different tabs above! Notice how both code groups switch together because they share the same `name`.
::: info
Your tab selection is automatically saved to localStorage. When you return to the page, your preferred tab for each name will be automatically selected.
:::
The `name` parameter accepts only alphanumeric characters, hyphens, and underscores. No whitespace is allowed.
Valid examples:
- `name=installs`
- `name=install-methods`
- `name=install_methods`
- `name=installMethods`
::: tip
This feature is especially useful in documentation where you show the same tool (like package managers or programming languages) in multiple places, providing a consistent experience for users.
:::
<!-- END TODO: Translate to Korean -->
## 마크다운 파일 포함 {#markdown-file-inclusion} ## 마크다운 파일 포함 {#markdown-file-inclusion}
마크다운 파일을 다른 마크다운 파일에 포함시킬 수 있으며, 중첩도 가능합니다. 마크다운 파일을 다른 마크다운 파일에 포함시킬 수 있으며, 중첩도 가능합니다.

@ -754,6 +754,104 @@ Você também pode [importar _snippets_ de código](#import-code-snippets) em gr
::: :::
<!-- TODO: Translate to Portuguese -->
### Named Code Groups
You can name code groups to synchronize tab selections across multiple groups. When you have multiple code groups with the same name, selecting a tab in one will automatically select the corresponding tab in all other groups with the same name.
**Input**
````md
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
<!-- Later in the same domain: -->
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
````
When you click on a tab (e.g., "pnpm") in one group, all other groups with `name=package-managers` will automatically switch to the same tab.
**Output**
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
Try clicking different tabs above! Notice how both code groups switch together because they share the same `name`.
::: info
Your tab selection is automatically saved to localStorage. When you return to the page, your preferred tab for each name will be automatically selected.
:::
The `name` parameter accepts only alphanumeric characters, hyphens, and underscores. No whitespace is allowed.
Valid examples:
- `name=installs`
- `name=install-methods`
- `name=install_methods`
- `name=installMethods`
::: tip
This feature is especially useful in documentation where you show the same tool (like package managers or programming languages) in multiple places, providing a consistent experience for users.
:::
<!-- END TODO: Translate to Portuguese -->
## Inclusão de Arquivo Markdown {#markdown-file-inclusion} ## Inclusão de Arquivo Markdown {#markdown-file-inclusion}
Você pode incluir um arquivo markdown em outro arquivo markdown, mesmo aninhado. Você pode incluir um arquivo markdown em outro arquivo markdown, mesmo aninhado.

@ -778,6 +778,104 @@ export default config
::: :::
<!-- TODO: Translate to Russian -->
### Named Code Groups
You can name code groups to synchronize tab selections across multiple groups. When you have multiple code groups with the same name, selecting a tab in one will automatically select the corresponding tab in all other groups with the same name.
**Input**
````md
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
<!-- Later in the same domain: -->
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
````
When you click on a tab (e.g., "pnpm") in one group, all other groups with `name=package-managers` will automatically switch to the same tab.
**Output**
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
Try clicking different tabs above! Notice how both code groups switch together because they share the same `name`.
::: info
Your tab selection is automatically saved to localStorage. When you return to the page, your preferred tab for each name will be automatically selected.
:::
The `name` parameter accepts only alphanumeric characters, hyphens, and underscores. No whitespace is allowed.
Valid examples:
- `name=installs`
- `name=install-methods`
- `name=install_methods`
- `name=installMethods`
::: tip
This feature is especially useful in documentation where you show the same tool (like package managers or programming languages) in multiple places, providing a consistent experience for users.
:::
<!-- END TODO: Translate to Russian -->
## Включение файла Markdown {#markdown-file-inclusion} ## Включение файла Markdown {#markdown-file-inclusion}
Вы можете включить файл Markdown в другой файл Markdown, даже вложенный. Вы можете включить файл Markdown в другой файл Markdown, даже вложенный.

@ -754,6 +754,104 @@ export default config
::: :::
<!-- TODO: Translate to Chinese -->
### Named Code Groups
You can name code groups to synchronize tab selections across multiple groups. When you have multiple code groups with the same name, selecting a tab in one will automatically select the corresponding tab in all other groups with the same name.
**Input**
````md
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
<!-- Later in the same domain: -->
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
````
When you click on a tab (e.g., "pnpm") in one group, all other groups with `name=package-managers` will automatically switch to the same tab.
**Output**
::: code-group name=package-managers
```bash [npm]
npm install vitepress
```
```bash [pnpm]
pnpm add vitepress
```
```bash [yarn]
yarn add vitepress
```
:::
::: code-group name=package-managers
```bash [npm]
npm run docs
```
```bash [pnpm]
pnpm run docs
```
```bash [yarn]
yarn docs
```
:::
Try clicking different tabs above! Notice how both code groups switch together because they share the same `name`.
::: info
Your tab selection is automatically saved to localStorage. When you return to the page, your preferred tab for each name will be automatically selected.
:::
The `name` parameter accepts only alphanumeric characters, hyphens, and underscores. No whitespace is allowed.
Valid examples:
- `name=installs`
- `name=install-methods`
- `name=install_methods`
- `name=installMethods`
::: tip
This feature is especially useful in documentation where you show the same tool (like package managers or programming languages) in multiple places, providing a consistent experience for users.
:::
<!-- END TODO: Translate to Chinese -->
## 包含 markdown 文件 {#markdown-file-inclusion} ## 包含 markdown 文件 {#markdown-file-inclusion}
可以像这样在一个 markdown 文件中包含另一个 markdown 文件,甚至是内嵌的。 可以像这样在一个 markdown 文件中包含另一个 markdown 文件,甚至是内嵌的。

@ -1,9 +1,41 @@
import { inBrowser, onContentUpdated } from 'vitepress' import { inBrowser, onContentUpdated } from 'vitepress'
const STORAGE_KEY = 'vitepress:tabsCache'
function getStoredTabIndex(groupName: string): number | null {
if (!inBrowser) return null
try {
const cache = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}')
return cache[groupName] ?? null
} catch {
return null
}
}
function setStoredTabIndex(groupName: string, index: number) {
if (!inBrowser) return
try {
const cache = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}')
cache[groupName] = index
localStorage.setItem(STORAGE_KEY, JSON.stringify(cache))
} catch {
// Silently ignore localStorage errors
}
}
export function useCodeGroups() { export function useCodeGroups() {
if (import.meta.env.DEV) { if (import.meta.env.DEV) {
onContentUpdated(() => { onContentUpdated(() => {
document.querySelectorAll('.vp-code-group > .blocks').forEach((el) => { document.querySelectorAll('.vp-code-group > .blocks').forEach((el) => {
const group = el.closest('.vp-code-group')
const groupName = group?.getAttribute('data-group-name')
// Don't reset if user has a saved preference
if (groupName && getStoredTabIndex(groupName) !== null) {
return // Keep user's preference
}
// Only reset groups without saved preferences
Array.from(el.children).forEach((child) => { Array.from(el.children).forEach((child) => {
child.classList.remove('active') child.classList.remove('active')
}) })
@ -13,6 +45,38 @@ export function useCodeGroups() {
} }
if (inBrowser) { if (inBrowser) {
// Restore tabs from localStorage on page load, but only on first content load
let hasRestoredTabs = false
onContentUpdated(() => {
if (hasRestoredTabs) return
hasRestoredTabs = true
document
.querySelectorAll('.vp-code-group[data-group-name]')
.forEach((group) => {
const groupName = group.getAttribute('data-group-name')
if (!groupName) return
const storedIndex = getStoredTabIndex(groupName)
if (storedIndex === null) return
const inputs = group.querySelectorAll('input')
const blocks = group.querySelector('.blocks')
if (!blocks || !inputs[storedIndex]) return
// Update radio input
inputs[storedIndex].checked = true
// Update active block
const currentActive = blocks.querySelector('.active')
const newActive = blocks.children[storedIndex]
if (currentActive && newActive && currentActive !== newActive) {
currentActive.classList.remove('active')
newActive.classList.add('active')
}
})
})
window.addEventListener('click', (e) => { window.addEventListener('click', (e) => {
const el = e.target as HTMLInputElement const el = e.target as HTMLInputElement
@ -24,6 +88,7 @@ export function useCodeGroups() {
const i = Array.from(group.querySelectorAll('input')).indexOf(el) const i = Array.from(group.querySelectorAll('input')).indexOf(el)
if (i < 0) return if (i < 0) return
// Update current group
const blocks = group.querySelector('.blocks') const blocks = group.querySelector('.blocks')
if (!blocks) return if (!blocks) return
@ -40,11 +105,49 @@ export function useCodeGroups() {
const label = group?.querySelector(`label[for="${el.id}"]`) const label = group?.querySelector(`label[for="${el.id}"]`)
label?.scrollIntoView({ block: 'nearest' }) label?.scrollIntoView({ block: 'nearest' })
// Sync other groups with same group-name and save to localStorage
const groupName = group.getAttribute('data-group-name')
if (groupName) {
setStoredTabIndex(groupName, i)
syncTabsInOtherGroups(groupName, i, group as HTMLElement)
}
} }
}) })
} }
} }
function syncTabsInOtherGroups(
groupName: string,
tabIndex: number,
currentGroup: HTMLElement
) {
// Find all code groups with the same group-name
const groups = document.querySelectorAll(
`.vp-code-group[data-group-name="${groupName}"]`
)
groups.forEach((g) => {
// Skip the current group that was clicked
if (g === currentGroup) return
const inputs = g.querySelectorAll('input')
const blocks = g.querySelector('.blocks')
if (!blocks || !inputs[tabIndex]) return
// Update radio input
inputs[tabIndex].checked = true
// Update active block
const currentActive = blocks.querySelector('.active')
const newActive = blocks.children[tabIndex]
if (currentActive && newActive && currentActive !== newActive) {
currentActive.classList.remove('active')
newActive.classList.add('active')
}
})
}
function activate(el: Element): void { function activate(el: Element): void {
el.classList.add('active') el.classList.add('active')
window.dispatchEvent( window.dispatchEvent(

@ -64,6 +64,23 @@ function createCodeGroup(md: MarkdownItAsync): ContainerArgs {
{ {
render(tokens, idx) { render(tokens, idx) {
if (tokens[idx].nesting === 1) { if (tokens[idx].nesting === 1) {
const token = tokens[idx]
const info = token.info.trim()
// Extract name parameter
const nameMatch = info.match(/name=(\S+)/)
let name = nameMatch ? nameMatch[1] : null
// Validate: only allow alphanumeric, hyphens, and underscores
if (name && !/^[a-zA-Z0-9_-]+$/.test(name)) {
name = null
}
// Build data attribute
const nameAttr = name
? ` data-group-name="${md.utils.escapeHtml(name)}"`
: ''
let tabs = '' let tabs = ''
let checked = 'checked' let checked = 'checked'
@ -95,7 +112,7 @@ function createCodeGroup(md: MarkdownItAsync): ContainerArgs {
} }
} }
return `<div class="vp-code-group"><div class="tabs">${tabs}</div><div class="blocks">\n` return `<div class="vp-code-group"${nameAttr}><div class="tabs">${tabs}</div><div class="blocks">\n`
} }
return `</div></div>\n` return `</div></div>\n`
} }

Loading…
Cancel
Save