@hutusi/amytis 1.15.0 → 1.16.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/CLAUDE.md +90 -219
  3. package/bun.lock +185 -547
  4. package/content/books/sample-book/index.mdx +3 -0
  5. package/content/posts/code-block-features-showcase.mdx +223 -0
  6. package/docs/ALERTS.md +112 -0
  7. package/docs/ARCHITECTURE.md +217 -5
  8. package/docs/CODE-BLOCKS.md +238 -0
  9. package/docs/CONTRIBUTING.md +25 -0
  10. package/docs/guides/README.md +11 -0
  11. package/docs/guides/importing-vuepress-books.md +178 -0
  12. package/eslint.config.mjs +18 -6
  13. package/package.json +42 -20
  14. package/scripts/generate-code-group-icons.ts +79 -0
  15. package/scripts/render-rst.py +207 -3
  16. package/scripts/sync-vuepress-book.ts +499 -0
  17. package/src/app/books/[slug]/{[chapter] → [...chapter]}/page.tsx +32 -10
  18. package/src/app/books/[slug]/page.tsx +67 -32
  19. package/src/app/globals.css +503 -123
  20. package/src/app/page.tsx +1 -1
  21. package/src/app/sitemap.ts +3 -3
  22. package/src/components/ArticleCopyCleaner.tsx +64 -0
  23. package/src/components/BookMobileNav.tsx +44 -50
  24. package/src/components/BookSidebar.tsx +0 -0
  25. package/src/components/CodeBlock.test.tsx +93 -8
  26. package/src/components/CodeBlock.tsx +39 -101
  27. package/src/components/CodeBlockToolbar.tsx +88 -0
  28. package/src/components/CodeGroup.tsx +81 -0
  29. package/src/components/CoverImage.tsx +1 -0
  30. package/src/components/ExternalLinkIcon.tsx +15 -0
  31. package/src/components/FeaturedStoriesSection.tsx +3 -3
  32. package/src/components/GithubAlert.tsx +97 -0
  33. package/src/components/MarkdownRenderer.test.tsx +14 -4
  34. package/src/components/MarkdownRenderer.tsx +144 -23
  35. package/src/components/Mermaid.tsx +32 -1
  36. package/src/components/PostList.tsx +1 -1
  37. package/src/components/PostNavigation.tsx +13 -2
  38. package/src/components/PostSidebar.tsx +13 -2
  39. package/src/components/RstRenderer.test.tsx +15 -15
  40. package/src/components/RstRenderer.tsx +37 -2
  41. package/src/components/Search.tsx +18 -4
  42. package/src/components/SeriesCatalog.tsx +1 -1
  43. package/src/components/ShareBar.tsx +5 -0
  44. package/src/components/TocPanel.tsx +10 -2
  45. package/src/i18n/translations.ts +2 -0
  46. package/src/layouts/BookLayout.tsx +35 -4
  47. package/src/layouts/PostLayout.tsx +5 -1
  48. package/src/lib/code-group-icons.test.ts +78 -0
  49. package/src/lib/code-group-icons.ts +148 -0
  50. package/src/lib/markdown.test.ts +56 -13
  51. package/src/lib/markdown.ts +203 -50
  52. package/src/lib/normalize-vuepress-math.ts +118 -0
  53. package/src/lib/rehype-fence-meta.ts +22 -0
  54. package/src/lib/remark-book-chapter-links.ts +106 -0
  55. package/src/lib/remark-code-group.ts +54 -0
  56. package/src/lib/remark-github-alerts.test.ts +83 -0
  57. package/src/lib/remark-github-alerts.ts +65 -0
  58. package/src/lib/remark-vuepress-containers.ts +130 -0
  59. package/src/lib/rst-renderer.ts +19 -7
  60. package/src/lib/rst.test.ts +212 -2
  61. package/src/lib/rst.ts +217 -13
  62. package/src/lib/shiki-rst.ts +185 -0
  63. package/src/lib/shiki.test.ts +153 -0
  64. package/src/lib/shiki.ts +292 -0
  65. package/src/lib/urls.ts +57 -0
  66. package/src/test-utils/render.ts +23 -0
  67. package/tests/fixtures/sync-vuepress-book/docs/.vuepress/config.js +43 -0
  68. package/tests/fixtures/sync-vuepress-book/docs/intro/welcome.md +7 -0
  69. package/tests/fixtures/sync-vuepress-book/docs/maths/linear/assets/diagram.png +1 -0
  70. package/tests/fixtures/sync-vuepress-book/docs/maths/linear/matrices.md +7 -0
  71. package/tests/fixtures/sync-vuepress-book/docs/maths/linear/vectors.md +9 -0
  72. package/tests/helpers/env.ts +19 -0
  73. package/tests/integration/book-chapter-links.test.ts +107 -0
  74. package/tests/integration/books-nested-toc.test.ts +176 -0
  75. package/tests/integration/books.test.ts +3 -2
  76. package/tests/integration/code-block-features.test.ts +188 -0
  77. package/tests/integration/code-group.test.ts +183 -0
  78. package/tests/integration/code-notation.test.ts +97 -0
  79. package/tests/integration/github-alerts.test.ts +82 -0
  80. package/tests/integration/markdown-external-links.test.ts +103 -0
  81. package/tests/integration/normalize-vuepress-math.test.ts +149 -0
  82. package/tests/integration/reading-time-headings.test.ts +8 -6
  83. package/tests/integration/series-draft.test.ts +6 -13
  84. package/tests/integration/sync-vuepress-book.test.ts +240 -0
  85. package/tests/integration/vuepress-containers.test.ts +107 -0
  86. package/tests/tooling/new-post.test.ts +1 -1
  87. package/tests/unit/static-params.test.ts +32 -19
package/CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.16.0] - 2026-06-01
9
+
10
+ ### Added
11
+ - **VuePress Book Pipeline**: Books are now first-class with nested chapter ids and TOCs, a catch-all chapter route, collapsible sections in the chapter sidebar, book-level LaTeX/math, VuePress `:::container` admonitions, and inter-chapter links. Includes a new `bun run sync-vuepress-book` importer that handles frontmatter-less chapters and stale `.md` references gracefully.
12
+ - **Shiki Syntax Highlighting**: Replaced `react-syntax-highlighter`/Prism with a build-time Shiki pipeline across both Markdown/MDX and rST. Unknown fence languages now degrade to plaintext with a build-log warning instead of crashing the build, and any bundled Shiki language is lazy-loaded on demand.
13
+ - **Code-Group Tabs**: New `:::code-group` (Markdown) and `.. code-group::` (rST) directives render fenced blocks as switchable tabs, with auto-detected icons for languages, package managers, and config files.
14
+ - **Shiki Notation Comments**: Inline `// [!code focus]`, `[!code error]`, `[!code warning]`, `[!code highlight]`, and diff markers render with visual indicators inside code blocks.
15
+ - **GitHub-Flavored Alert Blockquotes**: `> [!NOTE]`, `> [!TIP]`, `> [!IMPORTANT]`, `> [!WARNING]`, `> [!CAUTION]` render as styled callouts in both Markdown and rST (admonition visual parity).
16
+ - **Reading Meta**: Word count appears alongside reading time on post and chapter headers, with Chinese localization (`5 分钟阅读`).
17
+ - **External Link Affordance**: Outward-arrow icon on external links, opened in a new tab.
18
+ - **Code Block Polish**: Real tab icons via Iconify for code-group headers, proper-case language labels, a more prominent highlighted-line background, and rST `:linenos:` / `:emphasize-lines:` / `:caption:` support.
19
+
20
+ ### Changed
21
+ - **Mermaid Diagrams** now render frameless by default — no outer border, padding, background, or shadow. Diagram SVGs use the full column width.
22
+ - **`showChapterExcerpt`** defaults to `false` on books; opt back in per-book.
23
+ - **Markdown Rendering Fidelity**: Single-line `$$ x $$` block math now expands and parses as display math; block math centers via `.katex-display`; VuePress inline `<img>` styling is preserved through import; KaTeX `unicodeTextInMathMode` warnings (noise on CJK math labels) are scoped-silenced.
24
+ - **Dependency Refresh**: TypeScript 5.9 → 6.0, ESLint 9 → 10 (config rewritten without `eslint-config-next`), Next.js + safe minor/patch sweep, `@types/node` 24 → 25, `pdf-to-img` 5 → 6.
25
+
26
+ ### Fixed
27
+ - **Hydration Mismatches**: Locale-bound text in `TocPanel`, `PostNavigation`, `PostSidebar`, and `ShareBar` no longer logs a hydration error on first paint for users whose stored locale differs from `siteConfig.i18n.defaultLocale`. Mermaid's mutation-prone wrapper is likewise marked.
28
+ - **Table Padding**: Restored horizontal padding on prose table cells (the earlier `:where()`-based override was silently stripped by Tailwind v4 / Lightning CSS; replaced with a plain descendant selector that survives compilation).
29
+ - **rST Fallback Parser**: Added rendering for `.. figure::`, line blocks, admonitions (including custom `.. cnote::` with caption), `:doc:` / `:ref:` / `:numref:` roles; suppresses `.. toctree::` from rendered body; normalizes escaped whitespace in inline rST.
30
+ - **React 19 Warnings**: Resolved `react-hooks/set-state-in-effect` warnings and silenced image-preload warnings for local images. Eager `CoverImage` variants are no longer deprioritized.
31
+ - **Copy-Paste UX**: Stripped per-paragraph backgrounds on article copy to prevent "striping" when pasting into rich-text editors. Clipboard-API guard before `preventDefault`.
32
+ - **Misc**: `<summary>` styled as an interactive link; code-group icon lookups via `Object.hasOwn`; shiki dedup state reset in `finally`; `<style>` element scoping for shiki output; book import edge cases (URI decode, Vue component warnings, fenced blocks in container normalizer, chapter image paths relative to parent dir).
33
+
8
34
  ## [1.15.0] - 2026-04-12
9
35
 
10
36
  ### Added
package/CLAUDE.md CHANGED
@@ -1,231 +1,102 @@
1
1
  # CLAUDE.md
2
2
 
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
3
+ Guidance for Claude Code in this repo. Rules, gotchas, and pointers not reference material.
4
4
 
5
- ## Project Overview
5
+ ## Project
6
6
 
7
- Amytis is a static "digital garden" blog built with Next.js 15+ (App Router), React 19, and Tailwind CSS v4. Content is authored in MDX/Markdown files and statically generated at build time. Features include series support, multi-language (i18n), configurable themes, and comments integration.
7
+ Amytis: static digital-garden blog (Next.js 16 App Router, React 19, Tailwind v4, static export). Content in MDX / Markdown (primary), rST (legacy support); everything resolves at build time. Package manager: bun.
8
8
 
9
- ## Commands
9
+ ## Essential commands
10
10
 
11
11
  ```bash
12
- # Development
13
- bun dev # Start dev server at localhost:3000
14
- bun run lint # Run ESLint
15
-
16
- # Testing
17
- bun test # Run all tests
18
- bun run test:unit # Run unit tests (src/)
19
- bun run test:int # Run integration tests
20
- bun run test:e2e # Run end-to-end tests
21
- bun run test:mobile # Run Playwright mobile compatibility tests (requires dev server)
22
- bun test path/to/file.test.ts # Run a single test file
23
-
24
- # Build
25
- bun run build # Full production build (copies assets, builds Next.js, optimizes images)
26
- bun run build:dev # Development build (no image optimization, faster) — also regenerates Pagefind search index in public/pagefind/
27
- bun run clean # Remove .next, out, public/posts directories
28
-
29
- # Deploy
30
- bun run deploy # Deploy out/ to Linux/nginx server via rsync+sshpass (reads .env.local)
31
-
32
- # Content creation
33
- bun run new "Post Title" # Create new post
34
- bun run new-series "Series Name" # Create new series
35
- bun run new-from-pdf doc.pdf # Create post from PDF
36
- bun run new-from-images ./photos # Create post from image folder
37
- bun run new-flow # Create today's flow note
38
- bun run add-series-redirects # Add redirectFrom to all series posts (for autoPaths migration)
39
- bun run add-series-redirects <series-slug> # Add redirectFrom to one series only
40
- bun run add-series-redirects --dry-run # Preview without writing
41
- bun run new-flow-from-chat # Import all new files from imports/chats/
42
- bun run sync-book # Sync chapters list for all books from disk
43
- bun run sync-book <slug> # Sync chapters list for one book
12
+ bun dev # dev server (Turbopack, http://localhost:3000)
13
+ bun run lint # ESLint
14
+ bun test # all tests
15
+ bun run build # production build (image optimization + Pagefind)
16
+ bun run build:dev # faster build; regenerates public/pagefind/ for search-in-dev
17
+ bun run clean # nuke .next, out, public/posts when caches misbehave
44
18
  ```
45
19
 
46
- ## Design Principles
47
-
48
- - **Strict build over silent runtime failure.** This is a statically exported site — all misconfiguration must be caught at build time. Prefer `throw` in `generateStaticParams` and similar build-time functions over silent skips or `console.warn`. Examples: `validateSeriesAutoPaths` throws on slug collisions; `redirectFrom` alias conflicts should throw rather than silently producing broken redirects.
49
-
50
- ## Architecture
51
-
52
- ### Data Flow
53
-
54
- 1. **Content source**: MDX/Markdown files in `content/posts/` and `content/series/`
55
- 2. **Data layer**: `src/lib/markdown.ts` - reads files with Node `fs`, parses frontmatter with `gray-matter`, validates with Zod
56
- 3. **Static generation**: Routes use `generateStaticParams` to pre-render at build time
57
- 4. **Rendering**: `react-markdown` with remark/rehype plugins for GFM, math (KaTeX), syntax highlighting, and Mermaid diagrams
58
-
59
- ### Key Files
60
-
61
- - `site.config.ts` - Site configuration (nav, social, pagination, themes, i18n, analytics, comments)
62
- - `src/lib/markdown.ts` - Data access layer with all content query functions
63
- - `src/lib/urls.ts` - Central URL helpers (`getPostUrl`, `getPostsBasePath`, `getSeriesCustomPaths`, etc.) — always use these instead of hardcoding `/posts/[slug]`
64
- - `src/lib/search-utils.ts` - Pure search utilities (URL type detection, date extraction, title cleaning, markdown stripping) shared by `Search` and the search index route
65
- - `src/app/globals.css` - Theme CSS variables and color palettes
66
- - `src/components/MarkdownRenderer.tsx` - MDX rendering with all plugins
67
- - `src/i18n/translations.ts` - Language strings for i18n
68
-
69
- ### Route Structure
70
-
71
- - `/` - Homepage with hero, featured series, featured posts, latest writing
72
- - `/posts/[slug]` - Individual post pages
73
- - `/posts/page/[page]` - Paginated post listing
74
- - `/series` - All series overview
75
- - `/series/[slug]` - Single series with its posts
76
- - `/series/[slug]/page/[page]` - Series pagination
77
- - `/tags` - Tag cloud with post counts
78
- - `/tags/[tag]` - Posts filtered by tag
79
- - `/authors/[author]` - Posts filtered by author
80
- - `/archive` - Chronological listing grouped by year/month
81
- - `/books` - All books overview
82
- - `/books/[slug]` - Single book with chapter listing
83
- - `/books/[slug]/[chapter]` - Individual chapter page
84
- - `/flows` - Flow notes listing (paginated)
85
- - `/flows/page/[page]` - Paginated flow listing
86
- - `/flows/[year]` - Flows filtered by year
87
- - `/flows/[year]/[month]` - Flows filtered by month
88
- - `/flows/[year]/[month]/[day]` - Single flow detail page
89
- - `/[slug]` - Static pages (about, subscribe, etc.) and custom posts/series listing pages
90
- - `/[prefix]/[slug]` - Posts at custom URL prefixes (e.g. `/articles/my-post`, `/weeklies/my-post`)
91
- - `/[prefix]/page/[page]` - Paginated listing at custom URL prefixes
92
-
93
- ### Content Structure
94
-
95
- **Posts** support two formats:
96
- - **Flat file**: `content/posts/my-post.mdx`
97
- - **Nested folder**: `content/posts/my-post/index.mdx` (allows co-located images in `./images/`)
98
-
99
- **Series** live in `content/series/[slug]/index.mdx` with optional `images/` folder for cover images.
100
-
101
- **Books** live in `content/books/[slug]/` with `index.mdx` for metadata and separate `.mdx` files per chapter. The `index.mdx` frontmatter defines chapter ordering with an optional parts structure.
102
-
103
- **Flows** (daily notes) live in `content/flows/YYYY/MM/DD.md` (or `.mdx`). Each flow has a date, title, excerpt, tags, and markdown content.
104
-
105
- Date-prefixed filenames (`2026-01-01-my-post.mdx`) extract dates automatically.
106
-
107
- ## Config Files
108
-
109
- There are two config files that must be kept in sync:
110
-
111
- - **`site.config.ts`** — the live config for this repo (i18n enabled; locale-aware fields use `{ en: '...', zh: '...' }` objects)
112
- - **`site.config.example.ts`** — single-language starter template shipped via `create-amytis`; locale-aware fields use plain strings; optional features (books, flow) default to disabled
113
-
114
- **Rule:** Any schema change to `site.config.ts` (new field, renamed field, type change) must be mirrored in `site.config.example.ts`. Locale-aware values (`string | Record<string, string>`) should use plain strings in the example.
115
-
116
- ## Configuration (`site.config.ts`)
117
-
118
- Key configuration options:
119
- - `nav` - Navigation links with weights
120
- - `social` - GitHub, Twitter, email links for footer
121
- - `series.navbar` - Series slugs to show in navbar dropdown
122
- - `series.customPaths` - Per-series custom URL prefix e.g. `{ 'weeklies': 'weeklies' }` → posts at `/weeklies/[slug]`
123
- - `pagination.posts`, `pagination.series` - Items per page
124
- - `themeColor` - 'default' | 'blue' | 'rose' | 'amber'
125
- - `hero` - Homepage hero title and subtitle
126
- - `i18n` - Default locale and supported locales
127
- - `featured.series` - Scrollable series: `scrollThreshold` (default: 2), `maxItems` (default: 6)
128
- - `featured.stories` - Scrollable stories: `scrollThreshold` (default: 1), `maxItems` (default: 5)
129
- - `analytics.providers` - array of enabled providers: `['umami', 'google']`; `[]` disables analytics
130
- - `comments.provider` - 'giscus' | 'disqus' | null
131
- - `feed.format` - 'rss' | 'atom' | 'both'
132
- - `feed.content` - 'full' | 'excerpt'
133
- - `feed.maxItems` - max feed items (0 = unlimited)
134
- - `footer.bottomLinks` - custom links in the footer bottom bar (ICP, cookie policy, etc.); `text` accepts plain string or `{ en, zh }` locale map
135
- - `posts.basePath` - Custom URL prefix for all posts (default: `'posts'`); e.g. `'articles'` → posts at `/articles/[slug]`
136
- - `posts.authors.default` - Fallback authors when a post has none set in frontmatter
137
- - `posts.authors.showInHeader` - Show author byline below post title (default: true)
138
- - `posts.authors.showAuthorCard` - Show author card at end of post (default: true)
139
- - `posts.excludeFromListing` - Series slugs whose posts are hidden from `/posts` listing pages
140
- - `authors` - Per-author profiles: `bio`, `avatar` (image path), `social` (array of `{ image, description }`)
141
-
142
- ## Content Frontmatter
143
-
144
- ### Posts
145
-
146
- ```yaml
147
- ---
148
- title: "Post Title"
149
- subtitle: "Optional subtitle line" # Rendered below the title in italic
150
- date: "2026-01-01"
151
- excerpt: "Optional summary (auto-generated if omitted)"
152
- category: "Category Name"
153
- tags: ["tag1", "tag2"]
154
- authors: ["Author Name"]
155
- series: "series-slug" # Link to a series
156
- draft: true # Hidden in production
157
- featured: true # Show in featured section
158
- pinned: true # Always shown in featured section; never shuffled away (hero = most recent pinned)
159
- coverImage: "./images/cover.jpg" # Local or external URL
160
- latex: true # Enable KaTeX math
161
- toc: false # Hide table of contents
162
- layout: "simple" # Use simple layout (default: "post")
163
- externalLinks: # Links to external discussions
164
- - name: "Hacker News"
165
- url: "https://news.ycombinator.com/item?id=12345"
166
- - name: "V2EX"
167
- url: "https://v2ex.com/t/123456"
168
- redirectFrom: # Old URLs that should redirect to this post (prefix changes only)
169
- - /posts/my-old-slug
170
- - /old-series/my-old-slug
171
- ---
172
- ```
20
+ Content-creation scripts, test layout, validate pipeline → `docs/CONTRIBUTING.md`.
173
21
 
174
- ### Series (`content/series/[slug]/index.mdx`)
175
-
176
- ```yaml
177
- ---
178
- title: "Series Title"
179
- excerpt: "Series description"
180
- date: "2026-01-01"
181
- coverImage: "./images/cover.jpg"
182
- featured: true # Show in featured series
183
- draft: true # Hidden in production (default: false)
184
- sort: "date-asc" # 'date-asc' | 'date-desc' | 'manual'
185
- posts: ["post-1", "post-2"] # Manual post ordering (optional)
186
- ---
187
- ```
22
+ ## Architecture map
188
23
 
189
- ### Books (`content/books/[slug]/index.mdx`)
190
-
191
- ```yaml
192
- ---
193
- title: "Book Title"
194
- excerpt: "Book description"
195
- date: "2026-01-01"
196
- coverImage: "text:DG" # Cover image or text placeholder
197
- featured: true # Show in featured books
198
- draft: false
199
- authors: ["Author Name"]
200
- chapters:
201
- - part: "Part I: Getting Started" # Optional part grouping
202
- chapters:
203
- - title: "Chapter Title"
204
- id: "chapter-file" # Maps to chapter-file.mdx or chapter-file/index.mdx
205
- - part: "Part II: Advanced"
206
- chapters:
207
- - title: "Another Chapter"
208
- id: "another-chapter"
209
- ---
210
- ```
24
+ Quick "where do routes live" lookup. Full reference: `docs/ARCHITECTURE.md`.
25
+
26
+ - Standard routes follow folder names under `src/app/`: `/posts`, `/series`, `/tags`, `/notes`, `/books`, `/authors`, `/archive`, `/graph`, `/flows/[year]/[month]/[day]`.
27
+ - **Top-level `[slug]` and `[slug]/[postSlug]`** resolve `redirectFrom` aliases and `series.customPaths` — highest-risk dynamic surface; touch with care.
28
+ - Feeds at `feed.xml` / `feed.atom` / `all.xml` / `all.atom` / `flows/feed.{xml,atom}`; sitemap at `sitemap.ts`; search index at `search.json`.
29
+ - Rendering pipeline lives in `src/lib/`: Shiki (`shiki.ts`, `shiki-rst.ts`), remark/rehype plugins (`remark-github-alerts`, `remark-wikilinks`, `remark-code-group`, `rehype-fence-meta`, `rehype-image-metadata`), redirects (`series-redirects.ts`), feeds/JSON-LD (`feed-utils.ts`, `json-ld.ts`).
30
+
31
+ ## Design principles
32
+
33
+ - **Strict build over silent runtime failure.** Static export means misconfiguration must fail at build time. Use `throw` in `generateStaticParams` and similar — never silent skips or `console.warn`. Precedent: `validateSeriesAutoPaths` throws on slug collisions; `redirectFrom` alias conflicts (reserved slug or duplicate) should also throw, not produce broken redirects.
34
+ - **Exception**: fence-language resolution in `src/lib/shiki.ts` deliberately degrades to `plaintext` + a deduped `console.warn` instead of throwing. Authors can't reliably predict Shiki's alias coverage, and "typo" vs "legitimate community alias" are indistinguishable from our side, so production deploys shouldn't fail on a single unhighlighted code block. Real typos still surface via the warn output in local/CI build logs.
35
+ - **Validate author input at build time; keep content portable.** Frontmatter via Zod; slug / redirect conflicts throw. Files on disk stay valid `.md` / `.mdx` / `.rst` — no Amytis-specific syntax that breaks other tools.
36
+
37
+ ## Integration-point rules (always go through X)
38
+
39
+ - **URLs:** always `getPostUrl()` / `getPostsBasePath()` / `getSeriesCustomPaths()` from `src/lib/urls.ts`. Never hardcode `/posts/[slug]` — posts may live at `/articles/[slug]`, custom prefixes (`series.customPaths`), or under `posts.basePath`.
40
+ - **Content reads:** always via `src/lib/markdown.ts` (`getAllPosts`, `getPostBySlug`, `getSeriesData`, etc.). Frontmatter validation belongs in its Zod schemas, not in route files.
41
+ - **Search utilities:** pure helpers (URL type detection, date extraction, title cleaning, markdown stripping) live in `src/lib/search-utils.ts` and are shared by the `Search` component and the search-index route. Don't duplicate them.
42
+ - **i18n strings:** add to `src/i18n/translations.ts`. Locale-aware config fields are `string | Record<string, string>`; resolve via `resolveLocale()` / `resolveLocaleValue()` from `src/lib/i18n.ts`.
43
+
44
+ ## Config sync
45
+
46
+ `site.config.ts` (this repo — i18n object form, `{ en, zh }`) and `site.config.example.ts` (shipped via `create-amytis` — plain strings, single-locale, optional features default disabled) must stay in sync. Any schema change to one must be mirrored in the other.
47
+
48
+ ## Gotchas (things Claude will get wrong on first try)
49
+
50
+ - **`turbopackIgnore` on fs reads.** Any `fs.readFileSync()` path expression must be preceded by `/* turbopackIgnore: true */` (see `src/lib/markdown.ts`, `src/lib/rehype-image-metadata.ts`). Missing it causes incorrect bundling.
51
+ - **No AVIF for `coverImage`.** Upstream bug in `next-image-export-optimizer` emits `.webp` files but a `srcset` pointing at `.avif` → 404 in prod. Use `.jpg` / `.png` / `.webp`. See `docs/TROUBLESHOOTING.md`.
52
+ - **Unicode slugs.** Dynamic route pages call `safeDecodeParam()` and try decoded / raw / NFC / NFD variants — don't shortcut with bare `decodeURIComponent()` (it throws on malformed input). When touching dynamic routes, verify both ASCII and Unicode slugs.
53
+ - **`generateStaticParams` returns raw values.** Don't `encodeURIComponent` route params; Next.js handles encoding. Don't link to placeholder routes like `/posts/[slug]` — always link to concrete URLs.
54
+ - **Series format is locked.** A series index can be `index.md` / `.mdx` / `README.md` / `README.mdx` / `index.rst` / `README.rst` (first match wins). All child posts must match. Mixing formats is a build error.
55
+ - **rST needs Python `docutils`.** Set `AMYTIS_RST_PYTHON=/path/to/python` if not on `$PATH`. Without it, falls back to a lower-fidelity built-in parser.
56
+ - **Pagefind index.** `bun run build:dev` regenerates `public/pagefind/`; search returns stale results until you rerun it after content changes.
57
+ - **`trailingSlash: true` is load-bearing.** Lets co-located post assets (`posts/slug/images/`) coexist with `posts/slug/index.html`. Don't flip it in `next.config.ts`.
58
+ - **Shiki highlighter is a `globalThis` singleton.** Never instantiate it per render — `createHighlighter` loads Oniguruma WASM + grammars (~1–2 s). See `src/lib/shiki.ts`.
59
+ - **rST sanitize-html allowlist must keep `style` + `data-*` on `pre`/`code`/`span`/`div`.** Stripping any of these silently kills Shiki output (monochrome text in prod, looks fine locally because dev rST isn't sanitized). See `src/components/RstRenderer.tsx`.
60
+ - **Bump `RST_RENDERER_DISK_CACHE_VERSION` (`src/lib/rst-renderer.ts`) on highlighter-output changes.** Stale on-disk caches in `.cache/rst-renderer/` will serve old markup otherwise. Run `rm -rf .cache/rst-renderer` after pulling such a change.
61
+ - **Fence meta needs `rehype-fence-meta` BEFORE `rehype-raw`.** `mdast-util-to-hast` stores fence meta on `node.data.meta`, which `rehype-raw` drops during HTML round-trip. The plugin copies it to a real `data-meta` attribute first. Order matters in `MarkdownRenderer.tsx`.
62
+ - **Code-group tabs add `<input type="radio">` + `<label>` to the rST sanitize-html allowlist.** Keep the `transformTags` guard in `RstRenderer.tsx` that strips any `<input>` whose `type !== "radio"` — that's the defense against an rST author injecting password/file/etc. inputs through raw HTML.
63
+ - **GitHub alerts (`> [!NOTE]`, etc.) need the custom `remarkGithubAlerts` plugin.** `remark-gfm` v4 does NOT transform `[!TYPE]` blockquotes — they pass through as plain blockquotes with the literal marker visible. The custom plugin in `src/lib/remark-github-alerts.ts` is what detects them and routes to `<GithubAlert>`. If a future remark-gfm adds native alert support, that's a regression to watch for (covered by an integration test).
64
+ - **Single-line block math `$$ x $$` is silently inline.** `micromark-extension-math` (under `remark-math` v6) requires the `$$` markers on their own lines — single-line collapses to *inline* math (no `katex-display` wrapper, no centering, no display margin) and looks like a subtly under-styled formula. `src/lib/normalize-vuepress-math.ts` expands single-line `$$ x $$` to opener / body / closer before parsing, so authored content stays portable. If a chapter formula stops centering, suspect the normalizer's regex first.
65
+
66
+ ## Development workflow
67
+
68
+ - **Don't push or open PRs unless asked.** Local commits are fine; anything touching the remote needs explicit go-ahead.
69
+ - **Commit in focused slices.** Keep `bun run validate` green at each commit so the branch stays bisectable.
70
+ - **Tests + docs in the same change.** Update `docs/ARCHITECTURE.md` / `docs/CONTRIBUTING.md` / `docs/TROUBLESHOOTING.md` alongside seam/workflow/invariant changes — not as a follow-up.
71
+ - **Commits** follow Conventional Commits: `feat | fix | refactor | perf | chore | docs | test | release`. Subject under ~70 chars; body explains *why*.
72
+ - **Branches:** `<type>/<kebab-slug>` matching commit prefixes.
73
+ - **No Claude attribution.** Do not add `Co-Authored-By: Claude ...` trailers to commit messages or the `🤖 Generated with [Claude Code]` footer to PR descriptions. The default templates in the harness include both — strip them.
74
+
75
+ ## Verifying a change
76
+
77
+ - **Default verify loop: `bun run lint && bun run test` only.** Stop here.
78
+ - **Do NOT run `bun run build:dev` (or `bun run build` / `bun run validate`) unless the user asks OR it is genuinely needed.** It takes ~1–2 minutes (Turbopack compile + 800+ static pages + Pagefind index) and the cost adds up fast across many small steps. None of these are "needed" on their own: markdown pipeline edits, schema changes, route moves, branch tip, pre-PR readiness, "just to be safe."
79
+ - "Genuinely needed" means lint+test **cannot** catch the failure mode you're worried about — e.g. a strict-build invariant that only fires during static export (a `throw` in `generateStaticParams`, a `redirectFrom` collision, a Zod-schema failure that only triggers on real content). If unsure, prefer asking over running.
80
+ - `bun run validate` chains lint + test + build:dev; same rule — same bar.
81
+ - Touched `src/lib/markdown.ts`, `src/lib/urls.ts`, or `site.config.ts`? Add an integration test under `tests/integration/`.
82
+ - Touched any dynamic route? Verify both ASCII and Unicode slugs render.
83
+
84
+ ## Context compression hints
85
+
86
+ When compressing history, preserve in priority order:
87
+
88
+ 1. **Build-time invariants touched** — strict-build/throw boundaries, Zod schemas, `site.config.ts` shape, helpers in `src/lib/urls.ts` / `src/lib/markdown.ts`.
89
+ 2. **Modified files and why** — file list with one-line reasons, not the diff.
90
+ 3. **Verification status** — `bun run lint && bun test` (and `build:dev` if run) pass/fail.
91
+ 4. **Cache version bumps** — `RST_RENDERER_DISK_CACHE_VERSION` etc.; easy to lose after compression.
92
+ 5. **Open TODOs and rollback notes.**
93
+ 6. **Tool output**: drop; keep pass/fail summary only.
94
+
95
+ ## Where to find more
211
96
 
212
- ## Key Components
213
-
214
- - `PostLayout` / `SimpleLayout` - Post page layouts with TOC, series sidebar, external links, comments
215
- - `Hero` - Configurable homepage hero section with collapsible intro
216
- - `HorizontalScroll` - Scrollable container with navigation arrows for featured content
217
- - `PostList` - Card-based post listing with thumbnails, metadata, excerpts, and tags
218
- - `SeriesCatalog` - Timeline-style series post listing with numbered entries and progress indicator
219
- - `SeriesSidebar` - Series navigation sidebar with progress bar and color-coded states
220
- - `SeriesList` - Mobile-optimized series navigation matching sidebar design
221
- - `Search` - Full-text search (Cmd/Ctrl+K) powered by Pagefind (build-time index); features type filter tabs (All/Post/Flow/Book), recent searches, keyboard navigation (arrows + number keys), debounced input, body scroll lock, focus trap, ARIA accessibility, and search syntax tips (`"phrase"`, `-exclude`)
222
- - `TableOfContents` - Sticky TOC with scroll tracking, reading progress, and back-to-top
223
- - `MarkdownRenderer` - MDX rendering with GFM, math, syntax highlighting, diagrams
224
- - `CoverImage` - Optimized image component with WebP support
225
- - `Comments` - Giscus or Disqus integration (theme-aware)
226
- - `Analytics` - Umami, Plausible, or Google Analytics integration
227
- - `FlowContent` - Client wrapper for flow pages with tag filtering state management
228
- - `FlowCalendarSidebar` - Calendar sidebar with date navigation, browse panel, and clickable tag filters
229
- - `FlowTimelineEntry` - Individual flow entry in timeline list
230
- - `LanguageSwitch` - i18n language selector
231
- - `ThemeToggle` - Light/dark mode toggle
97
+ - `docs/ARCHITECTURE.md` — route map, content model, components, data layer, frontmatter schemas, full configuration reference
98
+ - `docs/CONTRIBUTING.md` — full command list, test layout, content-creation scripts
99
+ - `docs/TROUBLESHOOTING.md` known issues (AVIF, dev-mode browser-extension CSP/SharedStorage noise)
100
+ - `docs/deployment.md` production deploy steps
101
+ - `docs/guides/` — task-oriented walkthroughs (e.g. `importing-vuepress-books.md`)
102
+ - `site.config.ts` live config (read it directly; don't infer from this file)