@sugarat/theme 0.4.12 → 0.4.13

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/node.d.ts CHANGED
@@ -1,38 +1,12 @@
1
- import { Route, DefaultTheme, UserConfig } from 'vitepress';
2
- import { ElButton } from 'element-plus';
1
+ import { DefaultTheme, UserConfig } from 'vitepress';
3
2
  import { RSSOptions } from 'vitepress-plugin-rss';
4
3
  import { Repo, Mapping } from '@giscus/vue';
5
4
  import { Options } from 'oh-my-live2d';
6
- import { Ref } from 'vue';
7
5
  import { PagefindConfig } from 'vitepress-plugin-pagefind';
6
+ import { AnnouncementOptions } from 'vitepress-plugin-announcement';
8
7
  export { tabsMarkdownPlugin } from 'vitepress-plugin-tabs';
9
8
 
10
9
  type RSSPluginOptions = RSSOptions;
11
- declare namespace BlogPopover {
12
- interface Title {
13
- type: 'title';
14
- content: string;
15
- style?: string;
16
- }
17
- interface Text {
18
- type: 'text';
19
- content: string;
20
- style?: string;
21
- }
22
- interface Image {
23
- type: 'image';
24
- src: string;
25
- style?: string;
26
- }
27
- interface Button {
28
- type: 'button';
29
- link: string;
30
- content: string;
31
- style?: string;
32
- props?: InstanceType<typeof ElButton>['$props'];
33
- }
34
- type Value = Title | Text | Image | Button;
35
- }
36
10
  type ThemeableImage = string | {
37
11
  src: string;
38
12
  alt?: string;
@@ -279,50 +253,6 @@ declare namespace Theme {
279
253
  showIcon?: boolean;
280
254
  html?: string;
281
255
  }
282
- /**
283
- * 公告
284
- */
285
- interface Popover {
286
- title: string;
287
- /**
288
- * 细粒度的时间控制
289
- * 默认展示时间,-1 只展示1次,其它数字为每次都展示,一定时间后自动消失,0为不自动消失
290
- * 配置改变时,会重新触发展示
291
- */
292
- duration: number;
293
- /**
294
- * 移动端自动最小化
295
- * @default false
296
- */
297
- mobileMinify?: boolean;
298
- body?: BlogPopover.Value[];
299
- footer?: BlogPopover.Value[];
300
- /**
301
- * 手动重新打开
302
- * @default true
303
- */
304
- reopen?: boolean;
305
- /**
306
- * 是否打开闪烁提示,通常需要和 reopen 搭配使用
307
- * @default true
308
- */
309
- twinkle?: boolean;
310
- /**
311
- * 设置展示图标,svg
312
- * @recommend https://iconbuddy.app/search?q=fire
313
- */
314
- icon?: string;
315
- /**
316
- * 设置关闭图标,svg
317
- * @recommend https://iconbuddy.app/search?q=fire
318
- */
319
- closeIcon?: string;
320
- /**
321
- * 自定义展示策略
322
- * @param to 切换到的目标路由
323
- */
324
- onRouteChanged?: (to: Route, show: Ref<boolean>) => void;
325
- }
326
256
  interface FriendLink {
327
257
  nickname: string;
328
258
  des: string;
@@ -428,7 +358,7 @@ declare namespace Theme {
428
358
  * el-alert
429
359
  */
430
360
  alert?: Alert;
431
- popover?: Popover;
361
+ popover?: AnnouncementOptions;
432
362
  friend?: FriendLink[] | FriendConfig;
433
363
  authorList?: Omit<FriendLink, 'avatar'>[];
434
364
  /**
package/node.js CHANGED
@@ -464,6 +464,7 @@ var import_node_buffer = require("buffer");
464
464
  var import_vitepress_plugin_pagefind = require("vitepress-plugin-pagefind");
465
465
  var import_vitepress_plugin_rss = require("vitepress-plugin-rss");
466
466
  var import_theme_shared3 = require("@sugarat/theme-shared");
467
+ var import_vitepress_plugin_announcement = require("vitepress-plugin-announcement");
467
468
 
468
469
  // src/utils/node/hot-reload-plugin.ts
469
470
  function themeReloadPlugin() {
@@ -541,6 +542,9 @@ function getVitePlugins(cfg = {}) {
541
542
  ;
542
543
  [cfg?.RSS].flat().forEach((rssConfig) => plugins.push((0, import_vitepress_plugin_rss.RssPlugin)(rssConfig)));
543
544
  }
545
+ if (cfg?.popover) {
546
+ plugins.push((0, import_vitepress_plugin_announcement.AnnouncementPlugin)(cfg.popover));
547
+ }
544
548
  return plugins;
545
549
  }
546
550
  function registerVitePlugins(vpCfg, plugins) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sugarat/theme",
3
- "version": "0.4.12",
3
+ "version": "0.4.13",
4
4
  "description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
5
5
  "author": "sugar",
6
6
  "license": "MIT",
@@ -51,8 +51,9 @@
51
51
  "vitepress-plugin-mermaid": "2.0.13",
52
52
  "vitepress-plugin-tabs": "0.2.0",
53
53
  "@sugarat/theme-shared": "0.0.2",
54
- "vitepress-plugin-pagefind": "0.4.10",
55
- "vitepress-plugin-rss": "0.2.10"
54
+ "vitepress-plugin-rss": "0.2.10",
55
+ "vitepress-plugin-announcement": "0.1.1",
56
+ "vitepress-plugin-pagefind": "0.4.10"
56
57
  },
57
58
  "devDependencies": {
58
59
  "@element-plus/icons-vue": "^2.3.1",
@@ -11,7 +11,6 @@ import BlogSidebar from './BlogSidebar.vue'
11
11
  import BlogImagePreview from './BlogImagePreview.vue'
12
12
  import BlogArticleAnalyze from './BlogArticleAnalyze.vue'
13
13
  import BlogAlert from './BlogAlert.vue'
14
- import BlogPopover from './BlogPopover.vue'
15
14
  import BlogFooter from './BlogFooter.vue'
16
15
  import BlogHomeHeaderAvatar from './BlogHomeHeaderAvatar.vue'
17
16
  import BlogBackToTop from './BlogBackToTop.vue'
@@ -40,7 +39,6 @@ const openTransition = useDarkTransitionConfig()
40
39
  <ClientOnly>
41
40
  <BlogOml2d />
42
41
  <BlogAlert />
43
- <BlogPopover />
44
42
  </ClientOnly>
45
43
  </template>
46
44
 
@@ -1,45 +1,13 @@
1
1
  /* eslint-disable ts/no-namespace */
2
- import type { ElButton } from 'element-plus'
3
- import type { DefaultTheme, Route } from 'vitepress'
2
+ import type { DefaultTheme } from 'vitepress'
4
3
  import type { RSSOptions } from 'vitepress-plugin-rss'
5
4
  import type { Mapping, Repo } from '@giscus/vue'
6
5
  import type { Options as Oml2dOptions } from 'oh-my-live2d'
7
- import type { Ref } from 'vue'
8
6
  import type { PagefindConfig } from 'vitepress-plugin-pagefind'
7
+ import type { AnnouncementOptions } from 'vitepress-plugin-announcement'
9
8
 
10
9
  type RSSPluginOptions = RSSOptions
11
10
 
12
- // TODO: 重构 lint 问题
13
- export declare namespace BlogPopover {
14
- export interface Title {
15
- type: 'title'
16
- content: string
17
- style?: string
18
- }
19
-
20
- export interface Text {
21
- type: 'text'
22
- content: string
23
- style?: string
24
- }
25
-
26
- export interface Image {
27
- type: 'image'
28
- src: string
29
- style?: string
30
- }
31
-
32
- export interface Button {
33
- type: 'button'
34
- link: string
35
- content: string
36
- style?: string
37
- props?: InstanceType<typeof ElButton>['$props']
38
- }
39
-
40
- export type Value = Title | Text | Image | Button
41
- }
42
-
43
11
  export type ThemeableImage =
44
12
  | string
45
13
  | { src: string; alt?: string }
@@ -293,50 +261,6 @@ export namespace Theme {
293
261
  html?: string
294
262
  }
295
263
 
296
- /**
297
- * 公告
298
- */
299
- export interface Popover {
300
- title: string
301
- /**
302
- * 细粒度的时间控制
303
- * 默认展示时间,-1 只展示1次,其它数字为每次都展示,一定时间后自动消失,0为不自动消失
304
- * 配置改变时,会重新触发展示
305
- */
306
- duration: number
307
- /**
308
- * 移动端自动最小化
309
- * @default false
310
- */
311
- mobileMinify?: boolean
312
- body?: BlogPopover.Value[]
313
- footer?: BlogPopover.Value[]
314
- /**
315
- * 手动重新打开
316
- * @default true
317
- */
318
- reopen?: boolean
319
- /**
320
- * 是否打开闪烁提示,通常需要和 reopen 搭配使用
321
- * @default true
322
- */
323
- twinkle?: boolean
324
- /**
325
- * 设置展示图标,svg
326
- * @recommend https://iconbuddy.app/search?q=fire
327
- */
328
- icon?: string
329
- /**
330
- * 设置关闭图标,svg
331
- * @recommend https://iconbuddy.app/search?q=fire
332
- */
333
- closeIcon?: string
334
- /**
335
- * 自定义展示策略
336
- * @param to 切换到的目标路由
337
- */
338
- onRouteChanged?: (to: Route, show: Ref<boolean>) => void
339
- }
340
264
  export interface FriendLink {
341
265
  nickname: string
342
266
  des: string
@@ -462,7 +386,7 @@ export namespace Theme {
462
386
  * el-alert
463
387
  */
464
388
  alert?: Alert
465
- popover?: Popover
389
+ popover?: AnnouncementOptions
466
390
  friend?: FriendLink[] | FriendConfig
467
391
  authorList?: Omit<FriendLink, 'avatar'>[]
468
392
  /**
@@ -8,6 +8,7 @@ import {
8
8
  import { RssPlugin } from 'vitepress-plugin-rss'
9
9
  import type { PluginOption } from 'vite'
10
10
  import { joinPath } from '@sugarat/theme-shared'
11
+ import { AnnouncementPlugin } from 'vitepress-plugin-announcement'
11
12
  import type { Theme } from '../../composables/config/index'
12
13
  import { _require } from './mdPlugins'
13
14
  import { themeReloadPlugin } from './hot-reload-plugin'
@@ -50,6 +51,10 @@ export function getVitePlugins(cfg: Partial<Theme.BlogConfig> = {}) {
50
51
  if (cfg?.RSS) {
51
52
  ;[cfg?.RSS].flat().forEach(rssConfig => plugins.push(RssPlugin(rssConfig)))
52
53
  }
54
+
55
+ if (cfg?.popover) {
56
+ plugins.push(AnnouncementPlugin(cfg.popover))
57
+ }
53
58
  return plugins
54
59
  }
55
60
 
@@ -1,290 +0,0 @@
1
- <script lang="ts" setup>
2
- import { ElButton, ElIcon } from 'element-plus'
3
- import { CircleCloseFilled } from '@element-plus/icons-vue'
4
- import { computed, h, onMounted, ref, watch } from 'vue'
5
- import type { BlogPopover } from '@sugarat/theme'
6
- import { parseStringStyle } from '@vue/shared'
7
- import { useDebounceFn, useWindowSize } from '@vueuse/core'
8
- import { useRoute, useRouter } from 'vitepress'
9
- import { useBlogConfig } from '../composables/config/blog'
10
- import { vOuterHtml } from '../directives'
11
-
12
- const { popover: popoverProps } = useBlogConfig()
13
-
14
- const show = ref(false)
15
-
16
- const bodyContent = computed(() => {
17
- return popoverProps?.body || []
18
- })
19
-
20
- const footerContent = computed(() => {
21
- return popoverProps?.footer || []
22
- })
23
- const storageKey = 'theme-blog-popover'
24
- const closeFlag = `${storageKey}-close`
25
-
26
- // 移动端最小化
27
- const { width } = useWindowSize()
28
- const router = useRouter()
29
- const route = useRoute()
30
- onMounted(() => {
31
- if (!popoverProps?.title) {
32
- return
33
- }
34
-
35
- // 取旧值
36
- const oldValue = localStorage.getItem(storageKey)
37
- const newValue = JSON.stringify(popoverProps)
38
- localStorage.setItem(storageKey, newValue)
39
-
40
- // 移动端最小化
41
- if (width.value < 768 && popoverProps?.mobileMinify) {
42
- show.value = false
43
- return
44
- }
45
-
46
- // >= 0 每次都展示,区别是否自动消失
47
- if (Number(popoverProps?.duration ?? '') >= 0) {
48
- show.value = true
49
- if (popoverProps?.duration) {
50
- setTimeout(() => {
51
- show.value = false
52
- }, popoverProps?.duration)
53
- }
54
- return
55
- }
56
-
57
- if (oldValue !== newValue && popoverProps?.duration === -1) {
58
- // 当做新值处理
59
- show.value = true
60
- localStorage.removeItem(closeFlag)
61
- return
62
- }
63
-
64
- // 新旧相等,判断是否点击过close,没点击关闭依然展示
65
- if (oldValue === newValue && popoverProps?.duration === -1 && !localStorage.getItem(closeFlag)) {
66
- show.value = true
67
- }
68
- })
69
-
70
- const onAfterRouteChanged = useDebounceFn(() => {
71
- popoverProps?.onRouteChanged?.(route, show)
72
- }, 10)
73
-
74
- watch(route, onAfterRouteChanged, { immediate: true })
75
-
76
- function handleClose() {
77
- show.value = false
78
- if (popoverProps?.duration === -1) {
79
- localStorage.setItem(closeFlag, `${+new Date()}`)
80
- }
81
- }
82
-
83
- function PopoverValue(props: { key: number; item: BlogPopover.Value },
84
- { slots }: any) {
85
- const { key, item } = props
86
- if (item.type === 'title') {
87
- return h(
88
- 'h4',
89
- {
90
- style: parseStringStyle(item.style || '')
91
- },
92
- item.content
93
- )
94
- }
95
- if (item.type === 'text') {
96
- return h(
97
- 'p',
98
- {
99
- style: parseStringStyle(item.style || '')
100
- },
101
- item.content
102
- )
103
- }
104
- if (item.type === 'image') {
105
- return h('img', {
106
- src: item.src,
107
- style: parseStringStyle(item.style || '')
108
- })
109
- }
110
- if (item.type === 'button') {
111
- return h(
112
- ElButton,
113
- {
114
- type: 'primary',
115
- onClick: () => {
116
- if (/^\s*http(s)?:\/\//.test(item.link)) {
117
- window.open(item.link)
118
- }
119
- else {
120
- router.go(item.link)
121
- }
122
- },
123
- style: parseStringStyle(item.style || ''),
124
- ...item.props
125
- },
126
- slots
127
- )
128
- }
129
- return h(
130
- 'div',
131
- {
132
- key
133
- },
134
- ''
135
- )
136
- }
137
- </script>
138
-
139
- <template>
140
- <div v-show="show" class="theme-blog-popover" data-pagefind-ignore="all">
141
- <div class="header">
142
- <div class="title-wrapper">
143
- <ElIcon size="20px">
144
- <i v-if="popoverProps?.icon" v-outer-html="popoverProps.icon" />
145
- <svg v-else t="1716085184855" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4274" width="200" height="200"><path d="M660.48 872.448q6.144 0-3.584 15.36t-29.696 33.792-47.104 33.792-57.856 15.36q-27.648 0-53.248-15.36t-45.056-33.792-29.696-33.792-6.144-15.36l272.384 0zM914.432 785.408q7.168 9.216 6.656 17.92t-4.608 14.848-10.24 9.728-12.288 3.584l-747.52 0q-14.336 0-20.992-11.776t4.608-29.184q17.408-30.72 40.96-68.608t44.544-81.408 36.352-92.16 15.36-101.888q0-51.2 14.336-92.16t37.376-71.68 53.248-52.224 62.976-32.768q-16.384-26.624-16.384-55.296 0-41.984 28.672-70.656t70.656-28.672 70.656 28.672 28.672 70.656q0 14.336-4.096 28.16t-11.264 25.088q34.816 11.264 66.048 32.768t54.272 53.248 36.864 72.704 13.824 91.136q0 51.2 15.36 100.864t36.864 94.208 45.568 81.408 43.52 63.488zM478.208 142.336q0 16.384 11.264 28.16t27.648 11.776l2.048 0q16.384-1.024 27.648-12.288t11.264-27.648q0-17.408-11.264-28.672t-28.672-11.264-28.672 11.264-11.264 28.672z" p-id="4275" /></svg>
146
- </ElIcon>
147
- <span class="title">{{ popoverProps?.title }}</span>
148
- </div>
149
- <ElIcon class="close-icon" size="20px" @click="handleClose">
150
- <i v-if="popoverProps?.closeIcon" v-outer-html="popoverProps.closeIcon" />
151
- <CircleCloseFilled v-else />
152
- </ElIcon>
153
- </div>
154
- <div v-if="bodyContent.length" class="body content">
155
- <PopoverValue v-for="(v, idx) in bodyContent" :key="idx" :item="v">
156
- {{ v.type !== 'image' ? v.content : '' }}
157
- </PopoverValue>
158
- <hr v-if="footerContent.length">
159
- </div>
160
- <div class="footer content">
161
- <PopoverValue v-for="(v, idx) in footerContent" :key="idx" :item="v">
162
- {{ v.type !== 'image' ? v.content : '' }}
163
- </PopoverValue>
164
- </div>
165
- </div>
166
- <div
167
- v-show="!show && (popoverProps?.reopen ?? true) && popoverProps?.title" class="theme-blog-popover-close"
168
- :class="{ twinkle: !show && (popoverProps?.twinkle ?? true) }"
169
- @click="show = true"
170
- >
171
- <ElIcon>
172
- <i v-if="popoverProps?.icon" v-outer-html="popoverProps.icon" />
173
- <svg v-else t="1716085184855" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4274" width="200" height="200"><path d="M660.48 872.448q6.144 0-3.584 15.36t-29.696 33.792-47.104 33.792-57.856 15.36q-27.648 0-53.248-15.36t-45.056-33.792-29.696-33.792-6.144-15.36l272.384 0zM914.432 785.408q7.168 9.216 6.656 17.92t-4.608 14.848-10.24 9.728-12.288 3.584l-747.52 0q-14.336 0-20.992-11.776t4.608-29.184q17.408-30.72 40.96-68.608t44.544-81.408 36.352-92.16 15.36-101.888q0-51.2 14.336-92.16t37.376-71.68 53.248-52.224 62.976-32.768q-16.384-26.624-16.384-55.296 0-41.984 28.672-70.656t70.656-28.672 70.656 28.672 28.672 70.656q0 14.336-4.096 28.16t-11.264 25.088q34.816 11.264 66.048 32.768t54.272 53.248 36.864 72.704 13.824 91.136q0 51.2 15.36 100.864t36.864 94.208 45.568 81.408 43.52 63.488zM478.208 142.336q0 16.384 11.264 28.16t27.648 11.776l2.048 0q16.384-1.024 27.648-12.288t11.264-27.648q0-17.408-11.264-28.672t-28.672-11.264-28.672 11.264-11.264 28.672z" p-id="4275" /></svg>
174
- </ElIcon>
175
- </div>
176
- </template>
177
-
178
- <style lang="scss" scoped>
179
- .theme-blog-popover {
180
- width: 258px;
181
- position: fixed;
182
- top: 80px;
183
- right: 20px;
184
- z-index: 22;
185
- box-sizing: border-box;
186
- border: 1px solid var(--vp-c-brand-3);
187
- border-radius: 6px;
188
- background-color: rgba(var(--bg-gradient-home));
189
- box-shadow: var(--box-shadow);
190
-
191
- :deep(.el-button.el-button--primary) {
192
- background-color: var(--vp-c-brand-2);
193
- border-color: var(--vp-c-brand-2);
194
- }
195
- }
196
-
197
- .header {
198
- background-color: var(--vp-c-brand-3);
199
- color: #fff;
200
- padding: 6px 4px;
201
- display: flex;
202
- justify-content: space-between;
203
- align-items: center;
204
-
205
- .close-icon {
206
- cursor: pointer;
207
- }
208
- }
209
-
210
- .title-wrapper {
211
- display: flex;
212
- align-items: center;
213
-
214
- .title {
215
- font-size: 14px;
216
- padding-left: 6px;
217
- }
218
- }
219
-
220
- .body {
221
- box-sizing: border-box;
222
- padding: 10px 10px 0;
223
-
224
- hr {
225
- border: none;
226
- border-bottom: 1px solid #eaecef;
227
- }
228
- }
229
-
230
- .footer {
231
- box-sizing: border-box;
232
- padding: 10px;
233
- }
234
-
235
- .body.content,
236
- .footer.content {
237
- text-align: center;
238
-
239
- h4 {
240
- text-align: center;
241
- font-size: 12px;
242
- }
243
-
244
- p {
245
- text-align: center;
246
- padding: 10px 0;
247
- font-size: 14px;
248
- }
249
-
250
- img {
251
- width: 100%;
252
- // TODO: 未来优化,自动预获取图片高度填充
253
- height: 100px;
254
- object-fit: contain;
255
- margin: 0 auto;
256
- }
257
- }
258
-
259
- .theme-blog-popover-close {
260
- cursor: pointer;
261
- opacity: 0.5;
262
- position: fixed;
263
- z-index: 22;
264
- top: 80px;
265
- right: 10px;
266
- position: fixed;
267
- background-color: var(--vp-c-brand-3);
268
- padding: 8px;
269
- color: #fff;
270
- font-size: 12px;
271
- border-radius: 50%;
272
- display: flex;
273
- flex-direction: column;
274
- }
275
- .theme-blog-popover-close.twinkle {
276
- animation: twinkle 1s ease-in-out infinite;
277
- }
278
-
279
- @keyframes twinkle {
280
- 0% {
281
- opacity: 0.5;
282
- }
283
- 50% {
284
- opacity: 0;
285
- }
286
- 100% {
287
- opacity: 0.5;
288
- }
289
- }
290
- </style>