@xingwangzhe/stalux 1.57.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/index.ts +26 -13
- package/src/internal/modern-css.ts +1 -0
- 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/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/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",
|
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";
|
|
@@ -181,17 +183,20 @@ export function stalux(options: StaluxOptions = {}): AstroIntegration[] {
|
|
|
181
183
|
// Vite re-optimization would otherwise reload the
|
|
182
184
|
// entire document midway through a Swup navigation.
|
|
183
185
|
include: [
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
"@swup/
|
|
189
|
-
"@swup/
|
|
190
|
-
"@swup/
|
|
191
|
-
"@swup/
|
|
192
|
-
"@swup/
|
|
193
|
-
"@swup/
|
|
194
|
-
"@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",
|
|
195
200
|
"@microsoft/clarity",
|
|
196
201
|
"@pagefind/component-ui",
|
|
197
202
|
"@waline/client",
|
|
@@ -245,6 +250,12 @@ export function stalux(options: StaluxOptions = {}): AstroIntegration[] {
|
|
|
245
250
|
// 2. 注入全局 CSS
|
|
246
251
|
injectScript("page-ssr", `import "${srcDir}/styles/generated.css";`);
|
|
247
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
|
+
);
|
|
248
259
|
|
|
249
260
|
// 3. 注入所有页面路由(仅插件模式,源码模式下使用文件路由)
|
|
250
261
|
if (isPluginMode) {
|
|
@@ -679,8 +690,10 @@ export function stalux(options: StaluxOptions = {}): AstroIntegration[] {
|
|
|
679
690
|
};
|
|
680
691
|
|
|
681
692
|
// 打包内置集成:Astro 的 integrations 配置支持嵌套数组并自动展平(schema 里 val.flat(Infinity)),
|
|
682
|
-
// 因此这里返回 [coreIntegration, sitemap, expressiveCode],让消费方一行 `integrations: [stalux()]` 即可完成配置。
|
|
683
|
-
|
|
693
|
+
// 因此这里返回 [swup, coreIntegration, sitemap, expressiveCode],让消费方一行 `integrations: [stalux()]` 即可完成配置。
|
|
694
|
+
// 路由器必须排在第一位:@swup/astro 先注入初始化脚本,主题的护栏脚本才能在同一轮
|
|
695
|
+
// 模块求值里直接拿到 window.swup。
|
|
696
|
+
const bundled: AstroIntegration[] = [swup(SWUP_OPTIONS), coreIntegration];
|
|
684
697
|
|
|
685
698
|
if (opt.sitemap !== false) {
|
|
686
699
|
const userSitemap = opt.sitemap === true ? undefined : opt.sitemap;
|
|
@@ -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,
|
|
@@ -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.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/** Async native SDKs keep live DOM events out of worker proxies during Astro swaps. */
|
|
2
2
|
|
|
3
3
|
import { createClientLogger } from "./logger";
|
|
4
|
-
import { prepareRouteMotion, type RouteMotionEvent } from "./route-motion";
|
|
5
4
|
|
|
6
5
|
const logger = createClientLogger("analytics");
|
|
7
6
|
|
|
@@ -32,7 +31,6 @@ const state = window.__staluxAnalyticsRuntime;
|
|
|
32
31
|
state.scripts ??= new Map();
|
|
33
32
|
|
|
34
33
|
type GooglePage = {
|
|
35
|
-
ready: Promise<unknown>;
|
|
36
34
|
payload: {
|
|
37
35
|
page_location: string;
|
|
38
36
|
page_path: string;
|
|
@@ -41,7 +39,6 @@ type GooglePage = {
|
|
|
41
39
|
};
|
|
42
40
|
};
|
|
43
41
|
const googlePages: GooglePage[] = [];
|
|
44
|
-
let transitionReady: Promise<unknown> = Promise.resolve();
|
|
45
42
|
let googleScheduled = false;
|
|
46
43
|
let googleSchedule = 0;
|
|
47
44
|
let googleTimer: ReturnType<typeof setTimeout> | undefined;
|
|
@@ -77,9 +74,9 @@ function scheduleGooglePages(): void {
|
|
|
77
74
|
// A burst of visits stays FIFO, with a separate task for each SDK call.
|
|
78
75
|
scheduleGooglePages();
|
|
79
76
|
};
|
|
80
|
-
// A queued visit can outlive
|
|
81
|
-
//
|
|
82
|
-
void Promise.
|
|
77
|
+
// A queued visit can outlive the page it belongs to; only the paint
|
|
78
|
+
// ordering of the current document matters here.
|
|
79
|
+
void Promise.resolve(page).then(() => {
|
|
83
80
|
if (schedule !== googleSchedule) return;
|
|
84
81
|
// Let the routed content paint after native rendering resumes and its own
|
|
85
82
|
// motion is installed. The timer also covers throttled/background frames.
|
|
@@ -336,7 +333,6 @@ function trackGooglePage(): void {
|
|
|
336
333
|
// Capture the visit before yielding: another navigation must not replace its
|
|
337
334
|
// URL, title or virtual referrer while the SDK is waiting for its task.
|
|
338
335
|
googlePages.push({
|
|
339
|
-
ready: transitionReady,
|
|
340
336
|
payload: {
|
|
341
337
|
page_location: window.location.href,
|
|
342
338
|
page_path: window.location.pathname + window.location.search,
|
|
@@ -439,11 +435,10 @@ if (state.installed && (!state.native || state.revision !== 4)) {
|
|
|
439
435
|
state.native = true;
|
|
440
436
|
state.revision = 4;
|
|
441
437
|
state.installed = true;
|
|
442
|
-
document.addEventListener("astro:before-swap", (
|
|
438
|
+
document.addEventListener("astro:before-swap", () => {
|
|
443
439
|
pendingEarlyView++;
|
|
444
440
|
earlyViewPending = false;
|
|
445
441
|
viewedEarly = false;
|
|
446
|
-
transitionReady = prepareRouteMotion(event as Event & RouteMotionEvent).ready;
|
|
447
442
|
// Invalidate an older page's frames, timer and posted task before this
|
|
448
443
|
// incoming snapshot starts. Its captured visit remains in the FIFO.
|
|
449
444
|
cancelGoogleSchedule();
|
|
@@ -11,10 +11,6 @@ const MAX_ITEMS = 16;
|
|
|
11
11
|
const STAGGER_MS = 22;
|
|
12
12
|
const MAX_DELAY_MS = 200;
|
|
13
13
|
const DURATION_MS = 190;
|
|
14
|
-
// When a route cue is already moving the page body, the sheet waits for it to
|
|
15
|
-
// mostly settle before unrolling. Starting both at once makes the rows wipe
|
|
16
|
-
// open while the whole page is still semi-transparent, which reads as a smudge.
|
|
17
|
-
const ROUTE_LEAD_IN_MS = 75;
|
|
18
14
|
// Strong ease-out: the row arrives fast and settles softly. The built-in
|
|
19
15
|
// ease-out is too weak to read at this size.
|
|
20
16
|
const EASING = "cubic-bezier(0.23, 1, 0.32, 1)";
|
|
@@ -59,9 +55,7 @@ function canReveal(): boolean {
|
|
|
59
55
|
if (typeof IntersectionObserver !== "function") return false;
|
|
60
56
|
if (typeof document === "undefined") return false;
|
|
61
57
|
if (document.hidden) return false;
|
|
62
|
-
|
|
63
|
-
// Returning to a history entry restores what the reader already saw.
|
|
64
|
-
return document.documentElement.dataset.staluxVtPreset !== "restore";
|
|
58
|
+
return !window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
65
59
|
}
|
|
66
60
|
|
|
67
61
|
function groups(): Element[] {
|
|
@@ -102,11 +96,6 @@ function staggerOf(index: number): number {
|
|
|
102
96
|
return Math.min(index * STAGGER_MS, MAX_DELAY_MS);
|
|
103
97
|
}
|
|
104
98
|
|
|
105
|
-
function leadIn(): number {
|
|
106
|
-
// A route cue leaves its preset on the document element while it runs.
|
|
107
|
-
return document.documentElement.dataset.staluxVtPreset ? ROUTE_LEAD_IN_MS : 0;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
99
|
/**
|
|
111
100
|
* Hold every incoming row on the wipe's first frame the moment the router has
|
|
112
101
|
* its new document, before the browser paints it.
|
|
@@ -201,7 +190,6 @@ export function mountListReveal(): (() => void) | undefined {
|
|
|
201
190
|
};
|
|
202
191
|
const reveal = (container: Element) => {
|
|
203
192
|
if (document.hidden || preference.matches) return;
|
|
204
|
-
const lead = leadIn();
|
|
205
193
|
for (const [index, row] of rows(container).entries()) {
|
|
206
194
|
if (typeof row.animate !== "function") continue;
|
|
207
195
|
// Prefer the effect created during the swap: it already owns the
|
|
@@ -216,7 +204,7 @@ export function mountListReveal(): (() => void) | undefined {
|
|
|
216
204
|
primed?.animation ??
|
|
217
205
|
row.animate(UNROLL_KEYFRAMES, {
|
|
218
206
|
duration: DURATION_MS,
|
|
219
|
-
delay:
|
|
207
|
+
delay: staggerOf(index),
|
|
220
208
|
easing: EASING,
|
|
221
209
|
// Hidden during the stagger delay instead of popping in.
|
|
222
210
|
fill: "backwards",
|
|
@@ -224,7 +212,7 @@ export function mountListReveal(): (() => void) | undefined {
|
|
|
224
212
|
if (primed) {
|
|
225
213
|
// The stagger is counted from now, not from the swap.
|
|
226
214
|
primed.animation.effect?.updateTiming({
|
|
227
|
-
delay:
|
|
215
|
+
delay: primed.stagger,
|
|
228
216
|
});
|
|
229
217
|
primed.animation.play();
|
|
230
218
|
}
|
|
@@ -27,13 +27,12 @@ export function mountModuleMotion(): (() => void) | undefined {
|
|
|
27
27
|
// so the observer's target needs no assertion.
|
|
28
28
|
const element = entry.target;
|
|
29
29
|
observer.unobserve(element);
|
|
30
|
-
// The router owns
|
|
31
|
-
//
|
|
30
|
+
// The router owns the swap. Do not layer an observer entry over
|
|
31
|
+
// it or make long comments slide.
|
|
32
32
|
if (
|
|
33
33
|
document.hidden ||
|
|
34
34
|
motionPreference.matches ||
|
|
35
35
|
document.documentElement.classList.contains("is-changing") ||
|
|
36
|
-
document.documentElement.dataset.staluxVtPreset ||
|
|
37
36
|
// A persisted node kept its identity across the visit; it was
|
|
38
37
|
// never rebuilt, so re-entering it would be a second cue.
|
|
39
38
|
element.closest("[data-swup-persist]") ||
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import SwupScriptsPlugin from "@swup/scripts-plugin";
|
|
2
1
|
import { createClientLogger } from "./logger";
|
|
3
2
|
|
|
4
3
|
const logger = createClientLogger("navigation-scripts");
|
|
@@ -28,12 +27,18 @@ const javascript = new Set([
|
|
|
28
27
|
]);
|
|
29
28
|
|
|
30
29
|
/**
|
|
31
|
-
*
|
|
32
|
-
* needs an ordered completion barrier and once-per-session
|
|
33
|
-
*
|
|
34
|
-
*
|
|
30
|
+
* The theme owns script execution instead of the swup scripts plugin: Astro's
|
|
31
|
+
* page-load event needs an ordered completion barrier and once-per-session
|
|
32
|
+
* semantics. The plugin's whole-document pass would also replay analytics SDKs
|
|
33
|
+
* and the scripts of retained shells on every visit.
|
|
35
34
|
*/
|
|
36
|
-
|
|
35
|
+
function runScript(from: HTMLScriptElement): HTMLScriptElement {
|
|
36
|
+
const script = document.createElement("script");
|
|
37
|
+
for (const { name, value } of from.attributes) script.setAttribute(name, value);
|
|
38
|
+
script.textContent = from.textContent;
|
|
39
|
+
from.replaceWith(script);
|
|
40
|
+
return script;
|
|
41
|
+
}
|
|
37
42
|
|
|
38
43
|
function describeScript(script: HTMLScriptElement, base: string) {
|
|
39
44
|
const type = (script.getAttribute("type") ?? "").trim().toLowerCase();
|
|
@@ -112,7 +117,7 @@ export function prepareNavigationScripts(
|
|
|
112
117
|
);
|
|
113
118
|
if (copy) copy.replaceWith(inert);
|
|
114
119
|
else (item.head ? document.head : document.body).append(inert);
|
|
115
|
-
const script =
|
|
120
|
+
const script = runScript(inert);
|
|
116
121
|
script.async = false;
|
|
117
122
|
executedNodes.add(script);
|
|
118
123
|
let observed = script;
|