feat(plugin-md-power): rename demo-wrapper container to window (#858)

This commit is contained in:
pengzhanbo 2026-02-25 21:48:39 +08:00 committed by GitHub
parent f7d3546962
commit ce32605aee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 454 additions and 321 deletions

View File

@ -81,7 +81,7 @@ content right
**demo wrapper**
::: demo-wrapper title="Demo" no-padding height="200px"
::: window title="Demo" height="200px"
<style scoped>
.open-door {
display: flex;

View File

@ -95,9 +95,9 @@ H~2~O
- vscode - <Icon name="skill-icons:vscode-dark" size="2em" />
- twitter - <Icon name="skill-icons:twitter" size="2em" />
**demo wrapper**
**示例容器**
::: demo-wrapper title="示例" no-padding height="200px"
::: window title="示例" height="200px"
<style scoped>
.open-door {
display: flex;

View File

@ -102,9 +102,9 @@ H~2~O
- vscode - <Icon name="skill-icons:vscode-dark" size="2em" />
- twitter - <Icon name="skill-icons:twitter" size="2em" />
**demo wrapper**
**示例容器**
::: demo-wrapper title="示例" no-padding height="200px"
::: window title="示例" height="200px"
<style scoped>
.open-door {
display: flex;

View File

@ -129,8 +129,7 @@ config:
**Result:**
:::demo-wrapper img no-padding
::: window
![banner](/images/custom-banner.jpg)
:::
@ -246,7 +245,7 @@ config:
**Result:**
:::demo-wrapper img no-padding
::: window
<img src="/images/custom-doc-hero.jpg" alt="Theme Plume" />
:::
@ -359,7 +358,7 @@ config:
**Result:**
:::demo-wrapper img no-padding
:::window
<img src="/images/custom-features.jpg" alt="custom-features" />
:::
@ -432,11 +431,11 @@ config:
**Result:**
:::demo-wrapper img no-padding
:::window
<img src="/images/custom-image-text.jpg" alt="image-text" />
:::
:::demo-wrapper img no-padding
:::window
<img src="/images/custom-text-image.jpg" alt="text-image" />
:::
@ -505,7 +504,7 @@ config:
**Result:**
:::demo-wrapper img no-padding
:::window
<img src="/images/custom-profile.jpg" alt="profile" />
:::
@ -551,7 +550,7 @@ yarn add vuepress@next vuepress-theme-plume
**Result:**
:::demo-wrapper img no-padding
:::window
<img src="/images/custom-content.jpg" alt="content" />
:::

View File

@ -1,9 +1,12 @@
---
title: Demo Container
createTime: 2025/10/08 14:47:12
icon: icon-park-outline:eyes
permalink: /en/guide/markdown/demo-wrapper/
icon: fxemoji:window
permalink: /en/guide/markdown/window/
outline: 2
badge:
text: Change
type: warning
---
## Overview
@ -14,7 +17,7 @@ separately from other content. The theme supports adding demo containers in Mark
## Syntax
````md
::: demo-wrapper
::: window
Add your demo here
:::
````
@ -31,20 +34,20 @@ Add your demo here
Containing only images:
```md
::: demo-wrapper img no-padding
::: window
![hero](/images/custom-hero.jpg)
:::
```
**Output:**
::: demo-wrapper img no-padding
::: window
![hero](/images/custom-hero.jpg)
:::
Containing markdown syntax:
```md
::: demo-wrapper title="Title"
::: window title="Title"
### Level 3 Heading
This is content inside the demo container.
@ -52,7 +55,7 @@ This is content inside the demo container.
```
**Output:**
::: demo-wrapper title="Title"
::: window title="Title"
### Level 3 Heading
@ -62,7 +65,7 @@ This is content inside the demo container.
Containing HTML/Vue code:
```md
::: demo-wrapper
::: window
<h1 class="your-demo-title">This is a heading</h1>
<p class="your-demo-paragraph">This is a paragraph</p>
@ -78,7 +81,7 @@ Containing HTML/Vue code:
```
**Output:**
::: demo-wrapper
::: window
<h1 class="your-demo-title">This is a heading</h1>
<p class="your-demo-paragraph">This is a paragraph</p>

View File

@ -144,18 +144,18 @@ Included via `<!-- @include: ./foo.snippet.md#snippet -->`.
Using `<!-- @include: ./foo.snippet.md -->` to include the entire file:
:::: demo-wrapper title="Include by file"
:::: window title="Include by file"
<!-- @include: ../../snippet/include-2.snippet.md -->
::::
Using `<!-- @include: ./foo.snippet.md{5-7} -->` to include lines 5-7 of the file:
:::: demo-wrapper title="Include by lines"
:::: window title="Include by lines"
<!-- @include: ../../snippet/include-2.snippet.md{5-7} -->
::::
Using `<!-- @include: ./foo.snippet.md#snippet -->` to include the `snippet` region:
:::: demo-wrapper title="Include by file region"
:::: window title="Include by file region"
<!-- @include: ../../snippet/include-2.snippet.md#snippet -->
::::

View File

@ -12,7 +12,7 @@ This could be to pique readers' curiosity or simply to add a bit of reading diff
To satisfy this playful intention, the theme provides a fun little feature called **"plot" text**. It looks like this:
:::demo-wrapper
:::window
Did you know that !!Lu Xun!! once said: "!!I never said this!!!" It was an enlightening revelation that deeply
inspired me, filling me with unparalleled strength! So, !!I turned over in bed!!!
:::
@ -102,7 +102,7 @@ Did you know that !!Lu Xun!! once said: "!!I never said this!!!" It was an enlig
**Output**:
:::demo-wrapper
:::window
Did you know that !!Lu Xun!! once said: "!!I never said this!!!" It was an enlightening revelation that
deeply inspired me, filling me with unparalleled strength! So, !!I turned over in bed!!!!
:::
@ -118,7 +118,7 @@ Blur effect + click: !!Click to see me!!{.blur .click}
**Output**:
:::demo-wrapper
:::window
Mask effect + hover: !!Hover to see me!!{.mask .hover}
Mask effect + click: !!Click to see me!!{.mask .click}

View File

@ -128,7 +128,7 @@ config:
**效果:**
:::demo-wrapper img no-padding
:::window
![banner](/images/custom-banner.jpg)
:::
@ -245,7 +245,7 @@ config:
**效果:**
:::demo-wrapper img no-padding
:::window
<img src="/images/custom-doc-hero.jpg" alt="Theme Plume" />
:::
@ -358,7 +358,7 @@ config:
**效果:**
:::demo-wrapper img no-padding
:::window
<img src="/images/custom-features.jpg" alt="custom-features" />
:::
@ -431,11 +431,11 @@ config:
**效果:**
:::demo-wrapper img no-padding
:::window
<img src="/images/custom-image-text.jpg" alt="image-text" />
:::
:::demo-wrapper img no-padding
:::window
<img src="/images/custom-text-image.jpg" alt="text-image" />
:::
@ -504,7 +504,7 @@ config:
**效果:**
:::demo-wrapper img no-padding
:::window
<img src="/images/custom-profile.jpg" alt="profile" />
:::
@ -550,7 +550,7 @@ yarn add vuepress@next vuepress-theme-plume
**效果:**
:::demo-wrapper img no-padding
:::window
<img src="/images/custom-content.jpg" alt="content" />
:::

View File

@ -1,20 +1,23 @@
---
title: 示例容器
createTime: 2024/09/30 14:47:12
icon: icon-park-outline:eyes
permalink: /guide/markdown/demo-wrapper/
icon: fxemoji:window
permalink: /guide/markdown/window/
outline: 2
badge:
text: 变更
type: warning
---
## 概述
有时候,你可能需要在 内容中补充一些 示例,但期望能与 其它内容 分隔开来呈现。
主题支持在 Markdown 文件中添加示例容器。
主题支持在 Markdown 文件中添加示例窗口容器。
## 语法
````md
::: demo-wrapper
::: window
添加你的示例
:::
````
@ -22,29 +25,28 @@ outline: 2
## 选项
- `title="xxx"`:标题
- `no-padding`:不添加内边距
- `img`: 仅包含图片时使用
- `height="xxx"`: 高度
- `height="200px"`: 高度
- `gap="20px"`: 左右内边距
## 示例
仅包含图片:
```md
::: demo-wrapper img no-padding
::: window
![hero](/images/custom-hero.jpg)
:::
```
**输出:**
::: demo-wrapper img no-padding
::: window
![hero](/images/custom-hero.jpg)
:::
包含 markdown 语法:
```md
::: demo-wrapper title="标题"
::: window title="标题"
### 三级标题
这是示例容器中的内容。
@ -52,7 +54,7 @@ outline: 2
```
**输出:**
::: demo-wrapper title="标题"
::: window title="标题"
### 三级标题
@ -62,8 +64,8 @@ outline: 2
包含 html /vue 代码:
```md
::: demo-wrapper
<h1 class="your-demo-title">这是标题</h1>
::: window
<h2 class="your-demo-title">这是标题</h2>
<p class="your-demo-paragraph">这是段落</p>
<style>
@ -78,9 +80,9 @@ outline: 2
```
**输出:**
::: demo-wrapper
::: window
<h1 class="your-demo-title">这是标题</h1>
<h2 class="your-demo-title">这是标题</h2>
<p class="your-demo-paragraph">这是段落</p>
<style>

View File

@ -144,18 +144,18 @@ export default defineUserConfig({
使用 `<!-- @include: ./foo.snippet.md -->` 导入文件:
:::: demo-wrapper title="Include by file"
:::: window title="Include by file"
<!-- @include: ../../snippet/include-2.snippet.md -->
::::
使用 `<!-- @include: ./foo.snippet.md{5-7} -->` 导入文件内的 5 到 7 行:
:::: demo-wrapper title="Include by lines"
:::: window title="Include by lines"
<!-- @include: ../../snippet/include-2.snippet.md{5-7} -->
::::
使用 `<!-- @include: ./foo.snippet.md#snippet -->` 导入 `snippet` 区域
:::: demo-wrapper title="Include by file region"
:::: window title="Include by file region"
<!-- @include: ../../snippet/include-2.snippet.md#snippet -->
::::

View File

@ -12,7 +12,7 @@ permalink: /guide/markdown/plot/
为了满足这种小小的心思,主题提供了一个 **“隐秘”文本** 的有趣小功能。它看起来像这样:
:::demo-wrapper
:::window
你知道吗, !!鲁迅!! 曾说过:“ !!我没说过这句话!!! ” 令我醍醐灌顶,深受启发,浑身迸发出无可匹敌的
力量!于是,!!我在床上翻了个身!!
:::
@ -102,7 +102,7 @@ plot:
**输出**
:::demo-wrapper
:::window
你知道吗, !!鲁迅!! 曾说过:“ !!我没说过这句话!!!” 令我醍醐灌顶,深受启发,浑身迸发出无可匹敌的
力量!于是,!!我在床上翻了个身!!
:::
@ -118,7 +118,7 @@ plot:
**输出**
:::demo-wrapper
:::window
遮罩层效果 + 鼠标悬停:!!鼠标悬停看到我了!!{.mask .hover}
遮罩层效果 + 点击:!!点击看到我了!!{.mask .click}

View File

@ -97,9 +97,9 @@ H~2~O
- vscode - <Icon name="skill-icons:vscode-dark" size="2em" />
- twitter - <Icon name="skill-icons:twitter" size="2em" />
**demo wrapper**
**示例容器**
::: demo-wrapper title="示例" no-padding height="200px"
::: window title="示例" height="200px"
<style scoped>
.open-door {
display: flex;

View File

@ -1,33 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`demoWrapperPlugin > should work 1`] = `
"<div class="demo-wrapper">
<div class="demo-head">
<div class="demo-ctrl"><i></i><i></i><i></i></div>
</div>
<div class="demo-container" >
<p>content</p>
</div></div><div class="demo-wrapper has-title">
<div class="demo-head">
<div class="demo-ctrl"><i></i><i></i><i></i></div>
<h4 class="demo-title"><p>test</p></h4>
</div>
<div class="demo-container" >
<p>content</p>
</div></div><div class="demo-wrapper only-img no-padding has-height">
<div class="demo-head">
<div class="demo-ctrl"><i></i><i></i><i></i></div>
</div>
<div class="demo-container" style="--demo-container-height: 100px;">
<p><a href="/img.jpg">xxx</a></p>
</div></div><div class="demo-wrapper only-img no-padding has-height">
<div class="demo-head">
<div class="demo-ctrl"><i></i><i></i><i></i></div>
</div>
<div class="demo-container" style="--demo-container-height: 100px;">
<p><a href="/img.jpg">xxx</a></p>
</div></div>"
`;

View File

@ -0,0 +1,80 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`windowPlugin > legacy demo-wrapper container 1`] = `
"<article class="window-wrapper">
<header class="window-header">
<div class="window-left"><i></i><i></i><i></i></div>
<div class="window-right"><i class="vpi-window-share"></i><i class="vpi-window-add"></i><i class="vpi-window-copy"></i></div>
</header>
<section class="window-content" style="--window-gap:20px;"><p>content</p>
</section></article><article class="window-wrapper has-title">
<header class="window-header">
<div class="window-left"><i></i><i></i><i></i></div>
<div class="window-center"><h4 class="window-title ignore-header"><span>test</span><i class="vpi-window-reload"></i></h4></div>
<div class="window-right"><i class="vpi-window-share"></i><i class="vpi-window-add"></i><i class="vpi-window-copy"></i></div>
</header>
<section class="window-content" style="--window-gap:20px;"><p>content</p>
</section></article><article class="window-wrapper">
<header class="window-header">
<div class="window-left"><i></i><i></i><i></i></div>
<div class="window-right"><i class="vpi-window-share"></i><i class="vpi-window-add"></i><i class="vpi-window-copy"></i></div>
</header>
<section class="window-content" style="--window-gap:20px;--window-height:100px"><p><img src="/img.jpg" alt="xxx"></p>
</section></article><article class="window-wrapper">
<header class="window-header">
<div class="window-left"><i></i><i></i><i></i></div>
<div class="window-right"><i class="vpi-window-share"></i><i class="vpi-window-add"></i><i class="vpi-window-copy"></i></div>
</header>
<section class="window-content" style="--window-gap:20px;--window-height:100px"><p><img src="/img.jpg" alt="xxx"></p>
</section></article>"
`;
exports[`windowPlugin > should work 1`] = `
"<article class="window-wrapper">
<header class="window-header">
<div class="window-left"><i></i><i></i><i></i></div>
<div class="window-right"><i class="vpi-window-share"></i><i class="vpi-window-add"></i><i class="vpi-window-copy"></i></div>
</header>
<section class="window-content" style="--window-gap:20px;"><p>content</p>
</section></article><article class="window-wrapper has-title">
<header class="window-header">
<div class="window-left"><i></i><i></i><i></i></div>
<div class="window-center"><h4 class="window-title ignore-header"><span>test</span><i class="vpi-window-reload"></i></h4></div>
<div class="window-right"><i class="vpi-window-share"></i><i class="vpi-window-add"></i><i class="vpi-window-copy"></i></div>
</header>
<section class="window-content" style="--window-gap:20px;"><p>content</p>
</section></article><article class="window-wrapper">
<header class="window-header">
<div class="window-left"><i></i><i></i><i></i></div>
<div class="window-right"><i class="vpi-window-share"></i><i class="vpi-window-add"></i><i class="vpi-window-copy"></i></div>
</header>
<section class="window-content" style="--window-gap:0;--window-height:100px"><img src="/img.jpg" alt="xxx"></section></article><article class="window-wrapper">
<header class="window-header">
<div class="window-left"><i></i><i></i><i></i></div>
<div class="window-right"><i class="vpi-window-share"></i><i class="vpi-window-add"></i><i class="vpi-window-copy"></i></div>
</header>
<section class="window-content" style="--window-gap:20px;--window-height:100px"><img src="/img.jpg" alt="xxx"></section></article><article class="window-wrapper">
<header class="window-header">
<div class="window-left"><i></i><i></i><i></i></div>
<div class="window-right"><i class="vpi-window-share"></i><i class="vpi-window-add"></i><i class="vpi-window-copy"></i></div>
</header>
<section class="window-content" style="--window-gap:0;"> <img src="/img.jpg" alt="xxx">
</section></article><article class="window-wrapper">
<header class="window-header">
<div class="window-left"><i></i><i></i><i></i></div>
<div class="window-right"><i class="vpi-window-share"></i><i class="vpi-window-add"></i><i class="vpi-window-copy"></i></div>
</header>
<section class="window-content" style="--window-gap:0;"> <picture>
<source srcset="/img.jpg" type="image/webp" />
<img src="/img.jpg" alt="xxx" />
</picture>
</section></article>"
`;

View File

@ -1,29 +0,0 @@
import MarkdownIt from 'markdown-it'
import { describe, expect, it } from 'vitest'
import { demoWrapperPlugin } from '../src/node/container/demoWrapper.js'
describe('demoWrapperPlugin', () => {
const md = new MarkdownIt().use(demoWrapperPlugin)
it('should work', () => {
const code = `\
::: demo-wrapper
content
:::
::: demo-wrapper title="test"
content
:::
::: demo-wrapper no-padding img height="100px"
[xxx](/img.jpg)
:::
::: demo-wrapper no-padding img height="100"
[xxx](/img.jpg)
:::
`
expect(md.render(code)).toMatchSnapshot()
})
})

View File

@ -0,0 +1,61 @@
import MarkdownIt from 'markdown-it'
import { describe, expect, it } from 'vitest'
import { windowPlugin } from '../src/node/container/window.js'
describe('windowPlugin', () => {
const md = new MarkdownIt({ html: true }).use(windowPlugin)
it('should work', () => {
const code = `\
::: window
content
:::
::: window title="test"
content
:::
::: window height="100px"
![xxx](/img.jpg)
:::
::: window height="100" gap="20px"
![xxx](/img.jpg)
:::
::: window
<img src="/img.jpg" alt="xxx">
:::
::: window
<picture>
<source srcset="/img.jpg" type="image/webp" />
<img src="/img.jpg" alt="xxx" />
</picture>
:::
`
expect(md.render(code)).toMatchSnapshot()
})
it('legacy demo-wrapper container', () => {
const code = `\
::: demo-wrapper
content
:::
::: demo-wrapper title="test"
content
:::
::: demo-wrapper height="100px"
![xxx](/img.jpg)
:::
::: demo-wrapper height="100" gap="20px"
![xxx](/img.jpg)
:::
`
expect(md.render(code)).toMatchSnapshot()
})
})

View File

@ -1,140 +0,0 @@
.vp-doc .demo-wrapper {
display: flex;
flex-direction: column;
min-height: 40px;
margin: 40px -16px;
border: solid 1px var(--vp-c-divider);
border-radius: 8px;
box-shadow: var(--vp-shadow-2);
transition: var(--vp-t-color);
transition-property: border, box-shadow;
}
.vp-doc .demo-wrapper .demo-head {
display: flex;
align-items: center;
justify-content: flex-start;
min-height: 0;
border-bottom: solid 1px var(--vp-c-divider);
transition: border-bottom var(--vp-t-color);
}
.vp-doc .demo-wrapper .demo-container {
min-height: 0;
padding: 20px;
font-size: 14px;
line-height: 22px;
background-color: var(--vp-c-bg-alt);
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
transition: background-color var(--vp-t-color);
}
.vp-doc .demo-wrapper.has-title .demo-head {
border-bottom-color: transparent;
}
.vp-doc .demo-wrapper.only-img {
overflow: hidden;
}
.vp-doc .demo-wrapper.only-img img {
display: block;
}
.vp-doc .demo-wrapper.only-img .demo-container,
.vp-doc .demo-wrapper.no-padding .demo-container {
padding: 0;
}
.vp-doc .demo-wrapper.has-height .demo-container {
height: var(--demo-container-height);
overflow-y: auto;
}
.vp-doc .demo-wrapper .demo-ctrl {
display: flex;
gap: 5px;
align-items: center;
justify-content: flex-start;
padding: 5px 0 5px 8px;
}
.vp-doc .demo-wrapper .demo-ctrl i {
display: inline-block;
width: 10px;
height: 10px;
background-color: #ccc;
border-radius: 100%;
transition: background-color var(--vp-t-color);
}
.vp-doc .demo-wrapper .demo-ctrl i:nth-child(1) {
background-color: var(--vp-c-danger-3);
}
.vp-doc .demo-wrapper .demo-ctrl i:nth-child(2) {
background-color: var(--vp-c-warning-3);
}
.vp-doc .demo-wrapper .demo-ctrl i:nth-child(3) {
background-color: var(--vp-c-green-3);
}
.vp-doc .demo-wrapper .demo-title {
position: relative;
min-width: 0;
padding: 0 16px;
margin: 0 20px -1px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-2);
background-color: var(--vp-c-bg-alt);
border-top-left-radius: 8px;
border-top-right-radius: 8px;
transition: var(--vp-t-color);
transition-property: color, background-color;
}
.vp-doc .demo-wrapper .demo-title p {
max-width: 100%;
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
text-wrap: nowrap;
}
.vp-doc .demo-wrapper .demo-title::after,
.vp-doc .demo-wrapper .demo-title::before {
position: absolute;
bottom: 0;
z-index: 1;
width: 8px;
height: 8px;
content: " ";
transition: background var(--vp-t-color);
}
.vp-doc .demo-wrapper .demo-title::before {
left: 100%;
background: radial-gradient(16px at right top, transparent 50%, var(--vp-c-bg-alt) 50%);
}
.vp-doc .demo-wrapper .demo-title::after {
right: 100%;
background: radial-gradient(16px at left top, transparent 50%, var(--vp-c-bg-alt) 50%);
}
.vp-doc .demo-wrapper .demo-container > *:first-child {
margin-top: 0;
}
.vp-doc .demo-wrapper .demo-container > *:last-child {
margin-bottom: 0;
}
@media (min-width: 419px) {
.vp-doc .demo-wrapper {
margin: 40px 0;
}
}

View File

@ -1,4 +1,4 @@
@charset "UTF-8";
@import url("./demo-wrapper.css");
@import url("./window.css");
@import url("./steps.css");

View File

@ -0,0 +1,156 @@
.window-wrapper {
display: flex;
flex-direction: column;
min-height: 40px;
margin: 16px -16px;
border: solid 1px var(--vp-c-divider);
border-radius: 8px;
box-shadow: var(--vp-shadow-2);
transition: var(--vp-t-color);
transition-property: border, box-shadow;
}
@media (min-width: 419px) {
.window-wrapper {
margin: 16px 0;
}
}
.window-wrapper .window-header {
display: flex;
gap: 8px;
align-items: center;
justify-content: space-between;
height: 28px;
background-color: rgb(241 242 243);
border-bottom: solid 1px var(--vp-c-divider);
border-top-left-radius: 8px;
border-top-right-radius: 8px;
transition: border-bottom var(--vp-t-color), background-color var(--vp-t-color);
}
[data-theme="dark"] .window-wrapper .window-header {
background-color: rgb(52 53 54);
}
.window-wrapper.has-title .window-header {
height: 36px;
}
.window-wrapper .window-left {
display: flex;
gap: 5px;
align-items: center;
justify-content: flex-start;
padding: 5px 0 5px 8px;
}
.window-wrapper .window-left i {
display: inline-block;
width: 10px;
height: 10px;
background-color: #ccc;
border-radius: 100%;
transition: background-color var(--vp-t-color);
}
.window-wrapper .window-left i:nth-child(1) {
background-color: var(--vp-c-danger-3);
}
.window-wrapper .window-left i:nth-child(2) {
background-color: var(--vp-c-warning-3);
}
.window-wrapper .window-left i:nth-child(3) {
background-color: var(--vp-c-green-3);
}
.window-wrapper .window-right {
display: flex;
gap: 5px;
align-items: center;
justify-content: flex-end;
padding-right: 8px;
color: var(--vp-c-text-3);
}
.window-wrapper .window-center {
display: flex;
flex: 1;
justify-content: center;
min-width: 0;
}
.window-wrapper .window-title {
display: inline-flex;
gap: 8px;
align-items: center;
justify-content: flex-start;
min-width: 200px;
max-width: 100%;
padding: 0 8px;
margin: 0;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-2);
border: solid 1px var(--vp-c-divider);
border-radius: 8px;
transition: border-color var(--vp-t-color);
}
@media (min-width: 640px) {
.window-wrapper .window-title {
min-width: 320px;
}
}
.window-wrapper .window-title span {
flex: 1;
max-width: 100%;
margin: 0;
overflow: hidden;
text-align: center;
text-overflow: ellipsis;
text-wrap: nowrap;
}
.window-wrapper .window-title [class*="vpi-window-"] {
color: var(--vp-c-text-3);
transition: color var(--vp-t-color);
}
.window-wrapper .window-content {
height: var(--window-height, auto);
min-height: 0;
padding: 0 var(--window-gap, 20px);
overflow: auto;
font-size: 14px;
line-height: 22px;
background-color: var(--vp-c-bg);
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
transition: background-color var(--vp-t-color);
}
.window-wrapper .window-content > img:only-child {
display: block;
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
}
.vpi-window-share {
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M11.293 2.293a1 1 0 0 1 1.414 0l3 3a1 1 0 0 1-1.414 1.414L13 5.414V15a1 1 0 1 1-2 0V5.414L9.707 6.707a1 1 0 0 1-1.414-1.414zM4 11a2 2 0 0 1 2-2h2a1 1 0 0 1 0 2H6v9h12v-9h-2a1 1 0 1 1 0-2h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z'/%3E%3C/svg%3E");
}
.vpi-window-add {
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 4a1 1 0 0 1 1 1v6h6a1 1 0 1 1 0 2h-6v6a1 1 0 1 1-2 0v-6H5a1 1 0 1 1 0-2h6V5a1 1 0 0 1 1-1'/%3E%3C/svg%3E");
}
.vpi-window-copy {
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M2 4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v4h4a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H10a2 2 0 0 1-2-2v-4H4a2 2 0 0 1-2-2zm8 12v4h10V10h-4v4a2 2 0 0 1-2 2zm4-2V4H4v10z'/%3E%3C/svg%3E");
}
.vpi-window-reload {
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747'/%3E%3Cpath d='M20 4v5h-5'/%3E%3C/g%3E%3C/svg%3E");
}

View File

@ -1,57 +0,0 @@
import type { Markdown } from 'vuepress/markdown'
import { resolveAttrs } from '.././utils/resolveAttrs.js'
import { createContainerPlugin } from './createContainer.js'
/**
* Demo wrapper attributes
*
*
*/
interface DemoWrapperAttrs {
title?: string
img?: string
noPadding?: boolean
height?: string
}
/**
* Demo wrapper plugin - Enable demo wrapper container
*
* -
*
* Syntax: :::demo-wrapper img no-padding title="xxx" height="100px"
* :::demo-wrapper img no-padding title="xxx" height="100px"
*
* @param md - Markdown instance / Markdown
*/
export function demoWrapperPlugin(md: Markdown): void {
createContainerPlugin(md, 'demo-wrapper', {
before: (info: string) => {
const { attrs } = resolveAttrs<DemoWrapperAttrs>(info)
const wrapperClasses: string[] = ['demo-wrapper']
let containerStyle = ''
if (attrs.title)
wrapperClasses.push('has-title')
if (attrs.img)
wrapperClasses.push('only-img')
if (attrs.noPadding)
wrapperClasses.push('no-padding')
if (attrs.height) {
const h = Number.parseFloat(attrs.height) === Number(attrs.height) ? `${attrs.height}px` : attrs.height
containerStyle += `--demo-container-height: ${h};`
wrapperClasses.push('has-height')
}
return `<div class="${wrapperClasses.join(' ')}">
<div class="demo-head">
<div class="demo-ctrl"><i></i><i></i><i></i></div>
${attrs.title ? `<h4 class="demo-title"><p>${attrs.title}</p></h4>` : ''}
</div>
<div class="demo-container" ${containerStyle ? `style="${containerStyle}"` : ''}>\n`
},
after: () => '</div></div>',
})
}

View File

@ -10,7 +10,6 @@ import { chatPlugin } from './chat.js'
import { codeTabs } from './codeTabs.js'
import { codeTreePlugin } from './codeTree.js'
import { collapsePlugin } from './collapse.js'
import { demoWrapperPlugin } from './demoWrapper.js'
import { encryptPlugin } from './encrypt.js'
import { fieldPlugin } from './field.js'
import { fileTreePlugin } from './fileTree.js'
@ -20,6 +19,7 @@ import { stepsPlugin } from './steps.js'
import { tablePlugin } from './table.js'
import { tabs } from './tabs.js'
import { timelinePlugin } from './timeline.js'
import { windowPlugin } from './window.js'
/**
* Container plugin - Register all container plugins
@ -44,8 +44,8 @@ export async function containerPlugin(
// ::: code-tabs
codeTabs(md, options.codeTabs)
// ::: demo-wrapper
demoWrapperPlugin(md)
// ::: window
windowPlugin(md)
// ::: steps
stepsPlugin(md)

View File

@ -0,0 +1,91 @@
import type Token from 'markdown-it/lib/token.mjs'
import type { Markdown, MarkdownEnv } from 'vuepress/markdown'
import { isNumber, isString } from '@pengzhanbo/utils'
import { colors } from 'vuepress/utils'
import { logger } from '../utils/logger.js'
import { parseRect } from '../utils/parseRect.js'
import { resolveAttrs } from '../utils/resolveAttrs.js'
import { type ContainerOptions, createContainerPlugin } from './createContainer.js'
interface WindowAttrs {
title?: string
height?: string
gap?: string
/** @deprecated */
noPadding?: boolean
}
const RE_IMAGE_SYNTAX = /^!?\[[^\]]*\]\([^)]+\)$/
const render: NonNullable<ContainerOptions['before']> = (info, tokens, idx) => {
const elms: Token[] = []
for (let i = idx + 1; i < tokens.length; i++) {
if (tokens[i].type === 'container_window_close')
break
elms.push(tokens[i])
}
const { attrs } = resolveAttrs<WindowAttrs>(info)
let onlyImg = false
if (elms.length === 1) {
const { type } = elms[0]
const content = elms[0].content.trim()
if (type === 'html_block'
&& (content.startsWith('<img') || content.startsWith('<picture'))) {
onlyImg = true
}
}
if (elms.length === 3) {
const [op, img, cp] = elms
if (op.type === 'paragraph_open'
&& cp.type === 'paragraph_close'
&& img.type === 'inline'
&& RE_IMAGE_SYNTAX.test(img.content.trim())) {
op.type = 'text'
cp.type = 'text'
onlyImg = true
}
}
const { title, height, noPadding } = attrs
const gap = isString(attrs.gap) || isNumber(attrs.gap)
? parseRect(attrs.gap)
: (onlyImg || noPadding) ? '0' : '20px'
const classes: string[] = ['window-wrapper']
title && classes.push('has-title')
return `<article class="${classes.join(' ')}">
<header class="window-header">
<div class="window-left"><i></i><i></i><i></i></div>
${title ? `<div class="window-center"><h4 class="window-title ignore-header"><span>${title}</span><i class="vpi-window-reload"></i></h4></div>` : ''}
<div class="window-right"><i class="vpi-window-share"></i><i class="vpi-window-add"></i><i class="vpi-window-copy"></i></div>
</header>
<section class="window-content" style="--window-gap:${gap};${height ? `--window-height:${parseRect(height)}` : ''}">`
}
/**
* window plugin - Enable window container
*
* -
*
* Syntax: :::window title="xxx" height="100px" gap="20px"
* :::window title="xxx" height="100px" gap="20px"
*
* @param md - Markdown instance / Markdown
*/
export function windowPlugin(md: Markdown): void {
const after = () => '</section></article>'
createContainerPlugin(md, 'window', {
before: render,
after,
})
// legacy demo-wrapper container, keep for compatibility
createContainerPlugin(md, 'demo-wrapper', {
before: (info, tokens, idx, _, env: MarkdownEnv) => {
logger.warn('container', `::: demo-wrapper container is deprecated, please use ::: window container instead. (${colors.gray(env.filePathRelative || '')})`)
return render(info, tokens, idx, _, env)
},
after,
})
}