@xingwangzhe/stalux 1.6.1 → 1.7.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/README.md CHANGED
@@ -141,6 +141,7 @@ stalux/
141
141
  ## ✨ Features
142
142
 
143
143
  - 🌙 **Dark mode** as default, with elegant glassmorphism design
144
+ - 🔤 **Per-route font subsetting** — each page loads only its needed characters (25 MB full font → ~1 KB per route)
144
145
  - 🔍 **Full-text search** via Pagefind (auto-indexed on build)
145
146
  - 📡 **RSS & Atom feeds**
146
147
  - 🖼️ **PhotoSwipe** image lightbox
@@ -155,6 +156,33 @@ stalux/
155
156
 
156
157
  ---
157
158
 
159
+ ## 🔤 Font Optimization
160
+
161
+ Stalux ships with a 25 MB Chinese font (LXGW WenKai), but your visitors never download the full file. Instead, the build generates minimal font subsets per route:
162
+
163
+ - **Common subset** (~350 KB) — UI text, navigation, i18n, shared characters from all posts
164
+ - **Per-route subset** (~0.5–3 KB each) — unique characters for each individual page
165
+
166
+ Each HTML page loads only `common.css` + `subset-{route}.css`, ensuring every page gets exactly the characters it needs — nothing more.
167
+
168
+ This covers **all route types**:
169
+
170
+ | Route | Subset source |
171
+ | ------------------------------------ | --------------------------------------------------------- |
172
+ | `/` | Config YAML (site title, author, navs, typetexts, footer) |
173
+ | `/about` | About markdown |
174
+ | `/words` | All words content |
175
+ | `/posts/{slug}` | Single post content |
176
+ | `/archives` | All posts content |
177
+ | `/tags` / `/tags/{name}` | Tag names + matching posts |
178
+ | `/categories` / `/categories/{name}` | Category names + matching posts |
179
+ | `/links` | Links config YAML |
180
+ | `/404` | Covered by common subset |
181
+
182
+ The subsetting engine (`subset-font`, Harfbuzz WASM) runs at build time (`astro:build:start`) and on-demand in dev mode.
183
+
184
+ ---
185
+
158
186
  ## 🛠️ Development
159
187
 
160
188
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xingwangzhe/stalux",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "description": "A powerful, modern Astro blog theme — use as template or install as plugin",
5
5
  "keywords": [
6
6
  "astro",
@@ -39,7 +39,6 @@
39
39
  "src/scripts/**/*.ts",
40
40
  "src/assets/**/*",
41
41
  "scripts/cli.mjs",
42
- "template/**/*",
43
42
  "!src/content.config.ts"
44
43
  ],
45
44
  "type": "module",
@@ -95,14 +94,14 @@
95
94
  "temml": "^0.13.3"
96
95
  },
97
96
  "devDependencies": {
98
- "@types/node": "^26.1.1",
97
+ "@types/node": "^26.1.2",
99
98
  "knip": "^6.29.0",
100
99
  "license-checker-rseidelsohn": "5.0.1",
101
- "oxfmt": "^0.60.0",
102
- "oxlint": "^1.75.0",
100
+ "oxfmt": "^0.61.0",
101
+ "oxlint": "^1.76.0",
103
102
  "typescript": "^7.0.2"
104
103
  },
105
104
  "peerDependencies": {
106
- "astro": "^7.0.0"
105
+ "astro": "^7.1.5"
107
106
  }
108
107
  }
package/scripts/cli.mjs CHANGED
@@ -4,102 +4,333 @@
4
4
  * Stalux CLI — 主题初始化工具
5
5
  *
6
6
  * 用法:
7
- * npx stalux init # 在当前目录初始化内容模板
8
- * npx stalux init my-blog # 在子目录初始化项目
9
- * npm run init # 或通过 npm script
7
+ * bunx stalux init # 在当前目录初始化内容模板
8
+ * bunx stalux init my-blog # 在子目录初始化项目
9
+ *
10
+ * 说明:
11
+ * - 只在 stalux/ 目录下生成内容配置文件和示例文章
12
+ * - 不会覆盖已有的 package.json、astro.config.mjs、src/content.config.ts
13
+ * - 静默运行,无交互式问答
14
+ * - 静默运行,无交互式问答
10
15
  */
11
16
 
12
- import { existsSync, mkdirSync, cpSync, readdirSync, statSync } from "node:fs";
13
- import { join, resolve, dirname } from "node:path";
14
- import { fileURLToPath } from "node:url";
15
-
16
- const __dirname = dirname(fileURLToPath(import.meta.url));
17
- const PACKAGE_ROOT = resolve(__dirname, "..");
18
- const TEMPLATE_DIR = join(PACKAGE_ROOT, "template");
19
-
20
- function copyDir(src, dest, overwrite = false) {
21
- mkdirSync(dest, { recursive: true });
22
- for (const entry of readdirSync(src)) {
23
- const srcPath = join(src, entry);
24
- const destPath = join(dest, entry);
25
- if (entry === "node_modules") continue;
26
-
27
- if (statSync(srcPath).isDirectory()) {
28
- copyDir(srcPath, destPath, overwrite);
29
- } else {
30
- if (!overwrite && existsSync(destPath)) {
31
- console.log(` ⏭ ${entry} (already exists, skipped)`);
32
- continue;
33
- }
34
- cpSync(srcPath, destPath);
35
- console.log(` ✅ ${entry}`);
36
- }
37
- }
17
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
18
+ import { join, resolve as pathResolve } from "node:path";
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // 默认内容模板数据
22
+ // ---------------------------------------------------------------------------
23
+
24
+ function getConfigYamls() {
25
+ const year = new Date().getFullYear();
26
+ const now = new Date().toISOString();
27
+ return {
28
+ "site.yml": `id: site
29
+ lang: en
30
+ title: "My Blog"
31
+ description: "A blog built with Stalux theme"
32
+ url: "https://example.com"
33
+ timezone: "Asia/Shanghai"
34
+ `,
35
+ "author.yml": `id: author
36
+ name: "Your Name"
37
+ avatar: "/avatar.png"
38
+ bio: "Blogger & Developer"
39
+ `,
40
+ "navs.yml": `id: navs
41
+ items:
42
+ - title: "Home"
43
+ link: "/"
44
+ icon: "house"
45
+ - title: "Archives"
46
+ link: "/archives"
47
+ icon: "archive"
48
+ - title: "Tags"
49
+ link: "/tags"
50
+ icon: "tag"
51
+ - title: "Categories"
52
+ link: "/categories"
53
+ icon: "folder"
54
+ - title: "Words"
55
+ link: "/words"
56
+ icon: "quote"
57
+ - title: "Links"
58
+ link: "/links"
59
+ icon: "link"
60
+ `,
61
+ "head.yml": `id: head
62
+ # googleAnalyticsId: "G-XXXXXXXXXX"
63
+ # clarityId: "xxxxxxxxxx"
64
+ # umamiWebsiteId: ""
65
+ # umamiSrc: ""
66
+ # anyhead: ""
67
+ `,
68
+ "footer.yml": `id: footer
69
+ buildtime: "${now}"
70
+ copyright:
71
+ enabled: true
72
+ startYear: ${year}
73
+ customText: ""
74
+ theme:
75
+ showPoweredBy: true
76
+ showThemeInfo: true
77
+ beian:
78
+ icp:
79
+ enabled: false
80
+ number: ""
81
+ security:
82
+ enabled: false
83
+ text: ""
84
+ number: ""
85
+ badges:
86
+ - label: "Powered by"
87
+ message: "Astro"
88
+ color: "orange"
89
+ style: "flat-square"
90
+ href: "https://astro.build/"
91
+ custom: |
92
+ <div id="custom-footer-hook"></div>
93
+ `,
94
+ "media-links.yml": `id: medialinks
95
+ items:
96
+ - icon: "github"
97
+ link: "https://github.com/yourname"
98
+ `,
99
+ "links.yml": `id: links
100
+ title: "Links"
101
+ description: "Friends & Resources"
102
+ sites:
103
+ - name: "Astro"
104
+ description: "The web framework for content-driven websites"
105
+ url: "https://astro.build"
106
+ icon: "https://astro.build/favicon.svg"
107
+ `,
108
+ "comment.yml": `id: comment
109
+ enabled: false
110
+ waline:
111
+ serverURL: ""
112
+ lang: ""
113
+ locale: ""
114
+ pageSize: 10
115
+ recaptchaV3Key: ""
116
+ `,
117
+ "promote.yml": `id: promote
118
+ export_md: false
119
+ # llm_promote: |
120
+ `,
121
+ "ai-discovery.yml": `id: ai-discovery
122
+ conformance: "none"
123
+ `,
124
+ "typetexts.yml": `id: typetexts
125
+ items:
126
+ - "Free for free, not free for charge!"
127
+ - "Where's the any key?"
128
+ - "Press F12?"
129
+ - "Hello World!"
130
+ `,
131
+ };
38
132
  }
39
133
 
40
- function printHelp() {
41
- console.log(`
42
- Stalux — Modern Astro Blog Theme
134
+ function getExamplePost() {
135
+ const date = new Date().toISOString().replace("T", " ").slice(0, 19);
136
+ return `---
137
+ title: Hello Stalux!
138
+ abbrlink: hello-stalux
139
+ date: "${date}"
140
+ tags: [Stalux, Getting Started]
141
+ categories: [Blog]
142
+ desc: Welcome to Stalux!
143
+ cc: CC-BY-NC-SA-4.0
144
+ ---
43
145
 
44
- Usage:
45
- stalux init [dir] Initialize Stalux content template
46
- stalux --help Show this help message
146
+ Welcome to **Stalux**!
47
147
 
48
- Examples:
49
- npx stalux init Init content in current directory
50
- npx stalux init my-blog Init content in ./my-blog
51
- npm run init Init via npm script (after install)
148
+ This is your first post. Edit or delete it, then start writing your own content.
52
149
 
53
- Source code:
54
- git clone https://github.com/xingwangzhe/stalux.git
55
- `);
150
+ ## Features
151
+
152
+ - 🌙 Dark mode with elegant glassmorphism design
153
+ - 🔤 Per-route font subsetting — each page loads only needed characters
154
+ - 🔍 Full-text search (Pagefind)
155
+ - 📡 RSS & Atom feeds
156
+ - 💬 Comment system (Waline)
157
+ - 📊 Mermaid diagrams
158
+ - 📐 Math formula rendering
159
+ - 🖼️ PhotoSwipe lightbox
160
+
161
+ For more details, visit [Stalux documentation](https://stalux.needhelp.icu).
162
+ `;
56
163
  }
57
164
 
58
- function printNextSteps(targetDir) {
59
- console.log("");
60
- console.log(" 📝 Next Steps:");
61
- console.log("");
62
- console.log(` 1. cd ${targetDir}`);
63
- console.log(` 2. npm install stalux`);
64
- console.log(` 3. Edit astro.config.mjs to add stalux()`);
65
- console.log(` 4. Start writing content in ${targetDir}/stalux/posts/`);
66
- console.log(` 5. npm run dev`);
67
- console.log("");
68
- console.log(" 📖 Docs: https://stalux.needhelp.icu");
69
- console.log("");
165
+ function getAboutMd() {
166
+ return `---
167
+ title: About Me
168
+ description: About this blog and the author
169
+ ---
170
+
171
+ Hello! Welcome to my blog.
172
+
173
+ This is my personal space where I share technology, life, and thoughts.
174
+
175
+ ## About This Site
176
+
177
+ - Built with [Astro](https://astro.build) + [Stalux](https://stalux.needhelp.icu)
178
+ - Content licensed under CC-BY-NC-SA-4.0
179
+ - Source code hosted on GitHub
180
+ `;
70
181
  }
71
182
 
72
- // ---- Main ----
73
- const args = process.argv.slice(2);
74
- const command = args[0];
183
+ function getWordsTemplate() {
184
+ const date = new Date().toISOString().replace("T", " ").slice(0, 19);
185
+ return `---
186
+ source: "Author Name"
187
+ link: ""
188
+ sourceDate: ""
189
+ date: "${date}"
190
+ ---
75
191
 
76
- if (!command || command === "--help" || command === "-h") {
77
- printHelp();
78
- process.exit(0);
192
+ > A quote or short note...
193
+ `;
79
194
  }
80
195
 
81
- if (command === "init") {
82
- const targetArg = args[1] || ".";
83
- const targetPath = resolve(process.cwd(), targetArg);
196
+ function getWordsExample() {
197
+ const date = new Date().toISOString().replace("T", " ").slice(0, 19);
198
+ return `---
199
+ source: "Albert Einstein"
200
+ link: ""
201
+ sourceDate: ""
202
+ date: "${date}"
203
+ ---
204
+
205
+ > Imagination is more important than knowledge.
206
+ `;
207
+ }
208
+
209
+ // ---------------------------------------------------------------------------
210
+ // 主流程
211
+ // ---------------------------------------------------------------------------
212
+
213
+ function main() {
214
+ const args = process.argv.slice(2);
215
+
216
+ if (args[0] === "--help" || args[0] === "-h" || !args[0]) {
217
+ printHelp();
218
+ process.exit(0);
219
+ }
84
220
 
85
- if (!existsSync(TEMPLATE_DIR)) {
86
- console.error(
87
- "❌ Template directory not found. Ensure stalux package is properly installed.",
88
- );
221
+ if (args[0] !== "init") {
222
+ console.error(`❌ Unknown command: ${args[0]}`);
223
+ printHelp();
89
224
  process.exit(1);
90
225
  }
91
226
 
92
- console.log(`📦 Initializing Stalux content template...`);
93
- console.log(`📂 Target: ${targetPath}`);
227
+ const targetArg = args[1] || ".";
228
+ const targetPath = pathResolve(process.cwd(), targetArg);
229
+
230
+ console.log(`📦 Initializing Stalux content in ${targetPath}...`);
231
+ console.log("");
232
+
233
+ // 创建 stalux/ 目录结构
234
+ const contentRoot = join(targetPath, "stalux");
235
+ const dirs = ["config", "posts", "about", "words"];
236
+ for (const dir of dirs) {
237
+ mkdirSync(join(contentRoot, dir), { recursive: true });
238
+ }
239
+
240
+ // 生成 config YAML 文件(不覆盖已有)
241
+ const configs = getConfigYamls();
242
+ for (const [file, content] of Object.entries(configs)) {
243
+ const filePath = join(contentRoot, "config", file);
244
+ if (existsSync(filePath)) {
245
+ console.log(` ⏭ stalux/config/${file} (exists, skipped)`);
246
+ continue;
247
+ }
248
+ writeFileSync(filePath, content);
249
+ console.log(` ✅ stalux/config/${file}`);
250
+ }
251
+
252
+ // 生成示例文章
253
+ const postPath = join(contentRoot, "posts", "hello-stalux.md");
254
+ if (!existsSync(postPath)) {
255
+ writeFileSync(postPath, getExamplePost());
256
+ console.log(` ✅ stalux/posts/hello-stalux.md`);
257
+ } else {
258
+ console.log(` ⏭ stalux/posts/hello-stalux.md (exists, skipped)`);
259
+ }
260
+
261
+ // 生成 about 页面
262
+ const aboutPath = join(contentRoot, "about", "index.md");
263
+ if (!existsSync(aboutPath)) {
264
+ writeFileSync(aboutPath, getAboutMd());
265
+ console.log(` ✅ stalux/about/index.md`);
266
+ } else {
267
+ console.log(` ⏭ stalux/about/index.md (exists, skipped)`);
268
+ }
269
+
270
+ // 生成 words
271
+ const wordsDir = join(contentRoot, "words");
272
+ const wordsTemplatePath = join(wordsDir, "_template.md");
273
+ if (!existsSync(wordsTemplatePath)) {
274
+ writeFileSync(wordsTemplatePath, getWordsTemplate());
275
+ console.log(` ✅ stalux/words/_template.md`);
276
+ } else {
277
+ console.log(` ⏭ stalux/words/_template.md (exists, skipped)`);
278
+ }
279
+
280
+ const einsteinPath = join(wordsDir, "einstein-imagination.md");
281
+ if (!existsSync(einsteinPath)) {
282
+ writeFileSync(einsteinPath, getWordsExample());
283
+ console.log(` ✅ stalux/words/einstein-imagination.md`);
284
+ } else {
285
+ console.log(` ⏭ stalux/words/einstein-imagination.md (exists, skipped)`);
286
+ }
287
+
94
288
  console.log("");
289
+ console.log(" ✅ Done! Stalux content initialized.");
290
+ console.log("");
291
+ printNextSteps();
292
+ }
95
293
 
96
- copyDir(TEMPLATE_DIR, targetPath, false);
294
+ function printHelp() {
295
+ console.log(`
296
+ Stalux — Modern Astro Blog Theme
297
+
298
+ Usage:
299
+ stalux init Initialize Stalux content (configs, posts, pages)
300
+ stalux init my-blog Initialize in a subdirectory
301
+
302
+ What it does:
303
+ - Creates stalux/config/*.yml (all configuration files)
304
+ - Creates stalux/posts/hello-stalux.md (example post)
305
+ - Creates stalux/about/index.md (about page)
306
+ - Creates stalux/words/ (example quotes)
307
+ - Does NOT overwrite existing files
308
+
309
+ Prerequisites:
310
+ 1. Create an Astro project: bun create astro
311
+ 2. Install stalux: bun add @xingwangzhe/stalux
312
+ 3. Run init: bunx stalux init
313
+ `);
314
+ }
97
315
 
98
- printNextSteps(targetArg);
99
- process.exit(0);
316
+ function printNextSteps() {
317
+ console.log(" 📝 Next Steps:");
318
+ console.log("");
319
+ console.log(" 1. Add stalux to your astro.config.mjs:");
320
+ console.log(' import stalux from "@xingwangzhe/stalux";');
321
+ console.log(' integrations: [stalux({ contentDir: "stalux" })],');
322
+ console.log("");
323
+ console.log(" 2. Add content collections to src/content.config.ts:");
324
+ console.log(
325
+ ' import { defineCollections } from "@xingwangzhe/stalux/schemas/collections";',
326
+ );
327
+ console.log(' export const collections = defineCollections({ contentDir: "stalux" });');
328
+ console.log("");
329
+ console.log(" 3. Start writing content in stalux/posts/");
330
+ console.log(" 4. Run: bun run dev");
331
+ console.log("");
332
+ console.log(" 📖 Docs: https://stalux.needhelp.icu");
333
+ console.log("");
100
334
  }
101
335
 
102
- // Unknown command
103
- console.error(`❌ Unknown command: ${command}`);
104
- console.error(` Use "stalux init" to initialize Stalux content.`);
105
- process.exit(1);
336
+ main();
@@ -90,24 +90,38 @@ function getRouteChars(projectRoot: string, subsetId: string): string | null {
90
90
  return getCategoryContent(contentRoot, catName);
91
91
  }
92
92
 
93
- // ── Other HTML routes (home, links, 404) ──
94
- // These pages use i18n text + config YAML text, which are already
95
- // covered by the common subset. Return config text for extra safety.
96
- if (["home", "links", "common"].includes(subsetId)) {
97
- const configDir = join(contentRoot, "config");
98
- if (!existsSync(configDir)) return null;
99
- const all: string[] = [];
100
- for (const f of readdirSync(configDir)) {
101
- if (f.endsWith(".yml") || f.endsWith(".yaml")) {
102
- all.push(readFileSync(join(configDir, f), "utf-8"));
103
- }
104
- }
105
- return all.join("\n") || null;
93
+ // ── Home page ───────────────────────────────────────────
94
+ if (subsetId === "home") {
95
+ return getAllConfigContent(contentRoot);
96
+ }
97
+
98
+ // ── Links page ──────────────────────────────────────────
99
+ if (subsetId === "links") {
100
+ return getAllConfigContent(contentRoot);
101
+ }
102
+
103
+ // ── Other HTML routes (common) ──
104
+ // The common subset includes UI text from source + config YAML
105
+ if (subsetId === "common") {
106
+ return getAllConfigContent(contentRoot);
106
107
  }
107
108
 
108
109
  return null;
109
110
  }
110
111
 
112
+ /** Get all config YAML content */
113
+ function getAllConfigContent(contentRoot: string): string | null {
114
+ const configDir = join(contentRoot, "config");
115
+ if (!existsSync(configDir)) return null;
116
+ const all: string[] = [];
117
+ for (const f of readdirSync(configDir)) {
118
+ if (f.endsWith(".yml") || f.endsWith(".yaml")) {
119
+ all.push(readFileSync(join(configDir, f), "utf-8"));
120
+ }
121
+ }
122
+ return all.join("\n") || null;
123
+ }
124
+
111
125
  /** Get all post content concatenated (including template/_ files) */
112
126
  function getAllPostContent(contentRoot: string): string | null {
113
127
  const postsDir = join(contentRoot, "posts");
@@ -181,6 +181,25 @@ function scanContent(projectRoot: string): ContentFile[] {
181
181
  }
182
182
  }
183
183
 
184
+ // Build virtual routes for config-driven pages (home, links)
185
+ // These pages render text from config YAML files (nav titles, site title, etc.)
186
+ const configDir = join(contentRoot, "config");
187
+ if (existsSync(configDir)) {
188
+ const allConfigContent: string[] = [];
189
+ for (const f of readdirSync(configDir)) {
190
+ if (f.endsWith(".yml") || f.endsWith(".yaml")) {
191
+ allConfigContent.push(readFileSync(join(configDir, f), "utf-8"));
192
+ }
193
+ }
194
+ if (allConfigContent.length > 0) {
195
+ const configText = allConfigContent.join("\n");
196
+ // Home page: site title, author, navs, typetexts, social media, footer
197
+ virtualRouteCache.set("home", extractChars(configText));
198
+ // Links page: links config + navs + footer
199
+ virtualRouteCache.set("links", extractChars(configText));
200
+ }
201
+ }
202
+
184
203
  // About
185
204
  const aboutDir = join(contentRoot, "about");
186
205
  if (existsSync(aboutDir)) {
@@ -15,7 +15,7 @@ import ToArchive from "@components/stalux/archives/toArchive.astro";
15
15
  import { getSiteData, getAuthorData, getHeadData, getNavsData, getLinksData, getCommentData, getFooterData, getPromoteData, getTypetextsData, getMediaLinksData } from "@utils/config-utils";
16
16
  ---
17
17
 
18
- <Stalux title={siteTitle} description={siteDescription}>
18
+ <Stalux title={siteTitle} description={siteDescription} fontSubsetId="home">
19
19
  <div class={styles.homeBody}>
20
20
  <h1 class={styles.homeTitle}>{siteTitle}</h1>
21
21
  <div class={styles.homeNavs}>
@@ -13,12 +13,12 @@ const linksTitle = linksConfig.title;
13
13
  const linksDescription = linksConfig.description;
14
14
  ---
15
15
 
16
- <ListPage
17
- title={siteTitle}
18
- description={siteDescription}
19
- heading={linksTitle}
20
- subheading={linksDescription}
21
-
22
- >
16
+ <ListPage
17
+ title={siteTitle}
18
+ description={siteDescription}
19
+ heading={linksTitle}
20
+ subheading={linksDescription}
21
+ fontSubsetId="links"
22
+ >
23
23
  <LinkList />
24
24
  </ListPage>
@@ -1,75 +0,0 @@
1
- import { satteri } from "@astrojs/markdown-satteri";
2
- import sitemap from "@astrojs/sitemap";
3
- import { mermaidHast } from "@xingwangzhe/satteri-mermaid";
4
- import { photoswipe } from "@xingwangzhe/satteri-photoswipe";
5
- import stalux from "@xingwangzhe/stalux";
6
- import expressiveCode from "astro-expressive-code";
7
- import { defineConfig, fontProviders } from "astro/config";
8
-
9
- // https://astro.build/config
10
- export default defineConfig({
11
- output: "static",
12
- site: "https://example.com",
13
- prefetch: {
14
- prefetchAll: false,
15
- defaultStrategy: "hover",
16
- },
17
- fonts: [
18
- {
19
- provider: fontProviders.local(),
20
- name: "Google Sans Code",
21
- cssVariable: "--font-code",
22
- options: {
23
- variants: [
24
- {
25
- weight: "100 900",
26
- style: "normal",
27
- src: [
28
- "./node_modules/@xingwangzhe/stalux/src/assets/fonts/GoogleSansCode.woff2",
29
- ],
30
- },
31
- {
32
- weight: "100 900",
33
- style: "italic",
34
- src: [
35
- "./node_modules/@xingwangzhe/stalux/src/assets/fonts/GoogleSansCode-Italic.woff2",
36
- ],
37
- },
38
- ],
39
- },
40
- },
41
- ],
42
- integrations: [
43
- stalux({
44
- contentDir: "stalux",
45
- }),
46
- sitemap(),
47
- expressiveCode({
48
- themes: ["dark-plus", "github-light"],
49
- styleOverrides: {
50
- borderRadius: "0.5rem",
51
- codeFontFamily:
52
- '"JetBrains Mono", "Fira Code", "Consolas", "Courier New", monospace',
53
- },
54
- useDarkModeMediaQuery: true,
55
- }),
56
- ],
57
- markdown: {
58
- processor: satteri({
59
- features: {
60
- math: true,
61
- smartPunctuation: true,
62
- gfm: true,
63
- frontmatter: true,
64
- },
65
- mdastPlugins: [],
66
- hastPlugins: [
67
- photoswipe(),
68
- mermaidHast({
69
- responsive: true,
70
- theme: "dark",
71
- }),
72
- ],
73
- }),
74
- },
75
- });
@@ -1,27 +0,0 @@
1
- {
2
- "name": "my-stalux-blog",
3
- "version": "1.0.0",
4
- "type": "module",
5
- "scripts": {
6
- "dev": "astro dev",
7
- "build": "astro build",
8
- "preview": "astro preview",
9
- "astro": "astro"
10
- },
11
- "dependencies": {
12
- "@astrojs/markdown-satteri": "^1.0.0",
13
- "@astrojs/sitemap": "^4.0.0",
14
- "@pagefind/component-ui": "^1.5.0",
15
- "@xingwangzhe/satteri-mermaid": "^0.7.0",
16
- "@xingwangzhe/satteri-photoswipe": "^0.2.0",
17
- "astro": "^7.0.0",
18
- "astro-expressive-code": "^0.44.0",
19
- "pagefind": "^1.5.0",
20
- "stalux": "^2.0.0"
21
- },
22
- "devDependencies": {
23
- "@astrojs/rss": "^4.0.0",
24
- "@lucide/astro": "^1.0.0",
25
- "typescript": "^7.0.0"
26
- }
27
- }
@@ -1,3 +0,0 @@
1
- User-agent: *
2
- Allow: /
3
- Sitemap: https://example.com/sitemap-index.xml
@@ -1,11 +0,0 @@
1
- /**
2
- * Stalux 内容集合配置
3
- *
4
- * 使用 stalux 包提供的 defineCollections 快速配置。
5
- * 如需自定义内容路径,修改 contentDir 参数即可。
6
- */
7
- import { defineCollections } from "@xingwangzhe/stalux/schemas/collections";
8
-
9
- export const collections = defineCollections({
10
- contentDir: "stalux",
11
- });
@@ -1,14 +0,0 @@
1
- ---
2
- title: About Me
3
- description: About this blog and the author
4
- ---
5
-
6
- Hello! Welcome to my blog.
7
-
8
- This is my personal space where I share technology, life, and thoughts.
9
-
10
- ## About This Site
11
-
12
- - Built with [Astro](https://astro.build) + [Stalux](https://stalux.needhelp.icu)
13
- - Content licensed under CC-BY-NC-SA-4.0
14
- - Source code hosted on GitHub
@@ -1,2 +0,0 @@
1
- id: ai-discovery
2
- conformance: "essential"
@@ -1,4 +0,0 @@
1
- id: author
2
- name: Your Name
3
- avatar: /avatar.png
4
- bio: A passionate developer and writer
@@ -1,2 +0,0 @@
1
- id: comment
2
- enabled: false
@@ -1,13 +0,0 @@
1
- id: footer
2
- copyright:
3
- enabled: true
4
- startYear: 2026
5
- theme:
6
- showPoweredBy: true
7
- showThemeInfo: true
8
- beian:
9
- icp:
10
- enabled: false
11
- security:
12
- enabled: false
13
- badges: []
@@ -1,7 +0,0 @@
1
- id: head
2
- # googleAnalyticsId: ""
3
- # bingClarityId: ""
4
- # umami:
5
- # id: ""
6
- # url: ""
7
- anyhead: ""
@@ -1,4 +0,0 @@
1
- id: links
2
- title: Friends
3
- description: Friendly neighbors sharing great content
4
- sites: []
@@ -1,8 +0,0 @@
1
- id: media-links
2
- items:
3
- - icon: github
4
- link: https://github.com/
5
- - icon: bilibili
6
- link: https://bilibili.com/
7
- - icon: maildotru
8
- link: mailto:hello@example.com
@@ -1,23 +0,0 @@
1
- id: navs
2
- items:
3
- - icon: home
4
- link: /
5
- title: Home
6
- - icon: archive
7
- link: /archives/
8
- title: Archives
9
- - icon: folder
10
- link: /categories/
11
- title: Categories
12
- - icon: tag
13
- link: /tags/
14
- title: Tags
15
- - icon: quote
16
- link: /words/
17
- title: Words
18
- - icon: link
19
- link: /links/
20
- title: Links
21
- - icon: user
22
- link: /about/
23
- title: About
@@ -1,2 +0,0 @@
1
- id: promote
2
- export_md: false
@@ -1,7 +0,0 @@
1
- id: site
2
- lang: en
3
- title: My Blog
4
- url: https://example.com
5
- description: A personal blog powered by the Stalux theme
6
- timezone: Asia/Shanghai
7
- favicon: /favicon.ico
@@ -1,5 +0,0 @@
1
- id: typetexts
2
- items:
3
- - "Hello World!"
4
- - "Stay hungry, Stay foolish"
5
- - "Hello Stalux!"
@@ -1,47 +0,0 @@
1
- ---
2
- title: Hello Stalux!
3
- abbrlink: hello-stalux
4
- date: "2026-07-27 10:00:00"
5
- tags: [Stalux, Getting Started]
6
- categories: [Blog]
7
- desc: Welcome to Stalux! This is your first blog post.
8
- cc: CC-BY-NC-SA-4.0
9
- ---
10
-
11
- Welcome to my blog! This is the first post created with the **Stalux** theme.
12
-
13
- ## About Stalux
14
-
15
- Stalux is a modern, high-performance Astro blog theme featuring:
16
-
17
- - 🌙 Dark mode with elegant glassmorphism design
18
- - 🔍 Full-text search (Pagefind)
19
- - 📡 RSS & Atom feeds
20
- - 🤖 LLM discovery files (llms.txt)
21
- - 💬 Comment system (Waline)
22
- - 📊 Mermaid diagrams
23
- - 📐 Math formula rendering
24
- - 🖼️ PhotoSwipe lightbox
25
- - 📱 Fully responsive
26
-
27
- ## Markdown Examples
28
-
29
- **Bold**, _italic_, ~~strikethrough~~, `inline code`
30
-
31
- ```javascript
32
- // Code block
33
- function hello() {
34
- console.log("Hello, Stalux!");
35
- }
36
- ```
37
-
38
- > Blockquote example
39
-
40
- - List item 1
41
- - List item 2
42
- - List item 3
43
-
44
- 1. Ordered list 1
45
- 2. Ordered list 2
46
-
47
- For more features, check the [Stalux documentation](https://stalux.needhelp.icu).
@@ -1,13 +0,0 @@
1
- ---
2
- # source: "Source or Author"
3
- # link: "https://source-link" # Optional; when present, source becomes clickable
4
- # sourceDate: "1999" # Optional; source date
5
- # date: "2026-07-27 20:45:00" # Optional; when recorded
6
- # updated: "2026-07-27 21:00:00"
7
- # draft: false # Optional; true hides the entry
8
- # title: "Title" # Optional
9
- # abbrlink: "custom-id" # Optional
10
- # tags: [tag1, tag2] # Optional
11
- ---
12
-
13
- Write the quote body here. Supports **Markdown**, `inline code`, and code blocks.
@@ -1,8 +0,0 @@
1
- ---
2
- source: "Albert Einstein"
3
- link: "https://example.com"
4
- sourceDate: "1950"
5
- date: "2026-07-27 10:00:00"
6
- ---
7
-
8
- Imagination is more important than knowledge.
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "astro/tsconfigs/strict",
3
- "include": [".astro/types.d.ts", "**/*"],
4
- "exclude": ["dist"],
5
- "compilerOptions": {
6
- "strictNullChecks": true,
7
- "allowJs": true
8
- }
9
- }