@sugarat/theme 0.5.6 → 0.5.8
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 +22 -1
- package/node.js +28 -12
- package/node.mjs +29 -13
- package/package.json +6 -6
- package/src/components/BlogAuthor.vue +9 -9
- package/src/components/BlogHomeHeaderAvatar.vue +12 -7
- package/src/composables/config/index.ts +23 -1
- package/src/hooks/useHoverSpin.ts +114 -0
- package/src/node.ts +7 -7
- package/src/utils/node/theme.ts +14 -6
- package/src/utils/node/vitePlugins.ts +14 -1
package/node.d.ts
CHANGED
|
@@ -186,6 +186,27 @@ declare namespace Theme {
|
|
|
186
186
|
* @default false
|
|
187
187
|
*/
|
|
188
188
|
blogInfoCollapsible?: boolean;
|
|
189
|
+
/**
|
|
190
|
+
* 旋转的配置
|
|
191
|
+
*/
|
|
192
|
+
hoverSpin?: boolean | HoverSpinConfig;
|
|
193
|
+
}
|
|
194
|
+
interface HoverSpinConfig {
|
|
195
|
+
/**
|
|
196
|
+
* 旋转的加速度
|
|
197
|
+
* @default 180
|
|
198
|
+
*/
|
|
199
|
+
accel?: number;
|
|
200
|
+
/**
|
|
201
|
+
* 旋转的最大角速度
|
|
202
|
+
* @default 2160
|
|
203
|
+
*/
|
|
204
|
+
maxVel?: number;
|
|
205
|
+
/**
|
|
206
|
+
* 缓停时长
|
|
207
|
+
* @default 1500
|
|
208
|
+
*/
|
|
209
|
+
decelDuration?: number;
|
|
189
210
|
}
|
|
190
211
|
interface ArticleConfig {
|
|
191
212
|
/**
|
|
@@ -382,7 +403,7 @@ declare namespace Theme {
|
|
|
382
403
|
works?: UserWorks;
|
|
383
404
|
/**
|
|
384
405
|
* https://mermaid.js.org/config/setup/modules/mermaidAPI.html#mermaidapi-configuration-defaults for options
|
|
385
|
-
* @default
|
|
406
|
+
* @default false
|
|
386
407
|
*/
|
|
387
408
|
mermaid?: any;
|
|
388
409
|
/**
|
package/node.js
CHANGED
|
@@ -41,7 +41,7 @@ module.exports = __toCommonJS(node_exports);
|
|
|
41
41
|
// src/utils/node/mdPlugins.ts
|
|
42
42
|
var import_module = require("module");
|
|
43
43
|
|
|
44
|
-
// ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@
|
|
44
|
+
// ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@https+++pkg.pr.new+vitepress@3d61619_@types+node@_fc84fc5ced1bdf672a18932976f96d0a/node_modules/vitepress-plugin-tabs/dist/index.js
|
|
45
45
|
var tabsMarker = "=tabs";
|
|
46
46
|
var tabsMarkerLen = tabsMarker.length;
|
|
47
47
|
var ruleBlockTabs = (state, startLine, endLine, silent) => {
|
|
@@ -386,8 +386,9 @@ function patchDefaultThemeSideBar(cfg) {
|
|
|
386
386
|
} : void 0;
|
|
387
387
|
}
|
|
388
388
|
var defaultTimeZoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset() / -60;
|
|
389
|
-
async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset,
|
|
390
|
-
const fileContent = baseContent || await import_node_fs.default.promises.readFile(filepath, "utf-8");
|
|
389
|
+
async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset, ops) {
|
|
390
|
+
const fileContent = ops?.baseContent || await import_node_fs.default.promises.readFile(filepath, "utf-8");
|
|
391
|
+
const cacheDir = ops?.cacheDir;
|
|
391
392
|
const { data: frontmatter, excerpt, content } = (0, import_theme_shared2.grayMatter)(fileContent, {
|
|
392
393
|
excerpt: true
|
|
393
394
|
});
|
|
@@ -398,7 +399,7 @@ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset,
|
|
|
398
399
|
meta.title = (0, import_theme_shared2.getDefaultTitle)(content);
|
|
399
400
|
}
|
|
400
401
|
const utcValue = timeZone >= 0 ? `+${timeZone}` : `${timeZone}`;
|
|
401
|
-
const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}${utcValue}`) || (0, import_theme_shared2.getFileLastModifyTime)(filepath));
|
|
402
|
+
const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}${utcValue}`) || (0, import_theme_shared2.getFileLastModifyTime)(filepath, cacheDir));
|
|
402
403
|
meta.date = formatDate(date || /* @__PURE__ */ new Date(), "yyyy/MM/dd hh:mm:ss");
|
|
403
404
|
meta.categories = typeof meta.categories === "string" ? [meta.categories] : meta.categories;
|
|
404
405
|
meta.tags = typeof meta.tags === "string" ? [meta.tags] : meta.tags;
|
|
@@ -417,7 +418,12 @@ async function getArticles(cfg, vpConfig) {
|
|
|
417
418
|
const pages = (0, import_theme_shared2.getVitePressPages)(vpConfig);
|
|
418
419
|
const metaResults = pages.reduce((prev, value) => {
|
|
419
420
|
const { page, route, originRoute, filepath, isDynamic, dynamicRoute } = value;
|
|
420
|
-
const metaPromise = isDynamic && dynamicRoute ? getArticleMeta(filepath, originRoute, cfg?.timeZone,
|
|
421
|
+
const metaPromise = isDynamic && dynamicRoute ? getArticleMeta(filepath, originRoute, cfg?.timeZone, {
|
|
422
|
+
baseContent: (0, import_theme_shared2.renderDynamicMarkdown)(filepath, dynamicRoute.params, dynamicRoute.content),
|
|
423
|
+
cacheDir: vpConfig.cacheDir
|
|
424
|
+
}) : getArticleMeta(filepath, originRoute, cfg?.timeZone, {
|
|
425
|
+
cacheDir: vpConfig.cacheDir
|
|
426
|
+
});
|
|
421
427
|
prev[page] = {
|
|
422
428
|
route,
|
|
423
429
|
metaPromise
|
|
@@ -542,6 +548,7 @@ function themeReloadPlugin() {
|
|
|
542
548
|
// src/utils/node/vitePlugins.ts
|
|
543
549
|
function getVitePlugins(cfg = {}) {
|
|
544
550
|
const plugins = [];
|
|
551
|
+
plugins.push(cacheAllGitTimestampsPlugin());
|
|
545
552
|
plugins.push(coverImgTransform());
|
|
546
553
|
if (cfg.themeColor) {
|
|
547
554
|
plugins.push(setThemeScript(cfg.themeColor));
|
|
@@ -723,6 +730,15 @@ function providePageData(cfg) {
|
|
|
723
730
|
}
|
|
724
731
|
};
|
|
725
732
|
}
|
|
733
|
+
function cacheAllGitTimestampsPlugin() {
|
|
734
|
+
return {
|
|
735
|
+
name: "@sugarat/theme-plugin-cache-all-git-timestamps",
|
|
736
|
+
async config(config) {
|
|
737
|
+
const { srcDir } = config.vitepress;
|
|
738
|
+
await (0, import_theme_shared4.cacheAllGitTimestamps)(srcDir);
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
}
|
|
726
742
|
function setThemeScript(themeColor) {
|
|
727
743
|
let resolveConfig;
|
|
728
744
|
const pluginOps = {
|
|
@@ -759,13 +775,13 @@ function getThemeConfig(cfg = {}) {
|
|
|
759
775
|
const extraVPConfig = {
|
|
760
776
|
vite: {
|
|
761
777
|
// see https://sass-lang.com/documentation/breaking-changes/legacy-js-api/
|
|
762
|
-
css: {
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
},
|
|
778
|
+
// css: {
|
|
779
|
+
// preprocessorOptions: {
|
|
780
|
+
// scss: {
|
|
781
|
+
// api: 'modern',
|
|
782
|
+
// },
|
|
783
|
+
// },
|
|
784
|
+
// },
|
|
769
785
|
build: {
|
|
770
786
|
// https://vite.dev/config/build-options.html#build-chunksizewarninglimit
|
|
771
787
|
chunkSizeWarningLimit: 2048
|
package/node.mjs
CHANGED
|
@@ -9,7 +9,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
// src/utils/node/mdPlugins.ts
|
|
10
10
|
import { createRequire } from "module";
|
|
11
11
|
|
|
12
|
-
// ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@
|
|
12
|
+
// ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@https+++pkg.pr.new+vitepress@3d61619_@types+node@_fc84fc5ced1bdf672a18932976f96d0a/node_modules/vitepress-plugin-tabs/dist/index.js
|
|
13
13
|
var tabsMarker = "=tabs";
|
|
14
14
|
var tabsMarkerLen = tabsMarker.length;
|
|
15
15
|
var ruleBlockTabs = (state, startLine, endLine, silent) => {
|
|
@@ -352,8 +352,9 @@ function patchDefaultThemeSideBar(cfg) {
|
|
|
352
352
|
} : void 0;
|
|
353
353
|
}
|
|
354
354
|
var defaultTimeZoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset() / -60;
|
|
355
|
-
async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset,
|
|
356
|
-
const fileContent = baseContent || await fs.promises.readFile(filepath, "utf-8");
|
|
355
|
+
async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset, ops) {
|
|
356
|
+
const fileContent = ops?.baseContent || await fs.promises.readFile(filepath, "utf-8");
|
|
357
|
+
const cacheDir = ops?.cacheDir;
|
|
357
358
|
const { data: frontmatter, excerpt, content } = grayMatter(fileContent, {
|
|
358
359
|
excerpt: true
|
|
359
360
|
});
|
|
@@ -364,7 +365,7 @@ async function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset,
|
|
|
364
365
|
meta.title = getDefaultTitle(content);
|
|
365
366
|
}
|
|
366
367
|
const utcValue = timeZone >= 0 ? `+${timeZone}` : `${timeZone}`;
|
|
367
|
-
const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}${utcValue}`) || getFileLastModifyTime(filepath));
|
|
368
|
+
const date = await (meta.date && /* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}${utcValue}`) || getFileLastModifyTime(filepath, cacheDir));
|
|
368
369
|
meta.date = formatDate(date || /* @__PURE__ */ new Date(), "yyyy/MM/dd hh:mm:ss");
|
|
369
370
|
meta.categories = typeof meta.categories === "string" ? [meta.categories] : meta.categories;
|
|
370
371
|
meta.tags = typeof meta.tags === "string" ? [meta.tags] : meta.tags;
|
|
@@ -383,7 +384,12 @@ async function getArticles(cfg, vpConfig) {
|
|
|
383
384
|
const pages = getVitePressPages(vpConfig);
|
|
384
385
|
const metaResults = pages.reduce((prev, value) => {
|
|
385
386
|
const { page, route, originRoute, filepath, isDynamic, dynamicRoute } = value;
|
|
386
|
-
const metaPromise = isDynamic && dynamicRoute ? getArticleMeta(filepath, originRoute, cfg?.timeZone,
|
|
387
|
+
const metaPromise = isDynamic && dynamicRoute ? getArticleMeta(filepath, originRoute, cfg?.timeZone, {
|
|
388
|
+
baseContent: renderDynamicMarkdown(filepath, dynamicRoute.params, dynamicRoute.content),
|
|
389
|
+
cacheDir: vpConfig.cacheDir
|
|
390
|
+
}) : getArticleMeta(filepath, originRoute, cfg?.timeZone, {
|
|
391
|
+
cacheDir: vpConfig.cacheDir
|
|
392
|
+
});
|
|
387
393
|
prev[page] = {
|
|
388
394
|
route,
|
|
389
395
|
metaPromise
|
|
@@ -426,7 +432,7 @@ import {
|
|
|
426
432
|
pagefindPlugin
|
|
427
433
|
} from "vitepress-plugin-pagefind";
|
|
428
434
|
import { RssPlugin } from "vitepress-plugin-rss";
|
|
429
|
-
import { joinPath as joinPath2 } from "@sugarat/theme-shared";
|
|
435
|
+
import { cacheAllGitTimestamps, joinPath as joinPath2 } from "@sugarat/theme-shared";
|
|
430
436
|
import { AnnouncementPlugin } from "vitepress-plugin-announcement";
|
|
431
437
|
import { groupIconVitePlugin } from "vitepress-plugin-group-icons";
|
|
432
438
|
|
|
@@ -510,6 +516,7 @@ function themeReloadPlugin() {
|
|
|
510
516
|
// src/utils/node/vitePlugins.ts
|
|
511
517
|
function getVitePlugins(cfg = {}) {
|
|
512
518
|
const plugins = [];
|
|
519
|
+
plugins.push(cacheAllGitTimestampsPlugin());
|
|
513
520
|
plugins.push(coverImgTransform());
|
|
514
521
|
if (cfg.themeColor) {
|
|
515
522
|
plugins.push(setThemeScript(cfg.themeColor));
|
|
@@ -691,6 +698,15 @@ function providePageData(cfg) {
|
|
|
691
698
|
}
|
|
692
699
|
};
|
|
693
700
|
}
|
|
701
|
+
function cacheAllGitTimestampsPlugin() {
|
|
702
|
+
return {
|
|
703
|
+
name: "@sugarat/theme-plugin-cache-all-git-timestamps",
|
|
704
|
+
async config(config) {
|
|
705
|
+
const { srcDir } = config.vitepress;
|
|
706
|
+
await cacheAllGitTimestamps(srcDir);
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
}
|
|
694
710
|
function setThemeScript(themeColor) {
|
|
695
711
|
let resolveConfig;
|
|
696
712
|
const pluginOps = {
|
|
@@ -727,13 +743,13 @@ function getThemeConfig(cfg = {}) {
|
|
|
727
743
|
const extraVPConfig = {
|
|
728
744
|
vite: {
|
|
729
745
|
// see https://sass-lang.com/documentation/breaking-changes/legacy-js-api/
|
|
730
|
-
css: {
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
},
|
|
746
|
+
// css: {
|
|
747
|
+
// preprocessorOptions: {
|
|
748
|
+
// scss: {
|
|
749
|
+
// api: 'modern',
|
|
750
|
+
// },
|
|
751
|
+
// },
|
|
752
|
+
// },
|
|
737
753
|
build: {
|
|
738
754
|
// https://vite.dev/config/build-options.html#build-chunksizewarninglimit
|
|
739
755
|
chunkSizeWarningLimit: 2048
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sugarat/theme",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
|
|
5
5
|
"author": "sugar",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"vitepress-plugin-group-icons": "1.2.4",
|
|
54
54
|
"vitepress-plugin-mermaid": "2.0.13",
|
|
55
55
|
"vitepress-plugin-tabs": "0.2.0",
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"vitepress-plugin-pagefind": "0.4.
|
|
59
|
-
"vitepress-plugin-
|
|
56
|
+
"vitepress-plugin-rss": "0.3.3",
|
|
57
|
+
"@sugarat/theme-shared": "0.0.6",
|
|
58
|
+
"vitepress-plugin-pagefind": "0.4.16",
|
|
59
|
+
"vitepress-plugin-announcement": "0.1.5"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@element-plus/icons-vue": "^2.3.1",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"sass": "^1.80.6",
|
|
67
67
|
"typescript": "^5.4.5",
|
|
68
68
|
"vite": "^5.4.9",
|
|
69
|
-
"vitepress": "
|
|
69
|
+
"vitepress": "https://pkg.pr.new/vitepress@3d61619",
|
|
70
70
|
"vue": "^3.5.12",
|
|
71
71
|
"vitepress-plugin-51la": "0.1.0"
|
|
72
72
|
},
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { useData, withBase } from 'vitepress'
|
|
3
|
-
import { computed } from 'vue'
|
|
4
|
-
import {
|
|
3
|
+
import { computed, ref } from 'vue'
|
|
4
|
+
import { useGlobalAuthor, useHomeConfig } from '../composables/config/blog'
|
|
5
|
+
import { useHoverSpin } from '../hooks/useHoverSpin'
|
|
5
6
|
|
|
6
7
|
const home = useHomeConfig()
|
|
7
8
|
const { frontmatter, site } = useData()
|
|
@@ -20,11 +21,15 @@ const logo = computed(() =>
|
|
|
20
21
|
?? '/logo.png'
|
|
21
22
|
)
|
|
22
23
|
const show = computed(() => author.value || logo.value)
|
|
24
|
+
|
|
25
|
+
const imgRef = ref(null)
|
|
26
|
+
|
|
27
|
+
useHoverSpin(imgRef, home?.value?.hoverSpin)
|
|
23
28
|
</script>
|
|
24
29
|
|
|
25
30
|
<template>
|
|
26
31
|
<div v-if="show" class="blog-author">
|
|
27
|
-
<img v-if="logo" :src="withBase(logo)" alt="avatar">
|
|
32
|
+
<img v-if="logo" ref="imgRef" :src="withBase(logo)" alt="avatar">
|
|
28
33
|
<p v-if="author">
|
|
29
34
|
{{ author }}
|
|
30
35
|
</p>
|
|
@@ -42,12 +47,7 @@ const show = computed(() => author.value || logo.value)
|
|
|
42
47
|
height: 100px;
|
|
43
48
|
border-radius: 50%;
|
|
44
49
|
background-color: rgba(var(--bg-gradient-home));
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
img:hover {
|
|
48
|
-
transform: rotate(666turn);
|
|
49
|
-
transition-duration: 59s;
|
|
50
|
-
transition-timing-function: cubic-bezier(.34, 0, .84, 1)
|
|
50
|
+
cursor: pointer;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
p {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { useData, withBase } from 'vitepress'
|
|
3
|
-
import { computed } from 'vue'
|
|
3
|
+
import { computed, ref } from 'vue'
|
|
4
4
|
import { useHomeConfig } from '../composables/config/blog'
|
|
5
|
+
import { useHoverSpin } from '../hooks/useHoverSpin'
|
|
5
6
|
|
|
6
7
|
const home = useHomeConfig()
|
|
7
8
|
const { frontmatter, site } = useData()
|
|
@@ -13,11 +14,14 @@ const logo = computed(() =>
|
|
|
13
14
|
?? '/logo.png'
|
|
14
15
|
)
|
|
15
16
|
const alwaysHide = computed(() => frontmatter.value.blog?.minScreenAvatar === false)
|
|
17
|
+
|
|
18
|
+
const imgRef = ref(null)
|
|
19
|
+
useHoverSpin(imgRef, home?.value?.hoverSpin)
|
|
16
20
|
</script>
|
|
17
21
|
|
|
18
22
|
<template>
|
|
19
23
|
<div v-show="!alwaysHide" class="blog-home-header-avatar">
|
|
20
|
-
<img :src="withBase(logo)" alt="avatar">
|
|
24
|
+
<img ref="imgRef" :src="withBase(logo)" alt="avatar">
|
|
21
25
|
</div>
|
|
22
26
|
</template>
|
|
23
27
|
|
|
@@ -36,13 +40,14 @@ const alwaysHide = computed(() => frontmatter.value.blog?.minScreenAvatar === fa
|
|
|
36
40
|
background-color: transparent;
|
|
37
41
|
border: 5px solid rgba(var(--bg-gradient-home));
|
|
38
42
|
box-sizing: border-box;
|
|
43
|
+
cursor: pointer;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
img:hover {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
+
// img:hover {
|
|
47
|
+
// transform: rotate(666turn);
|
|
48
|
+
// transition-duration: 59s;
|
|
49
|
+
// transition-timing-function: cubic-bezier(.34, 0, .84, 1)
|
|
50
|
+
// }
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
@media screen and (min-width: 768px) {
|
|
@@ -191,6 +191,28 @@ export namespace Theme {
|
|
|
191
191
|
* @default false
|
|
192
192
|
*/
|
|
193
193
|
blogInfoCollapsible?: boolean
|
|
194
|
+
/**
|
|
195
|
+
* 旋转的配置
|
|
196
|
+
*/
|
|
197
|
+
hoverSpin?: boolean | HoverSpinConfig
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface HoverSpinConfig {
|
|
201
|
+
/**
|
|
202
|
+
* 旋转的加速度
|
|
203
|
+
* @default 180
|
|
204
|
+
*/
|
|
205
|
+
accel?: number
|
|
206
|
+
/**
|
|
207
|
+
* 旋转的最大角速度
|
|
208
|
+
* @default 2160
|
|
209
|
+
*/
|
|
210
|
+
maxVel?: number
|
|
211
|
+
/**
|
|
212
|
+
* 缓停时长
|
|
213
|
+
* @default 1500
|
|
214
|
+
*/
|
|
215
|
+
decelDuration?: number
|
|
194
216
|
}
|
|
195
217
|
|
|
196
218
|
export interface ArticleConfig {
|
|
@@ -408,7 +430,7 @@ export namespace Theme {
|
|
|
408
430
|
works?: UserWorks
|
|
409
431
|
/**
|
|
410
432
|
* https://mermaid.js.org/config/setup/modules/mermaidAPI.html#mermaidapi-configuration-defaults for options
|
|
411
|
-
* @default
|
|
433
|
+
* @default false
|
|
412
434
|
*/
|
|
413
435
|
mermaid?: any
|
|
414
436
|
/**
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { type Ref, onMounted, onUnmounted } from 'vue'
|
|
2
|
+
import type { Theme } from '../composables/config/index'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 元素悬停旋转
|
|
6
|
+
* @param elRef 元素引用
|
|
7
|
+
* @param accel 加速度
|
|
8
|
+
* @param maxVel 最大角速度
|
|
9
|
+
* @param decelDuration 缓停时长
|
|
10
|
+
*/
|
|
11
|
+
export function useHoverSpin(elRef: Ref<HTMLElement | null>, hoverSpinConfig?: Theme.HoverSpinConfig | boolean) {
|
|
12
|
+
const { accel = 180, maxVel = 2160, decelDuration = 1500 } = hoverSpinConfig === true ? {} : hoverSpinConfig || {}
|
|
13
|
+
let rafId = 0
|
|
14
|
+
let lastTs = 0
|
|
15
|
+
let rotation = 0
|
|
16
|
+
let velocity = 0
|
|
17
|
+
let decelStart = 0
|
|
18
|
+
let decelStartRotation = 0
|
|
19
|
+
let decelTargetRotation = 0
|
|
20
|
+
let decelBias = 0
|
|
21
|
+
let isDecel = false
|
|
22
|
+
let hovering = false
|
|
23
|
+
|
|
24
|
+
function animate(ts: number) {
|
|
25
|
+
if (!lastTs)
|
|
26
|
+
lastTs = ts
|
|
27
|
+
const dt = (ts - lastTs) / 1000
|
|
28
|
+
lastTs = ts
|
|
29
|
+
|
|
30
|
+
if (hovering) {
|
|
31
|
+
isDecel = false
|
|
32
|
+
decelStart = 0
|
|
33
|
+
velocity = Math.min(velocity + accel * dt, maxVel)
|
|
34
|
+
rotation += velocity * dt
|
|
35
|
+
}
|
|
36
|
+
else if (isDecel) {
|
|
37
|
+
const t = Math.min((ts - decelStart) / decelDuration, 1)
|
|
38
|
+
const durSec = decelDuration / 1000
|
|
39
|
+
const base = velocity * durSec * (t - t * t / 2)
|
|
40
|
+
const smooth = 3 * t * t - 2 * t * t * t
|
|
41
|
+
rotation = decelStartRotation + base + decelBias * smooth
|
|
42
|
+
if (t >= 1) {
|
|
43
|
+
decelTargetRotation = 0
|
|
44
|
+
decelStartRotation = 0
|
|
45
|
+
decelBias = 0
|
|
46
|
+
rotation = decelTargetRotation
|
|
47
|
+
velocity = 0
|
|
48
|
+
isDecel = false
|
|
49
|
+
if (rafId) {
|
|
50
|
+
cancelAnimationFrame(rafId)
|
|
51
|
+
rafId = 0
|
|
52
|
+
}
|
|
53
|
+
const el2 = elRef.value as HTMLElement | null
|
|
54
|
+
if (el2)
|
|
55
|
+
el2.style.transform = `rotate(${rotation}deg)`
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const el = elRef.value as HTMLElement | null
|
|
61
|
+
if (el)
|
|
62
|
+
el.style.transform = `rotate(${rotation}deg)`
|
|
63
|
+
if (hovering || isDecel) {
|
|
64
|
+
rafId = requestAnimationFrame(animate)
|
|
65
|
+
}
|
|
66
|
+
else if (rafId) {
|
|
67
|
+
cancelAnimationFrame(rafId)
|
|
68
|
+
rafId = 0
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function onEnter() {
|
|
73
|
+
hovering = true
|
|
74
|
+
if (!rafId) {
|
|
75
|
+
lastTs = 0
|
|
76
|
+
rafId = requestAnimationFrame(animate)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function onLeave() {
|
|
81
|
+
hovering = false
|
|
82
|
+
isDecel = true
|
|
83
|
+
decelStart = performance.now()
|
|
84
|
+
decelStartRotation = rotation
|
|
85
|
+
const durSec = decelDuration / 1000
|
|
86
|
+
const S = velocity * durSec / 2
|
|
87
|
+
const minTarget = decelStartRotation + S
|
|
88
|
+
decelTargetRotation = Math.ceil(minTarget / 360) * 360
|
|
89
|
+
decelBias = (decelTargetRotation - decelStartRotation) - S
|
|
90
|
+
lastTs = 0
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
onMounted(() => {
|
|
94
|
+
if (hoverSpinConfig === false)
|
|
95
|
+
return
|
|
96
|
+
const el = elRef.value
|
|
97
|
+
if (!el)
|
|
98
|
+
return
|
|
99
|
+
el.addEventListener('mouseenter', onEnter)
|
|
100
|
+
el.addEventListener('mouseleave', onLeave)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
onUnmounted(() => {
|
|
104
|
+
if (hoverSpinConfig === false)
|
|
105
|
+
return
|
|
106
|
+
const el = elRef.value
|
|
107
|
+
if (el) {
|
|
108
|
+
el.removeEventListener('mouseenter', onEnter)
|
|
109
|
+
el.removeEventListener('mouseleave', onLeave)
|
|
110
|
+
}
|
|
111
|
+
if (rafId)
|
|
112
|
+
cancelAnimationFrame(rafId)
|
|
113
|
+
})
|
|
114
|
+
}
|
package/src/node.ts
CHANGED
|
@@ -25,13 +25,13 @@ export function getThemeConfig(cfg: Partial<Theme.BlogConfig> = {}) {
|
|
|
25
25
|
const extraVPConfig: any = {
|
|
26
26
|
vite: {
|
|
27
27
|
// see https://sass-lang.com/documentation/breaking-changes/legacy-js-api/
|
|
28
|
-
css: {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
28
|
+
// css: {
|
|
29
|
+
// preprocessorOptions: {
|
|
30
|
+
// scss: {
|
|
31
|
+
// api: 'modern',
|
|
32
|
+
// },
|
|
33
|
+
// },
|
|
34
|
+
// },
|
|
35
35
|
build: {
|
|
36
36
|
// https://vite.dev/config/build-options.html#build-chunksizewarninglimit
|
|
37
37
|
chunkSizeWarningLimit: 2048
|
package/src/utils/node/theme.ts
CHANGED
|
@@ -26,9 +26,12 @@ export function getPageRoute(filepath: string, srcDir: string) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const defaultTimeZoneOffset = new Date().getTimezoneOffset() / -60
|
|
29
|
-
export async function getArticleMeta(filepath: string, route: string, timeZone = defaultTimeZoneOffset,
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
export async function getArticleMeta(filepath: string, route: string, timeZone = defaultTimeZoneOffset, ops?: {
|
|
30
|
+
baseContent?: string
|
|
31
|
+
cacheDir?: string
|
|
32
|
+
}) {
|
|
33
|
+
const fileContent = ops?.baseContent || await fs.promises.readFile(filepath, 'utf-8')
|
|
34
|
+
const cacheDir = ops?.cacheDir
|
|
32
35
|
const { data: frontmatter, excerpt, content } = grayMatter(fileContent, {
|
|
33
36
|
excerpt: true,
|
|
34
37
|
})
|
|
@@ -44,7 +47,7 @@ export async function getArticleMeta(filepath: string, route: string, timeZone =
|
|
|
44
47
|
const date = await (
|
|
45
48
|
(meta.date
|
|
46
49
|
&& new Date(`${new Date(meta.date).toUTCString()}${utcValue}`))
|
|
47
|
-
|| getFileLastModifyTime(filepath)
|
|
50
|
+
|| getFileLastModifyTime(filepath, cacheDir)
|
|
48
51
|
)
|
|
49
52
|
// 无法获取时兜底当前时间
|
|
50
53
|
meta.date = formatDate(date || new Date(), 'yyyy/MM/dd hh:mm:ss')
|
|
@@ -86,8 +89,13 @@ export async function getArticles(cfg: Partial<Theme.BlogConfig>, vpConfig: Site
|
|
|
86
89
|
const { page, route, originRoute, filepath, isDynamic, dynamicRoute } = value
|
|
87
90
|
|
|
88
91
|
const metaPromise = (isDynamic && dynamicRoute)
|
|
89
|
-
? getArticleMeta(filepath, originRoute, cfg?.timeZone,
|
|
90
|
-
|
|
92
|
+
? getArticleMeta(filepath, originRoute, cfg?.timeZone, {
|
|
93
|
+
baseContent: renderDynamicMarkdown(filepath, dynamicRoute.params, dynamicRoute.content),
|
|
94
|
+
cacheDir: vpConfig.cacheDir
|
|
95
|
+
})
|
|
96
|
+
: getArticleMeta(filepath, originRoute, cfg?.timeZone, {
|
|
97
|
+
cacheDir: vpConfig.cacheDir
|
|
98
|
+
})
|
|
91
99
|
|
|
92
100
|
// 提前获取,有缓存取缓存
|
|
93
101
|
prev[page] = {
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from 'vitepress-plugin-pagefind'
|
|
5
5
|
import { RssPlugin } from 'vitepress-plugin-rss'
|
|
6
6
|
import type { PluginOption } from 'vite'
|
|
7
|
-
import { joinPath } from '@sugarat/theme-shared'
|
|
7
|
+
import { cacheAllGitTimestamps, joinPath } from '@sugarat/theme-shared'
|
|
8
8
|
import { AnnouncementPlugin } from 'vitepress-plugin-announcement'
|
|
9
9
|
import { groupIconVitePlugin } from 'vitepress-plugin-group-icons'
|
|
10
10
|
import type { Theme } from '../../composables/config/index'
|
|
@@ -15,6 +15,9 @@ import { getArticles } from './theme'
|
|
|
15
15
|
export function getVitePlugins(cfg: Partial<Theme.BlogConfig> = {}) {
|
|
16
16
|
const plugins: any[] = []
|
|
17
17
|
|
|
18
|
+
// 缓存所有文章的 git 提交时间
|
|
19
|
+
plugins.push(cacheAllGitTimestampsPlugin())
|
|
20
|
+
|
|
18
21
|
// 处理 cover image 的路径(暂只支持自动识别的文章首图)
|
|
19
22
|
plugins.push(coverImgTransform())
|
|
20
23
|
|
|
@@ -266,6 +269,16 @@ export function providePageData(cfg: Partial<Theme.BlogConfig>) {
|
|
|
266
269
|
} as PluginOption
|
|
267
270
|
}
|
|
268
271
|
|
|
272
|
+
export function cacheAllGitTimestampsPlugin() {
|
|
273
|
+
return {
|
|
274
|
+
name: '@sugarat/theme-plugin-cache-all-git-timestamps',
|
|
275
|
+
async config(config: any) {
|
|
276
|
+
const { srcDir } = config.vitepress
|
|
277
|
+
await cacheAllGitTimestamps(srcDir)
|
|
278
|
+
},
|
|
279
|
+
} as PluginOption
|
|
280
|
+
}
|
|
281
|
+
|
|
269
282
|
export function setThemeScript(
|
|
270
283
|
themeColor: Theme.ThemeColor
|
|
271
284
|
) {
|