vitepress-plugin-abbr 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +155 -0
- package/dist/client/browser/index.d.ts +47 -0
- package/dist/client/browser/index.js +110 -0
- package/dist/client/ssr/index.d.ts +47 -0
- package/dist/client/ssr/index.js +128 -0
- package/dist/client/style.css +44 -0
- package/dist/node/index.d.ts +82 -0
- package/dist/node/index.js +282 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pengzhanbo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# vitepress-plugin-abbr
|
|
2
|
+
|
|
3
|
+
Add abbreviation support to your VitePress site, rendering abbreviations with an interactive tooltip
|
|
4
|
+
that displays the full description on hover or focus.
|
|
5
|
+
|
|
6
|
+
为 VitePress 添加缩写词支持,将缩写词渲染为可交互的提示框,在悬停或聚焦时显示其完整描述。
|
|
7
|
+
|
|
8
|
+
Forked and modified from [`markdown-it-abbr`](https://github.com/markdown-it/markdown-it-abbr).
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
### With Vitepress-tuck
|
|
13
|
+
|
|
14
|
+
**Installation:**
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# npm
|
|
18
|
+
npm install -D vitepress-tuck vitepress-plugin-abbr
|
|
19
|
+
# pnpm
|
|
20
|
+
pnpm add -D vitepress-tuck vitepress-plugin-abbr
|
|
21
|
+
# yarn
|
|
22
|
+
yarn add -D vitepress-tuck vitepress-plugin-abbr
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Configuration:**
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
// .vitepress/config.ts
|
|
29
|
+
import abbr from 'vitepress-plugin-abbr'
|
|
30
|
+
import { defineConfig } from 'vitepress-tuck'
|
|
31
|
+
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
plugins: [abbr()],
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// .vitepress/theme/index.ts
|
|
39
|
+
import type { Theme } from 'vitepress'
|
|
40
|
+
import enhanceApp from 'virtual:enhance-app'
|
|
41
|
+
import DefaultTheme from 'vitepress/theme'
|
|
42
|
+
import 'vitepress-plugin-abbr/style.css'
|
|
43
|
+
|
|
44
|
+
export default {
|
|
45
|
+
extends: DefaultTheme,
|
|
46
|
+
enhanceApp(ctx) {
|
|
47
|
+
enhanceApp(ctx)
|
|
48
|
+
},
|
|
49
|
+
} satisfies Theme
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### With Vitepress
|
|
53
|
+
|
|
54
|
+
**Installation:**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# npm
|
|
58
|
+
npm install -D vitepress-plugin-abbr
|
|
59
|
+
# pnpm
|
|
60
|
+
pnpm add -D vitepress-plugin-abbr
|
|
61
|
+
# yarn
|
|
62
|
+
yarn add -D vitepress-plugin-abbr
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Configuration:**
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
// .vitepress/config.ts
|
|
69
|
+
import { defineConfig } from 'vitepress'
|
|
70
|
+
import { abbrMarkdownPlugin } from 'vitepress-plugin-abbr'
|
|
71
|
+
|
|
72
|
+
export default defineConfig({
|
|
73
|
+
markdown: {
|
|
74
|
+
config: (md) => {
|
|
75
|
+
md.use(abbrMarkdownPlugin)
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
vite: {
|
|
79
|
+
ssr: {
|
|
80
|
+
noExternal: ['vitepress-plugin-abbr'],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// .vitepress/theme/index.ts
|
|
88
|
+
import type { Theme } from 'vitepress'
|
|
89
|
+
import { VPAbbreviation } from 'vitepress-plugin-abbr/client'
|
|
90
|
+
import DefaultTheme from 'vitepress/theme'
|
|
91
|
+
import 'vitepress-plugin-abbr/style.css'
|
|
92
|
+
|
|
93
|
+
export default {
|
|
94
|
+
extends: DefaultTheme,
|
|
95
|
+
enhanceApp(ctx) {
|
|
96
|
+
ctx.app.component('VPAbbreviation', VPAbbreviation)
|
|
97
|
+
},
|
|
98
|
+
} satisfies Theme
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Syntax
|
|
102
|
+
|
|
103
|
+
Define an abbreviation with the `*[ABBR]: Full description` syntax, then use the abbreviation anywhere
|
|
104
|
+
in your markdown content. The abbreviation text will be rendered with a dotted underline and a help cursor,
|
|
105
|
+
and hovering or focusing it shows a tooltip with the full description.
|
|
106
|
+
|
|
107
|
+
使用 `*[缩写]: 完整描述` 语法定义缩写词,然后在 markdown 内容的任意位置使用该缩写词。
|
|
108
|
+
缩写词文本将渲染为带点状下划线和 help 鼠标样式的文本,悬停或聚焦时会显示包含完整描述的提示框。
|
|
109
|
+
|
|
110
|
+
```md
|
|
111
|
+
The HTML specification is maintained by the W3C.
|
|
112
|
+
|
|
113
|
+
*[HTML]: HyperText Markup Language
|
|
114
|
+
*[W3C]: World Wide Web Consortium
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The description supports inline markdown, such as `**bold**`, `*italic*`, `[links](https://example.com)`, and `code`.
|
|
118
|
+
|
|
119
|
+
描述支持内联 markdown 语法,例如 `**加粗**`、`*斜体*`、`[链接](https://example.com)` 和 `代码`。
|
|
120
|
+
|
|
121
|
+
```md
|
|
122
|
+
The **HTML** spec is maintained by the W3C.
|
|
123
|
+
|
|
124
|
+
*[HTML]: HyperText Markup Language
|
|
125
|
+
*[W3C]: World [Wide Web](https://www.w3.org/) Consortium
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Global Abbreviations
|
|
129
|
+
|
|
130
|
+
Instead of (or in addition to) defining abbreviations inline in each markdown file,
|
|
131
|
+
you can provide a global preset of abbreviations through the plugin options. Inline definitions take
|
|
132
|
+
precedence over global ones when both define the same abbreviation.
|
|
133
|
+
|
|
134
|
+
可以在每个 markdown 文件中内联定义缩写词,也可以通过插件选项提供全局缩写词预设
|
|
135
|
+
当两者定义了相同的缩写词时,内联定义优先于全局定义。
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
// .vitepress/config.ts
|
|
139
|
+
import abbr from 'vitepress-plugin-abbr'
|
|
140
|
+
import { defineConfig } from 'vitepress-tuck'
|
|
141
|
+
|
|
142
|
+
export default defineConfig({
|
|
143
|
+
plugins: [
|
|
144
|
+
abbr({
|
|
145
|
+
HTML: 'HyperText Markup Language',
|
|
146
|
+
W3C: 'World Wide Web Consortium',
|
|
147
|
+
}),
|
|
148
|
+
],
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
With the configuration above, every occurrence of `HTML` and `W3C` across all pages will be
|
|
153
|
+
rendered as abbreviations automatically, without needing inline `*[...]:` definitions.
|
|
154
|
+
|
|
155
|
+
配置上述内容后,所有页面中出现的 `HTML` 和 `W3C` 都会自动渲染为缩写词,无需逐个文件添加 `*[...]:` 内联定义。
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { EnhanceAppContext } from "vitepress/client";
|
|
2
|
+
|
|
3
|
+
//#region src/client/VPAbbreviation.vue.d.ts
|
|
4
|
+
declare var __VLS_1: {}, __VLS_21: {};
|
|
5
|
+
type __VLS_Slots = {} & {
|
|
6
|
+
default?: (props: typeof __VLS_1) => any;
|
|
7
|
+
} & {
|
|
8
|
+
tooltip?: (props: typeof __VLS_21) => any;
|
|
9
|
+
};
|
|
10
|
+
declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
11
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
12
|
+
declare const _default: typeof __VLS_export;
|
|
13
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
14
|
+
new (): {
|
|
15
|
+
$slots: S;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/client/index.d.ts
|
|
20
|
+
/**
|
|
21
|
+
* Enhances the VitePress application by registering the `VPAbbreviation`
|
|
22
|
+
* component globally under the name `Abbreviation`, so it can be used
|
|
23
|
+
* directly in Vue templates or rendered markdown content.
|
|
24
|
+
*
|
|
25
|
+
* 通过全局注册 `VPAbbreviation` 组件来增强 VitePress 应用,
|
|
26
|
+
* 使其可在 Vue 模板或渲染的 markdown 内容中直接使用。
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* `.vitepress/theme/index.ts`
|
|
30
|
+
* ```ts
|
|
31
|
+
* import type { Theme } from 'vitepress'
|
|
32
|
+
* import { enhanceAppWithAbbr } from 'vitepress-plugin-abbr/client'
|
|
33
|
+
* import DefaultTheme from 'vitepress/theme'
|
|
34
|
+
*
|
|
35
|
+
* export default {
|
|
36
|
+
* extends: DefaultTheme,
|
|
37
|
+
* enhanceApp(ctx) {
|
|
38
|
+
* enhanceAppWithAbbr(ctx)
|
|
39
|
+
* },
|
|
40
|
+
* } satisfies Theme
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
declare function enhanceAppWithAbbr({
|
|
44
|
+
app
|
|
45
|
+
}: EnhanceAppContext): void;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { _default as VPAbbreviation, enhanceAppWithAbbr };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import "../style.css";
|
|
2
|
+
import { Fragment, Teleport, Transition, createBlock, createElementBlock, createElementVNode, createVNode, defineComponent, mergeProps, normalizeStyle, openBlock, ref, renderSlot, resolveComponent, unref, useTemplateRef, vShow, withCtx, withDirectives } from "vue";
|
|
3
|
+
import { arrow, autoUpdate, offset, shift, useFloating } from "@floating-ui/vue";
|
|
4
|
+
//#region src/client/VPAbbreviation.vue
|
|
5
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
6
|
+
inheritAttrs: false,
|
|
7
|
+
__name: "VPAbbreviation",
|
|
8
|
+
setup(__props) {
|
|
9
|
+
/**
|
|
10
|
+
* VPAbbreviation component for rendering abbreviations with an interactive
|
|
11
|
+
* tooltip in VitePress.
|
|
12
|
+
*
|
|
13
|
+
* VPAbbreviation 组件,用于在 VitePress 中渲染带有可交互提示框的缩写词。
|
|
14
|
+
*
|
|
15
|
+
* The abbreviation text is rendered as an underlined, help-cursor span. When
|
|
16
|
+
* the user hovers over or focuses the span, a floating tooltip anchored below
|
|
17
|
+
* the text is shown, displaying the full description provided via the
|
|
18
|
+
* `#tooltip` slot. The tooltip is teleported to `body` and positioned with
|
|
19
|
+
* `@floating-ui/vue`, applying offset, flip, shift, and arrow middleware for
|
|
20
|
+
* robust placement near viewport edges.
|
|
21
|
+
*
|
|
22
|
+
* 缩写词文本渲染为带下划线且鼠标为 help 样式的 span。当用户悬停或聚焦该
|
|
23
|
+
* span 时,会在文本下方显示一个浮动提示框,展示通过 `#tooltip` 插槽提供
|
|
24
|
+
* 的完整描述。提示框被传送到 `body`,并使用 `@floating-ui/vue` 定位,
|
|
25
|
+
* 应用 offset、flip、shift 和 arrow 中间件以保证在视口边缘附近的稳定定位。
|
|
26
|
+
*/
|
|
27
|
+
const abbr = useTemplateRef("abbr");
|
|
28
|
+
const tooltip = useTemplateRef("tooltip");
|
|
29
|
+
const tooltipArrow = useTemplateRef("tooltipArrow");
|
|
30
|
+
const show = ref(false);
|
|
31
|
+
const { floatingStyles, middlewareData } = useFloating(abbr, tooltip, {
|
|
32
|
+
whileElementsMounted: autoUpdate,
|
|
33
|
+
placement: "bottom",
|
|
34
|
+
middleware: [
|
|
35
|
+
offset(10),
|
|
36
|
+
shift({ padding: 20 }),
|
|
37
|
+
arrow({
|
|
38
|
+
element: tooltipArrow,
|
|
39
|
+
padding: 4
|
|
40
|
+
})
|
|
41
|
+
]
|
|
42
|
+
});
|
|
43
|
+
return (_ctx, _cache) => {
|
|
44
|
+
const _component_ClientOnly = resolveComponent("ClientOnly");
|
|
45
|
+
return openBlock(), createElementBlock(Fragment, null, [createElementVNode("span", mergeProps({
|
|
46
|
+
ref_key: "abbr",
|
|
47
|
+
ref: abbr,
|
|
48
|
+
class: "vp-abbr"
|
|
49
|
+
}, _ctx.$attrs, {
|
|
50
|
+
onMouseenter: _cache[0] || (_cache[0] = ($event) => show.value = true),
|
|
51
|
+
onMouseleave: _cache[1] || (_cache[1] = ($event) => show.value = false)
|
|
52
|
+
}), [renderSlot(_ctx.$slots, "default")], 16), createVNode(_component_ClientOnly, null, {
|
|
53
|
+
default: withCtx(() => [(openBlock(), createBlock(Teleport, { to: "body" }, [createVNode(Transition, {
|
|
54
|
+
name: "fade-in",
|
|
55
|
+
persisted: ""
|
|
56
|
+
}, {
|
|
57
|
+
default: withCtx(() => [withDirectives(createElementVNode("div", {
|
|
58
|
+
ref_key: "tooltip",
|
|
59
|
+
ref: tooltip,
|
|
60
|
+
role: "tooltip",
|
|
61
|
+
class: "vp-abbr-popover",
|
|
62
|
+
style: normalizeStyle(unref(floatingStyles)),
|
|
63
|
+
onMouseenter: _cache[2] || (_cache[2] = ($event) => show.value = true),
|
|
64
|
+
onMouseleave: _cache[3] || (_cache[3] = ($event) => show.value = false)
|
|
65
|
+
}, [createElementVNode("span", {
|
|
66
|
+
ref_key: "tooltipArrow",
|
|
67
|
+
ref: tooltipArrow,
|
|
68
|
+
class: "tooltip-arrow",
|
|
69
|
+
style: normalizeStyle({
|
|
70
|
+
left: unref(middlewareData).arrow?.x != null ? `${unref(middlewareData).arrow.x}px` : "",
|
|
71
|
+
top: unref(middlewareData).arrow?.y != null ? `${unref(middlewareData).arrow.y}px` : ""
|
|
72
|
+
})
|
|
73
|
+
}, null, 4), renderSlot(_ctx.$slots, "tooltip")], 36), [[vShow, show.value]])]),
|
|
74
|
+
_: 3
|
|
75
|
+
})]))]),
|
|
76
|
+
_: 3
|
|
77
|
+
})], 64);
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/client/index.ts
|
|
83
|
+
/**
|
|
84
|
+
* Enhances the VitePress application by registering the `VPAbbreviation`
|
|
85
|
+
* component globally under the name `Abbreviation`, so it can be used
|
|
86
|
+
* directly in Vue templates or rendered markdown content.
|
|
87
|
+
*
|
|
88
|
+
* 通过全局注册 `VPAbbreviation` 组件来增强 VitePress 应用,
|
|
89
|
+
* 使其可在 Vue 模板或渲染的 markdown 内容中直接使用。
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* `.vitepress/theme/index.ts`
|
|
93
|
+
* ```ts
|
|
94
|
+
* import type { Theme } from 'vitepress'
|
|
95
|
+
* import { enhanceAppWithAbbr } from 'vitepress-plugin-abbr/client'
|
|
96
|
+
* import DefaultTheme from 'vitepress/theme'
|
|
97
|
+
*
|
|
98
|
+
* export default {
|
|
99
|
+
* extends: DefaultTheme,
|
|
100
|
+
* enhanceApp(ctx) {
|
|
101
|
+
* enhanceAppWithAbbr(ctx)
|
|
102
|
+
* },
|
|
103
|
+
* } satisfies Theme
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
function enhanceAppWithAbbr({ app }) {
|
|
107
|
+
app.component("VPAbbreviation", _sfc_main);
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { _sfc_main as VPAbbreviation, enhanceAppWithAbbr };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { EnhanceAppContext } from "vitepress/client";
|
|
2
|
+
|
|
3
|
+
//#region src/client/VPAbbreviation.vue.d.ts
|
|
4
|
+
declare var __VLS_1: {}, __VLS_21: {};
|
|
5
|
+
type __VLS_Slots = {} & {
|
|
6
|
+
default?: (props: typeof __VLS_1) => any;
|
|
7
|
+
} & {
|
|
8
|
+
tooltip?: (props: typeof __VLS_21) => any;
|
|
9
|
+
};
|
|
10
|
+
declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
11
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
12
|
+
declare const _default: typeof __VLS_export;
|
|
13
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
14
|
+
new (): {
|
|
15
|
+
$slots: S;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/client/index.d.ts
|
|
20
|
+
/**
|
|
21
|
+
* Enhances the VitePress application by registering the `VPAbbreviation`
|
|
22
|
+
* component globally under the name `Abbreviation`, so it can be used
|
|
23
|
+
* directly in Vue templates or rendered markdown content.
|
|
24
|
+
*
|
|
25
|
+
* 通过全局注册 `VPAbbreviation` 组件来增强 VitePress 应用,
|
|
26
|
+
* 使其可在 Vue 模板或渲染的 markdown 内容中直接使用。
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* `.vitepress/theme/index.ts`
|
|
30
|
+
* ```ts
|
|
31
|
+
* import type { Theme } from 'vitepress'
|
|
32
|
+
* import { enhanceAppWithAbbr } from 'vitepress-plugin-abbr/client'
|
|
33
|
+
* import DefaultTheme from 'vitepress/theme'
|
|
34
|
+
*
|
|
35
|
+
* export default {
|
|
36
|
+
* extends: DefaultTheme,
|
|
37
|
+
* enhanceApp(ctx) {
|
|
38
|
+
* enhanceAppWithAbbr(ctx)
|
|
39
|
+
* },
|
|
40
|
+
* } satisfies Theme
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
declare function enhanceAppWithAbbr({
|
|
44
|
+
app
|
|
45
|
+
}: EnhanceAppContext): void;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { _default as VPAbbreviation, enhanceAppWithAbbr };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Teleport, Transition, createBlock, createVNode, defineComponent, mergeProps, openBlock, ref, renderSlot, resolveComponent, unref, useSSRContext, useTemplateRef, vShow, withCtx, withDirectives } from "vue";
|
|
2
|
+
import { ssrRenderAttrs, ssrRenderComponent, ssrRenderSlot, ssrRenderStyle, ssrRenderTeleport } from "vue/server-renderer";
|
|
3
|
+
import { arrow, autoUpdate, offset, shift, useFloating } from "@floating-ui/vue";
|
|
4
|
+
//#region src/client/VPAbbreviation.vue
|
|
5
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
6
|
+
inheritAttrs: false,
|
|
7
|
+
__name: "VPAbbreviation",
|
|
8
|
+
__ssrInlineRender: true,
|
|
9
|
+
setup(__props) {
|
|
10
|
+
/**
|
|
11
|
+
* VPAbbreviation component for rendering abbreviations with an interactive
|
|
12
|
+
* tooltip in VitePress.
|
|
13
|
+
*
|
|
14
|
+
* VPAbbreviation 组件,用于在 VitePress 中渲染带有可交互提示框的缩写词。
|
|
15
|
+
*
|
|
16
|
+
* The abbreviation text is rendered as an underlined, help-cursor span. When
|
|
17
|
+
* the user hovers over or focuses the span, a floating tooltip anchored below
|
|
18
|
+
* the text is shown, displaying the full description provided via the
|
|
19
|
+
* `#tooltip` slot. The tooltip is teleported to `body` and positioned with
|
|
20
|
+
* `@floating-ui/vue`, applying offset, flip, shift, and arrow middleware for
|
|
21
|
+
* robust placement near viewport edges.
|
|
22
|
+
*
|
|
23
|
+
* 缩写词文本渲染为带下划线且鼠标为 help 样式的 span。当用户悬停或聚焦该
|
|
24
|
+
* span 时,会在文本下方显示一个浮动提示框,展示通过 `#tooltip` 插槽提供
|
|
25
|
+
* 的完整描述。提示框被传送到 `body`,并使用 `@floating-ui/vue` 定位,
|
|
26
|
+
* 应用 offset、flip、shift 和 arrow 中间件以保证在视口边缘附近的稳定定位。
|
|
27
|
+
*/
|
|
28
|
+
const abbr = useTemplateRef("abbr");
|
|
29
|
+
const tooltip = useTemplateRef("tooltip");
|
|
30
|
+
const tooltipArrow = useTemplateRef("tooltipArrow");
|
|
31
|
+
const show = ref(false);
|
|
32
|
+
const { floatingStyles, middlewareData } = useFloating(abbr, tooltip, {
|
|
33
|
+
whileElementsMounted: autoUpdate,
|
|
34
|
+
placement: "bottom",
|
|
35
|
+
middleware: [
|
|
36
|
+
offset(10),
|
|
37
|
+
shift({ padding: 20 }),
|
|
38
|
+
arrow({
|
|
39
|
+
element: tooltipArrow,
|
|
40
|
+
padding: 4
|
|
41
|
+
})
|
|
42
|
+
]
|
|
43
|
+
});
|
|
44
|
+
return (_ctx, _push, _parent, _attrs) => {
|
|
45
|
+
const _component_ClientOnly = resolveComponent("ClientOnly");
|
|
46
|
+
_push(`<!--[--><span${ssrRenderAttrs(mergeProps({
|
|
47
|
+
ref_key: "abbr",
|
|
48
|
+
ref: abbr,
|
|
49
|
+
class: "vp-abbr"
|
|
50
|
+
}, _ctx.$attrs))}>`);
|
|
51
|
+
ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent);
|
|
52
|
+
_push(`</span>`);
|
|
53
|
+
_push(ssrRenderComponent(_component_ClientOnly, null, {
|
|
54
|
+
default: withCtx((_, _push, _parent, _scopeId) => {
|
|
55
|
+
if (_push) ssrRenderTeleport(_push, (_push) => {
|
|
56
|
+
_push(`<div role="tooltip" class="vp-abbr-popover" style="${ssrRenderStyle([unref(floatingStyles), show.value ? null : { display: "none" }])}"${_scopeId}><span class="tooltip-arrow" style="${ssrRenderStyle({
|
|
57
|
+
left: unref(middlewareData).arrow?.x != null ? `${unref(middlewareData).arrow.x}px` : "",
|
|
58
|
+
top: unref(middlewareData).arrow?.y != null ? `${unref(middlewareData).arrow.y}px` : ""
|
|
59
|
+
})}"${_scopeId}></span>`);
|
|
60
|
+
ssrRenderSlot(_ctx.$slots, "tooltip", {}, null, _push, _parent, _scopeId);
|
|
61
|
+
_push(`</div>`);
|
|
62
|
+
}, "body", false, _parent);
|
|
63
|
+
else return [(openBlock(), createBlock(Teleport, { to: "body" }, [createVNode(Transition, {
|
|
64
|
+
name: "fade-in",
|
|
65
|
+
persisted: ""
|
|
66
|
+
}, {
|
|
67
|
+
default: withCtx(() => [withDirectives(createVNode("div", {
|
|
68
|
+
ref_key: "tooltip",
|
|
69
|
+
ref: tooltip,
|
|
70
|
+
role: "tooltip",
|
|
71
|
+
class: "vp-abbr-popover",
|
|
72
|
+
style: unref(floatingStyles),
|
|
73
|
+
onMouseenter: ($event) => show.value = true,
|
|
74
|
+
onMouseleave: ($event) => show.value = false
|
|
75
|
+
}, [createVNode("span", {
|
|
76
|
+
ref_key: "tooltipArrow",
|
|
77
|
+
ref: tooltipArrow,
|
|
78
|
+
class: "tooltip-arrow",
|
|
79
|
+
style: {
|
|
80
|
+
left: unref(middlewareData).arrow?.x != null ? `${unref(middlewareData).arrow.x}px` : "",
|
|
81
|
+
top: unref(middlewareData).arrow?.y != null ? `${unref(middlewareData).arrow.y}px` : ""
|
|
82
|
+
}
|
|
83
|
+
}, null, 4), renderSlot(_ctx.$slots, "tooltip")], 44, ["onMouseenter", "onMouseleave"]), [[vShow, show.value]])]),
|
|
84
|
+
_: 3
|
|
85
|
+
})]))];
|
|
86
|
+
}),
|
|
87
|
+
_: 3
|
|
88
|
+
}, _parent));
|
|
89
|
+
_push(`<!--]-->`);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
const _sfc_setup = _sfc_main.setup;
|
|
94
|
+
_sfc_main.setup = (props, ctx) => {
|
|
95
|
+
const ssrContext = useSSRContext();
|
|
96
|
+
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/client/VPAbbreviation.vue");
|
|
97
|
+
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
|
|
98
|
+
};
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/client/index.ts
|
|
101
|
+
/**
|
|
102
|
+
* Enhances the VitePress application by registering the `VPAbbreviation`
|
|
103
|
+
* component globally under the name `Abbreviation`, so it can be used
|
|
104
|
+
* directly in Vue templates or rendered markdown content.
|
|
105
|
+
*
|
|
106
|
+
* 通过全局注册 `VPAbbreviation` 组件来增强 VitePress 应用,
|
|
107
|
+
* 使其可在 Vue 模板或渲染的 markdown 内容中直接使用。
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* `.vitepress/theme/index.ts`
|
|
111
|
+
* ```ts
|
|
112
|
+
* import type { Theme } from 'vitepress'
|
|
113
|
+
* import { enhanceAppWithAbbr } from 'vitepress-plugin-abbr/client'
|
|
114
|
+
* import DefaultTheme from 'vitepress/theme'
|
|
115
|
+
*
|
|
116
|
+
* export default {
|
|
117
|
+
* extends: DefaultTheme,
|
|
118
|
+
* enhanceApp(ctx) {
|
|
119
|
+
* enhanceAppWithAbbr(ctx)
|
|
120
|
+
* },
|
|
121
|
+
* } satisfies Theme
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
function enhanceAppWithAbbr({ app }) {
|
|
125
|
+
app.component("VPAbbreviation", _sfc_main);
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
export { _sfc_main as VPAbbreviation, enhanceAppWithAbbr };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
@import url("vitepress-plugin-toolkit/styles/transition/fade-in.css");
|
|
2
|
+
|
|
3
|
+
.vp-abbr {
|
|
4
|
+
position: relative;
|
|
5
|
+
text-decoration: underline dotted currentcolor;
|
|
6
|
+
text-underline-offset: 4px;
|
|
7
|
+
cursor: help;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.vp-abbr-popover {
|
|
11
|
+
position: absolute;
|
|
12
|
+
width: max-content;
|
|
13
|
+
padding: 4px 8px;
|
|
14
|
+
font-size: 14px;
|
|
15
|
+
font-weight: bold;
|
|
16
|
+
color: var(--vp-c-text-1);
|
|
17
|
+
background-color: var(--vp-c-bg);
|
|
18
|
+
border: 1px solid var(--vp-c-divider);
|
|
19
|
+
border-radius: 4px;
|
|
20
|
+
box-shadow: var(--vp-shadow-2);
|
|
21
|
+
transition: 0.25s ease-in-out;
|
|
22
|
+
transition-property: color, border-color, box-shadow;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.vp-abbr-popover .tooltip-arrow,
|
|
26
|
+
.vp-abbr-popover .tooltip-arrow::after {
|
|
27
|
+
position: absolute;
|
|
28
|
+
display: block;
|
|
29
|
+
width: 8px;
|
|
30
|
+
height: 8px;
|
|
31
|
+
border: 8px solid transparent;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.vp-abbr-popover .tooltip-arrow {
|
|
35
|
+
top: -16px;
|
|
36
|
+
left: 4px;
|
|
37
|
+
border-bottom-color: var(--vp-c-divider);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.vp-abbr-popover .tooltip-arrow::after {
|
|
41
|
+
content: "";
|
|
42
|
+
border-bottom-color: var(--vp-c-bg);
|
|
43
|
+
transform: translate(-8px, -7px);
|
|
44
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { PluginWithOptions } from "markdown-it";
|
|
2
|
+
|
|
3
|
+
//#region src/node/plugin.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* VitePress plugin for abbreviation support in markdown content.
|
|
6
|
+
*
|
|
7
|
+
* VitePress 插件,用于在 markdown 内容中支持缩写词。
|
|
8
|
+
*
|
|
9
|
+
* Forked and modified from `markdown-it-abbr`. The plugin registers the
|
|
10
|
+
* abbreviation definition and replace rules in markdown-it, and wires up the
|
|
11
|
+
* client-side `VPAbbreviation` component via `componentResolver` so that
|
|
12
|
+
* abbreviations render with an interactive tooltip showing their full
|
|
13
|
+
* description. It also pre-bundles the `@floating-ui/vue` dependency used by
|
|
14
|
+
* the tooltip.
|
|
15
|
+
*
|
|
16
|
+
* 从 `markdown-it-abbr` 分叉并修改。该插件在 markdown-it 中注册缩写词
|
|
17
|
+
* 定义与替换规则,并通过 `componentResolver` 接入客户端 `VPAbbreviation`
|
|
18
|
+
* 组件,使缩写词渲染时带有可交互的提示框,展示其完整描述。同时预构建
|
|
19
|
+
* 提示框所依赖的 `@floating-ui/vue`。
|
|
20
|
+
*
|
|
21
|
+
* Abbreviations can be defined inline in markdown using the syntax
|
|
22
|
+
* `*[ABBR]: Full description`, or provided globally through the
|
|
23
|
+
* `abbreviations` option. Inline definitions take precedence over global ones
|
|
24
|
+
* when both define the same abbreviation.
|
|
25
|
+
*
|
|
26
|
+
* 缩写词可以在 markdown 中通过 `*[缩写]: 完整描述` 语法内联定义,也可以
|
|
27
|
+
* 通过 `abbreviations` 选项全局提供。当两者定义了相同的缩写词时,内联
|
|
28
|
+
* 定义优先于全局定义。
|
|
29
|
+
*
|
|
30
|
+
* @param abbreviations - Global abbreviations preset, a map of abbreviation
|
|
31
|
+
* to its full description / 全局缩写词预设,缩写词到其完整描述的映射
|
|
32
|
+
* @example
|
|
33
|
+
* `.vitepress/config.ts` without global abbreviations
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { defineConfig } from 'vitepress-tuck'
|
|
36
|
+
* import abbr from 'vitepress-plugin-abbr'
|
|
37
|
+
*
|
|
38
|
+
* export default defineConfig({
|
|
39
|
+
* plugins: [abbr()],
|
|
40
|
+
* })
|
|
41
|
+
* ```
|
|
42
|
+
* @example
|
|
43
|
+
* `.vitepress/config.ts` with global abbreviations
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { defineConfig } from 'vitepress-tuck'
|
|
46
|
+
* import abbr from 'vitepress-plugin-abbr'
|
|
47
|
+
*
|
|
48
|
+
* export default defineConfig({
|
|
49
|
+
* plugins: [
|
|
50
|
+
* abbr({
|
|
51
|
+
* HTML: 'HyperText Markup Language',
|
|
52
|
+
* W3C: 'World Wide Web Consortium',
|
|
53
|
+
* }),
|
|
54
|
+
* ],
|
|
55
|
+
* })
|
|
56
|
+
* ```
|
|
57
|
+
* @example
|
|
58
|
+
* // Markdown usage
|
|
59
|
+
* ```md
|
|
60
|
+
* The HTML specification is maintained by the W3C.
|
|
61
|
+
*
|
|
62
|
+
* *[HTML]: HyperText Markup Language
|
|
63
|
+
* *[W3C]: World Wide Web Consortium
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
declare const abbr: (options?: Record<string, string> | undefined) => import("vitepress-tuck").VitepressPlugin;
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/node/markdown.d.ts
|
|
69
|
+
/**
|
|
70
|
+
* Abbreviation plugin - Enable abbreviation syntax
|
|
71
|
+
*
|
|
72
|
+
* 缩写词插件 - 启用缩写词语法
|
|
73
|
+
*
|
|
74
|
+
* Definition syntax: *[ABBREV]: Full description
|
|
75
|
+
* 定义语法:*[缩写]: 完整描述
|
|
76
|
+
*
|
|
77
|
+
* @param md - Markdown-it instance / Markdown-it 实例
|
|
78
|
+
* @param globalAbbreviations - Global abbreviations preset / 全局缩写词预设
|
|
79
|
+
*/
|
|
80
|
+
declare const abbrMarkdownPlugin: PluginWithOptions<Record<string, string>>;
|
|
81
|
+
//#endregion
|
|
82
|
+
export { abbr, abbr as default, abbrMarkdownPlugin };
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { definePlugin } from "vitepress-tuck";
|
|
2
|
+
import { cleanMarkdownEnv } from "vitepress-plugin-toolkit";
|
|
3
|
+
//#region ../../node_modules/.pnpm/@pengzhanbo+utils@3.7.3/node_modules/@pengzhanbo/utils/dist/index.js
|
|
4
|
+
const T_NULL = "null";
|
|
5
|
+
const DANGEROUS_KEYS = /* @__PURE__ */ new Set([
|
|
6
|
+
"__proto__",
|
|
7
|
+
"constructor",
|
|
8
|
+
"prototype"
|
|
9
|
+
]);
|
|
10
|
+
function getTypeName(s) {
|
|
11
|
+
return Object.prototype.toString.call(s);
|
|
12
|
+
}
|
|
13
|
+
function typeOf(s) {
|
|
14
|
+
const type = typeof s;
|
|
15
|
+
return s === null ? T_NULL : type === "object" || type === "function" ? getTypeName(s).slice(8, -1).toLowerCase() : type;
|
|
16
|
+
}
|
|
17
|
+
function isTypeof(s, type) {
|
|
18
|
+
return typeOf(s) === type;
|
|
19
|
+
}
|
|
20
|
+
function isNull(v) {
|
|
21
|
+
return v === null;
|
|
22
|
+
}
|
|
23
|
+
function isPlainObject(v) {
|
|
24
|
+
if (!isTypeof(v, "object")) return false;
|
|
25
|
+
const proto = Object.getPrototypeOf(v);
|
|
26
|
+
return isNull(proto) || proto === Object.prototype;
|
|
27
|
+
}
|
|
28
|
+
function isEmptyObject(v) {
|
|
29
|
+
if (!isPlainObject(v)) return false;
|
|
30
|
+
for (const _ in v) return false;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
function objectKeys(obj) {
|
|
34
|
+
return Object.keys(obj);
|
|
35
|
+
}
|
|
36
|
+
function objectMap(obj, mapper) {
|
|
37
|
+
return Object.fromEntries(Object.entries(obj).map(([k, v]) => mapper(k, v)).filter((entry) => !isUndefined(entry)).filter(([k]) => !DANGEROUS_KEYS.has(k)));
|
|
38
|
+
}
|
|
39
|
+
function isUndefined(v) {
|
|
40
|
+
return v === void 0;
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/node/markdown.ts
|
|
44
|
+
/**
|
|
45
|
+
* Abbreviation plugin - Enable abbreviation syntax
|
|
46
|
+
*
|
|
47
|
+
* 缩写词插件 - 启用缩写词语法
|
|
48
|
+
*
|
|
49
|
+
* Definition syntax: *[ABBREV]: Full description
|
|
50
|
+
* 定义语法:*[缩写]: 完整描述
|
|
51
|
+
*
|
|
52
|
+
* @param md - Markdown-it instance / Markdown-it 实例
|
|
53
|
+
* @param globalAbbreviations - Global abbreviations preset / 全局缩写词预设
|
|
54
|
+
*/
|
|
55
|
+
const abbrMarkdownPlugin = (md, globalAbbreviations = {}) => {
|
|
56
|
+
const { arrayReplaceAt, escapeRE, lib } = md.utils;
|
|
57
|
+
globalAbbreviations = objectMap(globalAbbreviations, (key, value) => [key.startsWith(":") ? key : `:${key}`, value]);
|
|
58
|
+
const WORDING_REGEXP_TEXT = `${lib.ucmicro.P.source}|${lib.ucmicro.Z.source}|[${" \r\n$+<=>^`|~".split("").map(escapeRE).join("")}]`;
|
|
59
|
+
/**
|
|
60
|
+
* Abbreviation definition rule
|
|
61
|
+
*
|
|
62
|
+
* 缩写词定义规则
|
|
63
|
+
*
|
|
64
|
+
* @param state - State block / 状态块
|
|
65
|
+
* @param startLine - Start line number / 开始行号
|
|
66
|
+
* @param _endLine - End line number / 结束行号
|
|
67
|
+
* @param silent - Silent mode / 静默模式
|
|
68
|
+
* @returns Whether matched / 是否匹配
|
|
69
|
+
*/
|
|
70
|
+
const abbrDefinition = (state, startLine, _endLine, silent) => {
|
|
71
|
+
let labelEnd = -1;
|
|
72
|
+
let pos = state.bMarks[startLine] + state.tShift[startLine];
|
|
73
|
+
const max = state.eMarks[startLine];
|
|
74
|
+
if (pos + 2 >= max || state.src.charAt(pos++) !== "*" || state.src.charAt(pos++) !== "[") return false;
|
|
75
|
+
const labelStart = pos;
|
|
76
|
+
while (pos < max) {
|
|
77
|
+
const ch = state.src.charAt(pos);
|
|
78
|
+
if (ch === "[") return false;
|
|
79
|
+
if (ch === "]") {
|
|
80
|
+
labelEnd = pos;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
if (ch === "\\") pos++;
|
|
84
|
+
pos++;
|
|
85
|
+
}
|
|
86
|
+
if (labelEnd < 0 || state.src.charAt(labelEnd + 1) !== ":") return false;
|
|
87
|
+
/* istanbul ignore if -- @preserve */
|
|
88
|
+
if (silent) return true;
|
|
89
|
+
const label = state.src.slice(labelStart, labelEnd).replace(/\\(.)/g, "$1");
|
|
90
|
+
const title = state.src.slice(labelEnd + 2, max).trim();
|
|
91
|
+
if (!label.length || !title.length) return false;
|
|
92
|
+
(state.env.abbreviations ??= {})[`:${label}`] ??= title;
|
|
93
|
+
state.line = startLine + 1;
|
|
94
|
+
return true;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Abbreviation replace rule
|
|
98
|
+
*
|
|
99
|
+
* 缩写词替换规则
|
|
100
|
+
*
|
|
101
|
+
* @param state - State core / 核心状态
|
|
102
|
+
*/
|
|
103
|
+
const abbrReplace = (state) => {
|
|
104
|
+
if (state.env.disableAbbrReplace) return;
|
|
105
|
+
const tokens = state.tokens;
|
|
106
|
+
const { abbreviations: localAbbreviations } = state.env;
|
|
107
|
+
if (!localAbbreviations && isEmptyObject(globalAbbreviations)) return;
|
|
108
|
+
const abbreviations = {
|
|
109
|
+
...globalAbbreviations,
|
|
110
|
+
...localAbbreviations
|
|
111
|
+
};
|
|
112
|
+
const abbreviationsRegExpText = objectKeys(abbreviations).map((x) => x.substring(1)).sort((a, b) => b.length - a.length).map(escapeRE).join("|");
|
|
113
|
+
const regexpSimple = new RegExp(`(?:${abbreviationsRegExpText})`);
|
|
114
|
+
const regExp = new RegExp(`(^|${WORDING_REGEXP_TEXT})(${abbreviationsRegExpText})($|${WORDING_REGEXP_TEXT})`, "g");
|
|
115
|
+
for (const token of tokens) {
|
|
116
|
+
if (token.type !== "inline") continue;
|
|
117
|
+
let children = token.children;
|
|
118
|
+
for (let index = children.length - 1; index >= 0; index--) {
|
|
119
|
+
const currentToken = children[index];
|
|
120
|
+
if (currentToken.type !== "text") continue;
|
|
121
|
+
const text = currentToken.content;
|
|
122
|
+
regExp.lastIndex = 0;
|
|
123
|
+
if (!regexpSimple.test(text)) continue;
|
|
124
|
+
const nodes = [];
|
|
125
|
+
let match;
|
|
126
|
+
let pos = 0;
|
|
127
|
+
while (match = regExp.exec(text)) {
|
|
128
|
+
const [, before, word, after] = match;
|
|
129
|
+
if (match.index > 0 || before.length > 0) {
|
|
130
|
+
const token = new state.Token("text", "", 0);
|
|
131
|
+
token.content = text.slice(pos, match.index + before.length);
|
|
132
|
+
nodes.push(token);
|
|
133
|
+
}
|
|
134
|
+
const abbrToken = new state.Token("abbreviation", "Abbreviation", 0);
|
|
135
|
+
abbrToken.content = word;
|
|
136
|
+
abbrToken.info = abbreviations[`:${word}`];
|
|
137
|
+
nodes.push(abbrToken);
|
|
138
|
+
regExp.lastIndex -= after.length;
|
|
139
|
+
pos = regExp.lastIndex;
|
|
140
|
+
}
|
|
141
|
+
/* istanbul ignore if -- @preserve */
|
|
142
|
+
if (!nodes.length) continue;
|
|
143
|
+
if (pos < text.length) {
|
|
144
|
+
const token = new state.Token("text", "", 0);
|
|
145
|
+
token.content = text.slice(pos);
|
|
146
|
+
nodes.push(token);
|
|
147
|
+
}
|
|
148
|
+
token.children = children = arrayReplaceAt(children, index, nodes);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
md.block.ruler.before("reference", "abbr_definition", abbrDefinition, { alt: ["paragraph", "reference"] });
|
|
153
|
+
md.core.ruler.after("linkify", "abbr_replace", abbrReplace);
|
|
154
|
+
/**
|
|
155
|
+
* Custom renderer for the `abbreviation` token.
|
|
156
|
+
*
|
|
157
|
+
* `abbreviation` token 的自定义渲染器。
|
|
158
|
+
*
|
|
159
|
+
* Renders the abbreviation as a `VPAbbreviation` component tag. The
|
|
160
|
+
* abbreviation text is placed as the default slot content, and when a
|
|
161
|
+
* description (`info`) exists, it is rendered as inline markdown and passed
|
|
162
|
+
* to the `#tooltip` slot. The rendered description is also used to produce
|
|
163
|
+
* an `aria-label` (HTML tags stripped) for accessibility.
|
|
164
|
+
*
|
|
165
|
+
* 将缩写词渲染为 `VPAbbreviation` 组件标签。缩写词文本作为默认插槽内容,
|
|
166
|
+
* 当存在描述(`info`)时,将其作为内联 markdown 渲染后传入 `#tooltip`
|
|
167
|
+
* 插槽。渲染后的描述还会去除 HTML 标签生成 `aria-label`,以支持无障碍访问。
|
|
168
|
+
*
|
|
169
|
+
* @param tokens - Token array / token 数组
|
|
170
|
+
* @param idx - Current token index / 当前 token 索引
|
|
171
|
+
* @param _ - Render options (unused) / 渲染选项(未使用)
|
|
172
|
+
* @param env - Render environment containing abbreviations / 包含缩写词的渲染环境
|
|
173
|
+
* @returns Rendered HTML string / 渲染后的 HTML 字符串
|
|
174
|
+
*/
|
|
175
|
+
md.renderer.rules.abbreviation = (tokens, idx, _, env) => {
|
|
176
|
+
const { content, info } = tokens[idx];
|
|
177
|
+
const rendered = md.renderInline(info, {
|
|
178
|
+
...cleanMarkdownEnv(env, ["abbreviations"]),
|
|
179
|
+
disableAbbrReplace: true
|
|
180
|
+
});
|
|
181
|
+
return `<VPAbbreviation aria-label="${rendered.replace(/<[^>]*>/g, "")}">${content}${info ? `<template #tooltip>${rendered}</template>` : ""}</VPAbbreviation>`;
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/node/plugin.ts
|
|
186
|
+
/**
|
|
187
|
+
* VitePress plugin for abbreviation support in markdown content.
|
|
188
|
+
*
|
|
189
|
+
* VitePress 插件,用于在 markdown 内容中支持缩写词。
|
|
190
|
+
*
|
|
191
|
+
* Forked and modified from `markdown-it-abbr`. The plugin registers the
|
|
192
|
+
* abbreviation definition and replace rules in markdown-it, and wires up the
|
|
193
|
+
* client-side `VPAbbreviation` component via `componentResolver` so that
|
|
194
|
+
* abbreviations render with an interactive tooltip showing their full
|
|
195
|
+
* description. It also pre-bundles the `@floating-ui/vue` dependency used by
|
|
196
|
+
* the tooltip.
|
|
197
|
+
*
|
|
198
|
+
* 从 `markdown-it-abbr` 分叉并修改。该插件在 markdown-it 中注册缩写词
|
|
199
|
+
* 定义与替换规则,并通过 `componentResolver` 接入客户端 `VPAbbreviation`
|
|
200
|
+
* 组件,使缩写词渲染时带有可交互的提示框,展示其完整描述。同时预构建
|
|
201
|
+
* 提示框所依赖的 `@floating-ui/vue`。
|
|
202
|
+
*
|
|
203
|
+
* Abbreviations can be defined inline in markdown using the syntax
|
|
204
|
+
* `*[ABBR]: Full description`, or provided globally through the
|
|
205
|
+
* `abbreviations` option. Inline definitions take precedence over global ones
|
|
206
|
+
* when both define the same abbreviation.
|
|
207
|
+
*
|
|
208
|
+
* 缩写词可以在 markdown 中通过 `*[缩写]: 完整描述` 语法内联定义,也可以
|
|
209
|
+
* 通过 `abbreviations` 选项全局提供。当两者定义了相同的缩写词时,内联
|
|
210
|
+
* 定义优先于全局定义。
|
|
211
|
+
*
|
|
212
|
+
* @param abbreviations - Global abbreviations preset, a map of abbreviation
|
|
213
|
+
* to its full description / 全局缩写词预设,缩写词到其完整描述的映射
|
|
214
|
+
* @example
|
|
215
|
+
* `.vitepress/config.ts` without global abbreviations
|
|
216
|
+
* ```ts
|
|
217
|
+
* import { defineConfig } from 'vitepress-tuck'
|
|
218
|
+
* import abbr from 'vitepress-plugin-abbr'
|
|
219
|
+
*
|
|
220
|
+
* export default defineConfig({
|
|
221
|
+
* plugins: [abbr()],
|
|
222
|
+
* })
|
|
223
|
+
* ```
|
|
224
|
+
* @example
|
|
225
|
+
* `.vitepress/config.ts` with global abbreviations
|
|
226
|
+
* ```ts
|
|
227
|
+
* import { defineConfig } from 'vitepress-tuck'
|
|
228
|
+
* import abbr from 'vitepress-plugin-abbr'
|
|
229
|
+
*
|
|
230
|
+
* export default defineConfig({
|
|
231
|
+
* plugins: [
|
|
232
|
+
* abbr({
|
|
233
|
+
* HTML: 'HyperText Markup Language',
|
|
234
|
+
* W3C: 'World Wide Web Consortium',
|
|
235
|
+
* }),
|
|
236
|
+
* ],
|
|
237
|
+
* })
|
|
238
|
+
* ```
|
|
239
|
+
* @example
|
|
240
|
+
* // Markdown usage
|
|
241
|
+
* ```md
|
|
242
|
+
* The HTML specification is maintained by the W3C.
|
|
243
|
+
*
|
|
244
|
+
* *[HTML]: HyperText Markup Language
|
|
245
|
+
* *[W3C]: World Wide Web Consortium
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
const abbr = definePlugin((abbreviations) => ({
|
|
249
|
+
name: "vitepress-plugin-abbr",
|
|
250
|
+
componentResolver: ["VPAbbreviation"],
|
|
251
|
+
markdown: { config(md) {
|
|
252
|
+
md.use(abbrMarkdownPlugin, abbreviations);
|
|
253
|
+
} },
|
|
254
|
+
vite: { optimizeDeps: { include: ["@floating-ui/vue"] } }
|
|
255
|
+
}));
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/node/index.ts
|
|
258
|
+
/**
|
|
259
|
+
* Node-side entry point for the VitePress abbreviation plugin.
|
|
260
|
+
*
|
|
261
|
+
* VitePress 缩写词插件的 Node 端入口。
|
|
262
|
+
*
|
|
263
|
+
* Re-exports the markdown-it plugin and the main plugin factory, and exposes
|
|
264
|
+
* the plugin factory as the default export for `vitepress-tuck` integration.
|
|
265
|
+
*
|
|
266
|
+
* 重新导出 markdown-it 插件和主插件工厂,并将插件工厂作为默认导出,
|
|
267
|
+
* 以便与 `vitepress-tuck` 集成。
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* `.vitepress/config.ts`
|
|
271
|
+
* ```ts
|
|
272
|
+
* import { defineConfig } from 'vitepress-tuck'
|
|
273
|
+
* import abbr from 'vitepress-plugin-abbr'
|
|
274
|
+
*
|
|
275
|
+
* export default defineConfig({
|
|
276
|
+
* plugins: [abbr()],
|
|
277
|
+
* })
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
var node_default = abbr;
|
|
281
|
+
//#endregion
|
|
282
|
+
export { abbr, abbrMarkdownPlugin, node_default as default };
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vitepress-plugin-abbr",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.5.0",
|
|
5
|
+
"description": "Add abbreviation support to VitePress",
|
|
6
|
+
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://tuck.pengzhanbo.cn/plugins/abbr",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/pengzhanbo/vitepress-tuck.git",
|
|
12
|
+
"directory": "packages/plugin-abbr"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/pengzhanbo/vitepress-tuck/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"vitepress",
|
|
19
|
+
"vitepress-plugin",
|
|
20
|
+
"abbr",
|
|
21
|
+
"abbreviation"
|
|
22
|
+
],
|
|
23
|
+
"exports": {
|
|
24
|
+
".": "./dist/node/index.js",
|
|
25
|
+
"./client": {
|
|
26
|
+
"browser": "./dist/client/browser/index.js",
|
|
27
|
+
"default": "./dist/client/ssr/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./style.css": "./dist/client/style.css"
|
|
30
|
+
},
|
|
31
|
+
"module": "./dist/node/index.js",
|
|
32
|
+
"types": "./dist/node/index.d.ts",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"vitepress": "^1.6.4 || ^2.0.0-alpha.17",
|
|
38
|
+
"vue": "^3.5.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@floating-ui/vue": "^2.0.0",
|
|
42
|
+
"@vueuse/core": "^14.3.0",
|
|
43
|
+
"vitepress-plugin-toolkit": "0.5.0",
|
|
44
|
+
"vitepress-tuck": "0.5.0"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"provenance": true
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"clean": "rimraf --glob ./dist",
|
|
52
|
+
"dev": "pnpm '/(tsdown|copy):watch/'",
|
|
53
|
+
"build": "pnpm tsdown && pnpm copy",
|
|
54
|
+
"copy": "cpx \"src/**/*.css\" dist",
|
|
55
|
+
"copy:watch": "pnpm copy -w",
|
|
56
|
+
"tsdown": "tsdown --config-loader unrun",
|
|
57
|
+
"tsdown:watch": "pnpm tsdown -w"
|
|
58
|
+
}
|
|
59
|
+
}
|