@xingwangzhe/stalux 1.49.0 → 1.51.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 +65 -330
- package/package.json +3 -2
- package/src/internal/sharp-loader.ts +45 -0
- package/src/internal/social-card-cover.ts +143 -0
- package/src/internal/social-card-data.ts +0 -1
- package/src/internal/social-card-layout.ts +153 -0
- package/src/internal/social-card-renderer.ts +138 -275
- package/src/layouts/Stalux.astro +1 -0
- package/src/pages/og/[image].[format].ts +20 -21
- package/src/scripts/route-prefetch.ts +120 -0
- package/src/utils/social-image.ts +17 -8
package/README.md
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
1
|
[中文文档](./README_CN.md) | English
|
|
2
2
|
|
|
3
3
|
[](https://github.com/xingwangzhe/stalux/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@xingwangzhe/stalux)
|
|
5
|
+
[](LICENSE)
|
|
4
6
|
|
|
5
|
-
# Stalux
|
|
7
|
+
# Stalux
|
|
6
8
|
|
|
7
|
-
**
|
|
9
|
+
**A modern, dark, glassmorphism-styled Astro blog theme.**
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
Built for content-first reading: per-page CJK font subsets, Pagefind full-text search, PhotoSwipe lightbox, Mermaid and math rendering, Waline comments, Swup view transitions, plus AI-friendly endpoints such as `llms.txt` and WebMCP. Use it as an npm plugin in an existing project or as a source template for deeper customization.
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
[Live demo](https://stalux.needhelp.icu) · [Documentation](./docs/README.md) · [Quick start](#quick-start) · [Issues](https://github.com/xingwangzhe/stalux/issues)
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="https://stalux.needhelp.icu">
|
|
17
|
+
<img src="./docs/assets/home.webp" alt="Stalux homepage: dark glassmorphism, maximalist background, typewriter heading and author card" width="1200">
|
|
18
|
+
</a>
|
|
19
|
+
</p>
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
## Features
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
- **Dark glassmorphism design** with 42 switchable maximalist backgrounds and a consistent glass surface across navigation, cards and footer.
|
|
24
|
+
- **Per-page CJK font subsets** built with Rust / N-API: each page gets a content-addressed WOFF2 subset, and pages with the same character set share the file.
|
|
25
|
+
- **Search that handles Chinese** via a Pagefind full-text index plus a sharded CJK substring index with single-character, NFKC and multi-word queries.
|
|
26
|
+
- **Markdown, ready to go**: PhotoSwipe lightbox, image captions, Mermaid, Temml math and Expressive Code line numbers are injected by the theme integration.
|
|
27
|
+
- **Comments and feeds**: Waline comments, RSS / Atom, sitemap, `llms.txt` / `llms-full.txt`.
|
|
28
|
+
- **Smooth navigation**: Swup owns soft navigation, replaces only the main content area, and keeps background, navigation and footer stable.
|
|
29
|
+
- **AI-friendly**: read-only in-page WebMCP tools, Markdown post export, an OpenAPI description and build-time social cards.
|
|
30
|
+
- **Zero-config integrations**: one `stalux()` call bundles sitemap, Expressive Code and Markdown plugins; site metadata lives in YAML.
|
|
31
|
+
- **Customizable**: one `accentColor` changes the theme color, 30+ components can be overridden, and the UI ships in English and Chinese.
|
|
18
32
|
|
|
19
|
-
|
|
33
|
+
## Quick start
|
|
20
34
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Swup owns soft navigation and motion timing. Astro renders static documents; only `main[data-stalux-route]` is replaced, while navigation, footer, scroll sentinel and background retain their DOM identities. After destination assets are ready, a visible height-bounded heading or introductory region receives a short exit and entry: up to 6px vertically for reading entry, 8px horizontally for explicit previous/next, 4px vertically for a neutral article change, and 6px horizontally within one taxonomy dimension. Cross-category/tag changes use a neutral fade. Desktop entry lasts 100–180ms with an 80ms exit; phones use a 120ms pure entry fade. Long documents remain directly readable without whole-main motion or hiding. History restoration, reduced motion, native browser gestures and same-page anchors skip page motion. Independent HTML and exported data use document navigation. See [Modules and view transitions](docs/MOTION.md) for the complete inventory and timing contract.
|
|
24
|
-
|
|
25
|
-
The runtime uses Swup's Head, Accessibility, Preload, Scroll and JS plugins with Stalux lifecycle handlers. It waits for incoming stylesheets and gives the destination font a bounded 150ms head start, disposes old interactions before replacement, loads first-seen page scripts, then dispatches the Astro-compatible page lifecycle events used by comments, counters, tag clouds and WebMCP. Each full document randomly selects and asynchronously decodes one background, revealing it from the stable dark canvas over 200ms. Swup soft navigations retain that image without selecting, decoding, or crossfading another SVG. Reduced motion reveals it immediately; saveData keeps the dark canvas without requesting decorative SVGs. Custom `window.__STALUX_BG_URLS__` remains supported, and slow or failed decoding never blocks reading. History positions are restored by the Swup runtime, including reduced-motion visits. This requires no new configuration or telemetry. Animation duration describes the effect itself, not total navigation time or a measured performance improvement.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## 🚀 Quick Start
|
|
30
|
-
|
|
31
|
-
### Plugin Mode (recommended)
|
|
35
|
+
### Plugin mode (recommended)
|
|
32
36
|
|
|
33
37
|
```bash
|
|
34
|
-
bun create astro # Choose
|
|
38
|
+
bun create astro # Choose the minimal template
|
|
35
39
|
cd myblog
|
|
36
|
-
bun add @xingwangzhe/stalux #
|
|
37
|
-
bunx stalux init # Generate stalux/ content directory
|
|
40
|
+
bun add @xingwangzhe/stalux # Theme and all dependencies
|
|
41
|
+
bunx stalux init # Generate the stalux/ content directory
|
|
38
42
|
```
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- **Markdown**: Mermaid (MDAST detection + HAST/SVG rendering), math formulas (Temml → MathML), word count / feature flags, and PhotoSwipe image lightbox are injected into the default `satteri()` processor automatically (math / frontmatter / gfm / smart punctuation are enabled by default).
|
|
43
|
-
- **Sitemap**: Automatically generates `/sitemap.xml` after build (copies `sitemap-0.xml` for a single sitemap, or the index for multiple sitemaps). `@astrojs/sitemap` is bundled (published post `/posts/*.md` endpoints are included when `promote.export_md` is enabled; other Markdown mirrors stay filtered by default).
|
|
44
|
-
- **Expressive Code**: bundled with line numbers enabled by default.
|
|
44
|
+
Configure `astro.config.ts`:
|
|
45
45
|
|
|
46
|
-
```ts
|
|
46
|
+
```ts title="astro.config.ts"
|
|
47
47
|
import { defineConfig } from "astro/config";
|
|
48
48
|
import stalux from "@xingwangzhe/stalux";
|
|
49
49
|
|
|
@@ -54,36 +54,21 @@ export default defineConfig({
|
|
|
54
54
|
});
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
Set the optional `accentColor` in `stalux/config/site.yml` to customize the shared theme and Waline accent. It accepts a six-digit HEX color and defaults to `#EAB308`; choose a color with sufficient contrast against your background.
|
|
58
|
-
|
|
59
|
-
```yaml title="stalux/config/site.yml"
|
|
60
|
-
accentColor: "#EAB308"
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
To customize the bundled integrations, pass options (or disable them with `false`):
|
|
64
|
-
|
|
65
|
-
```ts
|
|
66
|
-
integrations: [
|
|
67
|
-
stalux({
|
|
68
|
-
contentDir: "stalux",
|
|
69
|
-
sitemap: { filter: (page) => page.startsWith("https://example.com/posts/") },
|
|
70
|
-
expressiveCode: { themes: ["dark-plus"], useDarkModeMediaQuery: false },
|
|
71
|
-
}),
|
|
72
|
-
];
|
|
73
|
-
```
|
|
74
|
-
|
|
75
57
|
Create `src/content.config.ts`:
|
|
76
58
|
|
|
77
|
-
```ts
|
|
59
|
+
```ts title="src/content.config.ts"
|
|
78
60
|
import { defineCollections } from "@xingwangzhe/stalux/schemas/collections";
|
|
61
|
+
|
|
79
62
|
export const collections = defineCollections({ contentDir: "stalux" });
|
|
80
63
|
```
|
|
81
64
|
|
|
82
65
|
```bash
|
|
83
|
-
bun run dev
|
|
66
|
+
bun run dev # http://localhost:4321
|
|
84
67
|
```
|
|
85
68
|
|
|
86
|
-
|
|
69
|
+
`stalux init` only creates configuration and sample content under `stalux/`; it never overwrites an existing `package.json`, `astro.config.ts` or `src/content.config.ts`.
|
|
70
|
+
|
|
71
|
+
### Source template mode
|
|
87
72
|
|
|
88
73
|
```bash
|
|
89
74
|
git clone https://github.com/xingwangzhe/stalux.git my-blog
|
|
@@ -92,298 +77,48 @@ bun install
|
|
|
92
77
|
bun run dev
|
|
93
78
|
```
|
|
94
79
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Desktop navigation sticks to the top of the viewport and gains a frosted glass background after scrolling past 24px, returning to transparent at the top. The mobile menu button gains the same glass surface while scrolling and stays hidden while its drawer is open. Quote dates provide permalink navigation while content and source links remain independently accessible.
|
|
98
|
-
|
|
99
|
-
Waline loads near the viewport, destroys its instance before Astro swaps the page, and mounts a fresh instance on the next page. A reload-page button recovers from comment resource loading failures, including cached module failures. Comments and inputs use system fonts, with system monospace for code, so user-generated text does not inherit the build-time LXGW WenKai page subset. No additional configuration is required.
|
|
100
|
-
|
|
101
|
-
## ✨ Features
|
|
102
|
-
|
|
103
|
-
- 🌙 **Dark mode** with elegant glassmorphism design
|
|
104
|
-
- 🔤 **Native per-page font subsets** — Rust/N-API emits cached WOFF2 subsets for each page's visible CJK text
|
|
105
|
-
- 🔍 **Full-text search** via Pagefind (auto-indexed on build)
|
|
106
|
-
- 📡 **RSS & Atom feeds**
|
|
107
|
-
- 🗺️ **Sitemap** (bundled; `.md` source endpoints auto-filtered)
|
|
108
|
-
- 🖼️ **PhotoSwipe** image lightbox
|
|
109
|
-
- 📊 **Mermaid** diagrams and flowcharts
|
|
110
|
-
Uses Merman through `@xingwangzhe/satteri-mermaid@0.8.2`, with the `dark` theme and a transparent canvas by default. The plugin supports 11 theme presets; see the [configuration reference](https://github.com/xingwangzhe/satteri-mermaid/blob/v0.8.2/docs/configuration.md). Rendering failures throw by default.
|
|
111
|
-
- 📐 **Math formula rendering** (Temml → MathML)
|
|
112
|
-
- 💬 **Waline** comment system
|
|
113
|
-
- 🤖 **LLM discovery files** (llms.txt / llms-full.txt)
|
|
114
|
-
- 🤝 **WebMCP tools** for AI agents (W3C draft, pure front-end)
|
|
115
|
-
- ⚡ **View transitions** for smooth navigation
|
|
116
|
-
- 🌐 **i18n** (English / Simplified Chinese; missing locale keys fall back to English)
|
|
117
|
-
- 🏷️ **Tags, categories, archives** pages
|
|
118
|
-
- 🎨 **Component override system** (Starlight-style)
|
|
119
|
-
- 🛠️ **Easy YAML configuration** — no coding required
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## 🔤 Font Optimization
|
|
124
|
-
|
|
125
|
-
Stalux ships with LXGW WenKai and a variable code font (Google Sans Code). During the build, the Rust/N-API package `@xingwangzhe/cjk-font-split-native` creates one content-addressed WOFF2 subset from each page's visible or expandable CJK text. The browser downloads only the page subset; repeated character sets share the same file. CJK text inside code and MathML is covered too.
|
|
126
|
-
|
|
127
|
-
Arabic digits `0-9` are always reserved in every subset (`RESERVED_GLYPHS`), even on pages without any visible number. Digits produced at runtime by client scripts — clocks, counters, dates, injected values — never appear in the built HTML, so reserving them keeps numbers from falling back to a system font. Callers can append more characters through the optional `extraChars` argument of `writePageFontSubset`.
|
|
128
|
-
|
|
129
|
-
The subset CSS uses an exact per-character `unicode-range` and `font-display: swap`, so visible text and the downloaded glyphs stay aligned. The variable code font continues to use Astro's local Fonts API. A same-origin font preload in the HTML head discovers the body subset early; consumers need no manual font links.
|
|
130
|
-
|
|
131
|
-
The persistent BLAKE3 cache key includes font bytes, face index, normalized character set, and algorithm version. Cache files live under `node_modules/.astro/` and are reused across pages and incremental builds. Font inputs are read locally; builds do not fetch fonts from the network.
|
|
132
|
-
|
|
133
|
-
Multi-page builds reuse prepared font data and hashing state through `FontSubsetter`, with up to four native worker tasks for cold subsets. Font containers are decoded/extracted to SFNT before reusable HarfBuzz preprocessing preserves original outlines; Google Brotli encodes WOFF2 at quality 8. Image dimensions, image loading hints, linked CSS text, and font character extraction share one HTML parse. Shared image dimensions and CSS-generated text are also reused within each build, image and font nodes are mutated on the same parse5 AST, then changed nodes are emitted once using source locations. Untouched scripts, comments, templates and subtrees retain their original source.
|
|
134
|
-
|
|
135
|
-
Local image dimensions use `image-size`, including SVG and AVIF support. Normal file reads inspect at most 512 KiB; a JPEG compatibility fallback may read the full file when the size marker is first or beyond that window. Explicit dimensions and remote images are preserved. Referenced assets are hashed as streams, and content changes at the same URL invalidate the page cache regardless of image extension.
|
|
136
|
-
|
|
137
|
-
Processed pages are cached under `node_modules/.astro/stalux-page-output/`. The cache checks source HTML and page paths, referenced local CSS/image content, font inputs, and processing code. Changed inputs, missing font files, and corrupt records trigger reprocessing. Unchanged pages restore their processed output; Pagefind still rebuilds the full search index on every build. Preserve `node_modules/.astro/` to reuse caches across builds; removing it safely rebuilds them.
|
|
138
|
-
|
|
139
|
-
### Visibility-aware loading and updates
|
|
80
|
+
> Requires Node.js 26 or newer; Bun is recommended. See [Getting started](./docs/GETTING-STARTED.md) for complete steps, integration options and the content layout.
|
|
140
81
|
|
|
141
|
-
|
|
82
|
+
## Configuration
|
|
142
83
|
|
|
84
|
+
Site metadata, navigation, author, comments and analytics live in YAML under `stalux/config/`. Changing the theme color takes one line:
|
|
143
85
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
### Modern JavaScript
|
|
147
|
-
|
|
148
|
-
TypeScript uses ESNext. The theme defaults `vite.build.target` to `"esnext"` to retain modern syntax with minimal compatibility transforms; an explicit consumer target takes precedence. This targets modern browsers, does not polyfill missing Web APIs, and does not move ordinary scripts onto the GPU. Biome enforces `noVar` and `useConst` as errors in maintained source; dependencies and generated bundles are outside this source policy.
|
|
149
|
-
|
|
150
|
-
### EdgeOne build environment
|
|
151
|
-
|
|
152
|
-
The repository `edgeone.json` runs install and build through `bunx --bun bun@1.4.4`. Installation uses `--frozen-lockfile`, preventing an older preinstalled Bun from ignoring the v2 lockfile and resolving different dependencies. Both development and peer dependencies require Astro `^7.4.4`. Consuming projects should use the same pinned commands when deploying; their existing build script remains unchanged.
|
|
153
|
-
|
|
154
|
-
### CJK search enhancement
|
|
155
|
-
|
|
156
|
-
Builds generate both Pagefind and a sharded CJK substring index shared by the dialog and WebMCP. Titles, summaries, tags and categories are searchable. Optional post frontmatter `searchKeywords: [alias, abbreviation]` defaults to an empty array; keywords are published in HTML and static search files, so never put private information there. Unquoted Chinese, Japanese and Korean queries add verified contiguous substring matches, including single characters, NFKC normalization and space-separated terms. Title matches rank first; native results are retained and deduplicated. Quoted queries and active filters retain native Pagefind semantics. Automatic Traditional/Simplified conversion, pinyin, typo correction and semantic search are not included. Search files load on demand, failures fall back to Pagefind and can be retried, with no external search service or new telemetry.
|
|
157
|
-
|
|
158
|
-
### Initial CSS and search styles
|
|
159
|
-
|
|
160
|
-
The theme defaults to Astro's `build.inlineStylesheets: "never"`: hashed shared CSS can be cached across routes, while component styles load on the pages that use them. Normal blocking stylesheets prevent an unstyled first paint. An explicit consumer setting of `build.inlineStylesheets: "always"` restores inline styles at the cost of larger HTML responses.
|
|
161
|
-
|
|
162
|
-
Pagefind dialog CSS is emitted as a separate hashed static file with a `prefetch` hint. Opening search activates the stylesheet and waits for it before displaying the dialog. It does not block the initial content or depend on JavaScript generating a CSS asset. A `<noscript>` stylesheet fallback is included.
|
|
163
|
-
|
|
164
|
-
---
|
|
165
|
-
|
|
166
|
-
## 🎨 Component Override
|
|
167
|
-
|
|
168
|
-
```ts
|
|
169
|
-
stalux({
|
|
170
|
-
components: {
|
|
171
|
-
Navs: "./src/components/CustomNavs.astro",
|
|
172
|
-
Footer: "./src/components/CustomFooter.astro",
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
30+ components are overridable. See `src/internal/override.ts` for the full list.
|
|
178
|
-
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
## 📝 Content Structure
|
|
182
|
-
|
|
183
|
-
```
|
|
184
|
-
stalux/
|
|
185
|
-
├── config/ # YAML configuration
|
|
186
|
-
│ ├── site.yml # Site metadata
|
|
187
|
-
│ ├── author.yml # Author info (name/avatar/bio/jobTitle)
|
|
188
|
-
│ ├── navs.yml # Navigation menu
|
|
189
|
-
│ ├── footer.yml # Footer badges & copyright
|
|
190
|
-
│ ├── links.yml # Friend links
|
|
191
|
-
│ ├── comment.yml # Waline comment config
|
|
192
|
-
│ ├── head.yml # Analytics & custom head
|
|
193
|
-
│ ├── media-links.yml # Social media links
|
|
194
|
-
│ ├── promote.yml # LLM promotion
|
|
195
|
-
│ ├── ai-discovery.yml # AI discovery files
|
|
196
|
-
│ └── typetexts.yml # Typewriter text
|
|
197
|
-
├── posts/ # Blog posts (Markdown)
|
|
198
|
-
├── about/index.md # About page
|
|
199
|
-
└── words/ # Quotes / short notes
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Analytics configuration
|
|
203
|
-
|
|
204
|
-
Analytics are configured in `stalux/config/head.yml`, so no template changes are needed:
|
|
205
|
-
|
|
206
|
-
```yaml
|
|
207
|
-
id: head
|
|
208
|
-
bingClarityId: "YOUR_CLARITY_PROJECT_ID"
|
|
86
|
+
```yaml title="stalux/config/site.yml"
|
|
87
|
+
accentColor: "#EAB308"
|
|
209
88
|
```
|
|
210
89
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
For Google Analytics, turn off **Page changes based on browser history events** under **Admin → Data streams → Enhanced measurement → Page views → Advanced settings**. Stalux sets `send_page_view: false` and sends the URL and title for each navigation explicitly. GA's history tracking must also be disabled in the property to prevent duplicate soft-navigation pageviews. Other enhanced measurement options can remain enabled. See [Google's pageview documentation](https://developers.google.com/analytics/devguides/collection/ga4/views).
|
|
214
|
-
|
|
215
|
-
`bingClarityId` is the historical Stalux field name for a Microsoft Clarity Project ID. Get the ID from the Clarity project under **Settings → Setup → Get tracking code**. Stalux includes the official `@microsoft/clarity` npm dependency, imports it dynamically in the browser, and calls `Clarity.init()` once across soft navigation. Consumer projects such as myblog only configure the ID and upgrade the theme; no separate Clarity installation or script is needed. The theme observes tag loading failures and allows a later page to retry. Tag load, recorder readiness and successful collection are verified separately. The official npm package still loads the hosted recording engine and does not move DOM capture into a Worker. The SDK handles history navigation itself. Do not install the same project again through `anyhead`, a tag manager, or another plugin.
|
|
216
|
-
|
|
217
|
-
The Project ID is a public browser identifier. Never put a Clarity Data Export API token in this YAML or in client-side code. If the site uses a strict CSP, consent banner, or CMP, configure those host-site policies and signals separately; this theme does not make legal compliance decisions for the site.
|
|
218
|
-
|
|
219
|
-
After deployment, verify the script URL contains the exact Project ID and that the browser sends Clarity `/collect` requests (the collection host may be a regional endpoint such as `a.clarity.ms`).
|
|
90
|
+
`accentColor` accepts a six-digit HEX value and drives both the theme and Waline accents. See [Configuration](./docs/CONFIGURATION.md) for every field.
|
|
220
91
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
## 🛠️ Development Commands
|
|
92
|
+
## Deployment
|
|
224
93
|
|
|
225
94
|
```bash
|
|
226
|
-
bun
|
|
227
|
-
bun run
|
|
228
|
-
bun run build # Build to dist/
|
|
229
|
-
bun run preview # Preview production build
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
## 📖 Documentation
|
|
235
|
-
|
|
236
|
-
Full documentation and live demo: **[stalux.needhelp.icu](https://stalux.needhelp.icu)**
|
|
237
|
-
|
|
238
|
-
---
|
|
239
|
-
|
|
240
|
-
## 🤝 WebMCP / AI Agents
|
|
241
|
-
|
|
242
|
-
Stalux exposes page-local WebMCP tools to compatible browser agents or extensions. All data comes from static build artifacts, so **no backend is required**. This is not an HTTP `/mcp` server that a desktop MCP client can connect to directly: ordinary static hosting cannot process MCP JSON-RPC POST requests. See the [WebMCP community-group draft](https://webmachinelearning.github.io/webmcp/).
|
|
243
|
-
|
|
244
|
-
When a WebMCP-aware browser opens your site, these read-only tools are registered:
|
|
245
|
-
|
|
246
|
-
| Tool | What it does | Backing data |
|
|
247
|
-
| --------------------- | ----------------------------------------------------------------------- | ----------------------- |
|
|
248
|
-
| `stalux_list_posts` | Paginated list of all posts (with meta) | `/api/posts.json` |
|
|
249
|
-
| `stalux_get_post` | Fetch one post's metadata by abbrlink or title keyword | `/api/posts.json` |
|
|
250
|
-
| `stalux_current_post` | Metadata of the post currently being viewed | `/api/posts.json` |
|
|
251
|
-
| `stalux_random_post` | Pick a random post's metadata | `/api/posts.json` |
|
|
252
|
-
| `stalux_search_posts` | Full-text search across posts | Pagefind `/pagefind/` |
|
|
253
|
-
| `stalux_read_post` | Read Markdown in chunks, or indexed plain text when not exported | Markdown / Pagefind |
|
|
254
|
-
| `stalux_site_info` | Site title, URL, description + pointers to `llms.txt` / `llms-full.txt` | `site.yml` (build-time) |
|
|
255
|
-
|
|
256
|
-
All tools are `readOnlyHint: true` — they do not write site data or initiate navigation.
|
|
257
|
-
|
|
258
|
-
`stalux_search_posts` merges title, tag, category and description matches from the published metadata with body hits from the [Pagefind JavaScript API](https://pagefind.app/docs/api/), using `meta.title` and `plain_excerpt`. Results include `abbrlink` and absolute URL, are deduplicated, and prioritize title matches, with up to 10 posts per call. If the full-text index is unavailable, available metadata matches are labeled `source: "metadata"`, `fullText: false`; no metadata matches yields an index-unavailable error instead of pretending the full-text search succeeded with zero hits. Highlighted HTML is not returned as the tool excerpt. Failed metadata and Pagefind module loads remain retryable.
|
|
259
|
-
|
|
260
|
-
`stalux_read_post` accepts `id`, `offset` (default 0), and `maxChars` (default and maximum 50000). Offsets use UTF-16 units and never split surrogate pairs. With `promote.export_md` enabled, the result has `format: "text/markdown"` and `markdown`. If exports are disabled or the Markdown file is unavailable, it reads Pagefind using the exact `post` filter and returns `format: "text/plain"` and `content`; Markdown syntax and non-indexed content are not preserved by this fallback. Continue with `nextOffset` until it is `null`; `totalChars` and `truncated` describe the range. Disabling both Markdown exports and Pagefind returns `CONTENT_UNAVAILABLE`.
|
|
261
|
-
|
|
262
|
-
Tools initialize the polyfill and register on `astro:page-load`, then unregister and stop the outgoing batch on `astro:before-swap`. Requests only read static resources; search terms are not sent to an external search service, no model is called, and no telemetry is added. The article filter ID and Markdown export flag are public HTML attributes used to select available sources. JSON, Markdown and OpenAPI exports are static files and do not advertise dynamic quotas or fixed `RateLimit-*` balances.
|
|
263
|
-
|
|
264
|
-
**Enabling / disabling:** the tools follow the `conformance` setting in
|
|
265
|
-
`stalux/config/ai-discovery.yml`. Set it to `disabled` to stop registering
|
|
266
|
-
tools; `essential` / `recommended` / `complete` all enable them.
|
|
267
|
-
|
|
268
|
-
**Browser support:** WebMCP is a community-group draft; native support depends on browser and version. On browsers without a native `modelContext`, Stalux loads the
|
|
269
|
-
[`@mcp-b/webmcp-polyfill`](https://www.npmjs.com/package/@mcp-b/webmcp-polyfill)
|
|
270
|
-
so agents still work; the polyfill becomes a no-op once native support lands.
|
|
271
|
-
|
|
272
|
-
---
|
|
273
|
-
|
|
274
|
-
## 📄 License
|
|
275
|
-
|
|
276
|
-
MIT License — see [LICENSE](./LICENSE).
|
|
277
|
-
|
|
278
|
-
[](https://vercel.com/new/clone?repository-url=https://github.com/xingwangzhe/stalux)
|
|
279
|
-
[](https://edgeone.ai/pages/new?repository-url=https://github.com/xingwangzhe/stalux)
|
|
280
|
-
|
|
281
|
-
### Author JSON-LD configuration
|
|
282
|
-
|
|
283
|
-
`stalux/config/author.yml` accepts the optional `jobTitle` field. When present, Stalux includes it in the homepage and article JSON-LD `Person` entity so agents can identify the author's role without changing the visual author card:
|
|
284
|
-
|
|
285
|
-
```yaml title="stalux/config/author.yml"
|
|
286
|
-
id: author
|
|
287
|
-
name: xingwangzhe
|
|
288
|
-
avatar: /avatar.png
|
|
289
|
-
bio: Blog Theme Stalux
|
|
290
|
-
jobTitle: Software Engineer # Optional; emitted as JSON-LD Person.jobTitle
|
|
95
|
+
bun run build # Build the static site into dist/
|
|
96
|
+
bun run preview # Preview the production build locally
|
|
291
97
|
```
|
|
292
98
|
|
|
293
|
-
|
|
99
|
+
`dist/` can be deployed to any static host. The repository ships an EdgeOne Pages configuration (pinned Bun 1.4.4 + `--frozen-lockfile`); cache directories, output checks and timezone notes are covered in [Deployment](./docs/DEPLOYMENT.md).
|
|
294
100
|
|
|
101
|
+
## Documentation
|
|
295
102
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
The root reserves scrollbar space and uses explicit line height. The homepage typewriter reserves the maximum wrapped height of its complete messages and keeps the text origin fixed; clocks reserve line boxes and use tabular digits. Existing typing, cursor and route animations remain enabled. No additional configuration is required.
|
|
299
|
-
|
|
300
|
-
Swup's pre-replacement preparation warms the destination body font without installing it on the old page. The font receives at most 150ms of preparation time; failure, timeout and superseding navigation release that font wait. Incoming stylesheets and first-seen page modules have separate bounded loading waits so the next page can initialize its interactions. Initial loads on slow networks may still show the system fallback. Content authors must reserve dimensions for remote images and third-party embeds. Reduced-motion and print behavior remain supported.
|
|
301
|
-
|
|
302
|
-
See [style maintenance](docs/STYLING.md) and [lifecycle and static interfaces](docs/RUNTIME.md). Astro development documentation is available through the [official MCP endpoint](https://mcp.docs.astro.build/mcp); `.codex/config.toml`, `.mcp.json` and `AGENTS.md` record the project setup. This development MCP is separate from the blog's browser WebMCP tools.
|
|
303
|
-
|
|
304
|
-
## Development diagnostics
|
|
305
|
-
|
|
306
|
-
Stalux routes integration, content-loader and rendering diagnostics through Astro's official logger. Normal mode keeps summaries, warnings and errors. Verbose mode adds configuration/routes, component overrides, font cache, Markdown analysis/cache, content statistics, feeds, asset sync and Pagefind stages.
|
|
307
|
-
|
|
308
|
-
```bash title="Stalux diagnostics"
|
|
309
|
-
bun run dev:debug
|
|
310
|
-
bun run build:debug
|
|
311
|
-
# In a consuming project:
|
|
312
|
-
STALUX_DEBUG=1 bun run dev --verbose
|
|
313
|
-
STALUX_DEBUG=1 bun run build --verbose
|
|
314
|
-
# Silence Astro; standalone verification output remains separate:
|
|
315
|
-
bun run astro -- build --silent
|
|
316
|
-
```
|
|
103
|
+
The detailed documentation is currently maintained in Chinese.
|
|
317
104
|
|
|
318
|
-
|
|
|
105
|
+
| Document | Contents |
|
|
319
106
|
| --- | --- |
|
|
320
|
-
|
|
|
321
|
-
|
|
|
322
|
-
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
The friend links list is up to 1600px wide with 16px horizontal padding. Cards fill their grid columns, with one to five columns depending on the viewport.
|
|
339
|
-
|
|
340
|
-
Article bodies and summary cards are up to 840px wide. Summaries use the existing post `desc` and are omitted when empty. Markdown blockquotes appear as callout cards without new configuration or syntax.
|
|
341
|
-
|
|
342
|
-
Visitor weather is optional: set `weather: { enabled: true }` in `stalux/config/site.yml` to show it as plain text in the footer (with visitor IP and approximate location) and below the desktop article clock. Omitted or `false` disables it. It automatically requests an approximate city from IP.SB (with IPinfo as fallback) and weather from Open-Meteo, only while visible; no browser GPS permission is requested. These external services receive the visitor IP, and Open-Meteo receives rounded coordinates. A 20-minute session cache stores the city, visitor IP and weather, without coordinates. Visitors behind VPNs may see the exit city. Hidden tabs, offscreen cards and Astro navigation stop work; unavailable services show a retry action and retain previous weather. Open-Meteo's free endpoint is for non-commercial use subject to its limits; the card links to its attribution licence. No server endpoint or extra package is needed.
|
|
343
|
-
|
|
344
|
-
Archives keep a transparent, single-column year/month timeline with all articles expanded. Each month gently fades and moves into place once when it enters the viewport (280ms), using native browser APIs without extra dependencies. Reduced motion, printing, missing browser APIs and disabled JavaScript retain the complete static list. Mobile dates sit above wrapping article titles. No new configuration is required. Article titles retain a single hover underline; hovering or keyboard-focusing a title previews its existing `desc` on the right, falling below near viewport edges. Empty descriptions show no preview, and touch navigation stays direct. The preview uses the browser top layer (with a high stacking-level fallback) to avoid clipping or overlap.
|
|
345
|
-
|
|
346
|
-
### Production style compaction (1.35.0)
|
|
347
|
-
|
|
348
|
-
Development keeps readable source names. Production builds compact private Stalux classes, custom properties and keyframes before Astro compilation and asset hashing, using a version-local mapping shipped with the package. Templates, client selectors, inline styles and dynamic server-generated animations share the mapping; its contents and transformer source participate in incremental cache invalidation.
|
|
349
|
-
|
|
350
|
-
Stable `data-*` hooks, article anchors, public font properties (`--font-body`, `--font-code`, plus the legacy override hooks `--accent` and `--foreground-60p`), third-party interfaces and user content keep their meaning. Use your own classes and public component interfaces for extensions; the generated `s*`, `--s*` and `k*` names are reserved for the theme. Existing CSS import paths remain available and consumers need no additional configuration or UnoCSS installation.
|
|
351
|
-
|
|
352
|
-
Theme CSS uses native nesting, modern RGB syntax, `overflow-wrap: anywhere` and clipped one-pixel accessible content with `clip-path`. Lightning CSS targets modern browser engines instead of generating obsolete compatibility expansions. Necessary multiline-clamping and third-party MathML browser adaptations remain. Production source maps are disabled to avoid exposing the original names. Build assertions check semantic CSS/HTML/JS surfaces and resource integrity without modifying article text or code examples; detector results always identify the rule or scanner version used.
|
|
353
|
-
|
|
354
|
-
Theme code is MIT; fonts retain their own licenses. LXGW WenKai and Google Sans Code use OFL 1.1; Latin Modern Math uses the GUST Font License. Stalux Math Aux is independently rebuilt from KaTeX's OFL font data, replacing the Temml font with conflicting FontCreator noncommercial metadata. Script-letter outlines are unchanged; primes use an OFL source with matching bounds, advances and repetition spacing, with slightly different curves. Fonts and full licenses are synchronized to the consumer's `public/fonts/`; the old unused `Temml.woff2` can be removed. See `scripts/build-math-font.py` and `scripts/font-sources/` for reproducible inputs.
|
|
355
|
-
|
|
356
|
-
## LICENSES and scope
|
|
357
|
-
|
|
358
|
-
Theme code is **MIT**; the root [LICENSE](LICENSE) contains the standard text for GitHub detection. Bundled fonts retain their own licenses and are not relicensed as MIT. Independent license texts and their scopes are listed below:
|
|
359
|
-
|
|
360
|
-
| 组件 / Component | 许可 / License | 全文 / Full text |
|
|
361
|
-
| --- | --- | --- |
|
|
362
|
-
| Stalux theme code | MIT | [LICENSE-MIT.txt](LICENSES/LICENSE-MIT.txt) |
|
|
363
|
-
| LXGW WenKai | OFL-1.1, reserved names and additional web-font permission | [LXGW-WenKai-OFL.txt](public/fonts/LXGW-WenKai-OFL.txt) |
|
|
364
|
-
| Google Sans Code | OFL-1.1 | [Google-Sans-Code-OFL.txt](public/fonts/Google-Sans-Code-OFL.txt) |
|
|
365
|
-
| Stalux Math Aux, rebuilt from KaTeX OFL font data | OFL-1.1, independently named | [StaluxMathAux-OFL.txt](public/fonts/StaluxMathAux-OFL.txt) |
|
|
366
|
-
| Latin Modern Math | GUST Font License / LPPL-1.3c | [LICENSE-GUST.txt](LICENSES/LICENSE-GUST.txt) |
|
|
367
|
-
| Full LPPL 1.3c text | LPPL-1.3c | [LICENSE-LPPL-1.3c.txt](LICENSES/LICENSE-LPPL-1.3c.txt) |
|
|
368
|
-
| Standard OFL 1.1 text | OFL-1.1 | [LICENSE-OFL-1.1.txt](LICENSES/LICENSE-OFL-1.1.txt) |
|
|
369
|
-
| Font copyrights, reserved names and modification notices | Each font’s original license | [LICENSE-FONT-NOTICES.txt](LICENSES/LICENSE-FONT-NOTICES.txt) |
|
|
370
|
-
|
|
371
|
-
The npm package includes `LICENSES/` and font-specific texts in `public/fonts/`; builds synchronize font notices to the consumer’s public font directory alongside the served WOFF2 files. Native subsetting dependencies retain their [separate license inventory](https://github.com/xingwangzhe/cjk-font-split-native/blob/main/README.en.md#licenses-and-scope) and packaged `THIRD_PARTY_LICENSES.txt`. GitHub’s primary-license display identifies theme code; fonts and other third-party components retain the scopes listed above.
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
### Article social sharing cards
|
|
375
|
-
|
|
376
|
-
Image priority is **explicit `og:image` → a card with a valid `cover` → an automatic title card**. The article list continues to use `cover`. An explicit `og:image` is used as-is and accepts HTTP(S), protocol-relative URLs, or site-root paths; PNG/JPEG at 1200×630 is recommended. Omit it or leave it empty to generate a card automatically. Existing posts need no changes.
|
|
377
|
-
|
|
378
|
-
```yaml title="Article sharing images"
|
|
379
|
-
cover: /images/article-cover.webp
|
|
380
|
-
og:image: /images/custom-social-card.png
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
`@takumi-rs/core` renders a static 1200×630 WebP at `/og/<metadata-hash>.webp` using the bundled LXGW WenKai font. Cards include the title, summary, site identity, author, category, accent color and date in the site timezone. No renderer JavaScript is sent to the browser. Static `og:image`, `twitter:image` and BlogPosting `image` use the same absolute URL; generated images also declare dimensions and MIME type. Articles use `summary_large_image`. A second `og:image` points to a PNG fallback; platforms choose which format they can read. WebP quality is 86; AVIF is not the default.
|
|
384
|
-
|
|
385
|
-
Local covers are read from the final `publicDir`. Remote covers are requested by the build machine, with an 8-second timeout and an 8 MiB limit (remote bytes are cached for 24 hours); unreadable or undecodable artwork emits a warning and falls back to a title card. No remote image-generation service is used. WebP and PNG outputs are cached in `stalux-social-cards/` under Astro's cache directory and copied into each build output. Explicit sharing images are fetched by social platforms, so they must be publicly accessible images. Platforms maintain their own preview caches and may not refresh an existing share immediately after deployment.
|
|
386
|
-
|
|
387
|
-
Motion is specified for 55 visible semantic modules in [Modules and view transitions](docs/MOTION.md). Swup animates one visible, height-bounded heading or introductory region after destination assets are ready, with cues for reading, returning, filtering and previous/next articles. Long documents stay readable and the header, footer and dark canvas remain stable. Phones use a short fade; history restores the reading position; same-page anchors use interruptible native scrolling. Module hover, focus, press and asynchronous update feedback complement bounded secondary entry cues. Reduced motion and print remain static and readable. No new configuration is required.
|
|
388
|
-
|
|
389
|
-
Built-in backgrounds use committed WebP companions composited against the default #202020 canvas (quality 80, original resolution). Custom CSS canvas colors use the original transparent WebP; SVG sources are retained. Ratio-only SVG sizing is preserved with `contain`; custom background URLs retain their existing sizing. Regenerate companions after changing the SVG originals with `bun run backgrounds:build`.
|
|
107
|
+
| [Getting started](./docs/GETTING-STARTED.md) | Requirements, both install modes, layout, commands, diagnostics |
|
|
108
|
+
| [Configuration](./docs/CONFIGURATION.md) | Every `stalux/config/*.yml` field, color, weather, comments, AI discovery |
|
|
109
|
+
| [Features](./docs/FEATURES.md) | Typewriter, captions, lightbox, code theme, archives, soft navigation, backgrounds |
|
|
110
|
+
| [Fonts](./docs/FONTS.md) | Per-page CJK subsets, caches, font licenses |
|
|
111
|
+
| [Search](./docs/SEARCH.md) | Pagefind + CJK index, `searchKeywords`, fallbacks |
|
|
112
|
+
| [Analytics](./docs/ANALYTICS.md) | Google Analytics, Clarity, Umami, Vercount |
|
|
113
|
+
| [Performance](./docs/PERFORMANCE.md) | Visibility-based loading, first-screen CSS, route preload, backgrounds |
|
|
114
|
+
| [Deployment](./docs/DEPLOYMENT.md) | Static build, EdgeOne, caches, output checks |
|
|
115
|
+
| [WebMCP](./docs/WEBMCP.md) | In-page AI tools, data sources, enable/disable, polyfill |
|
|
116
|
+
| [Social cards](./docs/SOCIAL-CARDS.md) | `og:image` / `cover` priority and build-time generation |
|
|
117
|
+
| [Components](./docs/COMPONENTS.md) | Starlight-style component overrides |
|
|
118
|
+
| [License](./docs/LICENSING.md) | MIT theme code and font license scope |
|
|
119
|
+
|
|
120
|
+
The full index, including maintainer docs, is in [docs/README.md](./docs/README.md).
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
Theme code is [MIT licensed](LICENSE). Bundled fonts (LXGW WenKai, Google Sans Code, Latin Modern Math and others) keep their own licenses; see [Licensing](./docs/LICENSING.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xingwangzhe/stalux",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.51.0",
|
|
4
4
|
"description": "A powerful, modern Astro blog theme — use as template or install as plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -111,12 +111,12 @@
|
|
|
111
111
|
"@mcp-b/webmcp-polyfill": "^5.1.0",
|
|
112
112
|
"@microsoft/clarity": "1.0.2",
|
|
113
113
|
"@pagefind/component-ui": "^1.5.2",
|
|
114
|
+
"@resvg/resvg-js": "2.6.2",
|
|
114
115
|
"@swup/a11y-plugin": "^5.2.1",
|
|
115
116
|
"@swup/head-plugin": "^2.3.1",
|
|
116
117
|
"@swup/js-plugin": "^3.2.0",
|
|
117
118
|
"@swup/preload-plugin": "^3.2.12",
|
|
118
119
|
"@swup/scroll-plugin": "^4.0.0",
|
|
119
|
-
"@takumi-rs/core": "2.17.1",
|
|
120
120
|
"@waline/client": "^3.16.1",
|
|
121
121
|
"@xingwangzhe/cjk-font-split-native": "^0.4.10",
|
|
122
122
|
"@xingwangzhe/satteri-mermaid": "^0.8.2",
|
|
@@ -131,6 +131,7 @@
|
|
|
131
131
|
"pagefind": "^1.5.2",
|
|
132
132
|
"parse5": "^8.0.1",
|
|
133
133
|
"photoswipe": "^5.4.4",
|
|
134
|
+
"satori": "0.63.2",
|
|
134
135
|
"satteri": "^0.10.5",
|
|
135
136
|
"simple-icons": "^16.35.0",
|
|
136
137
|
"swup": "4.10.0",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
export interface SharpRawBuffer {
|
|
4
|
+
data: Buffer;
|
|
5
|
+
info: { width: number; height: number; channels: number };
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface SharpPipeline {
|
|
9
|
+
rotate: () => SharpPipeline;
|
|
10
|
+
resize: (options: Record<string, unknown>) => SharpPipeline;
|
|
11
|
+
flatten: (options: Record<string, unknown>) => SharpPipeline;
|
|
12
|
+
removeAlpha: () => SharpPipeline;
|
|
13
|
+
composite: (images: Record<string, unknown>[]) => SharpPipeline;
|
|
14
|
+
greyscale: () => SharpPipeline;
|
|
15
|
+
toColourspace: (colourspace: string) => SharpPipeline;
|
|
16
|
+
raw: () => SharpPipeline;
|
|
17
|
+
jpeg: (options: Record<string, unknown>) => SharpPipeline;
|
|
18
|
+
png: (options: Record<string, unknown>) => SharpPipeline;
|
|
19
|
+
toBuffer(): Promise<Buffer>;
|
|
20
|
+
toBuffer(options: { resolveWithObject: true }): Promise<SharpRawBuffer>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type SharpFactory = (input: Buffer) => SharpPipeline;
|
|
24
|
+
|
|
25
|
+
let cached: { factory: SharpFactory | undefined } | undefined;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* `sharp` is optional: it ships with Astro and is a dev dependency of the theme,
|
|
29
|
+
* but a project using another image service must still be able to build. It is
|
|
30
|
+
* loaded with `require` because the Vite module runner may already be closed and
|
|
31
|
+
* because a bare dynamic import would make the bundler resolve it eagerly.
|
|
32
|
+
*/
|
|
33
|
+
export function loadSharp(): SharpFactory | undefined {
|
|
34
|
+
if (cached) return cached.factory;
|
|
35
|
+
let factory: SharpFactory | undefined;
|
|
36
|
+
try {
|
|
37
|
+
const loaded = createRequire(import.meta.url)("sharp") as { default?: unknown };
|
|
38
|
+
const candidate = (loaded.default ?? loaded) as unknown;
|
|
39
|
+
if (typeof candidate === "function") factory = candidate as SharpFactory;
|
|
40
|
+
} catch {
|
|
41
|
+
factory = undefined;
|
|
42
|
+
}
|
|
43
|
+
cached = { factory };
|
|
44
|
+
return factory;
|
|
45
|
+
}
|