@hutusi/amytis 1.14.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/.github/workflows/ci.yml +1 -1
- package/.github/workflows/publish.yml +2 -2
- package/CHANGELOG.md +42 -0
- package/CLAUDE.md +90 -219
- package/README.md +33 -1
- package/README.zh.md +33 -1
- package/TODO.md +10 -0
- package/bun.lock +205 -539
- package/content/books/sample-book/index.mdx +3 -0
- package/content/posts/code-block-features-showcase.mdx +223 -0
- package/content/series/rst-legacy/deeper-notes/images/test.svg +4 -0
- package/content/series/rst-legacy/deeper-notes/index.rst +15 -0
- package/content/series/rst-legacy/getting-started.rst +24 -0
- package/content/series/rst-legacy/index.rst +9 -0
- package/content/series/rst-readme/README.rst +9 -0
- package/content/series/rst-readme/readme-index-post.rst +10 -0
- package/content/series/rst-toctree/first-post.rst +6 -0
- package/content/series/rst-toctree/index.rst +10 -0
- package/content/series/rst-toctree/second-post.rst +6 -0
- package/content/series/rst-toctree-precedence/first-post.rst +6 -0
- package/content/series/rst-toctree-precedence/index.rst +12 -0
- package/content/series/rst-toctree-precedence/second-post.rst +6 -0
- package/docs/ALERTS.md +112 -0
- package/docs/ARCHITECTURE.md +239 -8
- package/docs/CODE-BLOCKS.md +238 -0
- package/docs/CONTRIBUTING.md +36 -0
- package/docs/guides/README.md +11 -0
- package/docs/guides/importing-vuepress-books.md +178 -0
- package/eslint.config.mjs +20 -6
- package/next.config.ts +2 -2
- package/package.json +52 -24
- package/packages/create-amytis/package.json +1 -1
- package/packages/create-amytis/src/index.test.ts +43 -1
- package/packages/create-amytis/src/index.ts +64 -8
- package/public/next-image-export-optimizer-hashes.json +14 -73
- package/scripts/build-pagefind.ts +172 -0
- package/scripts/copy-assets.ts +246 -56
- package/scripts/generate-code-group-icons.ts +79 -0
- package/scripts/generate-knowledge-graph.ts +2 -1
- package/scripts/render-rst.py +923 -0
- package/scripts/run-with-rst-python.ts +42 -0
- package/scripts/sync-vuepress-book.ts +499 -0
- package/src/app/[slug]/[postSlug]/page.tsx +20 -10
- package/src/app/[slug]/page/[page]/page.tsx +15 -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 +639 -94
- package/src/app/page.tsx +1 -1
- package/src/app/series/[slug]/page/[page]/page.tsx +74 -6
- package/src/app/series/[slug]/page.tsx +11 -13
- package/src/app/series/page.tsx +3 -3
- package/src/app/sitemap.ts +3 -3
- package/src/components/ArticleCopyCleaner.tsx +64 -0
- package/src/components/AuthorCard.tsx +25 -16
- 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 +6 -2
- 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 +30 -4
- package/src/components/MarkdownRenderer.tsx +148 -24
- 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 +93 -0
- package/src/components/RstRenderer.tsx +157 -0
- 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 +10 -2
- package/src/layouts/SimpleLayout.tsx +10 -3
- package/src/lib/code-group-icons.test.ts +78 -0
- package/src/lib/code-group-icons.ts +148 -0
- package/src/lib/image-utils.test.ts +19 -0
- package/src/lib/image-utils.ts +11 -0
- package/src/lib/markdown.test.ts +195 -14
- package/src/lib/markdown.ts +928 -254
- package/src/lib/normalize-vuepress-math.ts +118 -0
- package/src/lib/rehype-fence-meta.ts +22 -0
- package/src/lib/rehype-image-metadata.ts +2 -2
- 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.test.ts +355 -0
- package/src/lib/rst-renderer.ts +629 -0
- package/src/lib/rst.test.ts +350 -0
- package/src/lib/rst.ts +674 -0
- package/src/lib/series-redirects.ts +42 -0
- 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/feed-utils.test.ts +13 -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 +12 -14
- package/tests/integration/series-draft.test.ts +12 -5
- package/tests/integration/series.test.ts +93 -0
- package/tests/integration/sync-vuepress-book.test.ts +240 -0
- package/tests/integration/vuepress-containers.test.ts +107 -0
- package/tests/tooling/build-pagefind.test.ts +66 -0
- package/tests/tooling/new-post.test.ts +1 -1
- package/tests/unit/static-params.test.ts +166 -13
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
# Architecture Overview
|
|
2
2
|
|
|
3
|
-
Amytis is a static-export-first Next.js 16 App Router project for Markdown/MDX publishing across posts, series, books, flows, and notes.
|
|
3
|
+
Amytis is a static-export-first Next.js 16 App Router project for Markdown/MDX publishing across posts, series, books, flows, and notes, with optional series-scoped rST support for legacy content.
|
|
4
4
|
|
|
5
5
|
## Core Stack
|
|
6
6
|
|
|
7
|
-
- Framework: Next.js 16.1
|
|
7
|
+
- Framework: Next.js 16.2.1 + React 19
|
|
8
8
|
- Runtime/tooling: Bun
|
|
9
9
|
- Styling: Tailwind CSS v4 + CSS variables + `next-themes`
|
|
10
10
|
- Content parsing: `gray-matter` + Zod validation in `src/lib/markdown.ts`
|
|
11
|
+
- rST rendering: Python `docutils` bridge in `scripts/render-rst.py` plus normalization in `src/lib/rst-renderer.ts`
|
|
11
12
|
- Search: Pagefind (`/pagefind/pagefind.js` loaded at runtime)
|
|
12
13
|
- Tests: Bun test suites in `src/` and `tests/`
|
|
13
14
|
|
|
14
15
|
## Content Model
|
|
15
16
|
|
|
16
17
|
- `content/posts/`: standalone posts (`.md/.mdx`) and folder posts (`index.mdx`)
|
|
17
|
-
- `content/series/<slug>/`: series metadata (`index.mdx`) + series posts
|
|
18
|
+
- `content/series/<slug>/`: series metadata (`index.mdx` / `index.md` / `README.mdx` / `README.md` / `index.rst` / `README.rst`) + series posts in the same format
|
|
18
19
|
- `content/books/<slug>/`: book metadata + chapter files
|
|
19
20
|
- `content/flows/YYYY/MM/DD.(md|mdx)`: daily flow entries
|
|
20
21
|
- `content/notes/`: evergreen notes
|
|
@@ -27,6 +28,8 @@ Amytis is a static-export-first Next.js 16 App Router project for Markdown/MDX p
|
|
|
27
28
|
3. Draft/future filtering and sorting are applied (based on `site.config.ts`).
|
|
28
29
|
4. Route files consume typed helpers (`getAllPosts`, `getBookData`, `getAllFlows`, `getAllNotes`, etc.).
|
|
29
30
|
5. `generateStaticParams()` precomputes dynamic routes for static export.
|
|
31
|
+
6. Series content format is inferred from the series index file; ambiguous or mixed-format series fail fast during content loading.
|
|
32
|
+
7. When `docutils` is available, rST files are rendered to HTML through the Python bridge; if the Python runtime is unavailable, Amytis falls back to the lightweight built-in rST compatibility path.
|
|
30
33
|
|
|
31
34
|
## Route Map (App Router)
|
|
32
35
|
|
|
@@ -75,21 +78,61 @@ src/app/
|
|
|
75
78
|
## URL Routing Rules
|
|
76
79
|
|
|
77
80
|
- `next.config.ts` sets `output: "export"` and `trailingSlash: true`.
|
|
81
|
+
- Series format is inferred from the index file:
|
|
82
|
+
- Markdown series: `index.md`, `index.mdx`, `README.md`, or `README.mdx`
|
|
83
|
+
- rST series: `index.rst` or `README.rst`
|
|
84
|
+
- A series may not mix Markdown and rST content files; ambiguous or mixed layouts are treated as build errors.
|
|
78
85
|
- Post URLs use `getPostUrl()` in `src/lib/urls.ts`:
|
|
79
86
|
- Default: `/<posts.basePath>/<post.slug>` (basePath defaults to `posts`)
|
|
80
87
|
- Series auto path: `/<series.slug>/<post.slug>` when `series.autoPaths` is enabled
|
|
81
88
|
- Series override: `/<series.customPaths[seriesSlug]>/<post.slug>`
|
|
89
|
+
- Legacy aliases declared in frontmatter `redirectFrom` are emitted as static redirect pages, so old URLs can continue resolving after a rename or path migration.
|
|
90
|
+
- Dynamic route handlers validate whether a request is canonical or legacy, then either render the content or return `RedirectPage`.
|
|
82
91
|
- Dynamic route params should return raw segment values from `generateStaticParams()` (do not pre-encode values).
|
|
83
92
|
- Links should always target concrete paths, not route placeholders such as `/posts/[slug]`.
|
|
84
93
|
- When moving series posts off the default posts path, `scripts/add-series-redirects.ts` updates frontmatter `redirectFrom` entries so static redirect pages can be generated.
|
|
85
94
|
|
|
86
95
|
## Key Components
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
97
|
+
Layout & navigation:
|
|
98
|
+
|
|
99
|
+
- `Navbar`, `Footer`, `Hero` (configurable homepage hero with collapsible intro)
|
|
100
|
+
- `LanguageSwitch` (i18n language selector), `ThemeToggle` (light/dark mode)
|
|
101
|
+
- `FlowHubTabs`
|
|
102
|
+
|
|
103
|
+
Content renderers:
|
|
104
|
+
|
|
105
|
+
- `MarkdownRenderer` — MDX with GFM, KaTeX math, build-time syntax highlighting via Shiki, Mermaid
|
|
106
|
+
- `CodeBlock` (async server component, calls Shiki) and `CodeBlockToolbar` (client: copy + word-wrap toggle), `Mermaid`
|
|
107
|
+
- `CoverImage` — optimized image component with WebP support
|
|
108
|
+
|
|
109
|
+
Post & series surfaces:
|
|
110
|
+
|
|
111
|
+
- `PostLayout` / `SimpleLayout` — post page layouts with TOC, series sidebar, external links, comments
|
|
112
|
+
- `PostList` — card-based post listing with thumbnails, metadata, excerpts, tags
|
|
113
|
+
- `PostCard`, `PostSidebar`, `RelatedPosts`, `ShareBar`
|
|
114
|
+
- `SeriesCatalog` — timeline-style listing with numbered entries and progress indicator
|
|
115
|
+
- `SeriesSidebar` — series navigation sidebar with progress bar and color-coded states
|
|
116
|
+
- `SeriesList` — mobile-optimized series navigation matching sidebar design
|
|
117
|
+
- `TableOfContents` — sticky TOC with scroll tracking, reading progress, back-to-top
|
|
118
|
+
- `HorizontalScroll` — scrollable container with navigation arrows for featured content
|
|
119
|
+
|
|
120
|
+
Notes & flows:
|
|
121
|
+
|
|
122
|
+
- `NoteSidebar`, `TagContentTabs`
|
|
123
|
+
- `FlowContent` — client wrapper for flow pages with tag filtering state
|
|
124
|
+
- `FlowCalendarSidebar` — calendar sidebar with date navigation, browse panel, clickable tag filters
|
|
125
|
+
- `FlowTimelineEntry` — individual flow entry in timeline list
|
|
126
|
+
|
|
127
|
+
Search & discovery:
|
|
128
|
+
|
|
129
|
+
- `Search` — full-text search (Cmd/Ctrl+K) powered by Pagefind; type filter tabs (All/Post/Flow/Book), recent searches, keyboard navigation, debounced input, focus trap, ARIA, search syntax (`"phrase"`, `-exclude`)
|
|
130
|
+
- `Pagination`, `KnowledgeGraph`
|
|
131
|
+
|
|
132
|
+
Integrations:
|
|
133
|
+
|
|
134
|
+
- `Comments` — Giscus or Disqus (theme-aware)
|
|
135
|
+
- `Analytics` — Umami, Plausible, or Google Analytics
|
|
93
136
|
|
|
94
137
|
## Data Layer Highlights (`src/lib/markdown.ts`)
|
|
95
138
|
|
|
@@ -99,6 +142,25 @@ src/app/
|
|
|
99
142
|
- Notes: `getAllNotes`, `getNoteBySlug`, `getNotesByTag`
|
|
100
143
|
- Discovery: `buildSlugRegistry`, `getBacklinks`, `getAllTags`, `getAllAuthors`
|
|
101
144
|
|
|
145
|
+
## Code Block Highlighting
|
|
146
|
+
|
|
147
|
+
- Highlighter: **Shiki** (build-time, dual `github-light` / `github-dark` theme via CSS variables). See `docs/CODE-BLOCKS.md` for author-facing fence/directive metadata.
|
|
148
|
+
- Singleton lives at `src/lib/shiki.ts` (`getHighlighter()`, cached on `globalThis` for HMR). It exposes `highlightToHast(code, language, opts)` and `parseFenceMeta(meta)`.
|
|
149
|
+
- Transformers: `transformerMetaHighlight` from `@shikijs/transformers` plus three custom transformers in `src/lib/shiki.ts` for the line-numbers data attribute, the title data attribute, and raw-`diff` line backgrounds.
|
|
150
|
+
- MDX/Markdown path: `MarkdownRenderer.tsx` → `rehype-fence-meta` (copies `node.data.meta` to a real `data-meta` HTML attribute so it survives `react-markdown`'s prop filtering and the `rehypeRaw` round-trip) → `CodeBlock` (async server component) → Shiki → inline HTML. Mermaid blocks are short-circuited before `CodeBlock` is reached.
|
|
151
|
+
- rST path: `scripts/render-rst.py` rewrites every `literal_block` into a `<pre data-amytis-code …>` marker carrying option attributes (`data-language`, `data-line-numbers`, `data-highlight-lines`, `data-title`); `src/lib/shiki-rst.ts` walks the rendered HTML inside `RstRenderer` (async server component) and replaces each marker with Shiki output. The fallback rST parser routes through `rstToMarkdown` and lands in the MDX path — single highlighter, single source of truth.
|
|
152
|
+
- Cache: `RST_RENDERER_DISK_CACHE_VERSION` in `src/lib/rst-renderer.ts` must be bumped whenever the docutils output shape or Shiki theme changes, otherwise stale cached entries in `.cache/rst-renderer/` keep rendering with the old markup.
|
|
153
|
+
|
|
154
|
+
## rST Notes
|
|
155
|
+
|
|
156
|
+
- Full-fidelity rST rendering depends on a Python environment with `docutils` (and ideally `pygments`) available.
|
|
157
|
+
- `src/lib/rst-renderer.ts` uses `AMYTIS_RST_PYTHON` when set; otherwise it falls back to `python3`.
|
|
158
|
+
- Top-of-document docinfo is parsed into Amytis metadata, but it is stripped from rendered article HTML so blog-style posts do not show duplicate author/version blocks above the content.
|
|
159
|
+
- Supported legacy roles are normalized or degraded intentionally:
|
|
160
|
+
- `:doc:` resolves to local site URLs when the target exists in the imported content tree
|
|
161
|
+
- `:ref:` / `:numref:` prefer local anchors
|
|
162
|
+
- unresolved legacy roles degrade to readable inline HTML instead of docutils system-message blocks
|
|
163
|
+
|
|
102
164
|
## Build Pipeline
|
|
103
165
|
|
|
104
166
|
1. `bun scripts/copy-assets.ts`: copy co-located media into `public/`
|
|
@@ -108,3 +170,172 @@ src/app/
|
|
|
108
170
|
5. Pagefind indexing:
|
|
109
171
|
- Production: `pagefind --site out` (writes to `out/pagefind`)
|
|
110
172
|
- Dev build: `pagefind --site out --output-path public/pagefind`
|
|
173
|
+
|
|
174
|
+
## Content Frontmatter
|
|
175
|
+
|
|
176
|
+
Validated by Zod in `src/lib/markdown.ts` — invalid frontmatter throws at build time.
|
|
177
|
+
|
|
178
|
+
### Posts
|
|
179
|
+
|
|
180
|
+
```yaml
|
|
181
|
+
---
|
|
182
|
+
title: "Post Title"
|
|
183
|
+
subtitle: "Optional subtitle line" # Rendered below the title in italic
|
|
184
|
+
date: "2026-01-01"
|
|
185
|
+
excerpt: "Optional summary (auto-generated if omitted)"
|
|
186
|
+
category: "Category Name"
|
|
187
|
+
tags: ["tag1", "tag2"]
|
|
188
|
+
authors: ["Author Name"]
|
|
189
|
+
series: "series-slug" # Link to a series
|
|
190
|
+
draft: true # Hidden in production
|
|
191
|
+
featured: true # Show in featured section
|
|
192
|
+
pinned: true # Always shown in featured section; hero = most recent pinned
|
|
193
|
+
coverImage: "./images/cover.jpg" # Local path, external URL, or text placeholder
|
|
194
|
+
latex: true # Enable KaTeX math
|
|
195
|
+
toc: false # Hide table of contents
|
|
196
|
+
layout: "simple" # Use simple layout (default: "post")
|
|
197
|
+
externalLinks: # Links to external discussions
|
|
198
|
+
- name: "Hacker News"
|
|
199
|
+
url: "https://news.ycombinator.com/item?id=12345"
|
|
200
|
+
- name: "V2EX"
|
|
201
|
+
url: "https://v2ex.com/t/123456"
|
|
202
|
+
redirectFrom: # Old URLs to redirect to this post (prefix changes only)
|
|
203
|
+
- /posts/my-old-slug
|
|
204
|
+
- /old-series/my-old-slug
|
|
205
|
+
---
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Series (`content/series/[slug]/index.mdx`)
|
|
209
|
+
|
|
210
|
+
```yaml
|
|
211
|
+
---
|
|
212
|
+
title: "Series Title"
|
|
213
|
+
excerpt: "Series description"
|
|
214
|
+
date: "2026-01-01"
|
|
215
|
+
coverImage: "./images/cover.jpg"
|
|
216
|
+
featured: true # Show in featured series
|
|
217
|
+
draft: true # Hidden in production (default: false)
|
|
218
|
+
sort: "date-asc" # 'date-asc' | 'date-desc' | 'manual'
|
|
219
|
+
posts: ["post-1", "post-2"] # Manual post ordering (optional)
|
|
220
|
+
---
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Books (`content/books/[slug]/index.mdx`)
|
|
224
|
+
|
|
225
|
+
A book's `chapters:` array accepts three item shapes (mix freely):
|
|
226
|
+
|
|
227
|
+
```yaml
|
|
228
|
+
---
|
|
229
|
+
title: "Book Title"
|
|
230
|
+
excerpt: "Book description"
|
|
231
|
+
date: "2026-01-01"
|
|
232
|
+
coverImage: "text:DG" # Image path or text placeholder
|
|
233
|
+
featured: true
|
|
234
|
+
draft: false
|
|
235
|
+
authors: ["Author Name"]
|
|
236
|
+
chapters:
|
|
237
|
+
# 1. Bare chapter ref — top-level chapter with no grouping
|
|
238
|
+
- title: "Standalone Chapter"
|
|
239
|
+
id: "standalone"
|
|
240
|
+
|
|
241
|
+
# 2. Legacy "part" — single-level grouping
|
|
242
|
+
- part: "Part I: Getting Started"
|
|
243
|
+
chapters:
|
|
244
|
+
- title: "Chapter Title"
|
|
245
|
+
id: "chapter-file" # Maps to chapter-file.mdx or chapter-file/index.mdx
|
|
246
|
+
|
|
247
|
+
# 3. "section" — recursive grouping with arbitrary nesting depth (≥ 2 layers)
|
|
248
|
+
- section: "机器学习数学基础"
|
|
249
|
+
collapsible: true # Optional UI hint for the sidebar
|
|
250
|
+
items:
|
|
251
|
+
- section: "线性代数"
|
|
252
|
+
items:
|
|
253
|
+
- title: "引言:机器学习的语言"
|
|
254
|
+
id: "maths/linear/introduction" # Slash-separated id → nested folder on disk
|
|
255
|
+
- title: "向量基础"
|
|
256
|
+
id: "maths/linear/vectors"
|
|
257
|
+
- section: "微积分"
|
|
258
|
+
items:
|
|
259
|
+
- title: "引言:变化与累积"
|
|
260
|
+
id: "maths/calculus/introduction"
|
|
261
|
+
---
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Chapter `id` values may contain `/` to map to nested folders. For id `maths/linear/introduction`,
|
|
265
|
+
the loader resolves to the first existing file under `<bookDir>/maths/linear/introduction.{md,mdx}`
|
|
266
|
+
or `<bookDir>/maths/linear/introduction/index.{md,mdx}`. Path traversal (`..`) is rejected.
|
|
267
|
+
|
|
268
|
+
Per the strict-build invariant, `getBookData` throws if any chapter id in the TOC has no
|
|
269
|
+
matching file on disk — silent skips are not allowed.
|
|
270
|
+
|
|
271
|
+
#### Book-level `latex: true`
|
|
272
|
+
|
|
273
|
+
Book frontmatter accepts an optional `latex: true` flag that enables KaTeX rendering for
|
|
274
|
+
every chapter in the book without having to annotate each chapter file. Chapter-level
|
|
275
|
+
`latex: true` still works and takes precedence. Math-heavy books (e.g. ML textbooks) should
|
|
276
|
+
set the book-level flag rather than copy it onto every chapter.
|
|
277
|
+
|
|
278
|
+
#### Book-level `showChapterExcerpt`
|
|
279
|
+
|
|
280
|
+
Book frontmatter accepts an optional `showChapterExcerpt` flag (default `false`)
|
|
281
|
+
controlling whether the chapter-page header renders the chapter's `excerpt` underneath
|
|
282
|
+
the title. The default suppresses it because the common case is a chapter that opens
|
|
283
|
+
with its own lede paragraph, and an excerpt line above it just duplicates that text.
|
|
284
|
+
Set it to `true` on books where the excerpt is a distinct subtitle the author actually
|
|
285
|
+
wants the reader to see. The excerpt is still used in metadata (OpenGraph, JSON-LD,
|
|
286
|
+
search) regardless of this flag.
|
|
287
|
+
|
|
288
|
+
#### Book-specific markdown extensions
|
|
289
|
+
|
|
290
|
+
When `MarkdownRenderer` renders a book chapter (i.e. `bookContext` prop is set, which
|
|
291
|
+
happens automatically inside `BookLayout`), two extra plugins fire:
|
|
292
|
+
|
|
293
|
+
- **`remark-vuepress-containers`** — converts VuePress fenced containers
|
|
294
|
+
(`:::note`, `:::tip`, `:::important`, `:::warning`, `:::danger`, `:::info`) into the
|
|
295
|
+
same `<github-alert>` hast element that `remark-github-alerts` produces. Custom titles
|
|
296
|
+
(`:::tip 智慧的疆界`) are preserved via `data-alert-title`. A small string-level
|
|
297
|
+
preprocessor (`normalizeVuepressContainerSyntax`) normalizes `::: name [label]` →
|
|
298
|
+
`:::name[label]` so `remark-directive` (which only parses the space-less form) sees the
|
|
299
|
+
containers.
|
|
300
|
+
- **`remark-book-chapter-links`** — rewrites relative `.md` / `.mdx` links to other
|
|
301
|
+
chapters into canonical `/books/<slug>/<chapter-id>[#fragment]` URLs. Resolution uses
|
|
302
|
+
the chapter's `sourcePath` (exposed by `getBookChapter`). Broken links (target chapter
|
|
303
|
+
id not in the TOC, or target outside the book directory) throw at build time.
|
|
304
|
+
|
|
305
|
+
Mermaid diagrams in book chapters already work via the existing `Mermaid` component (any
|
|
306
|
+
\`\`\`mermaid fenced block, with or without a `compact` modifier after the language tag).
|
|
307
|
+
|
|
308
|
+
## Configuration Reference (`site.config.ts`)
|
|
309
|
+
|
|
310
|
+
| Field | Notes |
|
|
311
|
+
| --- | --- |
|
|
312
|
+
| `nav` | Navigation links with weights |
|
|
313
|
+
| `social` | GitHub, Twitter, email links for the footer |
|
|
314
|
+
| `series.navbar` | Series slugs to show in the navbar dropdown |
|
|
315
|
+
| `series.customPaths` | Per-series URL prefix, e.g. `{ 'weeklies': 'weeklies' }` → `/weeklies/[slug]` |
|
|
316
|
+
| `pagination.posts`, `pagination.series` | Items per page |
|
|
317
|
+
| `themeColor` | `'default' \| 'blue' \| 'rose' \| 'amber'` |
|
|
318
|
+
| `hero` | Homepage hero title and subtitle |
|
|
319
|
+
| `i18n` | Default locale and supported locales |
|
|
320
|
+
| `featured.series` | Scrollable series: `scrollThreshold` (default 2), `maxItems` (default 6) |
|
|
321
|
+
| `featured.stories` | Scrollable stories: `scrollThreshold` (default 1), `maxItems` (default 5) |
|
|
322
|
+
| `analytics.providers` | Enabled providers, e.g. `['umami', 'google']`; `[]` disables |
|
|
323
|
+
| `comments.provider` | `'giscus' \| 'disqus' \| null` |
|
|
324
|
+
| `feed.format` | `'rss' \| 'atom' \| 'both'` |
|
|
325
|
+
| `feed.content` | `'full' \| 'excerpt'` |
|
|
326
|
+
| `feed.maxItems` | Max feed items (`0` = unlimited) |
|
|
327
|
+
| `footer.bottomLinks` | Custom footer links (ICP, cookie policy); `text` accepts plain string or `{ en, zh }` |
|
|
328
|
+
| `posts.basePath` | URL prefix for all posts (default `'posts'`) |
|
|
329
|
+
| `posts.authors.default` | Fallback authors when a post has none in frontmatter |
|
|
330
|
+
| `posts.authors.showInHeader` | Show author byline below post title (default `true`) |
|
|
331
|
+
| `posts.authors.showAuthorCard` | Show author card at end of post (default `true`) |
|
|
332
|
+
| `posts.excludeFromListing` | Series slugs whose posts are hidden from `/posts` listings |
|
|
333
|
+
| `authors` | Per-author profiles: `bio`, `avatar`, `social[]` |
|
|
334
|
+
|
|
335
|
+
### Config sync
|
|
336
|
+
|
|
337
|
+
`site.config.ts` (this repo, i18n object form) and `site.config.example.ts`
|
|
338
|
+
(shipped via `create-amytis`, plain strings, single-locale, optional features
|
|
339
|
+
default disabled) must stay in sync. Any schema change to one must be mirrored
|
|
340
|
+
in the other. Locale-aware fields use `{ en, zh }` in `site.config.ts` and
|
|
341
|
+
plain strings in the example.
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Code Blocks
|
|
2
|
+
|
|
3
|
+
Amytis highlights code at **build time** with [Shiki](https://shiki.style/).
|
|
4
|
+
The same pipeline runs for Markdown, MDX, and reStructuredText, so the
|
|
5
|
+
authoring syntax is symmetric across formats: write a fence with metadata in
|
|
6
|
+
Markdown/MDX, write directive options in rST, and the same features come out
|
|
7
|
+
the other side.
|
|
8
|
+
|
|
9
|
+
The features below are all opt-in — a plain `` ```ts `` fence still renders as a
|
|
10
|
+
normal highlighted block.
|
|
11
|
+
|
|
12
|
+
## Feature matrix
|
|
13
|
+
|
|
14
|
+
| Feature | Markdown / MDX | rST |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| Line numbers | `` ```ts linenos `` | `:linenos:` |
|
|
17
|
+
| Highlighted lines | `` ```ts {1,3-5} `` | `:emphasize-lines: 1,3-5` |
|
|
18
|
+
| Title / filename bar | `` ```ts title="app.ts" `` | `:caption: app.ts` |
|
|
19
|
+
| Override language | (set on the fence) | `:language: rust` |
|
|
20
|
+
| Diff `+`/`-` backgrounds | `` ```diff `` | `.. code-block:: diff` |
|
|
21
|
+
| Word-wrap toggle | header button (client) | header button (client) |
|
|
22
|
+
|
|
23
|
+
All four metadata fields can be combined freely:
|
|
24
|
+
|
|
25
|
+
````markdown
|
|
26
|
+
```tsx title="components/CodeBlock.tsx" linenos {3,7-9}
|
|
27
|
+
import { highlightToHast } from '@/lib/shiki';
|
|
28
|
+
// ...
|
|
29
|
+
```
|
|
30
|
+
````
|
|
31
|
+
|
|
32
|
+
```rst
|
|
33
|
+
.. code-block:: tsx
|
|
34
|
+
:caption: components/CodeBlock.tsx
|
|
35
|
+
:linenos:
|
|
36
|
+
:emphasize-lines: 3,7-9
|
|
37
|
+
|
|
38
|
+
import { highlightToHast } from '@/lib/shiki';
|
|
39
|
+
...
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Both produce identical output: header bar with the filename + language label,
|
|
43
|
+
a line-number gutter, and lines 3 and 7–9 highlighted.
|
|
44
|
+
|
|
45
|
+
## Tabbed code groups
|
|
46
|
+
|
|
47
|
+
Group adjacent fences into a tabbed widget. The mechanism is **CSS-only**
|
|
48
|
+
(hidden `<input type="radio">` + `<label>` siblings + attribute selectors)
|
|
49
|
+
— no JavaScript, no hydration cost. Keyboard navigation works for free via
|
|
50
|
+
the browser's native arrow-key cycling between radios in the same group.
|
|
51
|
+
|
|
52
|
+
**Markdown / MDX** — wrap fences in a `:::code-group` container directive
|
|
53
|
+
from [`remark-directive`](https://github.com/remarkjs/remark-directive).
|
|
54
|
+
Tab names come from a `[label]` token at the start of each fence's info
|
|
55
|
+
string (Docusaurus convention). When `[label]` is absent, the language
|
|
56
|
+
name is used as the tab name.
|
|
57
|
+
|
|
58
|
+
````markdown
|
|
59
|
+
:::code-group
|
|
60
|
+
```bash [npm]
|
|
61
|
+
npm install foo
|
|
62
|
+
```
|
|
63
|
+
```bash [yarn]
|
|
64
|
+
yarn add foo
|
|
65
|
+
```
|
|
66
|
+
```bash [bun]
|
|
67
|
+
bun add foo
|
|
68
|
+
```
|
|
69
|
+
:::
|
|
70
|
+
````
|
|
71
|
+
|
|
72
|
+
**rST** — use the custom `.. code-group::` directive wrapping nested
|
|
73
|
+
`.. code-block::` blocks, each with a `:label:` option for the tab name:
|
|
74
|
+
|
|
75
|
+
```rst
|
|
76
|
+
.. code-group::
|
|
77
|
+
|
|
78
|
+
.. code-block:: bash
|
|
79
|
+
:label: npm
|
|
80
|
+
|
|
81
|
+
npm install foo
|
|
82
|
+
|
|
83
|
+
.. code-block:: bash
|
|
84
|
+
:label: yarn
|
|
85
|
+
|
|
86
|
+
yarn add foo
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Both pipelines produce identical HTML and share the same CSS. Up to 10
|
|
90
|
+
tabs per group are supported out of the box (CSS rules hardcoded for
|
|
91
|
+
`data-idx="0"` through `data-idx="9"`); extend the rules in
|
|
92
|
+
`src/app/globals.css` if you need more.
|
|
93
|
+
|
|
94
|
+
### Tab icons
|
|
95
|
+
|
|
96
|
+
When a tab label matches a known package manager, language name, or common
|
|
97
|
+
config filename, an icon renders before the label automatically. Resolution
|
|
98
|
+
cascade (first hit wins): exact label → filename → extension → language
|
|
99
|
+
alias. See `src/lib/code-group-icons.ts` for the maps.
|
|
100
|
+
|
|
101
|
+
Built-in icon keys: `npm`, `yarn`, `pnpm`, `bun`, `deno`, `typescript`,
|
|
102
|
+
`javascript`, `python`, `rust`, `go`, `java`, `ruby`, `php`, `c`, `cpp`,
|
|
103
|
+
`html`, `css`, `json`, `yaml`, `markdown`, `bash`, `docker`, `vite`,
|
|
104
|
+
`react`, `vue`, `nextjs`, `node`, `tailwind`.
|
|
105
|
+
|
|
106
|
+
A label that doesn't resolve renders without an icon (graceful fallback).
|
|
107
|
+
To add an icon: extend the resolver in `src/lib/code-group-icons.ts` with
|
|
108
|
+
the new key, then add a matching `.cg-tab[data-cg-icon="<key>"]::before`
|
|
109
|
+
rule to `src/app/globals.css` with the SVG data URI.
|
|
110
|
+
|
|
111
|
+
## Notation comments
|
|
112
|
+
|
|
113
|
+
Six VitePress-style `[!code …]` markers can be embedded inline in any code
|
|
114
|
+
block. They're written using the language's native comment syntax (the
|
|
115
|
+
Shiki transformers detect `//`, `#`, `--`, `<!-- -->`, etc.):
|
|
116
|
+
|
|
117
|
+
| Marker | Effect |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `[!code focus]` | Dim every other line in the block; revert on `:hover` of the `<pre>` |
|
|
120
|
+
| `[!code highlight]` | Same `.line.highlighted` style as the `{1,3-5}` fence-meta range syntax |
|
|
121
|
+
| `[!code ++]` | Green-tinted line (same `.line.diff.add` as raw `+` in `diff` fences) |
|
|
122
|
+
| `[!code --]` | Red-tinted line (same `.line.diff.remove` as raw `-` in `diff` fences) |
|
|
123
|
+
| `[!code error]` | Red border + tint for error annotations |
|
|
124
|
+
| `[!code warning]` | Amber border + tint for warning annotations |
|
|
125
|
+
|
|
126
|
+
Example:
|
|
127
|
+
|
|
128
|
+
````markdown
|
|
129
|
+
```ts
|
|
130
|
+
function login(user: string) { // [!code focus]
|
|
131
|
+
const token = oldApi.auth(user) // [!code --]
|
|
132
|
+
const token = newApi.auth({ user }) // [!code ++]
|
|
133
|
+
validate(token) // [!code highlight]
|
|
134
|
+
throwIfExpired(token) // [!code error]
|
|
135
|
+
if (!token.refreshable) warn() // [!code warning]
|
|
136
|
+
return token
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
````
|
|
140
|
+
|
|
141
|
+
Notation comments and the meta-based features (`title="…"`, `linenos`,
|
|
142
|
+
`{1,3-5}`) compose freely on the same fence.
|
|
143
|
+
|
|
144
|
+
The four transformers (`transformerNotationFocus`, `transformerNotationErrorLevel`,
|
|
145
|
+
`transformerNotationHighlight`, `transformerNotationDiff`) ship with the
|
|
146
|
+
`@shikijs/transformers` package; see [`src/lib/shiki.ts`](../src/lib/shiki.ts)
|
|
147
|
+
for where they're registered.
|
|
148
|
+
|
|
149
|
+
## Supported languages
|
|
150
|
+
|
|
151
|
+
**Any of Shiki's ~235 bundled languages works automatically** — including
|
|
152
|
+
TypeScript, Python, Rust, Go, Bash, Make, Dockerfile, TOML, Kotlin, Swift,
|
|
153
|
+
GraphQL, PHP, Lua, SQL, YAML, JSON, and many more. Display names and
|
|
154
|
+
aliases come from Shiki's own metadata (e.g. ```ts``` / ```cts``` /
|
|
155
|
+
```mts``` all resolve to TypeScript), so authors don't need to look up the
|
|
156
|
+
canonical id.
|
|
157
|
+
|
|
158
|
+
Grammars are loaded lazily on first use — there's no curated allowlist to
|
|
159
|
+
maintain. Adding a new language to a post just works: write the fence with
|
|
160
|
+
Shiki's id or any of its aliases.
|
|
161
|
+
|
|
162
|
+
A small overlay also recognizes community-conventional aliases that aren't
|
|
163
|
+
in Shiki's native alias table: `golang` → Go, `node` / `nodejs` →
|
|
164
|
+
JavaScript, `obj-c` → Objective-C, `gnumakefile` / `bsdmakefile` → Make.
|
|
165
|
+
To add more, extend `COMMUNITY_ALIASES` in `src/lib/shiki.ts`.
|
|
166
|
+
|
|
167
|
+
Unknown languages **render as plaintext** with a one-line build-time
|
|
168
|
+
warning (deduped per language). This is a deliberate exception to the
|
|
169
|
+
`CLAUDE.md` "strict build over silent runtime failure" principle: at the
|
|
170
|
+
fence-language layer, "typo" and "community alias" are indistinguishable
|
|
171
|
+
from our side, and production deploys shouldn't fail on a single
|
|
172
|
+
unhighlighted code block. Authors running a clean local build still see
|
|
173
|
+
real typos in the warn output. For better highlighting on a known
|
|
174
|
+
community name that Shiki doesn't ship as an alias, add an entry to
|
|
175
|
+
`COMMUNITY_ALIASES` in `src/lib/shiki.ts`. To render unhighlighted code
|
|
176
|
+
on purpose without the warn, use `plaintext` (or `text` / `txt` /
|
|
177
|
+
`plain`).
|
|
178
|
+
|
|
179
|
+
For the full list of supported language IDs and aliases, see Shiki's
|
|
180
|
+
[bundle reference](https://shiki.style/languages) or:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
node --input-type=module -e "import('shiki').then(m => console.log(m.bundledLanguagesInfo.map(l => l.id)))"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Shiki v4 is ESM-only, so the snippet uses dynamic `import()` rather than
|
|
187
|
+
`require()`.
|
|
188
|
+
|
|
189
|
+
## Diff fences
|
|
190
|
+
|
|
191
|
+
When the language is exactly `diff`, lines starting with `+` get a green
|
|
192
|
+
background and lines starting with `-` get a red background, on top of
|
|
193
|
+
the normal token coloring. Lines like `+++` and `---` (diff headers) are
|
|
194
|
+
intentionally NOT colored so they read as headers, not changes.
|
|
195
|
+
|
|
196
|
+
## Word-wrap toggle
|
|
197
|
+
|
|
198
|
+
Every block ships with two header buttons: `Copy` (the existing behavior)
|
|
199
|
+
and `No wrap` / `Wrap`. The wrap toggle is per-block client state — it
|
|
200
|
+
flips `data-wrap` on the block's root and CSS does the rest. There's no
|
|
201
|
+
global default.
|
|
202
|
+
|
|
203
|
+
## Theming
|
|
204
|
+
|
|
205
|
+
Shiki runs in dual-theme mode with `github-light` and `github-dark`.
|
|
206
|
+
Every token gets two CSS variables (`--shiki-light` and `--shiki-dark`),
|
|
207
|
+
and `src/app/globals.css` picks one or the other based on the global
|
|
208
|
+
`html.dark` class.
|
|
209
|
+
|
|
210
|
+
To swap themes, edit `SHIKI_THEMES` in `src/lib/shiki.ts` and bump
|
|
211
|
+
`RST_RENDERER_DISK_CACHE_VERSION` in `src/lib/rst-renderer.ts` so cached
|
|
212
|
+
rST renders pick up the new theme (Markdown is highlighted on every
|
|
213
|
+
render and doesn't need the bump).
|
|
214
|
+
|
|
215
|
+
## How it works
|
|
216
|
+
|
|
217
|
+
- **Markdown/MDX**: `MarkdownRenderer.tsx` parses fence meta (preserved via
|
|
218
|
+
the small `rehype-fence-meta.ts` plugin because `react-markdown` strips
|
|
219
|
+
`node.data` before invoking overrides). `CodeBlock` is an async server
|
|
220
|
+
component that calls `highlightToHast` and inlines the resulting HTML.
|
|
221
|
+
- **rST**: `scripts/render-rst.py` rewrites each `literal_block` into an
|
|
222
|
+
opaque `<pre data-amytis-code …>` marker carrying option attributes.
|
|
223
|
+
`src/lib/shiki-rst.ts` walks the rendered HTML in `RstRenderer` and
|
|
224
|
+
replaces each marker with the same Shiki output.
|
|
225
|
+
- The fallback rST parser (`src/lib/rst.ts`) converts directive options
|
|
226
|
+
into Markdown fence meta and routes through the MDX pipeline, so both
|
|
227
|
+
rST paths produce identical output.
|
|
228
|
+
|
|
229
|
+
## Gotchas
|
|
230
|
+
|
|
231
|
+
- The rST sanitize-html allowlist in `src/components/RstRenderer.tsx` must
|
|
232
|
+
permit `style` and `data-*` attributes on `pre`/`code`/`span`/`div`.
|
|
233
|
+
Stripping them silently kills syntax highlighting for rST while leaving
|
|
234
|
+
Markdown unaffected.
|
|
235
|
+
- The Shiki highlighter is a singleton on `globalThis` — never instantiate
|
|
236
|
+
it per render. Loading WASM grammars takes ~1–2 s.
|
|
237
|
+
- Mermaid blocks are short-circuited in `MarkdownRenderer.tsx` before
|
|
238
|
+
`CodeBlock` is reached; they never go through Shiki.
|
package/docs/CONTRIBUTING.md
CHANGED
|
@@ -17,6 +17,18 @@
|
|
|
17
17
|
|
|
18
18
|
## Writing Content
|
|
19
19
|
|
|
20
|
+
For code-block features (line numbers, line highlighting, title bars, diff
|
|
21
|
+
backgrounds, word-wrap toggle, notation comments, tabbed groups) and the
|
|
22
|
+
per-format fence/directive syntax, see [`docs/CODE-BLOCKS.md`](./CODE-BLOCKS.md).
|
|
23
|
+
|
|
24
|
+
For GitHub-flavored alert callouts (`> [!NOTE]`, `.. note::`, etc.) and the
|
|
25
|
+
five supported alert types, see [`docs/ALERTS.md`](./ALERTS.md).
|
|
26
|
+
|
|
27
|
+
Markdown links to other hosts (anything whose host differs from
|
|
28
|
+
`siteConfig.baseUrl`) automatically render with a trailing `↗` icon and
|
|
29
|
+
open in a new tab — don't write `<a target="_blank">` manually. Internal
|
|
30
|
+
links, wikilinks, `mailto:`, and `tel:` are unaffected.
|
|
31
|
+
|
|
20
32
|
### Creating Posts
|
|
21
33
|
|
|
22
34
|
Use the CLI to scaffold new content:
|
|
@@ -47,6 +59,17 @@ bun run new-flow
|
|
|
47
59
|
bun run new-series "My Series Name"
|
|
48
60
|
```
|
|
49
61
|
|
|
62
|
+
Series are format-scoped:
|
|
63
|
+
|
|
64
|
+
- Markdown series use `content/series/<slug>/index.mdx` or `index.md`
|
|
65
|
+
- Markdown series may also use `README.mdx` or `README.md`
|
|
66
|
+
- rST series use `content/series/<slug>/index.rst` or `README.rst`
|
|
67
|
+
- Child posts inside the series must use the same format as the index file
|
|
68
|
+
- Mixed Markdown and rST files in one series are treated as build errors
|
|
69
|
+
- For full-fidelity rST rendering, install `docutils` (and `pygments` for code highlighting) in a Python environment available to the project
|
|
70
|
+
- If needed, set `AMYTIS_RST_PYTHON=/absolute/path/to/python` so Amytis uses a specific interpreter
|
|
71
|
+
- Without Python/docutils, Amytis falls back to a lightweight compatibility parser; that keeps loading/building working, but some legacy rST constructs will render with lower fidelity
|
|
72
|
+
|
|
50
73
|
### Creating Books
|
|
51
74
|
|
|
52
75
|
Books are manually structured in `content/books/`.
|
|
@@ -75,6 +98,10 @@ bun run new-from-images ./photos --title "Gallery"
|
|
|
75
98
|
|
|
76
99
|
# Chat logs to flows
|
|
77
100
|
bun run new-flow-from-chat
|
|
101
|
+
|
|
102
|
+
# VuePress 2 book → Amytis book directory.
|
|
103
|
+
# Full walkthrough at docs/guides/importing-vuepress-books.md.
|
|
104
|
+
bun run sync-vuepress-book --source /path/to/dmla/docs --dest content/books/dmla
|
|
78
105
|
```
|
|
79
106
|
|
|
80
107
|
### Maintenance Tools
|
|
@@ -82,10 +109,19 @@ bun run new-flow-from-chat
|
|
|
82
109
|
```bash
|
|
83
110
|
# Sync book chapters with files in the folder
|
|
84
111
|
bun run sync-book
|
|
112
|
+
bun run sync-book <slug> # one book only
|
|
85
113
|
|
|
86
114
|
# Bulk draft/publish a series
|
|
87
115
|
bun run series-draft "my-series"
|
|
88
116
|
bun run series-draft "my-series" --undraft
|
|
117
|
+
|
|
118
|
+
# Add redirectFrom to series posts (for autoPaths / path migrations)
|
|
119
|
+
bun run add-series-redirects # all series
|
|
120
|
+
bun run add-series-redirects <series-slug> # one series only
|
|
121
|
+
bun run add-series-redirects --dry-run # preview without writing
|
|
122
|
+
|
|
123
|
+
# Reset build caches (when copy-assets or the image optimizer misbehave)
|
|
124
|
+
bun run clean # removes .next, out, public/posts
|
|
89
125
|
```
|
|
90
126
|
|
|
91
127
|
## Running Tests
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Guides
|
|
2
|
+
|
|
3
|
+
Focused, task-oriented walkthroughs for non-trivial workflows. Reference material
|
|
4
|
+
lives in `docs/ARCHITECTURE.md`; commit conventions and the everyday command list
|
|
5
|
+
live in `docs/CONTRIBUTING.md`.
|
|
6
|
+
|
|
7
|
+
| Guide | Purpose |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| [Importing a VuePress 2 book](./importing-vuepress-books.md) | Mirror an external VuePress repo's `docs/` tree into an Amytis book directory, with sidebar conversion, math/container normalization, and idempotent re-runs. |
|
|
10
|
+
|
|
11
|
+
Add new guides as `docs/guides/<topic>.md` and link them here.
|