@xingwangzhe/stalux 1.25.11 → 1.25.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/package.json +9 -9
- package/src/components/stalux/layout/head.astro +2 -2
- package/src/components/stalux/layout/navs.astro +1 -1
- package/src/components/stalux/posts/postContent.astro +1 -0
- package/src/index.ts +6 -0
- package/src/internal/runtime-cache-key.ts +35 -0
- package/src/layouts/Stalux.astro +3 -0
- package/src/pages/posts/[post].astro +2 -0
- package/src/scripts/navigation.ts +17 -6
- package/src/scripts/photoswipe.ts +6 -16
- package/src/scripts/retryable-initializer.ts +13 -0
- package/src/scripts/search-dialog.ts +18 -0
- package/src/utils/taxonomy.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xingwangzhe/stalux",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.13",
|
|
4
4
|
"description": "A powerful, modern Astro blog theme — use as template or install as plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -85,12 +85,12 @@
|
|
|
85
85
|
"prepare": "git config core.hooksPath scripts/git-hooks"
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
|
-
"@astrojs/markdown-satteri": "0.3.
|
|
88
|
+
"@astrojs/markdown-satteri": "0.3.8",
|
|
89
89
|
"@astrojs/rss": "^4.0.19",
|
|
90
90
|
"@astrojs/sitemap": "^3.7.3",
|
|
91
91
|
"@expressive-code/plugin-line-numbers": "^0.44.1",
|
|
92
|
-
"@lucide/astro": "^1.
|
|
93
|
-
"@mcp-b/webmcp-polyfill": "^
|
|
92
|
+
"@lucide/astro": "^1.35.0",
|
|
93
|
+
"@mcp-b/webmcp-polyfill": "^5.0.1",
|
|
94
94
|
"@pagefind/component-ui": "^1.5.2",
|
|
95
95
|
"@waline/client": "^3.15.2",
|
|
96
96
|
"@xingwangzhe/satteri-mermaid": "0.7.7",
|
|
@@ -104,20 +104,20 @@
|
|
|
104
104
|
"photoswipe": "^5.4.4",
|
|
105
105
|
"satteri": "^0.10.5",
|
|
106
106
|
"simple-icons": "^16.28.0",
|
|
107
|
-
"subset-font": "^2.
|
|
107
|
+
"subset-font": "^2.6.0",
|
|
108
108
|
"temml": "^0.13.4"
|
|
109
109
|
},
|
|
110
110
|
"devDependencies": {
|
|
111
111
|
"@astrojs/check": "0.9.10",
|
|
112
|
-
"@biomejs/biome": "2.5.
|
|
112
|
+
"@biomejs/biome": "2.5.11",
|
|
113
113
|
"@vitest/coverage-v8": "4.1.11",
|
|
114
|
-
"astro": "^7.2.
|
|
115
|
-
"knip": "^6.32.
|
|
114
|
+
"astro": "^7.2.9",
|
|
115
|
+
"knip": "^6.32.3",
|
|
116
116
|
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
117
117
|
"vite": "8.2.2",
|
|
118
118
|
"vitest": "4.1.11"
|
|
119
119
|
},
|
|
120
120
|
"peerDependencies": {
|
|
121
|
-
"astro": "7.2.
|
|
121
|
+
"astro": "7.2.9"
|
|
122
122
|
}
|
|
123
123
|
}
|
|
@@ -5,8 +5,8 @@ import { ClientRouter } from "astro:transitions";
|
|
|
5
5
|
import BingClarity from "@components/stalux/analytics/clarity.astro";
|
|
6
6
|
import GoogleAnalytics from "@components/stalux/analytics/google.astro";
|
|
7
7
|
import VerCount from "@components/stalux/common/verCount.astro";
|
|
8
|
-
import { STALUX_VERSION } from "@internal/version";
|
|
9
8
|
import { SEO } from "astro-seo";
|
|
9
|
+
import packageJson from "../../../../package.json";
|
|
10
10
|
import "@styles/base/init.css";
|
|
11
11
|
import Umami from "@components/stalux/analytics/umami.astro";
|
|
12
12
|
import {
|
|
@@ -33,7 +33,7 @@ const seo = buildSeoDocument({
|
|
|
33
33
|
pathname: Astro.url.pathname,
|
|
34
34
|
protocol: Astro.url.protocol,
|
|
35
35
|
exportMarkdown: Boolean(promoteConfig?.export_md),
|
|
36
|
-
version:
|
|
36
|
+
version: packageJson.version,
|
|
37
37
|
});
|
|
38
38
|
const ldJson = JSON.stringify(seo.schema).replace(/<\//g, "<\\/");
|
|
39
39
|
---
|
|
@@ -15,7 +15,7 @@ const { t } = createTranslator(siteConfig.lang || "zh-CN");
|
|
|
15
15
|
const navs = navsConfig?.items ?? [];
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
<nav aria-label={t("nav.main")}
|
|
18
|
+
<nav aria-label={t("nav.main")}>
|
|
19
19
|
<!-- 汉堡菜单按钮 (仅窄屏显示) -->
|
|
20
20
|
<button
|
|
21
21
|
type="button"
|
package/src/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { expressiveCode } from "./expressive-code";
|
|
|
26
26
|
import { staluxComponentsAlias } from "./internal/components-plugin";
|
|
27
27
|
import { type FontSlice, runFontSlicing } from "./internal/font-slices";
|
|
28
28
|
import { createInjectedRoutes } from "./internal/injected-routes";
|
|
29
|
+
import { createRuntimeCacheKey } from "./internal/runtime-cache-key";
|
|
29
30
|
import {
|
|
30
31
|
appendUniquePlugin,
|
|
31
32
|
collectPluginNames,
|
|
@@ -179,6 +180,10 @@ export function stalux(options: StaluxOptions = {}): AstroIntegration[] {
|
|
|
179
180
|
logger,
|
|
180
181
|
}) => {
|
|
181
182
|
const srcDir = fileURLToPath(new URL(".", import.meta.url));
|
|
183
|
+
const runtimeCacheKey = createRuntimeCacheKey(
|
|
184
|
+
path.join(srcDir, "scripts"),
|
|
185
|
+
path.resolve(srcDir, "../package.json"),
|
|
186
|
+
);
|
|
182
187
|
|
|
183
188
|
// 1. 注入 Vite 别名 + 组件覆盖插件 + Vue 特性标记(Waline 依赖)
|
|
184
189
|
updateConfig({
|
|
@@ -191,6 +196,7 @@ export function stalux(options: StaluxOptions = {}): AstroIntegration[] {
|
|
|
191
196
|
__VUE_OPTIONS_API__: true,
|
|
192
197
|
__VUE_PROD_DEVTOOLS__: false,
|
|
193
198
|
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
|
|
199
|
+
__STALUX_RUNTIME_CACHE_KEY__: JSON.stringify(runtimeCacheKey),
|
|
194
200
|
},
|
|
195
201
|
},
|
|
196
202
|
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
declare const __STALUX_RUNTIME_CACHE_KEY__: string;
|
|
6
|
+
|
|
7
|
+
export function hashRuntimeSources(sources: ReadonlyArray<readonly [string, string]>): string {
|
|
8
|
+
const hash = createHash("sha256");
|
|
9
|
+
for (const [name, contents] of [...sources].sort(([left], [right]) =>
|
|
10
|
+
left.localeCompare(right),
|
|
11
|
+
)) {
|
|
12
|
+
hash.update(name);
|
|
13
|
+
hash.update("\0");
|
|
14
|
+
hash.update(contents);
|
|
15
|
+
hash.update("\0");
|
|
16
|
+
}
|
|
17
|
+
return hash.digest("hex").slice(0, 16);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function collectFiles(directory: string, root = directory): Array<readonly [string, string]> {
|
|
21
|
+
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
22
|
+
const target = path.join(directory, entry.name);
|
|
23
|
+
if (entry.isDirectory()) return collectFiles(target, root);
|
|
24
|
+
return [[path.relative(root, target), readFileSync(target, "utf8")] as const];
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createRuntimeCacheKey(scriptsDirectory: string, packageJson: string): string {
|
|
29
|
+
return hashRuntimeSources([
|
|
30
|
+
...collectFiles(scriptsDirectory),
|
|
31
|
+
["package.json", readFileSync(packageJson, "utf8")],
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const getRuntimeCacheKey = (): string => __STALUX_RUNTIME_CACHE_KEY__;
|
package/src/layouts/Stalux.astro
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { getCollection } from "astro:content";
|
|
3
3
|
import Footer from "@components/stalux/layout/footer.astro";
|
|
4
4
|
import Head from "@components/stalux/layout/head.astro";
|
|
5
|
+
// Keep Pagefind styles on the layout's static CSS graph. The component JavaScript
|
|
6
|
+
// remains lazy-loaded when search opens, but preview must always have stable CSS.
|
|
7
|
+
import "@pagefind/component-ui/css";
|
|
5
8
|
import "@styles/base/init.css";
|
|
6
9
|
import "@styles/shared/stagger.css";
|
|
7
10
|
import "@styles/shared/view-transitions.css";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { type CollectionEntry, getCollection, render } from "astro:content";
|
|
3
3
|
import Navs from "@components/stalux/layout/navs.astro";
|
|
4
|
+
import { getRuntimeCacheKey } from "@internal/runtime-cache-key";
|
|
4
5
|
import PostLayout from "@layouts/PostLayout.astro";
|
|
5
6
|
import Stalux from "@layouts/Stalux.astro";
|
|
6
7
|
import { analyzeFeatureFlags } from "@plugins/feature-flags";
|
|
@@ -33,6 +34,7 @@ export async function getStaticPaths() {
|
|
|
33
34
|
post.data.updated ?? post.data.date,
|
|
34
35
|
prevPost ? (prevPost.data.updated ?? prevPost.data.date) : "",
|
|
35
36
|
nextPost ? (nextPost.data.updated ?? nextPost.data.date) : "",
|
|
37
|
+
getRuntimeCacheKey(),
|
|
36
38
|
].join("|");
|
|
37
39
|
return {
|
|
38
40
|
params: { post: post.data.abbrlink },
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import { registerPageLifecycle } from "./page-runtime";
|
|
2
|
+
import { upgradeAndOpenSearchDialog } from "./search-dialog";
|
|
2
3
|
|
|
3
4
|
interface SearchDialog extends HTMLElement {
|
|
4
|
-
open
|
|
5
|
+
open?: () => void;
|
|
6
|
+
close?: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async function openSearchDialog(): Promise<void> {
|
|
10
|
+
await import("@pagefind/component-ui");
|
|
11
|
+
const dialog = document.querySelector<SearchDialog>("pagefind-modal#search");
|
|
12
|
+
await upgradeAndOpenSearchDialog(
|
|
13
|
+
{
|
|
14
|
+
whenDefined: (name) => customElements.whenDefined(name),
|
|
15
|
+
upgrade: (element) => customElements.upgrade(element),
|
|
16
|
+
},
|
|
17
|
+
dialog,
|
|
18
|
+
);
|
|
5
19
|
}
|
|
6
20
|
|
|
7
21
|
registerPageLifecycle("navigation", () => {
|
|
@@ -64,11 +78,7 @@ registerPageLifecycle("navigation", () => {
|
|
|
64
78
|
async (event) => {
|
|
65
79
|
event.preventDefault();
|
|
66
80
|
closeNav();
|
|
67
|
-
await
|
|
68
|
-
import("@pagefind/component-ui"),
|
|
69
|
-
import("@pagefind/component-ui/css"),
|
|
70
|
-
]);
|
|
71
|
-
document.querySelector<SearchDialog>("#search")?.open();
|
|
81
|
+
await openSearchDialog();
|
|
72
82
|
},
|
|
73
83
|
listenerOptions,
|
|
74
84
|
);
|
|
@@ -102,6 +112,7 @@ registerPageLifecycle("navigation", () => {
|
|
|
102
112
|
scheduleUpdate();
|
|
103
113
|
|
|
104
114
|
return () => {
|
|
115
|
+
document.querySelector<SearchDialog>("pagefind-modal#search")?.close?.();
|
|
105
116
|
controller.abort();
|
|
106
117
|
resizeObserver.disconnect();
|
|
107
118
|
if (frameId) cancelAnimationFrame(frameId);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { registerPageLifecycle } from "./page-runtime";
|
|
2
|
+
import { createRetryableInitializer } from "./retryable-initializer";
|
|
2
3
|
|
|
3
4
|
const GALLERY_SELECTOR = "a.pswp-gallery-item";
|
|
4
5
|
|
|
@@ -34,23 +35,13 @@ registerPageLifecycle("photoswipe", () => {
|
|
|
34
35
|
|
|
35
36
|
const controller = new AbortController();
|
|
36
37
|
let lightbox: import("photoswipe/lightbox").default | undefined;
|
|
37
|
-
let initPromise: Promise<void> | undefined;
|
|
38
38
|
let initialized = false;
|
|
39
39
|
|
|
40
40
|
const initialize = async () => {
|
|
41
|
-
const [{ default: PhotoSwipeLightbox }, { default: PhotoSwipe }
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
import("photoswipe/dist/photoswipe.css?raw"),
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
|
-
if (!document.querySelector("style[data-photoswipe]") && css) {
|
|
49
|
-
const style = document.createElement("style");
|
|
50
|
-
style.dataset.photoswipe = "true";
|
|
51
|
-
style.textContent = css;
|
|
52
|
-
document.head.appendChild(style);
|
|
53
|
-
}
|
|
41
|
+
const [{ default: PhotoSwipeLightbox }, { default: PhotoSwipe }] = await Promise.all([
|
|
42
|
+
import("photoswipe/lightbox"),
|
|
43
|
+
import("photoswipe"),
|
|
44
|
+
]);
|
|
54
45
|
|
|
55
46
|
void enrichPhotoSwipeData(container);
|
|
56
47
|
lightbox = new PhotoSwipeLightbox({
|
|
@@ -62,7 +53,7 @@ registerPageLifecycle("photoswipe", () => {
|
|
|
62
53
|
lightbox.init();
|
|
63
54
|
};
|
|
64
55
|
|
|
65
|
-
const ensureInitialized = (
|
|
56
|
+
const ensureInitialized = createRetryableInitializer(initialize);
|
|
66
57
|
const handleClick = (event: MouseEvent) => {
|
|
67
58
|
const target = event.target;
|
|
68
59
|
if (!(target instanceof Element)) return;
|
|
@@ -93,7 +84,6 @@ registerPageLifecycle("photoswipe", () => {
|
|
|
93
84
|
controller.abort();
|
|
94
85
|
lightbox?.destroy();
|
|
95
86
|
lightbox = undefined;
|
|
96
|
-
initPromise = undefined;
|
|
97
87
|
initialized = false;
|
|
98
88
|
};
|
|
99
89
|
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function createRetryableInitializer(initialize: () => Promise<void>): () => Promise<void> {
|
|
2
|
+
let pending: Promise<void> | undefined;
|
|
3
|
+
|
|
4
|
+
return () => {
|
|
5
|
+
if (!pending) {
|
|
6
|
+
pending = initialize().catch((error: unknown) => {
|
|
7
|
+
pending = undefined;
|
|
8
|
+
throw error;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
return pending;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SearchDialogLike {
|
|
2
|
+
open?: () => void;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface CustomElementRegistryLike<ElementType> {
|
|
6
|
+
whenDefined(name: string): Promise<unknown>;
|
|
7
|
+
upgrade(element: ElementType): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function upgradeAndOpenSearchDialog<ElementType extends SearchDialogLike>(
|
|
11
|
+
registry: CustomElementRegistryLike<ElementType>,
|
|
12
|
+
dialog: ElementType | null,
|
|
13
|
+
): Promise<void> {
|
|
14
|
+
await registry.whenDefined("pagefind-modal");
|
|
15
|
+
if (!dialog) return;
|
|
16
|
+
registry.upgrade(dialog);
|
|
17
|
+
dialog.open?.();
|
|
18
|
+
}
|
package/src/utils/taxonomy.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { GetStaticPathsResult } from "astro";
|
|
2
2
|
|
|
3
|
+
import { getRuntimeCacheKey } from "../internal/runtime-cache-key";
|
|
3
4
|
import { getPostContentIndex, type TaxonomyKey } from "./content-index";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -17,7 +18,7 @@ export async function buildTaxonomyStaticPaths(key: TaxonomyKey): Promise<GetSta
|
|
|
17
18
|
return [...taxonomy].map(([name, entry]) => {
|
|
18
19
|
return {
|
|
19
20
|
params: { [paramName]: name },
|
|
20
|
-
cacheKey: entry.cacheKey
|
|
21
|
+
cacheKey: `${entry.cacheKey}|${getRuntimeCacheKey()}`,
|
|
21
22
|
props: {
|
|
22
23
|
[paramName]: name,
|
|
23
24
|
count: entry.posts.length,
|