vuepress-plugin-md-power 1.0.0-rc.48
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 +201 -0
- package/lib/client/components/Bilibili.vue +45 -0
- package/lib/client/components/PDFViewer.vue +39 -0
- package/lib/client/components/Replit.vue +54 -0
- package/lib/client/components/Youtube.vue +41 -0
- package/lib/client/composables/pdf.d.ts +3 -0
- package/lib/client/composables/pdf.js +75 -0
- package/lib/client/composables/setupCanIUse.d.ts +1 -0
- package/lib/client/composables/setupCanIUse.js +18 -0
- package/lib/client/composables/size.d.ts +10 -0
- package/lib/client/composables/size.js +33 -0
- package/lib/client/config.d.ts +4 -0
- package/lib/client/config.js +27 -0
- package/lib/client/index.d.ts +1 -0
- package/lib/client/index.js +1 -0
- package/lib/client/options.d.ts +2 -0
- package/lib/client/options.js +1 -0
- package/lib/client/shim.d.ts +6 -0
- package/lib/client/utils/is.d.ts +3 -0
- package/lib/client/utils/is.js +13 -0
- package/lib/client/utils/link.d.ts +1 -0
- package/lib/client/utils/link.js +5 -0
- package/lib/node/features/caniuse.d.ts +25 -0
- package/lib/node/features/caniuse.js +129 -0
- package/lib/node/features/codepen.d.ts +7 -0
- package/lib/node/features/codepen.js +72 -0
- package/lib/node/features/icons/index.d.ts +2 -0
- package/lib/node/features/icons/index.js +2 -0
- package/lib/node/features/icons/plugin.d.ts +10 -0
- package/lib/node/features/icons/plugin.js +60 -0
- package/lib/node/features/icons/writer.d.ts +11 -0
- package/lib/node/features/icons/writer.js +100 -0
- package/lib/node/features/pdf.d.ts +2 -0
- package/lib/node/features/pdf.js +68 -0
- package/lib/node/features/replit.d.ts +7 -0
- package/lib/node/features/replit.js +59 -0
- package/lib/node/features/video/bilibili.d.ts +2 -0
- package/lib/node/features/video/bilibili.js +83 -0
- package/lib/node/features/video/youtube.d.ts +2 -0
- package/lib/node/features/video/youtube.js +74 -0
- package/lib/node/index.d.ts +2 -0
- package/lib/node/index.js +2 -0
- package/lib/node/markdown-it-container.d.ts +6 -0
- package/lib/node/plugin.d.ts +3 -0
- package/lib/node/plugin.js +55 -0
- package/lib/node/utils/package.d.ts +4 -0
- package/lib/node/utils/package.js +4 -0
- package/lib/node/utils/parseRect.d.ts +1 -0
- package/lib/node/utils/parseRect.js +5 -0
- package/lib/node/utils/resolveAttrs.d.ts +4 -0
- package/lib/node/utils/resolveAttrs.js +29 -0
- package/lib/node/utils/timeToSeconds.d.ts +1 -0
- package/lib/node/utils/timeToSeconds.js +8 -0
- package/lib/shared/caniuse.d.ts +18 -0
- package/lib/shared/caniuse.js +1 -0
- package/lib/shared/codepen.d.ts +10 -0
- package/lib/shared/codepen.js +1 -0
- package/lib/shared/icons.d.ts +17 -0
- package/lib/shared/icons.js +1 -0
- package/lib/shared/index.d.ts +6 -0
- package/lib/shared/index.js +6 -0
- package/lib/shared/pdf.d.ts +15 -0
- package/lib/shared/pdf.js +1 -0
- package/lib/shared/plugin.d.ts +12 -0
- package/lib/shared/plugin.js +1 -0
- package/lib/shared/replit.d.ts +6 -0
- package/lib/shared/replit.js +1 -0
- package/lib/shared/size.d.ts +5 -0
- package/lib/shared/size.js +1 -0
- package/lib/shared/video.d.ts +22 -0
- package/lib/shared/video.js +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (C) 2021 - PRESENT by 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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# vuepress-plugin-md-power
|
|
2
|
+
|
|
3
|
+
为 vuepress 提供 丰富的 markdown 语法支持。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- caniuse 支持,提供前端各种特性在各个浏览器版本中的支持情况查看器
|
|
8
|
+
- 嵌入 PDF 支持
|
|
9
|
+
- 嵌入 视频支持,当前支持嵌入 bilibili 和 youtube 的视频
|
|
10
|
+
- 内联 iconify 图标支持
|
|
11
|
+
|
|
12
|
+
## 安装
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
pnpm add vuepress-plugin-md-power
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 使用
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { defineUserConfig } from 'vuepress'
|
|
22
|
+
import { md } from 'vuepress-plugin-md-power'
|
|
23
|
+
|
|
24
|
+
export default defineUserConfig({
|
|
25
|
+
plugins: [
|
|
26
|
+
markdownPowerPlugin({
|
|
27
|
+
caniuse: true,
|
|
28
|
+
pdf: true,
|
|
29
|
+
bilibili: true,
|
|
30
|
+
youtube: true,
|
|
31
|
+
icons: true,
|
|
32
|
+
})
|
|
33
|
+
]
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### caniuse
|
|
38
|
+
|
|
39
|
+
插件默认不启用该功能,你需要手动设置 `caniuse` 为 `true`
|
|
40
|
+
|
|
41
|
+
#### 语法
|
|
42
|
+
|
|
43
|
+
```md
|
|
44
|
+
@[caniuse](feature)
|
|
45
|
+
@[caniuse image](feature)
|
|
46
|
+
@[caniuse embed{versions}](feature)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
你可以从 [caniuse](https://caniuse.bitsofco.de/) 获取 feature 的值。
|
|
50
|
+
|
|
51
|
+
默认情况下,插件通过 `iframe` 嵌入 `caniuse` 的支持情况查看器。
|
|
52
|
+
你也可以使用 `@[caniuse image](feature)` 直接嵌入图片。
|
|
53
|
+
|
|
54
|
+
caniuse 默认查看最近的5个浏览器版本。你可以通过 `{versions}` 手动设置查看的浏览器版本。
|
|
55
|
+
格式为 `{number,number,...}`。取值范围为 `-5 ~ 3` 。
|
|
56
|
+
|
|
57
|
+
- 小于0 表示低于当前浏览器版本的支持情况
|
|
58
|
+
- 0 表示当前浏览器版本的支持情况
|
|
59
|
+
- 大于0 表示高于当前浏览器版本的支持情况
|
|
60
|
+
|
|
61
|
+
如 `{-2,-1,1,2}` 表示查看低于当前 2 个版本 到 高于当前 2 个版本的支持情况。
|
|
62
|
+
|
|
63
|
+
### pdf
|
|
64
|
+
|
|
65
|
+
插件默认不启用该功能,你需要手动设置 `pdf` 为 `true`
|
|
66
|
+
|
|
67
|
+
#### 语法
|
|
68
|
+
|
|
69
|
+
```md
|
|
70
|
+
@[pdf](url)
|
|
71
|
+
@[pdf 1](url)
|
|
72
|
+
@[pdf 1 no-toolbar width="100%" height="600px" zoom="1" ratio="16:9"](url)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`url` 只支持绝对路径以及完整的资源链接地址,请勿传入相对路径。
|
|
76
|
+
|
|
77
|
+
你可以在 `pdf` 后紧跟空格,设置一个数字表示默认显示的 pdf 页码
|
|
78
|
+
|
|
79
|
+
- `no-toolbar` 表示不显示工具栏
|
|
80
|
+
- `width` 设置宽度
|
|
81
|
+
- `height` 设置高度
|
|
82
|
+
- `zoom` 设置缩放
|
|
83
|
+
- `ratio` 设置宽高比, 仅当 `width` 有值, `height` 未设置时有效
|
|
84
|
+
|
|
85
|
+
### icons
|
|
86
|
+
|
|
87
|
+
插件默认不启用该功能,你需要手动设置 `icons` 为 `true`。
|
|
88
|
+
|
|
89
|
+
你还需要手动安装 `@iconify/json` 依赖。
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
pnpm add @iconify/json
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### 语法
|
|
96
|
+
|
|
97
|
+
```md
|
|
98
|
+
:[collect:icon]:
|
|
99
|
+
:[collect:icon size]:
|
|
100
|
+
:[collect:icon /color]:
|
|
101
|
+
:[collect:icon size/color]:
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
你可以从 [icon-sets.iconify](https://icon-sets.iconify.design/) 获取 图标集。
|
|
105
|
+
|
|
106
|
+
显示 `logos` 图标集合下的 `vue` 图标
|
|
107
|
+
|
|
108
|
+
```md
|
|
109
|
+
:[logos:vue]:
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
图标默认大小为 `1em` ,你可以通过 `size` 设置图标大小
|
|
113
|
+
|
|
114
|
+
```md
|
|
115
|
+
:[logos:vue 1.2em]:
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
图标默认颜色为 `currentColor` 你可以通过 `/color` 设置图标颜色
|
|
119
|
+
|
|
120
|
+
```md
|
|
121
|
+
:[logos:vue /blue]:
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
也可以通过 `size/color` 设置图标大小和颜色
|
|
125
|
+
|
|
126
|
+
```md
|
|
127
|
+
:[logos:vue 1.2em/blue]:
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### bilibili
|
|
131
|
+
|
|
132
|
+
插件默认不启用该功能,你需要手动设置 `bilibili` 为 `true`
|
|
133
|
+
|
|
134
|
+
#### 语法
|
|
135
|
+
|
|
136
|
+
```md
|
|
137
|
+
@[bilibili](bvid)
|
|
138
|
+
@[bilibili autoplay time="0"](bvid)
|
|
139
|
+
@[bilibili p1 autoplay time="0" ratio="16:9"](aid cid)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- 设置 `autoplay` 以自动播放视频。
|
|
143
|
+
- 设置 `time` 以指定开始播放的时间点,单位为秒。还可以传入 `mm:ss` 或者 `hh:mm:ss`。
|
|
144
|
+
- 如果为 分p(非合集),还可以设置 `p\d` (第\d 个分p),此时可以只传入 `aid` 和 `cid`。
|
|
145
|
+
- 设置 `ratio` 以指定视频的宽高比。
|
|
146
|
+
|
|
147
|
+
### youtube
|
|
148
|
+
|
|
149
|
+
插件默认不启用该功能,你需要手动设置 `youtube` 为 `true`
|
|
150
|
+
|
|
151
|
+
#### 语法
|
|
152
|
+
|
|
153
|
+
```md
|
|
154
|
+
@[youtube](id)
|
|
155
|
+
@[youtube autoplay loop ratio="16:9" star="0" end="0"](id)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
- `id` 为 YouTube 视频 ID
|
|
159
|
+
- `autoplay` 为是否自动播放
|
|
160
|
+
- `loop` 为是否循环播放
|
|
161
|
+
- `ratio` 为视频的宽高比
|
|
162
|
+
- `star` 为开始时间,单位为秒,还可以传入 `mm:ss` 或者 `hh:mm:ss`。
|
|
163
|
+
- `end` 为结束时间,单位为秒,还可以传入 `mm:ss` 或者 `hh:mm:ss`。
|
|
164
|
+
|
|
165
|
+
### CodePen
|
|
166
|
+
|
|
167
|
+
插件默认不启用该功能,你需要手动设置 `codepen` 为 `true`
|
|
168
|
+
|
|
169
|
+
#### 语法
|
|
170
|
+
|
|
171
|
+
```md
|
|
172
|
+
@[codepen](user/slash)
|
|
173
|
+
@[codepen preview editable title="" height="400px" tab="css,result" theme="dark"](user/slash)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- `user` 为 CodePen 用户名
|
|
177
|
+
- `slash` 为 CodePen slash
|
|
178
|
+
- `preview` 为是否为预览模式
|
|
179
|
+
- `editable` 为是否为可编辑模式
|
|
180
|
+
- `title` 为标题
|
|
181
|
+
- `height` 为高度
|
|
182
|
+
- `tab` 为选项卡,默认为 `result`, 多个以逗号分隔,如 `css,result`
|
|
183
|
+
- `theme` 为主题, 可选值包括 `dark` 和 `light`
|
|
184
|
+
|
|
185
|
+
### Replit
|
|
186
|
+
|
|
187
|
+
插件默认不启用该功能,你需要手动设置 `replit` 为 `true`
|
|
188
|
+
|
|
189
|
+
#### 语法
|
|
190
|
+
|
|
191
|
+
```md
|
|
192
|
+
@[replit](user/repl-name)
|
|
193
|
+
@[replit title="" height="450px" theme="dark"](user/repl-name#filepath)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
- `user` 为 Replit 用户名
|
|
197
|
+
- `repl-name` 为 Replit Repl 名
|
|
198
|
+
- `filepath` 为文件路径
|
|
199
|
+
- `title` 为标题
|
|
200
|
+
- `height` 为高度
|
|
201
|
+
- `theme` 为主题, 可选值包括 `dark` 和 `light`
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { toRefs } from 'vue'
|
|
3
|
+
import { useSize } from '../composables/size.js'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
src: string
|
|
7
|
+
title: string
|
|
8
|
+
width?: string
|
|
9
|
+
height?: string
|
|
10
|
+
ratio?: string
|
|
11
|
+
}>()
|
|
12
|
+
|
|
13
|
+
const IFRAME_ALLOW = 'accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture'
|
|
14
|
+
|
|
15
|
+
const options = toRefs(props)
|
|
16
|
+
|
|
17
|
+
const { el, width, height, resize } = useSize(options)
|
|
18
|
+
|
|
19
|
+
function onLoad() {
|
|
20
|
+
resize()
|
|
21
|
+
}
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<ClientOnly>
|
|
26
|
+
<iframe
|
|
27
|
+
ref="el"
|
|
28
|
+
class="video_bilibili_iframe"
|
|
29
|
+
:src="src"
|
|
30
|
+
:title="title || 'Bilibili'"
|
|
31
|
+
:style="{ width, height }"
|
|
32
|
+
:allow="IFRAME_ALLOW"
|
|
33
|
+
@load="onLoad"
|
|
34
|
+
/>
|
|
35
|
+
</ClientOnly>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<style>
|
|
39
|
+
.video_bilibili_iframe {
|
|
40
|
+
width: 100%;
|
|
41
|
+
margin: 16px auto;
|
|
42
|
+
border: none;
|
|
43
|
+
border-radius: 5px;
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted, toRefs } from 'vue'
|
|
3
|
+
import type { PDFTokenMeta } from '../../shared/pdf.js'
|
|
4
|
+
import { useSize } from '../composables/size.js'
|
|
5
|
+
import { usePDF } from '../composables/pdf.js'
|
|
6
|
+
|
|
7
|
+
const props = defineProps<PDFTokenMeta>()
|
|
8
|
+
|
|
9
|
+
const options = toRefs(props)
|
|
10
|
+
const { el, width, height, resize } = useSize(options)
|
|
11
|
+
|
|
12
|
+
onMounted(() => {
|
|
13
|
+
if (!el.value)
|
|
14
|
+
return
|
|
15
|
+
usePDF(el.value, props.src!, {
|
|
16
|
+
page: props.page,
|
|
17
|
+
zoom: props.zoom,
|
|
18
|
+
noToolbar: props.noToolbar,
|
|
19
|
+
})
|
|
20
|
+
resize()
|
|
21
|
+
})
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<div ref="el" class="pdf-viewer-wrapper" :style="{ width, height }" />
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<style>
|
|
29
|
+
.pdf-viewer-wrapper {
|
|
30
|
+
position: relative;
|
|
31
|
+
overflow: hidden;
|
|
32
|
+
border-radius: 4px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.pdf-viewer {
|
|
36
|
+
width: 100%;
|
|
37
|
+
height: 100%;
|
|
38
|
+
}
|
|
39
|
+
</style>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, getCurrentInstance, ref } from 'vue'
|
|
3
|
+
import type { ReplitTokenMeta } from '../../shared/replit.js'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<ReplitTokenMeta>()
|
|
6
|
+
|
|
7
|
+
const current = getCurrentInstance()
|
|
8
|
+
// magic height
|
|
9
|
+
const height = ref('47px')
|
|
10
|
+
|
|
11
|
+
const REPLIT_LINK = 'https://replit.com/'
|
|
12
|
+
|
|
13
|
+
const isDark = computed(() => current?.appContext.config.globalProperties.$isDark.value)
|
|
14
|
+
|
|
15
|
+
const link = computed(() => {
|
|
16
|
+
const url = new URL(`/${props.source}`, REPLIT_LINK)
|
|
17
|
+
url.searchParams.set('embed', 'true')
|
|
18
|
+
|
|
19
|
+
const theme = props.theme || (isDark.value ? 'dark' : 'light')
|
|
20
|
+
url.searchParams.set('theme', theme)
|
|
21
|
+
|
|
22
|
+
return url.toString()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
function onload() {
|
|
26
|
+
height.value = props.height || '450px'
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<ClientOnly>
|
|
32
|
+
<iframe
|
|
33
|
+
class="replit-iframe-wrapper"
|
|
34
|
+
:src="link"
|
|
35
|
+
:title="title || 'Replit'"
|
|
36
|
+
:style="{ width, height }"
|
|
37
|
+
allowtransparency="true"
|
|
38
|
+
allowfullscree="true"
|
|
39
|
+
@load="onload"
|
|
40
|
+
/>
|
|
41
|
+
</ClientOnly>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<style>
|
|
45
|
+
.replit-iframe-wrapper {
|
|
46
|
+
width: 100%;
|
|
47
|
+
margin: 16px auto;
|
|
48
|
+
border: none;
|
|
49
|
+
border-top: 1px solid var(--vp-c-divider);
|
|
50
|
+
border-bottom-right-radius: 8px;
|
|
51
|
+
border-bottom-left-radius: 8px;
|
|
52
|
+
transition: border 0.25s;
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { toRefs } from 'vue'
|
|
3
|
+
import { useSize } from '../composables/size.js'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
src: string
|
|
7
|
+
title: string
|
|
8
|
+
width?: string
|
|
9
|
+
height?: string
|
|
10
|
+
ratio?: string
|
|
11
|
+
}>()
|
|
12
|
+
|
|
13
|
+
const IFRAME_ALLOW = 'accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture'
|
|
14
|
+
|
|
15
|
+
const options = toRefs(props)
|
|
16
|
+
|
|
17
|
+
const { el, width, height, resize } = useSize(options)
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<ClientOnly>
|
|
22
|
+
<iframe
|
|
23
|
+
ref="el"
|
|
24
|
+
class="video_youtube_iframe"
|
|
25
|
+
:src="src"
|
|
26
|
+
:title="title || 'Youtube'"
|
|
27
|
+
:style="{ width, height }"
|
|
28
|
+
:allow="IFRAME_ALLOW"
|
|
29
|
+
@load="resize"
|
|
30
|
+
/>
|
|
31
|
+
</ClientOnly>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<style>
|
|
35
|
+
.video_youtube_iframe {
|
|
36
|
+
width: 100%;
|
|
37
|
+
margin: 16px auto;
|
|
38
|
+
border: none;
|
|
39
|
+
border-radius: 5px;
|
|
40
|
+
}
|
|
41
|
+
</style>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { PDFEmbedType, PDFTokenMeta } from '../../shared/pdf.js';
|
|
2
|
+
export declare function renderPDF(el: HTMLElement, url: string, embedType: PDFEmbedType, options: PDFTokenMeta): void;
|
|
3
|
+
export declare function usePDF(el: HTMLElement, url: string, options: PDFTokenMeta): void;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ensureEndingSlash } from 'vuepress/shared';
|
|
2
|
+
import { withBase } from 'vuepress/client';
|
|
3
|
+
import { normalizeLink } from '../utils/link.js';
|
|
4
|
+
import { pluginOptions } from '../options.js';
|
|
5
|
+
import { checkIsMobile, checkIsSafari, checkIsiPad } from '../utils/is.js';
|
|
6
|
+
function queryStringify(options) {
|
|
7
|
+
const { page, noToolbar, zoom } = options;
|
|
8
|
+
const params = [
|
|
9
|
+
`page=${page}`,
|
|
10
|
+
`toolbar=${noToolbar ? 0 : 1}`,
|
|
11
|
+
`zoom=${zoom}`,
|
|
12
|
+
];
|
|
13
|
+
let queryString = params.join('&');
|
|
14
|
+
if (queryString)
|
|
15
|
+
queryString = `#${queryString}`;
|
|
16
|
+
return queryString;
|
|
17
|
+
}
|
|
18
|
+
export function renderPDF(el, url, embedType, options) {
|
|
19
|
+
if (!pluginOptions.pdf)
|
|
20
|
+
return;
|
|
21
|
+
url = normalizeLink(url);
|
|
22
|
+
const pdfOptions = pluginOptions.pdf === true ? {} : pluginOptions.pdf;
|
|
23
|
+
const pdfjsUrl = pdfOptions.pdfjsUrl
|
|
24
|
+
? `${ensureEndingSlash(withBase(pdfOptions.pdfjsUrl))}web/viewer.html`
|
|
25
|
+
: '';
|
|
26
|
+
const queryString = queryStringify(options);
|
|
27
|
+
const source = embedType === 'pdfjs'
|
|
28
|
+
? `${pdfjsUrl}?file=${encodeURIComponent(url)}${queryString}`
|
|
29
|
+
: `${url}${queryString}`;
|
|
30
|
+
const tagName = embedType === 'pdfjs' || embedType === 'iframe'
|
|
31
|
+
? 'iframe'
|
|
32
|
+
: 'embed';
|
|
33
|
+
el.innerHTML = '';
|
|
34
|
+
const pdf = document.createElement(tagName);
|
|
35
|
+
pdf.className = 'pdf-viewer';
|
|
36
|
+
pdf.type = 'application/pdf';
|
|
37
|
+
pdf.title = options.title || 'PDF Viewer';
|
|
38
|
+
pdf.src = source;
|
|
39
|
+
if (pdf instanceof HTMLIFrameElement)
|
|
40
|
+
pdf.allow = 'fullscreen';
|
|
41
|
+
el.appendChild(pdf);
|
|
42
|
+
}
|
|
43
|
+
export function usePDF(el, url, options) {
|
|
44
|
+
if (typeof window === 'undefined' || !window?.navigator?.userAgent)
|
|
45
|
+
return;
|
|
46
|
+
const { navigator } = window;
|
|
47
|
+
const { userAgent } = navigator;
|
|
48
|
+
const isModernBrowser = typeof window.Promise === 'function';
|
|
49
|
+
// Quick test for mobile devices.
|
|
50
|
+
const isMobileDevice = checkIsiPad(userAgent) || checkIsMobile(userAgent);
|
|
51
|
+
// Safari desktop requires special handling
|
|
52
|
+
const isSafariDesktop = !isMobileDevice && checkIsSafari(userAgent);
|
|
53
|
+
const isFirefoxWithPDFJS = !isMobileDevice
|
|
54
|
+
&& /firefox/iu.test(userAgent)
|
|
55
|
+
&& userAgent.split('rv:').length > 1
|
|
56
|
+
? Number.parseInt(userAgent.split('rv:')[1].split('.')[0], 10) > 18
|
|
57
|
+
: false;
|
|
58
|
+
// Determines whether PDF support is available
|
|
59
|
+
const supportsPDFs
|
|
60
|
+
// As of Sept 2020 no mobile browsers properly support PDF embeds
|
|
61
|
+
= !isMobileDevice
|
|
62
|
+
// We're moving into the age of MIME-less browsers. They mostly all support PDF rendering without plugins.
|
|
63
|
+
&& (isModernBrowser
|
|
64
|
+
// Modern versions of Firefox come bundled with PDFJS
|
|
65
|
+
|| isFirefoxWithPDFJS);
|
|
66
|
+
if (!url)
|
|
67
|
+
return;
|
|
68
|
+
if (supportsPDFs || !isMobileDevice) {
|
|
69
|
+
const embedType = isSafariDesktop ? 'iframe' : 'embed';
|
|
70
|
+
return renderPDF(el, url, embedType, options);
|
|
71
|
+
}
|
|
72
|
+
if (typeof pluginOptions.pdf === 'object' && pluginOptions.pdf.pdfjsUrl)
|
|
73
|
+
return renderPDF(el, url, 'pdfjs', options);
|
|
74
|
+
el.innerHTML = `<p>This browser does not support embedding PDFs. Please download the PDF to view it: <a href='${url}' target='_blank'>Download PDF</a></p>`;
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function setupCanIUse(): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
let isBind = false;
|
|
2
|
+
export function setupCanIUse() {
|
|
3
|
+
if (isBind)
|
|
4
|
+
return;
|
|
5
|
+
isBind = true;
|
|
6
|
+
window.addEventListener('message', (message) => {
|
|
7
|
+
const data = message.data;
|
|
8
|
+
if (typeof data === 'string' && data.includes('ciu_embed')) {
|
|
9
|
+
const [, feature, height] = data.split(':');
|
|
10
|
+
const el = document.querySelector(`.ciu_embed[data-feature="${feature}"]:not([data-skip])`);
|
|
11
|
+
if (el) {
|
|
12
|
+
const h = Number.parseInt(height) + 30;
|
|
13
|
+
el.childNodes[0].height = `${h}px`;
|
|
14
|
+
el.setAttribute('data-skip', 'true');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MaybeRef } from '@vueuse/core';
|
|
2
|
+
import type { Ref, ShallowRef, ToRefs } from 'vue';
|
|
3
|
+
import type { SizeOptions } from '../../shared/size.js';
|
|
4
|
+
export interface SizeInfo<T extends HTMLElement> {
|
|
5
|
+
el: ShallowRef<T | undefined>;
|
|
6
|
+
width: Ref<string>;
|
|
7
|
+
height: Ref<string>;
|
|
8
|
+
resize: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function useSize<T extends HTMLElement>(options: ToRefs<SizeOptions>, extraHeight?: MaybeRef<number>): SizeInfo<T>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useEventListener } from '@vueuse/core';
|
|
2
|
+
import { computed, isRef, onMounted, ref, shallowRef, toValue, watch } from 'vue';
|
|
3
|
+
export function useSize(options, extraHeight = 0) {
|
|
4
|
+
const el = shallowRef();
|
|
5
|
+
const width = computed(() => toValue(options.width) || '100%');
|
|
6
|
+
const height = ref('auto');
|
|
7
|
+
const getRadio = (ratio) => {
|
|
8
|
+
if (typeof ratio === 'string') {
|
|
9
|
+
const [width, height] = ratio.split(':');
|
|
10
|
+
const parsedRadio = Number(width) / Number(height);
|
|
11
|
+
if (!Number.isNaN(parsedRadio))
|
|
12
|
+
return parsedRadio;
|
|
13
|
+
}
|
|
14
|
+
return typeof ratio === 'number' ? ratio : 16 / 9;
|
|
15
|
+
};
|
|
16
|
+
const getHeight = (width) => {
|
|
17
|
+
const height = toValue(options.height);
|
|
18
|
+
const ratio = getRadio(toValue(options.ratio));
|
|
19
|
+
return height || `${Number(width) / ratio + toValue(extraHeight)}px`;
|
|
20
|
+
};
|
|
21
|
+
const resize = () => {
|
|
22
|
+
if (el.value)
|
|
23
|
+
height.value = getHeight(el.value.offsetWidth);
|
|
24
|
+
};
|
|
25
|
+
onMounted(() => {
|
|
26
|
+
resize();
|
|
27
|
+
if (isRef(extraHeight))
|
|
28
|
+
watch(extraHeight, resize);
|
|
29
|
+
useEventListener('orientationchange', resize);
|
|
30
|
+
useEventListener('resize', resize);
|
|
31
|
+
});
|
|
32
|
+
return { el, width, height, resize };
|
|
33
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineClientConfig } from 'vuepress/client';
|
|
2
|
+
import { pluginOptions } from './options.js';
|
|
3
|
+
import { setupCanIUse } from './composables/setupCanIUse.js';
|
|
4
|
+
import PDFViewer from './components/PDFViewer.vue';
|
|
5
|
+
import Bilibili from './components/Bilibili.vue';
|
|
6
|
+
import Youtube from './components/Youtube.vue';
|
|
7
|
+
import Replit from './components/Replit.vue';
|
|
8
|
+
import '@internal/md-power/icons.css';
|
|
9
|
+
export default defineClientConfig({
|
|
10
|
+
enhance({ router, app }) {
|
|
11
|
+
if (pluginOptions.pdf)
|
|
12
|
+
app.component('PDFViewer', PDFViewer);
|
|
13
|
+
if (pluginOptions.bilibili)
|
|
14
|
+
app.component('VideoBilibili', Bilibili);
|
|
15
|
+
if (pluginOptions.youtube)
|
|
16
|
+
app.component('VideoYoutube', Youtube);
|
|
17
|
+
if (pluginOptions.replit)
|
|
18
|
+
app.component('ReplitViewer', Replit);
|
|
19
|
+
if (__VUEPRESS_SSR__)
|
|
20
|
+
return;
|
|
21
|
+
if (pluginOptions.caniuse) {
|
|
22
|
+
router.afterEach(() => {
|
|
23
|
+
setupCanIUse();
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../shared/index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../shared/index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const pluginOptions = __MD_POWER_INJECT_OPTIONS__;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function checkIsMobile(ua) {
|
|
2
|
+
return /\b(?:Android|iPhone)/i.test(ua);
|
|
3
|
+
}
|
|
4
|
+
export function checkIsSafari(ua) {
|
|
5
|
+
return /version\/([\w.]+) .*(mobile ?safari|safari)/i.test(ua);
|
|
6
|
+
}
|
|
7
|
+
export function checkIsiPad(ua) {
|
|
8
|
+
return [
|
|
9
|
+
/\((ipad);[-\w),; ]+apple/i,
|
|
10
|
+
/applecoremedia\/[\w.]+ \((ipad)/i,
|
|
11
|
+
/\b(ipad)\d\d?,\d\d?[;\]].+ios/i,
|
|
12
|
+
].some(item => item.test(ua));
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function normalizeLink(url: string): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @[caniuse embed{1,2,3,4}](feature_name)
|
|
3
|
+
* @[caniuse image](feature_name)
|
|
4
|
+
*/
|
|
5
|
+
import type { PluginWithOptions } from 'markdown-it';
|
|
6
|
+
import type { Markdown } from 'vuepress/markdown';
|
|
7
|
+
import type { CanIUseOptions } from '../../shared/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* @example
|
|
10
|
+
* ```md
|
|
11
|
+
* @[caniuse](feature_name)
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare const caniusePlugin: PluginWithOptions<CanIUseOptions>;
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated use caniuse plugin
|
|
17
|
+
*
|
|
18
|
+
* 兼容旧语法
|
|
19
|
+
* @example
|
|
20
|
+
* ```md
|
|
21
|
+
* :::caniuse <feature_name>
|
|
22
|
+
* :::
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function legacyCaniuse(md: Markdown, { mode }?: CanIUseOptions): void;
|