@xingwangzhe/stalux 1.56.0 → 1.58.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/package.json +4 -12
- package/src/components/stalux/archives/archivesList.astro +1 -1
- package/src/components/stalux/categories/categoriesCard.astro +1 -1
- package/src/components/stalux/posts/PostTaxonomy.astro +1 -1
- package/src/components/stalux/posts/postCard.astro +3 -3
- package/src/components/stalux/tags/tagHeader.astro +2 -2
- package/src/components/stalux/tags/tagsCloud.astro +2 -2
- package/src/index.ts +39 -13
- package/src/internal/html-output.ts +18 -0
- package/src/internal/modern-css.ts +1 -0
- package/src/internal/route-hrefs.ts +47 -0
- package/src/internal/social-card-data.ts +1 -1
- package/src/internal/style-names.generated.ts +87 -94
- package/src/internal/swup-options.ts +98 -0
- package/src/layouts/Stalux.astro +0 -1
- package/src/pages/404.astro +1 -1
- package/src/pages/api/posts.json.ts +1 -1
- package/src/pages/atom.xml.ts +2 -0
- package/src/pages/posts/[post].md.ts +2 -2
- package/src/pages/rss.xml.ts +2 -0
- package/src/scripts/analytics-runtime.ts +4 -9
- package/src/scripts/list-reveal.ts +3 -15
- package/src/scripts/module-motion.ts +2 -3
- package/src/scripts/navigation-scripts.ts +12 -7
- package/src/scripts/route-guards.ts +200 -0
- package/src/scripts/swup-internals.d.ts +15 -0
- package/src/styles/base.css +0 -8
- package/src/styles/generated-prose.css +1 -1
- package/src/styles/generated-waline.css +1 -1
- package/src/styles/generated.css +1 -1
- package/src/styles/integrations.css +14 -16
- package/src/styles/math.css +3 -1
- package/src/styles/motion.css +1 -22
- package/src/utils/accent-color.ts +52 -0
- package/src/utils/ai-discovery.ts +1 -1
- package/src/utils/feed.ts +1 -1
- package/src/utils/public-routes.ts +14 -4
- package/src/utils/seo-document.ts +3 -2
- package/src/scripts/route-motion.ts +0 -340
- package/src/scripts/route-out.ts +0 -63
- package/src/scripts/smooth-scroll.ts +0 -139
- package/src/scripts/swup-router.ts +0 -580
- package/src/scripts/view-transitions.ts +0 -4
- package/src/utils/route-motion-policy.ts +0 -72
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xingwangzhe/stalux",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.58.0",
|
|
4
4
|
"description": "A powerful, modern Astro blog theme — use as template or install as plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
"engines": {
|
|
23
23
|
"bun": ">=1.4.0"
|
|
24
24
|
},
|
|
25
|
+
"// browserslist": "现代基线,与 src/internal/modern-css.ts 的 lightningcss targets 保持一致;当前构建不使用 autoprefixer/postcss,此字段用于声明目标并约束后续工具链。",
|
|
26
|
+
"browserslist": ["chrome >= 154", "firefox >= 157", "safari >= 26.6"],
|
|
25
27
|
"bin": {
|
|
26
28
|
"stalux": ".release/cli.mjs"
|
|
27
29
|
},
|
|
@@ -110,17 +112,7 @@
|
|
|
110
112
|
"@lucide/astro": "^1.71.0",
|
|
111
113
|
"@microsoft/clarity": "^1.0.2",
|
|
112
114
|
"@pagefind/component-ui": "^1.6.0",
|
|
113
|
-
"@swup/
|
|
114
|
-
"@swup/body-class-plugin": "^3.3.0",
|
|
115
|
-
"@swup/fragment-plugin": "^1.3.1",
|
|
116
|
-
"@swup/head-plugin": "^2.3.1",
|
|
117
|
-
"@swup/js-plugin": "^3.2.0",
|
|
118
|
-
"@swup/parallel-plugin": "^0.4.0",
|
|
119
|
-
"@swup/preload-plugin": "^3.2.12",
|
|
120
|
-
"@swup/progress-plugin": "^3.2.0",
|
|
121
|
-
"@swup/route-name-plugin": "^4.1.0",
|
|
122
|
-
"@swup/scripts-plugin": "^2.1.0",
|
|
123
|
-
"@swup/scroll-plugin": "^4.0.0",
|
|
115
|
+
"@swup/astro": "^1.8.0",
|
|
124
116
|
"@takumi-rs/core": "^2.20.0",
|
|
125
117
|
"@w3c/webmcp-polyfill": "^0.1.0",
|
|
126
118
|
"@waline/client": "^3.16.2",
|
|
@@ -98,7 +98,7 @@ const archiveYears = [...archivesByYearMonth.entries()]
|
|
|
98
98
|
)}
|
|
99
99
|
</time>
|
|
100
100
|
<a
|
|
101
|
-
href={`/posts/${post.data.abbrlink}
|
|
101
|
+
href={`/posts/${post.data.abbrlink}`}
|
|
102
102
|
class="stalux-archive-post-link"
|
|
103
103
|
data-archive-description={post.data.desc || ""}
|
|
104
104
|
>
|
|
@@ -23,7 +23,7 @@ const { t } = createTranslator((siteConfig.lang as string) || "zh-CN");
|
|
|
23
23
|
<!-- Modern Web Guidance (html): 分类卡本身即 <article>;外层曾经的 <div class="stalux-category-shell">
|
|
24
24
|
已上移到列表的 <li>(见 categoriesList.astro),避免多一层无语义容器。 -->
|
|
25
25
|
<article class="stalux-category-card" data-stalux-control="category-card">
|
|
26
|
-
<a href={`/categories/${props.category.name}
|
|
26
|
+
<a href={`/categories/${props.category.name}`} class="stalux-categoryheader a-none">
|
|
27
27
|
<span class="stalux-category-icon">
|
|
28
28
|
<svg
|
|
29
29
|
aria-hidden="true"
|
|
@@ -51,7 +51,7 @@ const cfg = {
|
|
|
51
51
|
<ul class={cfg.listClass} data-stalux-reveal>
|
|
52
52
|
{items.map((item) => (
|
|
53
53
|
<li data-stalux-reveal-item>
|
|
54
|
-
<a href={`${cfg.prefix}${item}
|
|
54
|
+
<a href={`${cfg.prefix}${item}`} class={`${cfg.itemClass} a-none`}>
|
|
55
55
|
{item}
|
|
56
56
|
</a>
|
|
57
57
|
</li>
|
|
@@ -27,7 +27,7 @@ const description = post.data.desc || "";
|
|
|
27
27
|
class="stalux-post-card-shell stalux-post-card h-full text-inherit no-underline stalux-u-flex stalux-u-flex-col stalux-u-w-full stalux-u-min-w-0"
|
|
28
28
|
>
|
|
29
29
|
{post.data.cover && (
|
|
30
|
-
<a href={`/posts/${post.data.abbrlink}
|
|
30
|
+
<a href={`/posts/${post.data.abbrlink}`} class="a-none" aria-label={post.data.title}>
|
|
31
31
|
<span class="stalux-card-post-cover stalux-u-w-full stalux-u-min-w-0">
|
|
32
32
|
<img
|
|
33
33
|
src={post.data.cover}
|
|
@@ -43,7 +43,7 @@ const description = post.data.desc || "";
|
|
|
43
43
|
</a>
|
|
44
44
|
)}
|
|
45
45
|
<h2 class="stalux-card-post-title">
|
|
46
|
-
<a href={`/posts/${post.data.abbrlink}
|
|
46
|
+
<a href={`/posts/${post.data.abbrlink}`} class="a-none">
|
|
47
47
|
{post.data.title}
|
|
48
48
|
</a>
|
|
49
49
|
</h2>
|
|
@@ -90,5 +90,5 @@ const description = post.data.desc || "";
|
|
|
90
90
|
))}
|
|
91
91
|
</ul>
|
|
92
92
|
</footer>
|
|
93
|
-
<a href={`/posts/${post.data.abbrlink}
|
|
93
|
+
<a href={`/posts/${post.data.abbrlink}`} class="stalux-card-read-more a-none"> Read More </a>
|
|
94
94
|
</article>
|
|
@@ -21,9 +21,9 @@ const { t } = createTranslator(siteConfig.lang || "zh-CN");
|
|
|
21
21
|
const typeKey = props.typeKey || props.type;
|
|
22
22
|
let typeUrl = "";
|
|
23
23
|
if (typeKey === "category" || typeKey === "categories" || typeKey === "分类") {
|
|
24
|
-
typeUrl = "/categories
|
|
24
|
+
typeUrl = "/categories";
|
|
25
25
|
} else if (typeKey === "tag" || typeKey === "tags" || typeKey === "标签") {
|
|
26
|
-
typeUrl = "/tags
|
|
26
|
+
typeUrl = "/tags";
|
|
27
27
|
}
|
|
28
28
|
---
|
|
29
29
|
|
|
@@ -17,7 +17,7 @@ const cloudDensity = Math.sqrt(Math.max(tagList.length, 100) / 100);
|
|
|
17
17
|
const tagLinks = tagList.map((t) => ({
|
|
18
18
|
type: "link" as const,
|
|
19
19
|
text: t.name,
|
|
20
|
-
url: `/tags/${t.name}
|
|
20
|
+
url: `/tags/${t.name}`,
|
|
21
21
|
}));
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -36,7 +36,7 @@ const tagLinks = tagList.map((t) => ({
|
|
|
36
36
|
{group.map(({ name, count }) => (
|
|
37
37
|
<li data-stalux-reveal-item>
|
|
38
38
|
<a
|
|
39
|
-
href={`/tags/${name}
|
|
39
|
+
href={`/tags/${name}`}
|
|
40
40
|
class="stalux-cloud-tag-card"
|
|
41
41
|
data-stalux-card
|
|
42
42
|
data-stalux-tag-card
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { compactStylesPlugin } from "./internal/compact-styles.ts";
|
|
|
4
4
|
import { modernCssTargets } from "./internal/modern-css.ts";
|
|
5
5
|
import { styleNames } from "./internal/style-names.generated.ts";
|
|
6
6
|
import { swupDebugPlugin } from "./internal/swup-debug-plugin.ts";
|
|
7
|
+
import { SWUP_OPTIONS } from "./internal/swup-options.ts";
|
|
7
8
|
/**
|
|
8
9
|
* Stalux — Astro 博客主题集成入口
|
|
9
10
|
*
|
|
@@ -18,6 +19,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
|
18
19
|
import { createRequire } from "node:module";
|
|
19
20
|
import path from "node:path";
|
|
20
21
|
import sitemap from "@astrojs/sitemap";
|
|
22
|
+
import swup from "@swup/astro";
|
|
21
23
|
import { mermaidHast, mermaidMdast } from "@xingwangzhe/satteri-mermaid";
|
|
22
24
|
import { photoswipe } from "@xingwangzhe/satteri-photoswipe";
|
|
23
25
|
import type { AstroIntegration } from "astro";
|
|
@@ -53,6 +55,7 @@ import { createPageOutputCache } from "./internal/page-output-cache";
|
|
|
53
55
|
import { loadCoverEncoder, restoreCoverFiles, upgradeCoverImages } from "./internal/post-cover";
|
|
54
56
|
import { isPluginProject } from "./internal/project-mode";
|
|
55
57
|
import { syncBackgroundSvgs, syncFontAssets } from "./internal/public-assets";
|
|
58
|
+
import { normalizeRouteHrefs } from "./internal/route-hrefs";
|
|
56
59
|
import { createRuntimeCacheKey } from "./internal/runtime-cache-key";
|
|
57
60
|
import {
|
|
58
61
|
appendUniquePlugin,
|
|
@@ -180,17 +183,20 @@ export function stalux(options: StaluxOptions = {}): AstroIntegration[] {
|
|
|
180
183
|
// Vite re-optimization would otherwise reload the
|
|
181
184
|
// entire document midway through a Swup navigation.
|
|
182
185
|
include: [
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
"@swup/
|
|
188
|
-
"@swup/
|
|
189
|
-
"@swup/
|
|
190
|
-
"@swup/
|
|
191
|
-
"@swup/
|
|
192
|
-
"@swup/
|
|
193
|
-
"@swup/
|
|
186
|
+
// The router and its plugins are assembled by
|
|
187
|
+
// @swup/astro and imported through its client
|
|
188
|
+
// entry points, so those are the specifiers Vite
|
|
189
|
+
// has to see up front.
|
|
190
|
+
"@swup/astro/serialise",
|
|
191
|
+
"@swup/astro/client/Swup",
|
|
192
|
+
"@swup/astro/client/SwupA11yPlugin",
|
|
193
|
+
"@swup/astro/client/SwupBodyClassPlugin",
|
|
194
|
+
"@swup/astro/client/SwupFragmentPlugin",
|
|
195
|
+
"@swup/astro/client/SwupHeadPlugin",
|
|
196
|
+
"@swup/astro/client/SwupPreloadPlugin",
|
|
197
|
+
"@swup/astro/client/SwupProgressPlugin",
|
|
198
|
+
"@swup/astro/client/SwupRouteNamePlugin",
|
|
199
|
+
"@swup/astro/client/SwupScrollPlugin",
|
|
194
200
|
"@microsoft/clarity",
|
|
195
201
|
"@pagefind/component-ui",
|
|
196
202
|
"@waline/client",
|
|
@@ -244,6 +250,12 @@ export function stalux(options: StaluxOptions = {}): AstroIntegration[] {
|
|
|
244
250
|
// 2. 注入全局 CSS
|
|
245
251
|
injectScript("page-ssr", `import "${srcDir}/styles/generated.css";`);
|
|
246
252
|
injectScript("page", `import "${srcDir}/scripts/core-runtime.ts";`);
|
|
253
|
+
// 路由层:@swup/astro 负责装配实例与插件,这里只挂三个护栏
|
|
254
|
+
// (脚本去重执行、过期 visit 中止、过期样式剪枝)。
|
|
255
|
+
injectScript(
|
|
256
|
+
"page",
|
|
257
|
+
`import { installRouteGuards } from "${srcDir}/scripts/route-guards.ts"; installRouteGuards();`,
|
|
258
|
+
);
|
|
247
259
|
|
|
248
260
|
// 3. 注入所有页面路由(仅插件模式,源码模式下使用文件路由)
|
|
249
261
|
if (isPluginMode) {
|
|
@@ -529,6 +541,8 @@ export function stalux(options: StaluxOptions = {}): AstroIntegration[] {
|
|
|
529
541
|
promoteCodeStyles(document);
|
|
530
542
|
containMermaidSvgs(document);
|
|
531
543
|
removePageFontStyles(document);
|
|
544
|
+
// 正文里手写的 /posts/x/ 也统一成路由器实际提供的形态
|
|
545
|
+
normalizeRouteHrefs(document);
|
|
532
546
|
const images = findHtmlImages(before, document);
|
|
533
547
|
await updateLocalImageDimensions(images, outDir, {
|
|
534
548
|
cache: dimensionsCache,
|
|
@@ -676,8 +690,10 @@ export function stalux(options: StaluxOptions = {}): AstroIntegration[] {
|
|
|
676
690
|
};
|
|
677
691
|
|
|
678
692
|
// 打包内置集成:Astro 的 integrations 配置支持嵌套数组并自动展平(schema 里 val.flat(Infinity)),
|
|
679
|
-
// 因此这里返回 [coreIntegration, sitemap, expressiveCode],让消费方一行 `integrations: [stalux()]` 即可完成配置。
|
|
680
|
-
|
|
693
|
+
// 因此这里返回 [swup, coreIntegration, sitemap, expressiveCode],让消费方一行 `integrations: [stalux()]` 即可完成配置。
|
|
694
|
+
// 路由器必须排在第一位:@swup/astro 先注入初始化脚本,主题的护栏脚本才能在同一轮
|
|
695
|
+
// 模块求值里直接拿到 window.swup。
|
|
696
|
+
const bundled: AstroIntegration[] = [swup(SWUP_OPTIONS), coreIntegration];
|
|
681
697
|
|
|
682
698
|
if (opt.sitemap !== false) {
|
|
683
699
|
const userSitemap = opt.sitemap === true ? undefined : opt.sitemap;
|
|
@@ -740,6 +756,16 @@ export function stalux(options: StaluxOptions = {}): AstroIntegration[] {
|
|
|
740
756
|
...userSitemap,
|
|
741
757
|
customPages,
|
|
742
758
|
filter,
|
|
759
|
+
// Page routes are served without a trailing slash; keep the root
|
|
760
|
+
// and real files (`.md`, `.json`, `.xml`, images) exactly as built.
|
|
761
|
+
async serialize(item) {
|
|
762
|
+
const next = userSitemap?.serialize ? await userSitemap.serialize(item) : item;
|
|
763
|
+
if (!next) return next;
|
|
764
|
+
const url = new URL(next.url);
|
|
765
|
+
if (url.pathname !== "/") url.pathname = url.pathname.replace(/\/+$/u, "");
|
|
766
|
+
next.url = url.href;
|
|
767
|
+
return next;
|
|
768
|
+
},
|
|
743
769
|
}),
|
|
744
770
|
{
|
|
745
771
|
name: "stalux/sitemap-alias",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type DefaultTreeAdapterMap, serialize, serializeOuter } from "parse5";
|
|
2
|
+
import { normalizeRouteHref } from "./route-hrefs";
|
|
2
3
|
|
|
3
4
|
type Element = DefaultTreeAdapterMap["element"];
|
|
4
5
|
|
|
@@ -17,6 +18,9 @@ export function createHtmlMutationEmitter(
|
|
|
17
18
|
attrs: Element["attrs"];
|
|
18
19
|
text: string[];
|
|
19
20
|
}> = [];
|
|
21
|
+
// Anchor hrefs are rewritten by value range only: reserializing the whole
|
|
22
|
+
// element would re-escape attribute text the emitter never touched.
|
|
23
|
+
const hrefSnapshots: Array<{ node: Element; start: number; end: number }> = [];
|
|
20
24
|
let head: Element | undefined;
|
|
21
25
|
function visit(node: DefaultTreeAdapterMap["node"]) {
|
|
22
26
|
if ("tagName" in node) {
|
|
@@ -37,6 +41,10 @@ export function createHtmlMutationEmitter(
|
|
|
37
41
|
attrs: node.attrs.map((attribute) => ({ ...attribute })),
|
|
38
42
|
text: node.childNodes.map((child) => ("value" in child ? child.value : "")),
|
|
39
43
|
});
|
|
44
|
+
} else if (node.tagName === "a") {
|
|
45
|
+
const href = location?.attrs?.href;
|
|
46
|
+
if (href)
|
|
47
|
+
hrefSnapshots.push({ node, start: href.startOffset, end: href.endOffset });
|
|
40
48
|
}
|
|
41
49
|
}
|
|
42
50
|
if ("childNodes" in node) for (const child of node.childNodes) visit(child);
|
|
@@ -45,6 +53,16 @@ export function createHtmlMutationEmitter(
|
|
|
45
53
|
|
|
46
54
|
return () => {
|
|
47
55
|
const edits: Array<{ start: number; end: number; value: string }> = [];
|
|
56
|
+
for (const { node, start, end } of hrefSnapshots) {
|
|
57
|
+
const current = node.attrs.find((attribute) => attribute.name === "href")?.value;
|
|
58
|
+
if (current === undefined) continue;
|
|
59
|
+
const raw = source.slice(start, end);
|
|
60
|
+
const parsed = /^(href\s*=\s*["'])([^"']*)(["'])$/u.exec(raw);
|
|
61
|
+
if (!parsed) continue;
|
|
62
|
+
const next = normalizeRouteHref(parsed[2] ?? "");
|
|
63
|
+
if (next === parsed[2]) continue;
|
|
64
|
+
edits.push({ start, end, value: `${parsed[1]}${next}${parsed[3]}` });
|
|
65
|
+
}
|
|
48
66
|
for (const snapshot of snapshots) {
|
|
49
67
|
const { node, start, end, attrs, text } = snapshot;
|
|
50
68
|
if (!node.parentNode) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Modern CSS output floor for the 1.35 release. Older fallback expansions are unnecessary.
|
|
2
2
|
// Acceptance uses current stable browser engines; targets intentionally retain native nesting.
|
|
3
|
+
// Keep in sync with the `browserslist` field in package.json (documentation for other tools).
|
|
3
4
|
export const modernCssTargets = {
|
|
4
5
|
chrome: 154 << 16,
|
|
5
6
|
firefox: 157 << 16,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { DefaultTreeAdapterMap } from "parse5";
|
|
2
|
+
|
|
3
|
+
type Document = DefaultTreeAdapterMap["document"];
|
|
4
|
+
|
|
5
|
+
/** Endpoints that are real files: the extension identifies them, never a slash. */
|
|
6
|
+
const REAL_FILE =
|
|
7
|
+
/\.(?:md|json|xml|txt|html?|pdf|png|webp|avif|svg|jpe?g|gif|ico|css|js|mjs|map|zip|mp4|webm|woff2?|ttf|otf)$/iu;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Rewrite a site-absolute href to the form the router actually serves.
|
|
11
|
+
*
|
|
12
|
+
* Page routes carry no trailing slash (`/posts/x`, `/archives`), while real
|
|
13
|
+
* files keep theirs (`/posts/x.md`, `/api/posts.json`). Only rooted paths are
|
|
14
|
+
* touched: relative links, protocol-relative URLs and anchors pass through.
|
|
15
|
+
*/
|
|
16
|
+
export function normalizeRouteHref(href: string): string {
|
|
17
|
+
if (!href.startsWith("/") || href.startsWith("//")) return href;
|
|
18
|
+
const match = /^([^?#]*)([?#].*)?$/u.exec(href);
|
|
19
|
+
const path = match?.[1] ?? href;
|
|
20
|
+
const suffix = match?.[2] ?? "";
|
|
21
|
+
if (path === "/" || !path.endsWith("/") || REAL_FILE.test(path)) return href;
|
|
22
|
+
return `${path.replace(/\/+$/u, "")}${suffix}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Content-authored links still use the old trailing-slash form. Normalising the
|
|
27
|
+
* parsed document here keeps hand-written Markdown from reintroducing a URL
|
|
28
|
+
* shape the router no longer serves, without touching the author's source.
|
|
29
|
+
*/
|
|
30
|
+
export function normalizeRouteHrefs(document: Document): number {
|
|
31
|
+
let changed = 0;
|
|
32
|
+
const visit = (node: DefaultTreeAdapterMap["node"]): void => {
|
|
33
|
+
if ("tagName" in node && node.tagName === "a") {
|
|
34
|
+
for (const attribute of node.attrs) {
|
|
35
|
+
if (attribute.name !== "href") continue;
|
|
36
|
+
const next = normalizeRouteHref(attribute.value);
|
|
37
|
+
if (next !== attribute.value) {
|
|
38
|
+
attribute.value = next;
|
|
39
|
+
changed++;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if ("childNodes" in node) for (const child of node.childNodes) visit(child);
|
|
44
|
+
};
|
|
45
|
+
visit(document);
|
|
46
|
+
return changed;
|
|
47
|
+
}
|
|
@@ -22,7 +22,7 @@ export function createSocialCardData(
|
|
|
22
22
|
siteTitle: site.title,
|
|
23
23
|
author: author.name,
|
|
24
24
|
host: siteUrl.host,
|
|
25
|
-
postUrl: new URL(`/posts/${encodeURIComponent(post.abbrlink)}
|
|
25
|
+
postUrl: new URL(`/posts/${encodeURIComponent(post.abbrlink)}`, siteUrl).href,
|
|
26
26
|
tags: post.tags ?? [],
|
|
27
27
|
date: formatInTimeZone(
|
|
28
28
|
parseDate(post.date, site.timezone || "Asia/Shanghai"),
|
|
@@ -247,101 +247,94 @@ export const styleNames: { classes: Record<string, string>; variables: Record<st
|
|
|
247
247
|
"--border-thick": "--sh",
|
|
248
248
|
"--border-thin": "--si",
|
|
249
249
|
"--card-bg": "--sj",
|
|
250
|
-
"--card-
|
|
251
|
-
"--card-box-shadow": "--sl",
|
|
252
|
-
"--
|
|
253
|
-
"--category-bg": "--sn",
|
|
254
|
-
"--
|
|
255
|
-
"--cloud-
|
|
256
|
-
"--
|
|
257
|
-
"--date-
|
|
258
|
-
"--
|
|
259
|
-
"--
|
|
260
|
-
"--
|
|
261
|
-
"--font-size-
|
|
262
|
-
"--font-size-
|
|
263
|
-
"--font-size-
|
|
264
|
-
"--
|
|
265
|
-
"--
|
|
266
|
-
"--
|
|
267
|
-
"--
|
|
268
|
-
"--radius-
|
|
269
|
-
"--radius-
|
|
270
|
-
"--
|
|
271
|
-
"--
|
|
272
|
-
"--shadow-
|
|
273
|
-
"--shadow-
|
|
274
|
-
"--shadow-
|
|
275
|
-
"--shadow-
|
|
276
|
-
"--
|
|
277
|
-
"--
|
|
278
|
-
"--
|
|
279
|
-
"--space-
|
|
280
|
-
"--space-
|
|
281
|
-
"--space-
|
|
282
|
-
"--space-
|
|
283
|
-
"--space-
|
|
284
|
-
"--
|
|
285
|
-
"--
|
|
286
|
-
"--
|
|
287
|
-
"--stalux-
|
|
288
|
-
"--stalux-
|
|
289
|
-
"--stalux-
|
|
290
|
-
"--stalux-
|
|
291
|
-
"--stalux-
|
|
292
|
-
"--stalux-
|
|
293
|
-
"--stalux-
|
|
294
|
-
"--stalux-
|
|
295
|
-
"--
|
|
296
|
-
"--
|
|
297
|
-
"--
|
|
298
|
-
"--
|
|
299
|
-
"--
|
|
300
|
-
"--
|
|
301
|
-
"--
|
|
302
|
-
"--
|
|
303
|
-
"--
|
|
304
|
-
"--transition-
|
|
305
|
-
"--
|
|
306
|
-
"--
|
|
307
|
-
"--
|
|
308
|
-
"--
|
|
309
|
-
"--
|
|
310
|
-
"--un-
|
|
311
|
-
"--un-
|
|
312
|
-
"--un-
|
|
313
|
-
"--un-
|
|
314
|
-
"--un-
|
|
315
|
-
"--un-
|
|
316
|
-
"--un-
|
|
317
|
-
"--
|
|
318
|
-
"--
|
|
319
|
-
"--
|
|
320
|
-
"--
|
|
321
|
-
"--
|
|
322
|
-
"--
|
|
323
|
-
"--
|
|
324
|
-
"--white-
|
|
325
|
-
"--white-
|
|
326
|
-
"--white-
|
|
327
|
-
"--white-
|
|
328
|
-
"--white-
|
|
329
|
-
"--white-
|
|
330
|
-
"--white-
|
|
331
|
-
"--white-
|
|
332
|
-
"--white-
|
|
333
|
-
"--white-
|
|
334
|
-
"--white-80p": "--s2w",
|
|
335
|
-
"--white-85p": "--s2x",
|
|
336
|
-
"--white-90p": "--s2y",
|
|
337
|
-
"--white-95p": "--s2z",
|
|
338
|
-
"--white-full": "--s30"
|
|
250
|
+
"--card-box-shadow": "--sk",
|
|
251
|
+
"--card-box-shadow-hover": "--sl",
|
|
252
|
+
"--category-bg": "--sm",
|
|
253
|
+
"--category-bg-hover": "--sn",
|
|
254
|
+
"--cloud-base-diameter": "--so",
|
|
255
|
+
"--cloud-density": "--sp",
|
|
256
|
+
"--date-bg": "--sq",
|
|
257
|
+
"--date-border": "--sr",
|
|
258
|
+
"--dot": "--ss",
|
|
259
|
+
"--font-size-base": "--st",
|
|
260
|
+
"--font-size-small": "--su",
|
|
261
|
+
"--font-size-smaller": "--sv",
|
|
262
|
+
"--font-size-subtitle": "--sw",
|
|
263
|
+
"--font-size-tiny": "--sx",
|
|
264
|
+
"--muted-text": "--sy",
|
|
265
|
+
"--nav-scale": "--sz",
|
|
266
|
+
"--radius-full": "--s10",
|
|
267
|
+
"--radius-lg": "--s11",
|
|
268
|
+
"--radius-md": "--s12",
|
|
269
|
+
"--radius-sm": "--s13",
|
|
270
|
+
"--shadow-dark": "--s14",
|
|
271
|
+
"--shadow-lg": "--s15",
|
|
272
|
+
"--shadow-md": "--s16",
|
|
273
|
+
"--shadow-sidebar": "--s17",
|
|
274
|
+
"--shadow-sm": "--s18",
|
|
275
|
+
"--shadow-xl": "--s19",
|
|
276
|
+
"--space-2xl": "--s1a",
|
|
277
|
+
"--space-2xs": "--s1b",
|
|
278
|
+
"--space-3xl": "--s1c",
|
|
279
|
+
"--space-lg": "--s1d",
|
|
280
|
+
"--space-md": "--s1e",
|
|
281
|
+
"--space-sm": "--s1f",
|
|
282
|
+
"--space-xl": "--s1g",
|
|
283
|
+
"--space-xs": "--s1h",
|
|
284
|
+
"--stalux-accent-color": "--s1i",
|
|
285
|
+
"--stalux-accent-rgb": "--s1j",
|
|
286
|
+
"--stalux-bg-color": "--s1k",
|
|
287
|
+
"--stalux-bg-filter": "--s1l",
|
|
288
|
+
"--stalux-nav-offset": "--s1m",
|
|
289
|
+
"--stalux-scrollbar-thumb": "--s1n",
|
|
290
|
+
"--stalux-scrollbar-thumb-hover": "--s1o",
|
|
291
|
+
"--stalux-scrollbar-track": "--s1p",
|
|
292
|
+
"--stalux-site-header-height": "--s1q",
|
|
293
|
+
"--stalux-site-header-min-height": "--s1r",
|
|
294
|
+
"--stalux-text-shadow": "--s1s",
|
|
295
|
+
"--surface-glass": "--s1t",
|
|
296
|
+
"--surface-glass-shadow": "--s1u",
|
|
297
|
+
"--tag-bg": "--s1v",
|
|
298
|
+
"--tag-bg-hover": "--s1w",
|
|
299
|
+
"--tl-line": "--s1x",
|
|
300
|
+
"--tl-pad": "--s1y",
|
|
301
|
+
"--transition-fast": "--s1z",
|
|
302
|
+
"--transition-medium": "--s20",
|
|
303
|
+
"--transition-normal": "--s21",
|
|
304
|
+
"--transition-slow": "--s22",
|
|
305
|
+
"--un-rotate": "--s23",
|
|
306
|
+
"--un-rotate-x": "--s24",
|
|
307
|
+
"--un-rotate-y": "--s25",
|
|
308
|
+
"--un-rotate-z": "--s26",
|
|
309
|
+
"--un-scale-x": "--s27",
|
|
310
|
+
"--un-scale-y": "--s28",
|
|
311
|
+
"--un-scale-z": "--s29",
|
|
312
|
+
"--un-skew-x": "--s2a",
|
|
313
|
+
"--un-skew-y": "--s2b",
|
|
314
|
+
"--un-translate-x": "--s2c",
|
|
315
|
+
"--un-translate-y": "--s2d",
|
|
316
|
+
"--un-translate-z": "--s2e",
|
|
317
|
+
"--updated-bg": "--s2f",
|
|
318
|
+
"--updated-border": "--s2g",
|
|
319
|
+
"--white-05p": "--s2h",
|
|
320
|
+
"--white-10p": "--s2i",
|
|
321
|
+
"--white-15p": "--s2j",
|
|
322
|
+
"--white-20p": "--s2k",
|
|
323
|
+
"--white-30p": "--s2l",
|
|
324
|
+
"--white-50p": "--s2m",
|
|
325
|
+
"--white-60p": "--s2n",
|
|
326
|
+
"--white-70p": "--s2o",
|
|
327
|
+
"--white-75p": "--s2p",
|
|
328
|
+
"--white-78p": "--s2q",
|
|
329
|
+
"--white-80p": "--s2r",
|
|
330
|
+
"--white-85p": "--s2s",
|
|
331
|
+
"--white-90p": "--s2t",
|
|
332
|
+
"--white-95p": "--s2u",
|
|
333
|
+
"--white-full": "--s2v"
|
|
339
334
|
},
|
|
340
335
|
"animations": {
|
|
341
|
-
"
|
|
342
|
-
"stalux-
|
|
343
|
-
"stalux-
|
|
344
|
-
"stalux-random-spin": "k3",
|
|
345
|
-
"stalux-social-stalux-social-arrive": "k4"
|
|
336
|
+
"stalux-cursor-blink": "k0",
|
|
337
|
+
"stalux-random-spin": "k1",
|
|
338
|
+
"stalux-social-stalux-social-arrive": "k2"
|
|
346
339
|
}
|
|
347
340
|
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swup configuration handed to `@swup/astro`.
|
|
3
|
+
*
|
|
4
|
+
* `@swup/astro` owns construction of the Swup instance and all plugin
|
|
5
|
+
* assembly; the theme only describes *which* behaviour it wants. Anything that
|
|
6
|
+
* cannot be expressed here lives in `src/scripts/route-guards.ts`.
|
|
7
|
+
*/
|
|
8
|
+
import type { Options, Route } from "@swup/astro";
|
|
9
|
+
|
|
10
|
+
/** Named routes shared by the router and the route classifier. */
|
|
11
|
+
export const SWUP_ROUTES: Route[] = [
|
|
12
|
+
{ name: "home", path: "/" },
|
|
13
|
+
{ name: "article", path: "/posts/:post" },
|
|
14
|
+
{ name: "archive", path: "/archives" },
|
|
15
|
+
{ name: "tags-index", path: "/tags" },
|
|
16
|
+
{ name: "tags-detail", path: "/tags/:tag" },
|
|
17
|
+
{ name: "categories-index", path: "/categories" },
|
|
18
|
+
{ name: "categories-detail", path: "/categories/:category" },
|
|
19
|
+
...["about", "links", "words"].map((name) => ({ name, path: `/${name}` })),
|
|
20
|
+
{ name: "not-found", path: "/404" },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Real files are never pages. `@swup/astro` builds `ignoreVisit` itself and
|
|
25
|
+
* only lets `ignore` extend it, so this list is the whole hook.
|
|
26
|
+
*/
|
|
27
|
+
export const SWUP_IGNORE_FILES = /\.(?:md|json|xml|txt|html?|pdf|png|webp|avif|svg|jpe?g)$/iu;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Paths the theme renders as soft-navigable pages.
|
|
31
|
+
*
|
|
32
|
+
* `@swup/astro` cannot express "only these routes" as a predicate: `ignore`
|
|
33
|
+
* accepts regexes, and a regex is the only form that survives serialisation
|
|
34
|
+
* into the init script. Keeping the alternation here also keeps the rule next
|
|
35
|
+
* to `SWUP_ROUTES` above, which is the same set of pages.
|
|
36
|
+
*/
|
|
37
|
+
const SOFT_NAV_PATHS =
|
|
38
|
+
/^\/(?:|archives|links|words|about|404|tags(?:\/[^/?#]+)?|categories(?:\/[^/?#]+)?|posts\/[^/?#]+)\/?(?:[?#].*)?$/u;
|
|
39
|
+
|
|
40
|
+
/** Every path the theme itself renders; anything else is left to the browser. */
|
|
41
|
+
export const SWUP_IGNORE_UNKNOWN_ROUTES = new RegExp(`^(?!${SOFT_NAV_PATHS.source})`, "u");
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* `@swup/astro` types most plugin options as booleans, but forwards any object
|
|
45
|
+
* it is given straight into the plugin constructor. Two of these need their
|
|
46
|
+
* real plugin options (body-class prefix, head persistence, scroll behaviour),
|
|
47
|
+
* so the object form is used deliberately and the cast is made once here.
|
|
48
|
+
*/
|
|
49
|
+
export const SWUP_OPTIONS = {
|
|
50
|
+
containers: ["#swup-main"],
|
|
51
|
+
// No route animation: no theme, and no `[class*="transition-"]` selector to
|
|
52
|
+
// wait on. The swap is a plain, synchronous DOM replacement.
|
|
53
|
+
theme: false,
|
|
54
|
+
animationClass: false,
|
|
55
|
+
// The init script must run while the first document is still parsing; on
|
|
56
|
+
// idle the first click can arrive before the router is listening.
|
|
57
|
+
loadOnIdle: false,
|
|
58
|
+
// The guard script needs the instance; route-prefetch already reads it.
|
|
59
|
+
globalInstance: true,
|
|
60
|
+
// Page scripts are re-executed by the theme's own once-per-session runner
|
|
61
|
+
// (see route-guards.ts). The plugin's whole-document pass would replay
|
|
62
|
+
// analytics SDKs on every visit.
|
|
63
|
+
reloadScripts: false,
|
|
64
|
+
cache: true,
|
|
65
|
+
native: false,
|
|
66
|
+
morph: false,
|
|
67
|
+
forms: false,
|
|
68
|
+
preload: { hover: true, visible: false },
|
|
69
|
+
progress: { delay: 80, transition: 200 },
|
|
70
|
+
accessibility: true,
|
|
71
|
+
updateBodyClass: { prefix: "page-", attributes: [/^data-stalux-(?!bg-)/u] },
|
|
72
|
+
updateHead: {
|
|
73
|
+
awaitAssets: true,
|
|
74
|
+
// Keep old CSS readable while the incoming sheet loads; route-guards
|
|
75
|
+
// removes the styles the new page does not use once it is in place.
|
|
76
|
+
persistTags: "link[rel=stylesheet], script[src], style",
|
|
77
|
+
},
|
|
78
|
+
routes: SWUP_ROUTES,
|
|
79
|
+
fragments: [
|
|
80
|
+
{
|
|
81
|
+
from: ["/tags/:slug", "/categories/:slug"],
|
|
82
|
+
to: ["/tags/:slug", "/categories/:slug"],
|
|
83
|
+
containers: ["#swup-taxonomy"],
|
|
84
|
+
scroll: true,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
from: "/posts/:post",
|
|
88
|
+
to: "/posts/:post",
|
|
89
|
+
containers: ["#swup-article"],
|
|
90
|
+
scroll: true,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
smoothScrolling: {
|
|
94
|
+
doScrollingRightAway: false,
|
|
95
|
+
animateScroll: { betweenPages: false, samePageWithHash: true, samePage: true },
|
|
96
|
+
},
|
|
97
|
+
ignore: [SWUP_IGNORE_FILES, SWUP_IGNORE_UNKNOWN_ROUTES],
|
|
98
|
+
} as unknown as Partial<Options>;
|
package/src/layouts/Stalux.astro
CHANGED
|
@@ -74,7 +74,6 @@ const routeKind = getRouteKind(Astro.url.pathname);
|
|
|
74
74
|
/>
|
|
75
75
|
<script>
|
|
76
76
|
import "../scripts/background.ts";
|
|
77
|
-
import "../scripts/view-transitions.ts";
|
|
78
77
|
// Every page type has at least one list, so the unroll cue is registered
|
|
79
78
|
// once in the shared layout instead of per page. Page-runtime dedupes it
|
|
80
79
|
// against the article and archive components that also request it.
|