@xingwangzhe/stalux 1.37.1 → 1.38.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 +1 -1
- package/src/components/stalux/archives/toArchive.astro +5 -1
- package/src/components/stalux/common/Typewriter.astro +7 -1
- package/src/components/stalux/common/author.astro +2 -0
- package/src/components/stalux/common/date.astro +8 -1
- package/src/components/stalux/layout/PageHeading.astro +27 -0
- package/src/components/stalux/layout/footer.astro +6 -1
- package/src/components/stalux/layout/listPage.astro +2 -6
- package/src/components/stalux/links/socialMedia.astro +3 -0
- package/src/components/stalux/posts/ArticleSummary.astro +8 -2
- package/src/components/stalux/posts/PostTaxonomy.astro +10 -3
- package/src/components/stalux/posts/postCC.astro +3 -2
- package/src/components/stalux/posts/postContent.astro +46 -27
- package/src/components/stalux/posts/postLeft.astro +17 -3
- package/src/components/stalux/posts/postRight.astro +20 -4
- package/src/components/stalux/posts/postToc.astro +5 -2
- package/src/components/stalux/tags/tagHeader.astro +5 -1
- package/src/index.ts +47 -4
- package/src/internal/font-slices.ts +159 -24
- package/src/internal/page-output-cache.ts +3 -1
- package/src/internal/style-names.generated.ts +121 -120
- package/src/layouts/PostLayout.astro +12 -2
- package/src/layouts/Stalux.astro +14 -2
- package/src/pages/404.astro +0 -3
- package/src/pages/about.astro +3 -3
- package/src/pages/archives.astro +5 -7
- package/src/pages/categories/[category].astro +0 -3
- package/src/pages/categories/index.astro +2 -5
- package/src/pages/index.astro +6 -6
- package/src/pages/posts/[post].astro +0 -3
- package/src/pages/tags/[tag].astro +0 -3
- package/src/pages/tags/index.astro +2 -5
- package/src/pages/words.astro +5 -7
- package/src/scripts/navigation.ts +16 -1
- package/src/scripts/view-transitions.ts +46 -1
- package/src/styles/base.css +4 -3
- package/src/styles/generated.css +1 -1
- package/src/styles/motion.css +60 -0
|
@@ -2,8 +2,9 @@ import { styleNames } from "./style-names.generated.ts";
|
|
|
2
2
|
/**
|
|
3
3
|
* Build-time CJK font subsetting engine.
|
|
4
4
|
*
|
|
5
|
-
* LXGW WenKai
|
|
6
|
-
*
|
|
5
|
+
* LXGW WenKai uses shared subsets for the persistent header and footer, then
|
|
6
|
+
* one content-addressed subset per rendered page for the remaining glyphs.
|
|
7
|
+
* Only the independent Google Sans Code font uses Astro's local Fonts API.
|
|
7
8
|
*
|
|
8
9
|
* Astro's local provider reads font files from disk (`readFile`), so no
|
|
9
10
|
* network access is involved at build time — identical behavior on GitHub
|
|
@@ -12,7 +13,8 @@ import { styleNames } from "./style-names.generated.ts";
|
|
|
12
13
|
*
|
|
13
14
|
* Output layout:
|
|
14
15
|
* node_modules/.astro/stalux-page-fonts/
|
|
15
|
-
* page-{hash}.woff2
|
|
16
|
+
* page-{hash}.woff2 — page-specific content-addressed CJK subset
|
|
17
|
+
* shared-header/footer-*.woff2 — persistent shell glyph subsets
|
|
16
18
|
*/
|
|
17
19
|
|
|
18
20
|
import { existsSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
@@ -87,7 +89,7 @@ function findFont(projectRoot: string, rel: string): string | undefined {
|
|
|
87
89
|
// ---------------------------------------------------------------------------
|
|
88
90
|
|
|
89
91
|
/**
|
|
90
|
-
* Resolve the body-font source for native page subsetting and code fonts for
|
|
92
|
+
* Resolve the body-font source for native shell/page subsetting and code fonts for
|
|
91
93
|
* Astro's local provider. Returns `null` when a required font is missing so
|
|
92
94
|
* the caller can skip font injection (pages fall back to system fonts).
|
|
93
95
|
*
|
|
@@ -130,8 +132,30 @@ export interface PageFontSubset {
|
|
|
130
132
|
filename: string;
|
|
131
133
|
sourcePath: string;
|
|
132
134
|
cacheHit: boolean;
|
|
135
|
+
glyphCount: number;
|
|
133
136
|
}
|
|
134
137
|
|
|
138
|
+
export type SharedFontGroup = "header" | "footer";
|
|
139
|
+
|
|
140
|
+
export interface SharedFontGlyphs {
|
|
141
|
+
header: Set<string>;
|
|
142
|
+
footer: Set<string>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface SharedFontSubset {
|
|
146
|
+
group: SharedFontGroup;
|
|
147
|
+
family: string;
|
|
148
|
+
chars: string[];
|
|
149
|
+
filename: string;
|
|
150
|
+
sourcePath: string;
|
|
151
|
+
cacheHit: boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const SHARED_FONT_FAMILIES: Record<SharedFontGroup, string> = {
|
|
155
|
+
header: "LXGW WenKai Header Subset",
|
|
156
|
+
footer: "LXGW WenKai Footer Subset",
|
|
157
|
+
};
|
|
158
|
+
|
|
135
159
|
type HtmlNode = DefaultTreeAdapterMap["node"];
|
|
136
160
|
|
|
137
161
|
const NON_RENDERED_ELEMENTS = new Set(["script", "style", "template", "title"]);
|
|
@@ -230,28 +254,96 @@ function isHiddenElement(attrs: Array<{ name: string; value: string }>): boolean
|
|
|
230
254
|
);
|
|
231
255
|
}
|
|
232
256
|
|
|
233
|
-
/** Collect renderable
|
|
257
|
+
/** Collect renderable text nodes, including content inside collapsed controls. */
|
|
234
258
|
const CONTROL_CHARACTER = /\p{C}/u;
|
|
235
259
|
function addGlyphs(text: string, output: Set<string>): void {
|
|
236
260
|
for (const char of text)
|
|
237
261
|
if (!output.has(char) && !CONTROL_CHARACTER.test(char)) output.add(char);
|
|
238
262
|
}
|
|
239
263
|
|
|
240
|
-
function collectBodyText(node: HtmlNode, output: Set<string
|
|
264
|
+
function collectBodyText(node: HtmlNode, output: Set<string>, skipSharedComponents = false): void {
|
|
241
265
|
if (node.nodeName === "#text" && "value" in node) {
|
|
242
266
|
addGlyphs(node.value, output);
|
|
243
267
|
return;
|
|
244
268
|
}
|
|
245
269
|
|
|
246
270
|
if (!("childNodes" in node)) return;
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
271
|
+
if ("tagName" in node) {
|
|
272
|
+
const sharedGroup = node.attrs.find(
|
|
273
|
+
(attribute) => attribute.name === "data-stalux-font-group",
|
|
274
|
+
)?.value;
|
|
275
|
+
if (skipSharedComponents && (sharedGroup === "header" || sharedGroup === "footer")) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (NON_RENDERED_ELEMENTS.has(node.tagName) || isHiddenElement(node.attrs)) return;
|
|
252
279
|
}
|
|
253
280
|
|
|
254
|
-
for (const child of node.childNodes) collectBodyText(child, output);
|
|
281
|
+
for (const child of node.childNodes) collectBodyText(child, output, skipSharedComponents);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** Collect common shell glyphs separately so every page can reuse the same files. */
|
|
285
|
+
export function createSharedFontGlyphs(): SharedFontGlyphs {
|
|
286
|
+
const glyphs: SharedFontGlyphs = { header: new Set(), footer: new Set() };
|
|
287
|
+
addGlyphs(RESERVED_GLYPHS, glyphs.footer);
|
|
288
|
+
return glyphs;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function collectSharedFontGlyphs(
|
|
292
|
+
document: DefaultTreeAdapterMap["document"],
|
|
293
|
+
output: SharedFontGlyphs,
|
|
294
|
+
): void {
|
|
295
|
+
function visit(node: HtmlNode): void {
|
|
296
|
+
if ("tagName" in node) {
|
|
297
|
+
const group = node.attrs.find(
|
|
298
|
+
(attribute) => attribute.name === "data-stalux-font-group",
|
|
299
|
+
)?.value as SharedFontGroup | undefined;
|
|
300
|
+
if (group === "header" || group === "footer") {
|
|
301
|
+
collectBodyText(node, output[group]);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if ("childNodes" in node) for (const child of node.childNodes) visit(child);
|
|
306
|
+
}
|
|
307
|
+
visit(document);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function sortGlyphs(glyphs: Iterable<string>): string[] {
|
|
311
|
+
return [...new Set(glyphs)].sort(
|
|
312
|
+
(a, b) => (a.codePointAt(0) as number) - (b.codePointAt(0) as number),
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Build content-addressed header and footer subsets once for the whole site build. */
|
|
317
|
+
export async function writeSharedFontSubsetsAsync(
|
|
318
|
+
fontBuffer: Buffer,
|
|
319
|
+
glyphs: SharedFontGlyphs,
|
|
320
|
+
cacheDir: string,
|
|
321
|
+
): Promise<SharedFontSubset[]> {
|
|
322
|
+
let prepared = preparedFonts.get(fontBuffer);
|
|
323
|
+
if (!prepared) {
|
|
324
|
+
prepared = new FontSubsetter(fontBuffer);
|
|
325
|
+
preparedFonts.set(fontBuffer, prepared);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const subsets: SharedFontSubset[] = [];
|
|
329
|
+
for (const group of ["header", "footer"] as const) {
|
|
330
|
+
const chars = sortGlyphs(glyphs[group]);
|
|
331
|
+
if (chars.length === 0) continue;
|
|
332
|
+
const result = await prepared.subsetAsync(chars.join(""), cacheDir);
|
|
333
|
+
subsets.push({
|
|
334
|
+
group,
|
|
335
|
+
family: SHARED_FONT_FAMILIES[group],
|
|
336
|
+
chars,
|
|
337
|
+
filename: `shared-${group}-${result.hash}.woff2`,
|
|
338
|
+
sourcePath: result.path,
|
|
339
|
+
cacheHit: result.cacheHit,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return subsets;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function sharedFontCacheKey(subsets: readonly SharedFontSubset[]): string {
|
|
346
|
+
return subsets.map(({ group, chars }) => `${group}:${chars.join("")}`).join("\0");
|
|
255
347
|
}
|
|
256
348
|
|
|
257
349
|
function formatUnicodeRange(chars: string[]): string {
|
|
@@ -286,8 +378,9 @@ export function writePageFontSubset(
|
|
|
286
378
|
publicUrlPrefix,
|
|
287
379
|
input.chars,
|
|
288
380
|
input.prepared.subset(input.chars.join(""), cacheDir),
|
|
381
|
+
[],
|
|
289
382
|
);
|
|
290
|
-
return { ...result, html: serialize(document) };
|
|
383
|
+
return { ...result, glyphCount: input.chars.length, html: serialize(document) };
|
|
291
384
|
}
|
|
292
385
|
|
|
293
386
|
export async function writePageFontSubsetAsync(
|
|
@@ -303,7 +396,8 @@ export async function writePageFontSubsetAsync(
|
|
|
303
396
|
if (!input) return undefined;
|
|
304
397
|
const result = await input.prepared.subsetAsync(input.chars.join(""), cacheDir);
|
|
305
398
|
return {
|
|
306
|
-
...renderPageFont(document, publicUrlPrefix, input.chars, result),
|
|
399
|
+
...renderPageFont(document, publicUrlPrefix, input.chars, result, []),
|
|
400
|
+
glyphCount: input.chars.length,
|
|
307
401
|
html: serialize(document),
|
|
308
402
|
};
|
|
309
403
|
}
|
|
@@ -313,6 +407,7 @@ function preparePageFont(
|
|
|
313
407
|
linkedStylesheetText: string,
|
|
314
408
|
extraChars: string,
|
|
315
409
|
document: DefaultTreeAdapterMap["document"],
|
|
410
|
+
excludedChars: ReadonlySet<string> = new Set(),
|
|
316
411
|
) {
|
|
317
412
|
const body = findBody(document);
|
|
318
413
|
if (!body) return undefined;
|
|
@@ -323,7 +418,10 @@ function preparePageFont(
|
|
|
323
418
|
addGlyphs(linkedStylesheetText, glyphs);
|
|
324
419
|
for (const style of collectInlineStyles(document))
|
|
325
420
|
addGlyphs(cssGeneratedText(styleText(style)), glyphs);
|
|
326
|
-
collectBodyText(body, glyphs);
|
|
421
|
+
collectBodyText(body, glyphs, true);
|
|
422
|
+
for (const char of excludedChars) glyphs.delete(char);
|
|
423
|
+
// Keep runtime digits available even when the shell subset does not contain them.
|
|
424
|
+
addGlyphs(RESERVED_GLYPHS, glyphs);
|
|
327
425
|
const chars = [...glyphs].sort(
|
|
328
426
|
(a, b) => (a.codePointAt(0) as number) - (b.codePointAt(0) as number),
|
|
329
427
|
);
|
|
@@ -342,6 +440,7 @@ function renderPageFont(
|
|
|
342
440
|
publicUrlPrefix: string,
|
|
343
441
|
chars: string[],
|
|
344
442
|
result: { hash: string; path: string; cacheHit: boolean },
|
|
443
|
+
sharedFonts: readonly SharedFontSubset[],
|
|
345
444
|
): Omit<PageFontSubset, "html"> {
|
|
346
445
|
const filename = `page-${result.hash}.woff2`;
|
|
347
446
|
|
|
@@ -359,12 +458,20 @@ function renderPageFont(
|
|
|
359
458
|
.map(styleText)
|
|
360
459
|
.map((css) => css.match(/--font-code:\s*([^;}]+)/u)?.[1]?.trim())
|
|
361
460
|
.find(Boolean);
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
461
|
+
const sharedStack = sharedFonts.map(({ family }) => `"${family}"`).join(",");
|
|
462
|
+
const pageFontStack = [sharedStack, '"LXGW WenKai-Page Subset"', fallbackStack]
|
|
463
|
+
.filter(Boolean)
|
|
464
|
+
.join(",");
|
|
465
|
+
const codeStack = originalCodeStack ? `${originalCodeStack},${pageFontStack}` : pageFontStack;
|
|
365
466
|
// The source font is regular; advertise a weight range so browsers synthesize
|
|
366
467
|
// bold weights from these same glyphs instead of falling back to system CJK.
|
|
367
|
-
const
|
|
468
|
+
const sharedFaces = sharedFonts
|
|
469
|
+
.map(
|
|
470
|
+
({ family, filename, chars: sharedChars }) =>
|
|
471
|
+
`@font-face{font-family:"${family}";src:url("${publicUrlPrefix}${filename}") format("woff2");font-style:normal;font-weight:100 900;font-display:swap;unicode-range:${formatUnicodeRange(sharedChars)}}`,
|
|
472
|
+
)
|
|
473
|
+
.join("");
|
|
474
|
+
const css = `${sharedFaces}@font-face{font-family:"LXGW WenKai-Page Subset";src:url("${rel}") format("woff2");font-style:normal;font-weight:100 900;font-display:swap;unicode-range:${formatUnicodeRange(chars)}}:root{--font-body:${pageFontStack};--font-code:${codeStack}}body{font-family:var(--font-body),${fallbackStack}}`;
|
|
368
475
|
for (const style of styles) {
|
|
369
476
|
for (const node of style.childNodes) {
|
|
370
477
|
if (node.nodeName !== "#text" || !("value" in node)) continue;
|
|
@@ -398,11 +505,28 @@ function renderPageFont(
|
|
|
398
505
|
{ name: "href", value: rel },
|
|
399
506
|
{ name: "data-stalux-page-font-preload", value: "" },
|
|
400
507
|
]);
|
|
508
|
+
for (const sharedFont of sharedFonts) {
|
|
509
|
+
if (sharedFont.group !== "header") continue;
|
|
510
|
+
const sharedPreload = defaultTreeAdapter.createElement("link", htmlNamespaces.NS.HTML, [
|
|
511
|
+
{ name: "rel", value: "preload" },
|
|
512
|
+
{ name: "as", value: "font" },
|
|
513
|
+
{ name: "type", value: "font/woff2" },
|
|
514
|
+
{ name: "crossorigin", value: "anonymous" },
|
|
515
|
+
{ name: "href", value: `${publicUrlPrefix}${sharedFont.filename}` },
|
|
516
|
+
{ name: "data-stalux-shared-font-preload", value: sharedFont.group },
|
|
517
|
+
]);
|
|
518
|
+
defaultTreeAdapter.appendChild(head, sharedPreload);
|
|
519
|
+
}
|
|
401
520
|
defaultTreeAdapter.appendChild(head, preload);
|
|
402
521
|
defaultTreeAdapter.insertText(style, css);
|
|
403
522
|
defaultTreeAdapter.appendChild(head, style);
|
|
404
523
|
}
|
|
405
|
-
return {
|
|
524
|
+
return {
|
|
525
|
+
filename,
|
|
526
|
+
sourcePath: result.path,
|
|
527
|
+
cacheHit: result.cacheHit,
|
|
528
|
+
glyphCount: chars.length,
|
|
529
|
+
};
|
|
406
530
|
}
|
|
407
531
|
|
|
408
532
|
/** Styles are selected structurally, so examples in scripts/comments/templates are untouched. */
|
|
@@ -430,7 +554,11 @@ export function removePageFontStyles(document: DefaultTreeAdapterMap["document"]
|
|
|
430
554
|
if (
|
|
431
555
|
"tagName" in node &&
|
|
432
556
|
node.tagName === "link" &&
|
|
433
|
-
node.attrs.some(
|
|
557
|
+
node.attrs.some(
|
|
558
|
+
(attr) =>
|
|
559
|
+
attr.name === "data-stalux-page-font-preload" ||
|
|
560
|
+
attr.name === "data-stalux-shared-font-preload",
|
|
561
|
+
)
|
|
434
562
|
) {
|
|
435
563
|
defaultTreeAdapter.detachNode(node);
|
|
436
564
|
return;
|
|
@@ -451,15 +579,18 @@ export async function writePageFontSubsetIntoDocumentAsync(
|
|
|
451
579
|
cacheDir: string,
|
|
452
580
|
publicUrlPrefix = "/_astro/fonts/",
|
|
453
581
|
linkedStylesheetText = "",
|
|
582
|
+
sharedFonts: readonly SharedFontSubset[] = [],
|
|
454
583
|
): Promise<Omit<PageFontSubset, "html"> | undefined> {
|
|
455
|
-
const
|
|
584
|
+
const sharedChars = new Set(sharedFonts.flatMap(({ chars }) => chars));
|
|
585
|
+
const input = preparePageFont(fontBuffer, linkedStylesheetText, "", document, sharedChars);
|
|
456
586
|
if (!input) return undefined;
|
|
457
587
|
const result = await input.prepared.subsetAsync(input.chars.join(""), cacheDir);
|
|
458
|
-
const rendered = renderPageFont(document, publicUrlPrefix, input.chars, result);
|
|
588
|
+
const rendered = renderPageFont(document, publicUrlPrefix, input.chars, result, sharedFonts);
|
|
459
589
|
return {
|
|
460
590
|
filename: rendered.filename,
|
|
461
591
|
sourcePath: rendered.sourcePath,
|
|
462
592
|
cacheHit: rendered.cacheHit,
|
|
593
|
+
glyphCount: rendered.glyphCount,
|
|
463
594
|
};
|
|
464
595
|
}
|
|
465
596
|
|
|
@@ -470,7 +601,11 @@ export function clearPageFontSubsets(
|
|
|
470
601
|
): void {
|
|
471
602
|
if (!existsSync(outputDir)) return;
|
|
472
603
|
for (const name of readdirSync(outputDir)) {
|
|
473
|
-
|
|
604
|
+
const isSubset =
|
|
605
|
+
name.startsWith("page-") ||
|
|
606
|
+
name.startsWith("shared-header-") ||
|
|
607
|
+
name.startsWith("shared-footer-");
|
|
608
|
+
if (isSubset && name.endsWith(".woff2") && !referencedFiles.has(name)) {
|
|
474
609
|
unlinkSync(join(outputDir, name));
|
|
475
610
|
}
|
|
476
611
|
}
|
|
@@ -15,9 +15,11 @@ export async function createPageOutputCache(
|
|
|
15
15
|
dependencies: readonly string[],
|
|
16
16
|
font?: Buffer,
|
|
17
17
|
assets: readonly string[] = [],
|
|
18
|
+
contentKey = "",
|
|
18
19
|
) {
|
|
19
|
-
const hash = createHash("sha256").update("stalux-page-output-
|
|
20
|
+
const hash = createHash("sha256").update("stalux-page-output-v2\0");
|
|
20
21
|
if (font) hash.update(font);
|
|
22
|
+
if (contentKey) hash.update("\0content-key\0").update(contentKey);
|
|
21
23
|
for (const dependency of [...dependencies].sort()) {
|
|
22
24
|
hash.update(dependency).update("\0");
|
|
23
25
|
hash.update(await fs.readFile(dependency)).update("\0");
|
|
@@ -89,84 +89,84 @@ export const styleNames: { classes: Record<string, string>; variables: Record<st
|
|
|
89
89
|
"stalux-footer": "s2d",
|
|
90
90
|
"stalux-footer-content": "s2e",
|
|
91
91
|
"stalux-home-body": "s2f",
|
|
92
|
-
"stalux-home-
|
|
93
|
-
"stalux-
|
|
94
|
-
"stalux-license-
|
|
95
|
-
"stalux-license-
|
|
96
|
-
"stalux-license-
|
|
97
|
-
"stalux-license-
|
|
98
|
-
"stalux-license-
|
|
99
|
-
"stalux-license-
|
|
100
|
-
"stalux-license-
|
|
101
|
-
"stalux-
|
|
102
|
-
"stalux-link-card": "s2q",
|
|
103
|
-
"stalux-link-card-
|
|
104
|
-
"stalux-link-card-
|
|
105
|
-
"stalux-link-card-
|
|
106
|
-
"stalux-link-
|
|
107
|
-
"stalux-link-
|
|
108
|
-
"stalux-
|
|
109
|
-
"stalux-
|
|
110
|
-
"stalux-
|
|
111
|
-
"stalux-nav-
|
|
112
|
-
"stalux-nav-
|
|
113
|
-
"stalux-nav-
|
|
114
|
-
"stalux-nav-
|
|
115
|
-
"stalux-nav-
|
|
116
|
-
"stalux-nav-
|
|
117
|
-
"stalux-nav-
|
|
118
|
-
"stalux-
|
|
119
|
-
"stalux-page-
|
|
120
|
-
"stalux-
|
|
121
|
-
"stalux-pagination": "s39",
|
|
122
|
-
"stalux-pagination-
|
|
123
|
-
"stalux-pagination-
|
|
124
|
-
"stalux-pagination-
|
|
125
|
-
"stalux-pagination-
|
|
126
|
-
"stalux-pagination-
|
|
127
|
-
"stalux-pagination-
|
|
128
|
-
"stalux-pagination-
|
|
129
|
-
"stalux-pagination-
|
|
130
|
-
"stalux-
|
|
131
|
-
"stalux-post-article-
|
|
132
|
-
"stalux-post-
|
|
133
|
-
"stalux-post-
|
|
134
|
-
"stalux-post-card": "s3m",
|
|
135
|
-
"stalux-post-
|
|
136
|
-
"stalux-post-
|
|
137
|
-
"stalux-post-
|
|
138
|
-
"stalux-post-
|
|
139
|
-
"stalux-post-meta-
|
|
140
|
-
"stalux-post-
|
|
141
|
-
"stalux-post-
|
|
142
|
-
"stalux-post-page": "s3u",
|
|
143
|
-
"stalux-post-
|
|
144
|
-
"stalux-post-
|
|
145
|
-
"stalux-post-source-link": "s3x",
|
|
146
|
-
"stalux-post-
|
|
147
|
-
"stalux-post-taxonomy-
|
|
148
|
-
"stalux-post-taxonomy-
|
|
149
|
-
"stalux-post-taxonomy-item-
|
|
150
|
-
"stalux-post-taxonomy-
|
|
151
|
-
"stalux-post-taxonomy-list": "s43",
|
|
152
|
-
"stalux-post-taxonomy-list-
|
|
153
|
-
"stalux-post-taxonomy-
|
|
154
|
-
"stalux-post-
|
|
155
|
-
"stalux-
|
|
156
|
-
"stalux-
|
|
157
|
-
"stalux-random-
|
|
158
|
-
"stalux-random-
|
|
159
|
-
"stalux-random-
|
|
160
|
-
"stalux-random-
|
|
161
|
-
"stalux-random-
|
|
162
|
-
"stalux-random-
|
|
163
|
-
"stalux-random-
|
|
164
|
-
"stalux-random-
|
|
165
|
-
"stalux-
|
|
166
|
-
"stalux-
|
|
167
|
-
"stalux-sidebar": "s4j",
|
|
168
|
-
"stalux-sidebar-
|
|
169
|
-
"stalux-
|
|
92
|
+
"stalux-home-title": "s2g",
|
|
93
|
+
"stalux-license-author": "s2h",
|
|
94
|
+
"stalux-license-box": "s2i",
|
|
95
|
+
"stalux-license-content": "s2j",
|
|
96
|
+
"stalux-license-icon": "s2k",
|
|
97
|
+
"stalux-license-license": "s2l",
|
|
98
|
+
"stalux-license-title": "s2m",
|
|
99
|
+
"stalux-license-url": "s2n",
|
|
100
|
+
"stalux-license-watermark": "s2o",
|
|
101
|
+
"stalux-link-card": "s2p",
|
|
102
|
+
"stalux-link-card-avatar": "s2q",
|
|
103
|
+
"stalux-link-card-content": "s2r",
|
|
104
|
+
"stalux-link-card-description": "s2s",
|
|
105
|
+
"stalux-link-card-title": "s2t",
|
|
106
|
+
"stalux-link-list-item": "s2u",
|
|
107
|
+
"stalux-link-shell": "s2v",
|
|
108
|
+
"stalux-links": "s2w",
|
|
109
|
+
"stalux-main": "s2x",
|
|
110
|
+
"stalux-nav-container": "s2y",
|
|
111
|
+
"stalux-nav-item": "s2z",
|
|
112
|
+
"stalux-nav-link": "s30",
|
|
113
|
+
"stalux-nav-list": "s31",
|
|
114
|
+
"stalux-nav-overlay": "s32",
|
|
115
|
+
"stalux-nav-scroll-btn": "s33",
|
|
116
|
+
"stalux-nav-text": "s34",
|
|
117
|
+
"stalux-nav-toggle": "s35",
|
|
118
|
+
"stalux-page-description": "s36",
|
|
119
|
+
"stalux-page-title": "s37",
|
|
120
|
+
"stalux-pagination": "s38",
|
|
121
|
+
"stalux-pagination-arrow": "s39",
|
|
122
|
+
"stalux-pagination-content": "s3a",
|
|
123
|
+
"stalux-pagination-disabled": "s3b",
|
|
124
|
+
"stalux-pagination-inner": "s3c",
|
|
125
|
+
"stalux-pagination-label": "s3d",
|
|
126
|
+
"stalux-pagination-link": "s3e",
|
|
127
|
+
"stalux-pagination-next-post": "s3f",
|
|
128
|
+
"stalux-pagination-prev-post": "s3g",
|
|
129
|
+
"stalux-pagination-title": "s3h",
|
|
130
|
+
"stalux-post-article-footer": "s3i",
|
|
131
|
+
"stalux-post-article-header": "s3j",
|
|
132
|
+
"stalux-post-body": "s3k",
|
|
133
|
+
"stalux-post-card": "s3l",
|
|
134
|
+
"stalux-post-card-shell": "s3m",
|
|
135
|
+
"stalux-post-footer-content": "s3n",
|
|
136
|
+
"stalux-post-layout": "s3o",
|
|
137
|
+
"stalux-post-list-container": "s3p",
|
|
138
|
+
"stalux-post-meta-item": "s3q",
|
|
139
|
+
"stalux-post-meta-row": "s3r",
|
|
140
|
+
"stalux-post-metadata": "s3s",
|
|
141
|
+
"stalux-post-page": "s3t",
|
|
142
|
+
"stalux-post-page-stats": "s3u",
|
|
143
|
+
"stalux-post-reading-time": "s3v",
|
|
144
|
+
"stalux-post-source-link": "s3w",
|
|
145
|
+
"stalux-post-source-link-row": "s3x",
|
|
146
|
+
"stalux-post-taxonomy-container": "s3y",
|
|
147
|
+
"stalux-post-taxonomy-empty": "s3z",
|
|
148
|
+
"stalux-post-taxonomy-item-column": "s40",
|
|
149
|
+
"stalux-post-taxonomy-item-pill": "s41",
|
|
150
|
+
"stalux-post-taxonomy-list": "s42",
|
|
151
|
+
"stalux-post-taxonomy-list-column": "s43",
|
|
152
|
+
"stalux-post-taxonomy-list-wrap": "s44",
|
|
153
|
+
"stalux-post-taxonomy-title": "s45",
|
|
154
|
+
"stalux-post-title": "s46",
|
|
155
|
+
"stalux-prose": "s47",
|
|
156
|
+
"stalux-random-container": "s48",
|
|
157
|
+
"stalux-random-error": "s49",
|
|
158
|
+
"stalux-random-header": "s4a",
|
|
159
|
+
"stalux-random-list": "s4b",
|
|
160
|
+
"stalux-random-loading": "s4c",
|
|
161
|
+
"stalux-random-no-more": "s4d",
|
|
162
|
+
"stalux-random-refresh": "s4e",
|
|
163
|
+
"stalux-random-rotating": "s4f",
|
|
164
|
+
"stalux-random-title": "s4g",
|
|
165
|
+
"stalux-root": "s4h",
|
|
166
|
+
"stalux-sidebar": "s4i",
|
|
167
|
+
"stalux-sidebar-container": "s4j",
|
|
168
|
+
"stalux-sidebar-right": "s4k",
|
|
169
|
+
"stalux-site-header": "s4l",
|
|
170
170
|
"stalux-site-nav": "s4m",
|
|
171
171
|
"stalux-social": "s4n",
|
|
172
172
|
"stalux-social-custom-icon": "s4o",
|
|
@@ -287,48 +287,49 @@ export const styleNames: { classes: Record<string, string>; variables: Record<st
|
|
|
287
287
|
"--stalux-bg-color": "--s1n",
|
|
288
288
|
"--stalux-bg-filter": "--s1o",
|
|
289
289
|
"--stalux-nav-offset": "--s1p",
|
|
290
|
-
"--stalux-
|
|
291
|
-
"--
|
|
292
|
-
"--surface-glass
|
|
293
|
-
"--
|
|
294
|
-
"--tag-bg
|
|
295
|
-
"--
|
|
296
|
-
"--tl-
|
|
297
|
-
"--
|
|
298
|
-
"--transition-
|
|
299
|
-
"--transition-
|
|
300
|
-
"--transition-
|
|
301
|
-
"--transition-
|
|
302
|
-
"--transition-
|
|
303
|
-
"--
|
|
304
|
-
"--un-rotate
|
|
305
|
-
"--un-rotate-
|
|
306
|
-
"--un-rotate-
|
|
307
|
-
"--un-
|
|
308
|
-
"--un-scale-
|
|
309
|
-
"--un-scale-
|
|
310
|
-
"--un-
|
|
311
|
-
"--un-skew-
|
|
312
|
-
"--un-
|
|
313
|
-
"--un-translate-
|
|
314
|
-
"--un-translate-
|
|
315
|
-
"--
|
|
316
|
-
"--updated-
|
|
317
|
-
"--
|
|
318
|
-
"--white-
|
|
319
|
-
"--white-
|
|
320
|
-
"--white-
|
|
321
|
-
"--white-
|
|
322
|
-
"--white-
|
|
323
|
-
"--white-
|
|
324
|
-
"--white-
|
|
325
|
-
"--white-
|
|
326
|
-
"--white-
|
|
327
|
-
"--white-
|
|
328
|
-
"--white-
|
|
329
|
-
"--white-
|
|
330
|
-
"--white-
|
|
331
|
-
"--white-
|
|
290
|
+
"--stalux-site-header-height": "--s1q",
|
|
291
|
+
"--stalux-text-shadow": "--s1r",
|
|
292
|
+
"--surface-glass": "--s1s",
|
|
293
|
+
"--surface-glass-shadow": "--s1t",
|
|
294
|
+
"--tag-bg": "--s1u",
|
|
295
|
+
"--tag-bg-hover": "--s1v",
|
|
296
|
+
"--tl-line": "--s1w",
|
|
297
|
+
"--tl-pad": "--s1x",
|
|
298
|
+
"--transition-fast": "--s1y",
|
|
299
|
+
"--transition-medium": "--s1z",
|
|
300
|
+
"--transition-normal": "--s20",
|
|
301
|
+
"--transition-slow": "--s21",
|
|
302
|
+
"--transition-slower": "--s22",
|
|
303
|
+
"--transition-slowest": "--s23",
|
|
304
|
+
"--un-rotate": "--s24",
|
|
305
|
+
"--un-rotate-x": "--s25",
|
|
306
|
+
"--un-rotate-y": "--s26",
|
|
307
|
+
"--un-rotate-z": "--s27",
|
|
308
|
+
"--un-scale-x": "--s28",
|
|
309
|
+
"--un-scale-y": "--s29",
|
|
310
|
+
"--un-scale-z": "--s2a",
|
|
311
|
+
"--un-skew-x": "--s2b",
|
|
312
|
+
"--un-skew-y": "--s2c",
|
|
313
|
+
"--un-translate-x": "--s2d",
|
|
314
|
+
"--un-translate-y": "--s2e",
|
|
315
|
+
"--un-translate-z": "--s2f",
|
|
316
|
+
"--updated-bg": "--s2g",
|
|
317
|
+
"--updated-border": "--s2h",
|
|
318
|
+
"--white-05p": "--s2i",
|
|
319
|
+
"--white-10p": "--s2j",
|
|
320
|
+
"--white-15p": "--s2k",
|
|
321
|
+
"--white-20p": "--s2l",
|
|
322
|
+
"--white-30p": "--s2m",
|
|
323
|
+
"--white-50p": "--s2n",
|
|
324
|
+
"--white-60p": "--s2o",
|
|
325
|
+
"--white-70p": "--s2p",
|
|
326
|
+
"--white-75p": "--s2q",
|
|
327
|
+
"--white-78p": "--s2r",
|
|
328
|
+
"--white-80p": "--s2s",
|
|
329
|
+
"--white-85p": "--s2t",
|
|
330
|
+
"--white-90p": "--s2u",
|
|
331
|
+
"--white-95p": "--s2v",
|
|
332
|
+
"--white-full": "--s2w"
|
|
332
333
|
},
|
|
333
334
|
"animations": {
|
|
334
335
|
"fadeInUpComment": "k0",
|
|
@@ -52,7 +52,11 @@ const walineConfig = walineSection?.serverURL
|
|
|
52
52
|
---
|
|
53
53
|
|
|
54
54
|
<div class="stalux-post-page">
|
|
55
|
-
<div
|
|
55
|
+
<div
|
|
56
|
+
class="stalux-post-layout"
|
|
57
|
+
transition:name="stalux-article-layout"
|
|
58
|
+
transition:animate="none"
|
|
59
|
+
>
|
|
56
60
|
<!-- Left Sidebar -->
|
|
57
61
|
<PostLeft author={author} authorAvatar={props.authorAvatar} />
|
|
58
62
|
|
|
@@ -71,7 +75,13 @@ const walineConfig = walineSection?.serverURL
|
|
|
71
75
|
hasImage={props.hasImage}
|
|
72
76
|
>
|
|
73
77
|
<slot />
|
|
74
|
-
<PostCC
|
|
78
|
+
<PostCC
|
|
79
|
+
title={props.title}
|
|
80
|
+
author={author}
|
|
81
|
+
url={Astro.url.href}
|
|
82
|
+
cc={props.cc}
|
|
83
|
+
transitionName="stalux-article-license"
|
|
84
|
+
/>
|
|
75
85
|
<PostNavigation prev={props.prev} next={props.next} />
|
|
76
86
|
{commentEnabled && walineConfig?.serverURL && (
|
|
77
87
|
<WalineComment
|
package/src/layouts/Stalux.astro
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
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
|
+
import Navs from "@components/stalux/layout/navs.astro";
|
|
5
6
|
import { getAccentColorStyle } from "@utils/accent-color";
|
|
6
7
|
import {
|
|
7
8
|
getAiDiscoveryData,
|
|
@@ -84,6 +85,15 @@ const routeAnimation = getRouteAnimation(routeKind);
|
|
|
84
85
|
data-stalux-umami-id={headConfig?.umami?.id}
|
|
85
86
|
data-stalux-umami-url={headConfig?.umami?.url}
|
|
86
87
|
>
|
|
88
|
+
<header
|
|
89
|
+
class="stalux-site-header"
|
|
90
|
+
data-stalux-font-group="header"
|
|
91
|
+
transition:name="stalux-site-header"
|
|
92
|
+
transition:animate="none"
|
|
93
|
+
transition:persist="stalux-site-header"
|
|
94
|
+
>
|
|
95
|
+
<Navs />
|
|
96
|
+
</header>
|
|
87
97
|
{llmPromote && (
|
|
88
98
|
<section class="stalux-ai-prompt" data-stalux-ai-prompt aria-label="AI site context">
|
|
89
99
|
<p>{llmPromote}</p>
|
|
@@ -105,8 +115,10 @@ const routeAnimation = getRouteAnimation(routeKind);
|
|
|
105
115
|
<main
|
|
106
116
|
class="stalux-main"
|
|
107
117
|
data-stalux-route={routeKind}
|
|
108
|
-
transition:name="stalux-page"
|
|
109
|
-
transition:animate={
|
|
118
|
+
transition:name={routeKind === "article" ? "stalux-article-page" : "stalux-page"}
|
|
119
|
+
transition:animate={routeKind === "home" || routeKind === "article"
|
|
120
|
+
? "none"
|
|
121
|
+
: routeAnimation}
|
|
110
122
|
>
|
|
111
123
|
<slot />
|
|
112
124
|
</main>
|