@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.
Files changed (128) hide show
  1. package/.github/workflows/ci.yml +1 -1
  2. package/.github/workflows/publish.yml +2 -2
  3. package/CHANGELOG.md +42 -0
  4. package/CLAUDE.md +90 -219
  5. package/README.md +33 -1
  6. package/README.zh.md +33 -1
  7. package/TODO.md +10 -0
  8. package/bun.lock +205 -539
  9. package/content/books/sample-book/index.mdx +3 -0
  10. package/content/posts/code-block-features-showcase.mdx +223 -0
  11. package/content/series/rst-legacy/deeper-notes/images/test.svg +4 -0
  12. package/content/series/rst-legacy/deeper-notes/index.rst +15 -0
  13. package/content/series/rst-legacy/getting-started.rst +24 -0
  14. package/content/series/rst-legacy/index.rst +9 -0
  15. package/content/series/rst-readme/README.rst +9 -0
  16. package/content/series/rst-readme/readme-index-post.rst +10 -0
  17. package/content/series/rst-toctree/first-post.rst +6 -0
  18. package/content/series/rst-toctree/index.rst +10 -0
  19. package/content/series/rst-toctree/second-post.rst +6 -0
  20. package/content/series/rst-toctree-precedence/first-post.rst +6 -0
  21. package/content/series/rst-toctree-precedence/index.rst +12 -0
  22. package/content/series/rst-toctree-precedence/second-post.rst +6 -0
  23. package/docs/ALERTS.md +112 -0
  24. package/docs/ARCHITECTURE.md +239 -8
  25. package/docs/CODE-BLOCKS.md +238 -0
  26. package/docs/CONTRIBUTING.md +36 -0
  27. package/docs/guides/README.md +11 -0
  28. package/docs/guides/importing-vuepress-books.md +178 -0
  29. package/eslint.config.mjs +20 -6
  30. package/next.config.ts +2 -2
  31. package/package.json +52 -24
  32. package/packages/create-amytis/package.json +1 -1
  33. package/packages/create-amytis/src/index.test.ts +43 -1
  34. package/packages/create-amytis/src/index.ts +64 -8
  35. package/public/next-image-export-optimizer-hashes.json +14 -73
  36. package/scripts/build-pagefind.ts +172 -0
  37. package/scripts/copy-assets.ts +246 -56
  38. package/scripts/generate-code-group-icons.ts +79 -0
  39. package/scripts/generate-knowledge-graph.ts +2 -1
  40. package/scripts/render-rst.py +923 -0
  41. package/scripts/run-with-rst-python.ts +42 -0
  42. package/scripts/sync-vuepress-book.ts +499 -0
  43. package/src/app/[slug]/[postSlug]/page.tsx +20 -10
  44. package/src/app/[slug]/page/[page]/page.tsx +15 -0
  45. package/src/app/books/[slug]/{[chapter] → [...chapter]}/page.tsx +32 -10
  46. package/src/app/books/[slug]/page.tsx +67 -32
  47. package/src/app/globals.css +639 -94
  48. package/src/app/page.tsx +1 -1
  49. package/src/app/series/[slug]/page/[page]/page.tsx +74 -6
  50. package/src/app/series/[slug]/page.tsx +11 -13
  51. package/src/app/series/page.tsx +3 -3
  52. package/src/app/sitemap.ts +3 -3
  53. package/src/components/ArticleCopyCleaner.tsx +64 -0
  54. package/src/components/AuthorCard.tsx +25 -16
  55. package/src/components/BookMobileNav.tsx +44 -50
  56. package/src/components/BookSidebar.tsx +0 -0
  57. package/src/components/CodeBlock.test.tsx +93 -8
  58. package/src/components/CodeBlock.tsx +39 -101
  59. package/src/components/CodeBlockToolbar.tsx +88 -0
  60. package/src/components/CodeGroup.tsx +81 -0
  61. package/src/components/CoverImage.tsx +6 -2
  62. package/src/components/ExternalLinkIcon.tsx +15 -0
  63. package/src/components/FeaturedStoriesSection.tsx +3 -3
  64. package/src/components/GithubAlert.tsx +97 -0
  65. package/src/components/MarkdownRenderer.test.tsx +30 -4
  66. package/src/components/MarkdownRenderer.tsx +148 -24
  67. package/src/components/Mermaid.tsx +32 -1
  68. package/src/components/PostList.tsx +1 -1
  69. package/src/components/PostNavigation.tsx +13 -2
  70. package/src/components/PostSidebar.tsx +13 -2
  71. package/src/components/RstRenderer.test.tsx +93 -0
  72. package/src/components/RstRenderer.tsx +157 -0
  73. package/src/components/Search.tsx +18 -4
  74. package/src/components/SeriesCatalog.tsx +1 -1
  75. package/src/components/ShareBar.tsx +5 -0
  76. package/src/components/TocPanel.tsx +10 -2
  77. package/src/i18n/translations.ts +2 -0
  78. package/src/layouts/BookLayout.tsx +35 -4
  79. package/src/layouts/PostLayout.tsx +10 -2
  80. package/src/layouts/SimpleLayout.tsx +10 -3
  81. package/src/lib/code-group-icons.test.ts +78 -0
  82. package/src/lib/code-group-icons.ts +148 -0
  83. package/src/lib/image-utils.test.ts +19 -0
  84. package/src/lib/image-utils.ts +11 -0
  85. package/src/lib/markdown.test.ts +195 -14
  86. package/src/lib/markdown.ts +928 -254
  87. package/src/lib/normalize-vuepress-math.ts +118 -0
  88. package/src/lib/rehype-fence-meta.ts +22 -0
  89. package/src/lib/rehype-image-metadata.ts +2 -2
  90. package/src/lib/remark-book-chapter-links.ts +106 -0
  91. package/src/lib/remark-code-group.ts +54 -0
  92. package/src/lib/remark-github-alerts.test.ts +83 -0
  93. package/src/lib/remark-github-alerts.ts +65 -0
  94. package/src/lib/remark-vuepress-containers.ts +130 -0
  95. package/src/lib/rst-renderer.test.ts +355 -0
  96. package/src/lib/rst-renderer.ts +629 -0
  97. package/src/lib/rst.test.ts +350 -0
  98. package/src/lib/rst.ts +674 -0
  99. package/src/lib/series-redirects.ts +42 -0
  100. package/src/lib/shiki-rst.ts +185 -0
  101. package/src/lib/shiki.test.ts +153 -0
  102. package/src/lib/shiki.ts +292 -0
  103. package/src/lib/urls.ts +57 -0
  104. package/src/test-utils/render.ts +23 -0
  105. package/tests/fixtures/sync-vuepress-book/docs/.vuepress/config.js +43 -0
  106. package/tests/fixtures/sync-vuepress-book/docs/intro/welcome.md +7 -0
  107. package/tests/fixtures/sync-vuepress-book/docs/maths/linear/assets/diagram.png +1 -0
  108. package/tests/fixtures/sync-vuepress-book/docs/maths/linear/matrices.md +7 -0
  109. package/tests/fixtures/sync-vuepress-book/docs/maths/linear/vectors.md +9 -0
  110. package/tests/helpers/env.ts +19 -0
  111. package/tests/integration/book-chapter-links.test.ts +107 -0
  112. package/tests/integration/books-nested-toc.test.ts +176 -0
  113. package/tests/integration/books.test.ts +3 -2
  114. package/tests/integration/code-block-features.test.ts +188 -0
  115. package/tests/integration/code-group.test.ts +183 -0
  116. package/tests/integration/code-notation.test.ts +97 -0
  117. package/tests/integration/feed-utils.test.ts +13 -0
  118. package/tests/integration/github-alerts.test.ts +82 -0
  119. package/tests/integration/markdown-external-links.test.ts +103 -0
  120. package/tests/integration/normalize-vuepress-math.test.ts +149 -0
  121. package/tests/integration/reading-time-headings.test.ts +12 -14
  122. package/tests/integration/series-draft.test.ts +12 -5
  123. package/tests/integration/series.test.ts +93 -0
  124. package/tests/integration/sync-vuepress-book.test.ts +240 -0
  125. package/tests/integration/vuepress-containers.test.ts +107 -0
  126. package/tests/tooling/build-pagefind.test.ts +66 -0
  127. package/tests/tooling/new-post.test.ts +1 -1
  128. package/tests/unit/static-params.test.ts +166 -13
@@ -6,6 +6,9 @@ coverImage: "/images/flowers.jpg"
6
6
  featured: true
7
7
  draft: false
8
8
  authors: ["Amytis"]
9
+ # Render each chapter's excerpt as a subtitle under its title.
10
+ # Default is false; enabled here so the template demonstrates the feature.
11
+ showChapterExcerpt: true
9
12
  chapters:
10
13
  - part: "Part I: Getting Started"
11
14
  chapters:
@@ -0,0 +1,223 @@
1
+ ---
2
+ title: "Code Block Features Showcase"
3
+ date: "2026-05-25"
4
+ excerpt: "Walk through the advanced code-block features: line numbers, line highlighting, title bars, diff colors, word-wrap toggle, and plaintext fallback."
5
+ category: "Showcase"
6
+ tags: ["test", "code", "shiki"]
7
+ authors: ["Amytis Team"]
8
+ toc: true
9
+ ---
10
+
11
+ The default Shiki pipeline applies build-time syntax highlighting with a
12
+ dual `github-light` / `github-dark` theme. The features below are opt-in
13
+ via fence metadata.
14
+
15
+ ## Title bar
16
+
17
+ ```ts title="src/app.ts"
18
+ export const greet = (name: string) => `Hello, ${name}!`;
19
+ ```
20
+
21
+ ## Line numbers
22
+
23
+ ```python linenos
24
+ def fib(n):
25
+ if n < 2:
26
+ return n
27
+ return fib(n - 1) + fib(n - 2)
28
+ ```
29
+
30
+ ## Highlighted lines
31
+
32
+ ```rust {2,4-6}
33
+ fn main() {
34
+ let x = compute();
35
+ println!("{x}");
36
+ if x > 10 {
37
+ warn();
38
+ recover();
39
+ }
40
+ }
41
+ ```
42
+
43
+ ## Title + line numbers + highlights combined
44
+
45
+ ```tsx title="components/CodeBlock.tsx" linenos {3,7-9}
46
+ import { highlightToHast } from '@/lib/shiki';
47
+ import { toHtml } from 'hast-util-to-html';
48
+ import CodeBlockToolbar from './CodeBlockToolbar';
49
+
50
+ export default async function CodeBlock({ language, children, title }) {
51
+ const hast = await highlightToHast(children, language, { title });
52
+ const html = toHtml(hast);
53
+ return (
54
+ <div className="cb-root">
55
+ {title && <span className="cb-title">{title}</span>}
56
+ <CodeBlockToolbar code={children} />
57
+ <div dangerouslySetInnerHTML={{ __html: html }} />
58
+ </div>
59
+ );
60
+ }
61
+ ```
62
+
63
+ ## Diff with red/green backgrounds
64
+
65
+ ```diff
66
+ -export const VERSION = "1.0";
67
+ +export const VERSION = "2.0";
68
+ export const NAME = "amytis";
69
+ -export const STAGE = "alpha";
70
+ +export const STAGE = "beta";
71
+ ```
72
+
73
+ ## Word-wrap toggle
74
+
75
+ Long lines in code blocks overflow horizontally by default — the block grows
76
+ a scrollbar at the bottom. Click the **Wrap** button in any block's header
77
+ to soft-wrap long lines onto multiple visual lines instead; click again to
78
+ restore horizontal scrolling.
79
+
80
+ ```bash
81
+ curl -X POST "https://api.example.com/v1/items?fields=id,name,description,createdAt,updatedAt&sort=createdAt:desc&limit=100&offset=0&filter[status]=active&filter[category]=blog&include=author,tags" -H "Authorization: Bearer YOUR_API_TOKEN_HERE" -H "Content-Type: application/json" -d '{"name":"example","description":"a sufficiently long single line to demonstrate the wrap toggle"}'
82
+ ```
83
+
84
+ Try the **Wrap** button in the header above ↑ to see the long line collapse
85
+ into multiple soft-wrapped lines.
86
+
87
+ ## Mermaid still works (regression check)
88
+
89
+ ```mermaid
90
+ graph LR
91
+ A[Markdown] --> B[remark]
92
+ B --> C[rehype + Shiki]
93
+ C --> D[Static HTML]
94
+ ```
95
+
96
+ ## Tabbed code groups
97
+
98
+ Group adjacent fences into a single tabbed widget by wrapping them in a
99
+ `:::code-group` container directive. Tab names come from the `[label]`
100
+ token after the language. The mechanism is pure CSS (radio inputs + sibling
101
+ selectors) — zero JavaScript, zero hydration cost.
102
+
103
+ When a tab label matches a known package manager, language, or common
104
+ config filename, an icon appears automatically before the label. Resolution
105
+ is handled by `resolveCodeGroupIcon` in `src/lib/code-group-icons.ts`.
106
+
107
+ :::code-group
108
+ ```bash [npm]
109
+ npm install amytis
110
+ ```
111
+ ```bash [yarn]
112
+ yarn add amytis
113
+ ```
114
+ ```bash [pnpm]
115
+ pnpm add amytis
116
+ ```
117
+ ```bash [bun]
118
+ bun add amytis
119
+ ```
120
+ :::
121
+
122
+ Filename labels also resolve — e.g. `tsconfig.json`, `vite.config.ts`, or
123
+ `Dockerfile`:
124
+
125
+ :::code-group
126
+ ```json [tsconfig.json]
127
+ { "compilerOptions": { "target": "es2022", "module": "esnext" } }
128
+ ```
129
+ ```ts [vite.config.ts]
130
+ import { defineConfig } from 'vite';
131
+ export default defineConfig({ server: { port: 5173 } });
132
+ ```
133
+ ```dockerfile [Dockerfile]
134
+ FROM node:20-alpine
135
+ WORKDIR /app
136
+ COPY . .
137
+ RUN npm ci && npm run build
138
+ ```
139
+ :::
140
+
141
+ Tabs work across languages too — handy for showing the same algorithm in
142
+ multiple implementations:
143
+
144
+ :::code-group
145
+ ```ts [TypeScript]
146
+ export function greet(name: string): string {
147
+ return `Hello, ${name}!`;
148
+ }
149
+ ```
150
+ ```python [Python]
151
+ def greet(name: str) -> str:
152
+ return f"Hello, {name}!"
153
+ ```
154
+ ```rust [Rust]
155
+ fn greet(name: &str) -> String {
156
+ format!("Hello, {name}!")
157
+ }
158
+ ```
159
+ :::
160
+
161
+ ## Notation comments
162
+
163
+ Annotate individual lines with `// [!code …]` comments. Six markers are
164
+ supported — focus, diff, highlight, error, warning — using the language's
165
+ native comment style (`//` in C-family, `#` in Python, `--` in SQL, etc.):
166
+
167
+ ```ts
168
+ function login(user: string) { // [!code focus]
169
+ const token = oldApi.auth(user) // [!code --]
170
+ const token = newApi.auth({ user }) // [!code ++]
171
+ validate(token) // [!code highlight]
172
+ throwIfExpired(token) // [!code error]
173
+ if (!token.refreshable) warn() // [!code warning]
174
+ return token
175
+ }
176
+ ```
177
+
178
+ `// [!code focus]` dims the rest of the block so the focused subset stands
179
+ out; hover the block to reveal the dimmed lines. `// [!code error]` /
180
+ `[!code warning]` tint the line with a colored left-border (red / amber).
181
+ `[!code ++]` / `[!code --]` color individual lines without needing a
182
+ `diff`-language fence.
183
+
184
+ Notation comments work in **any** language — Python:
185
+
186
+ ```python
187
+ def fib(n):
188
+ if n < 2: return n # [!code focus]
189
+ return fib(n-1) + fib(n-2)
190
+ ```
191
+
192
+ ## GitHub-flavored alerts
193
+
194
+ Add a `[!TYPE]` marker on the first line of a blockquote to render it as a
195
+ GitHub-style callout. Five types are supported, each with its own color,
196
+ icon, and label:
197
+
198
+ > [!NOTE]
199
+ > Highlights information that users should take into account, even when skimming.
200
+
201
+ > [!TIP]
202
+ > Optional information to help a user be more successful.
203
+
204
+ > [!IMPORTANT]
205
+ > Crucial information necessary for users to succeed.
206
+
207
+ > [!WARNING]
208
+ > Critical content demanding immediate user attention due to potential risks.
209
+
210
+ > [!CAUTION]
211
+ > Negative potential consequences of an action.
212
+
213
+ rST equivalents (`.. note::` / `.. tip::` / `.. important::` / `.. warning::`
214
+ / `.. caution::`) render with matching colors via docutils' admonition
215
+ directives — same visual treatment across both pipelines.
216
+
217
+ ## Explicit plaintext
218
+
219
+ ```plaintext
220
+ This block opts out of syntax highlighting entirely. Use `plaintext` (or its
221
+ aliases `text`, `txt`, `plain`) for prose-like blocks where token coloring
222
+ would be noisy or misleading. Unknown language names will fail the build.
223
+ ```
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="160" height="80" viewBox="0 0 160 80">
2
+ <rect width="160" height="80" rx="10" fill="#d7efe3"/>
3
+ <text x="80" y="48" text-anchor="middle" font-family="sans-serif" font-size="18" fill="#21543d">rST</text>
4
+ </svg>
@@ -0,0 +1,15 @@
1
+ Deeper Notes
2
+ ============
3
+
4
+ :date: 2026-01-05
5
+ :category: Legacy
6
+ :tags: rst, notes
7
+ :authors: John Hu
8
+
9
+ This post uses a folder layout.
10
+
11
+ Images
12
+ ------
13
+
14
+ .. image:: ./images/test.svg
15
+ :alt: Test image
@@ -0,0 +1,24 @@
1
+ Getting Started With rST
2
+ ========================
3
+
4
+ :date: 2026-01-03
5
+ :excerpt: A simple rST-based post inside a legacy series.
6
+ :category: Legacy
7
+ :tags: rst, migration
8
+ :authors: John Hu
9
+ :redirectFrom: /posts/getting-started-rst
10
+
11
+ Intro paragraph with an inline link to `Amytis <https://github.com/hutusi/amytis>`_.
12
+
13
+ Overview
14
+ --------
15
+
16
+ - Keep the routing layer unchanged.
17
+ - Parse the source format at the series boundary.
18
+
19
+ Code Sample
20
+ ~~~~~~~~~~~
21
+
22
+ .. code-block:: ts
23
+
24
+ export const feature = "rst";
@@ -0,0 +1,9 @@
1
+ Rst Legacy Series
2
+ =================
3
+
4
+ :excerpt: Legacy notes imported from reStructuredText.
5
+ :authors: John Hu
6
+ :sort: manual
7
+ :posts: getting-started, deeper-notes
8
+
9
+ This is a small legacy series used to validate series-scoped rST support.
@@ -0,0 +1,9 @@
1
+ Rst README Series
2
+ =================
3
+
4
+ :excerpt: Legacy series metadata loaded from README.rst.
5
+ :authors: John Hu
6
+ :sort: manual
7
+ :posts: readme-index-post
8
+
9
+ This series uses README.rst as its series index file.
@@ -0,0 +1,10 @@
1
+ README Index Post
2
+ =================
3
+
4
+ :date: 2026-01-09
5
+ :excerpt: Post inside a series whose metadata is loaded from README.rst.
6
+ :category: Legacy
7
+ :tags: rst, readme
8
+ :authors: John Hu
9
+
10
+ Content for the README-indexed series.
@@ -0,0 +1,6 @@
1
+ First Post
2
+ ==========
3
+
4
+ :date: 2024-01-01
5
+
6
+ This post appears second in the toctree order.
@@ -0,0 +1,10 @@
1
+ Rst Toctree Series
2
+ ==================
3
+
4
+ :excerpt: Legacy series order derived from toctree.
5
+
6
+ .. toctree::
7
+ :maxdepth: 1
8
+
9
+ second-post
10
+ first-post
@@ -0,0 +1,6 @@
1
+ Second Post
2
+ ===========
3
+
4
+ :date: 2024-01-02
5
+
6
+ This post appears first in the toctree order.
@@ -0,0 +1,6 @@
1
+ First Post
2
+ ==========
3
+
4
+ :date: 2024-01-01
5
+
6
+ This post should remain first because explicit posts metadata wins.
@@ -0,0 +1,12 @@
1
+ Rst Toctree Precedence Series
2
+ =============================
3
+
4
+ :excerpt: Explicit posts metadata should override toctree order.
5
+ :sort: manual
6
+ :posts: first-post, second-post
7
+
8
+ .. toctree::
9
+ :maxdepth: 1
10
+
11
+ second-post
12
+ first-post
@@ -0,0 +1,6 @@
1
+ Second Post
2
+ ===========
3
+
4
+ :date: 2024-01-02
5
+
6
+ This post should remain second because explicit posts metadata wins.
package/docs/ALERTS.md ADDED
@@ -0,0 +1,112 @@
1
+ # Alerts (GitHub-flavored callouts)
2
+
3
+ Amytis renders the five GitHub-flavored alert types as styled callouts.
4
+ Both Markdown / MDX (`> [!TYPE]` blockquote markers) and rST (the built-in
5
+ `.. note::` / `.. tip::` etc. directives) produce visually consistent
6
+ output — same border color, same icon-less title bar for rST (docutils
7
+ supplies the title), same per-type accent palette.
8
+
9
+ ## Markdown / MDX
10
+
11
+ Start a blockquote with `[!TYPE]` on its own first line:
12
+
13
+ ```markdown
14
+ > [!NOTE]
15
+ > Highlights information that users should take into account, even when skimming.
16
+
17
+ > [!TIP]
18
+ > Optional information to help a user be more successful.
19
+
20
+ > [!IMPORTANT]
21
+ > Crucial information necessary for users to succeed.
22
+
23
+ > [!WARNING]
24
+ > Critical content demanding immediate user attention due to potential risks.
25
+
26
+ > [!CAUTION]
27
+ > Negative potential consequences of an action.
28
+ ```
29
+
30
+ The marker is **case-insensitive** (`[!note]` works too). Body content
31
+ keeps full Markdown — paragraphs, lists, links, inline code, even other
32
+ code blocks.
33
+
34
+ A blockquote without a recognized marker stays as a plain blockquote.
35
+ An unknown type like `[!UNKNOWN]` also passes through unchanged.
36
+
37
+ ## reStructuredText
38
+
39
+ rST uses docutils' built-in admonition directives. All five GitHub types
40
+ have a docutils equivalent, plus a few aliases:
41
+
42
+ ```rst
43
+ .. note::
44
+
45
+ Highlights information that users should take into account.
46
+
47
+ .. tip::
48
+
49
+ Optional information to help a user be more successful.
50
+
51
+ .. hint::
52
+
53
+ Also styled as a tip.
54
+
55
+ .. important::
56
+
57
+ Crucial information necessary for users to succeed.
58
+
59
+ .. warning::
60
+
61
+ Critical content demanding immediate user attention.
62
+
63
+ .. attention::
64
+
65
+ Also styled as a warning.
66
+
67
+ .. caution::
68
+
69
+ Negative potential consequences of an action.
70
+
71
+ .. danger::
72
+
73
+ Also styled as a caution.
74
+ ```
75
+
76
+ The CSS rules in `src/app/globals.css` apply the same `--alert-accent`
77
+ color variable to docutils' `.admonition-note` / `.admonition-tip` /
78
+ `.admonition-hint` / `.admonition-important` / `.admonition-warning` /
79
+ `.admonition-attention` / `.admonition-caution` / `.admonition-danger`
80
+ classes, so `> [!NOTE]` in MDX and `.. note::` in rST land at the same
81
+ visual output.
82
+
83
+ ## Visual style
84
+
85
+ - Per-type accent color drives the left border, the title bar text, and
86
+ a tinted background (8% accent over the page background).
87
+ - Dark mode uses brighter accent variants matching GitHub Primer's
88
+ dark-tier alert colors.
89
+ - MDX alerts use an inline SVG icon; rST admonitions skip the icon (docutils
90
+ doesn't emit one) but keep the colored title.
91
+
92
+ ## How it works
93
+
94
+ - **MDX**: a small remark plugin at `src/lib/remark-github-alerts.ts`
95
+ detects the `[!TYPE]` marker, strips it from the blockquote, and routes
96
+ the node through a `<GithubAlert>` React server component
97
+ (`src/components/GithubAlert.tsx`). `remark-gfm` v4 does NOT include
98
+ the alert transform — it passes blockquotes through with the marker
99
+ intact — so the plugin is what makes this work.
100
+ - **rST**: docutils' built-in admonition directives produce the
101
+ `<aside class="admonition admonition-<type>">` markup. The shared CSS
102
+ in `globals.css` matches both pipelines.
103
+
104
+ ## Gotchas
105
+
106
+ - Don't rely on a blank line between the marker and body — `> [!NOTE]\n> body`
107
+ works; `> [!NOTE]\n>\n> body` also works.
108
+ - The marker must be `[!TYPE]` *exactly* (square brackets, exclamation,
109
+ type). VitePress-style colon variants like `:::tip` aren't recognized.
110
+ - Custom alert types beyond the five GitHub ones aren't supported. If you
111
+ need one, extend the regex in `remark-github-alerts.ts` and add a CSS
112
+ rule.