@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.
- package/CHANGELOG.md +26 -0
- package/CLAUDE.md +90 -219
- package/bun.lock +185 -547
- package/content/books/sample-book/index.mdx +3 -0
- package/content/posts/code-block-features-showcase.mdx +223 -0
- package/docs/ALERTS.md +112 -0
- package/docs/ARCHITECTURE.md +217 -5
- package/docs/CODE-BLOCKS.md +238 -0
- package/docs/CONTRIBUTING.md +25 -0
- package/docs/guides/README.md +11 -0
- package/docs/guides/importing-vuepress-books.md +178 -0
- package/eslint.config.mjs +18 -6
- package/package.json +42 -20
- package/scripts/generate-code-group-icons.ts +79 -0
- package/scripts/render-rst.py +207 -3
- package/scripts/sync-vuepress-book.ts +499 -0
- package/src/app/books/[slug]/{[chapter] → [...chapter]}/page.tsx +32 -10
- package/src/app/books/[slug]/page.tsx +67 -32
- package/src/app/globals.css +503 -123
- package/src/app/page.tsx +1 -1
- package/src/app/sitemap.ts +3 -3
- package/src/components/ArticleCopyCleaner.tsx +64 -0
- package/src/components/BookMobileNav.tsx +44 -50
- package/src/components/BookSidebar.tsx +0 -0
- package/src/components/CodeBlock.test.tsx +93 -8
- package/src/components/CodeBlock.tsx +39 -101
- package/src/components/CodeBlockToolbar.tsx +88 -0
- package/src/components/CodeGroup.tsx +81 -0
- package/src/components/CoverImage.tsx +1 -0
- package/src/components/ExternalLinkIcon.tsx +15 -0
- package/src/components/FeaturedStoriesSection.tsx +3 -3
- package/src/components/GithubAlert.tsx +97 -0
- package/src/components/MarkdownRenderer.test.tsx +14 -4
- package/src/components/MarkdownRenderer.tsx +144 -23
- package/src/components/Mermaid.tsx +32 -1
- package/src/components/PostList.tsx +1 -1
- package/src/components/PostNavigation.tsx +13 -2
- package/src/components/PostSidebar.tsx +13 -2
- package/src/components/RstRenderer.test.tsx +15 -15
- package/src/components/RstRenderer.tsx +37 -2
- package/src/components/Search.tsx +18 -4
- package/src/components/SeriesCatalog.tsx +1 -1
- package/src/components/ShareBar.tsx +5 -0
- package/src/components/TocPanel.tsx +10 -2
- package/src/i18n/translations.ts +2 -0
- package/src/layouts/BookLayout.tsx +35 -4
- package/src/layouts/PostLayout.tsx +5 -1
- package/src/lib/code-group-icons.test.ts +78 -0
- package/src/lib/code-group-icons.ts +148 -0
- package/src/lib/markdown.test.ts +56 -13
- package/src/lib/markdown.ts +203 -50
- package/src/lib/normalize-vuepress-math.ts +118 -0
- package/src/lib/rehype-fence-meta.ts +22 -0
- package/src/lib/remark-book-chapter-links.ts +106 -0
- package/src/lib/remark-code-group.ts +54 -0
- package/src/lib/remark-github-alerts.test.ts +83 -0
- package/src/lib/remark-github-alerts.ts +65 -0
- package/src/lib/remark-vuepress-containers.ts +130 -0
- package/src/lib/rst-renderer.ts +19 -7
- package/src/lib/rst.test.ts +212 -2
- package/src/lib/rst.ts +217 -13
- package/src/lib/shiki-rst.ts +185 -0
- package/src/lib/shiki.test.ts +153 -0
- package/src/lib/shiki.ts +292 -0
- package/src/lib/urls.ts +57 -0
- package/src/test-utils/render.ts +23 -0
- package/tests/fixtures/sync-vuepress-book/docs/.vuepress/config.js +43 -0
- package/tests/fixtures/sync-vuepress-book/docs/intro/welcome.md +7 -0
- package/tests/fixtures/sync-vuepress-book/docs/maths/linear/assets/diagram.png +1 -0
- package/tests/fixtures/sync-vuepress-book/docs/maths/linear/matrices.md +7 -0
- package/tests/fixtures/sync-vuepress-book/docs/maths/linear/vectors.md +9 -0
- package/tests/helpers/env.ts +19 -0
- package/tests/integration/book-chapter-links.test.ts +107 -0
- package/tests/integration/books-nested-toc.test.ts +176 -0
- package/tests/integration/books.test.ts +3 -2
- package/tests/integration/code-block-features.test.ts +188 -0
- package/tests/integration/code-group.test.ts +183 -0
- package/tests/integration/code-notation.test.ts +97 -0
- package/tests/integration/github-alerts.test.ts +82 -0
- package/tests/integration/markdown-external-links.test.ts +103 -0
- package/tests/integration/normalize-vuepress-math.test.ts +149 -0
- package/tests/integration/reading-time-headings.test.ts +8 -6
- package/tests/integration/series-draft.test.ts +6 -13
- package/tests/integration/sync-vuepress-book.test.ts +240 -0
- package/tests/integration/vuepress-containers.test.ts +107 -0
- package/tests/tooling/new-post.test.ts +1 -1
- 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
|
-
|
|
3
|
+
Guidance for Claude Code in this repo. Rules, gotchas, and pointers — not reference material.
|
|
4
4
|
|
|
5
|
-
## Project
|
|
5
|
+
## Project
|
|
6
6
|
|
|
7
|
-
Amytis
|
|
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
|
-
##
|
|
9
|
+
## Essential commands
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
#
|
|
13
|
-
bun
|
|
14
|
-
bun
|
|
15
|
-
|
|
16
|
-
#
|
|
17
|
-
bun
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
- `
|
|
215
|
-
- `
|
|
216
|
-
- `
|
|
217
|
-
- `
|
|
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)
|