@hutusi/amytis 1.9.0 → 1.11.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 (143) hide show
  1. package/.entire/settings.json +4 -0
  2. package/AGENTS.md +13 -7
  3. package/CHANGELOG.md +41 -0
  4. package/CLAUDE.md +30 -2
  5. package/README.md +84 -58
  6. package/README.zh.md +195 -0
  7. package/TODO.md +6 -4
  8. package/bun.lock +30 -4
  9. package/content/books/sample-book/images/digital-garden.svg +25 -0
  10. package/content/books/sample-book/index.mdx +1 -1
  11. package/content/books/sample-book/introduction.mdx +2 -0
  12. package/content/posts/2026-01-21-kitchen-sink/index.mdx +26 -1
  13. package/content/posts/legacy-markdown.md +0 -2
  14. package/content/posts/markdown-features.mdx +1 -1
  15. package/content/posts/multilingual-test.mdx +0 -1
  16. package/content/posts/syntax-highlighting-showcase.mdx +1 -0
  17. package/content/posts/understanding-react-hooks.mdx +1 -0
  18. package/content/series/ai-nexus-weekly/index.mdx +1 -1
  19. package/content/series/digital-garden/01-philosophy.mdx +2 -0
  20. package/content/series/digital-garden/index.mdx +1 -1
  21. package/content/series/markdown-showcase/index.mdx +1 -1
  22. package/content/series/markdown-showcase/syntax-highlighting.mdx +2 -0
  23. package/content/series/nextjs-deep-dive/01-getting-started.mdx +2 -1
  24. package/content/series/nextjs-deep-dive/index.mdx +1 -1
  25. package/content/subscribe.mdx +27 -0
  26. package/docs/ARCHITECTURE.md +85 -105
  27. package/docs/CONTRIBUTING.md +11 -3
  28. package/docs/DIGITAL_GARDEN.md +11 -9
  29. package/docs/deployment.md +31 -13
  30. package/eslint.config.mjs +2 -0
  31. package/package.json +13 -4
  32. package/packages/create-amytis/package.json +13 -0
  33. package/packages/create-amytis/src/index.test.ts +187 -0
  34. package/packages/create-amytis/src/index.ts +230 -0
  35. package/packages/create-amytis/tsconfig.json +14 -0
  36. package/public/images/amytis-screenshot.jpg +0 -0
  37. package/public/images/antelope-canyon.jpg +0 -0
  38. package/public/images/avatar.jpg +0 -0
  39. package/public/images/cappadocia.jpg +0 -0
  40. package/public/images/flowers.jpg +0 -0
  41. package/public/images/galaxy.jpg +0 -0
  42. package/public/images/lake.jpg +0 -0
  43. package/public/images/mountains.jpg +0 -0
  44. package/public/images/vibrant-waves.avif +0 -0
  45. package/public/images/wechat-qr.jpg +0 -0
  46. package/public/next-image-export-optimizer-hashes.json +72 -2
  47. package/scripts/deploy.ts +77 -0
  48. package/site.config.example.ts +268 -0
  49. package/site.config.ts +82 -10
  50. package/src/app/[slug]/[postSlug]/page.tsx +161 -0
  51. package/src/app/[slug]/page/[page]/page.tsx +202 -0
  52. package/src/app/[slug]/page.tsx +162 -7
  53. package/src/app/archive/page.tsx +5 -8
  54. package/src/app/books/[slug]/[chapter]/page.tsx +45 -3
  55. package/src/app/books/[slug]/page.tsx +16 -2
  56. package/src/app/books/page.tsx +12 -8
  57. package/src/app/feed.atom/route.ts +62 -0
  58. package/src/app/feed.xml/route.ts +45 -20
  59. package/src/app/flows/[year]/[month]/[day]/page.tsx +9 -1
  60. package/src/app/flows/[year]/page.tsx +1 -1
  61. package/src/app/flows/page/[page]/page.tsx +2 -0
  62. package/src/app/flows/page.tsx +4 -0
  63. package/src/app/globals.css +9 -3
  64. package/src/app/layout.tsx +22 -1
  65. package/src/app/notes/[slug]/page.tsx +2 -3
  66. package/src/app/page.tsx +86 -8
  67. package/src/app/posts/[slug]/page.tsx +33 -7
  68. package/src/app/posts/page/[page]/page.tsx +9 -5
  69. package/src/app/posts/page.tsx +13 -7
  70. package/src/app/series/[slug]/page/[page]/page.tsx +8 -25
  71. package/src/app/series/[slug]/page.tsx +21 -19
  72. package/src/app/series/page.tsx +14 -28
  73. package/src/app/sitemap.ts +2 -1
  74. package/src/app/tags/[tag]/page.tsx +1 -1
  75. package/src/app/tags/page.tsx +1 -1
  76. package/src/components/Analytics.tsx +34 -36
  77. package/src/components/AuthorCard.tsx +56 -14
  78. package/src/components/BookMobileNav.tsx +8 -34
  79. package/src/components/BookSidebar.tsx +17 -107
  80. package/src/components/CodeBlock.test.tsx +19 -0
  81. package/src/components/CodeBlock.tsx +20 -16
  82. package/src/components/Comments.tsx +4 -3
  83. package/src/components/CoverImage.tsx +11 -3
  84. package/src/components/CuratedSeriesSection.tsx +17 -15
  85. package/src/components/ExternalLinks.tsx +1 -1
  86. package/src/components/FeaturedStoriesSection.tsx +124 -58
  87. package/src/components/FlowCalendarSidebar.tsx +10 -7
  88. package/src/components/FlowContent.tsx +33 -5
  89. package/src/components/Footer.tsx +28 -6
  90. package/src/components/Hero.tsx +44 -92
  91. package/src/components/InlineBookToc.tsx +56 -0
  92. package/src/components/KnowledgeGraph.tsx +26 -0
  93. package/src/components/LanguageProvider.tsx +7 -2
  94. package/src/components/LanguageSwitch.tsx +1 -1
  95. package/src/components/LatestWritingSection.tsx +12 -22
  96. package/src/components/MarkdownRenderer.test.tsx +41 -0
  97. package/src/components/MarkdownRenderer.tsx +43 -8
  98. package/src/components/Navbar.tsx +24 -15
  99. package/src/components/NoteSidebar.tsx +6 -76
  100. package/src/components/Pagination.tsx +2 -2
  101. package/src/components/PostCard.tsx +2 -1
  102. package/src/components/PostList.tsx +81 -62
  103. package/src/components/PostNavigation.tsx +8 -7
  104. package/src/components/PostSidebar.tsx +12 -107
  105. package/src/components/PrevNextNav.tsx +63 -0
  106. package/src/components/RecentNotesSection.tsx +2 -2
  107. package/src/components/RelatedPosts.tsx +3 -2
  108. package/src/components/RssFeedWidget.tsx +55 -0
  109. package/src/components/SectionLabel.tsx +16 -0
  110. package/src/components/SelectedBooksSection.tsx +12 -15
  111. package/src/components/SeriesCatalog.tsx +74 -69
  112. package/src/components/SeriesList.tsx +11 -36
  113. package/src/components/SeriesSidebar.tsx +4 -2
  114. package/src/components/TagContentTabs.tsx +17 -12
  115. package/src/components/TagPageHeader.tsx +12 -2
  116. package/src/components/TagSidebar.tsx +3 -2
  117. package/src/components/TagsIndexClient.tsx +1 -1
  118. package/src/components/TocPanel.tsx +67 -0
  119. package/src/hooks/useActiveHeading.ts +32 -0
  120. package/src/hooks/useSidebarAutoScroll.ts +19 -0
  121. package/src/i18n/translations.ts +26 -4
  122. package/src/layouts/BookLayout.tsx +10 -40
  123. package/src/layouts/PostLayout.tsx +37 -26
  124. package/src/layouts/SimpleLayout.tsx +5 -5
  125. package/src/lib/feed-utils.ts +68 -0
  126. package/src/lib/format-utils.test.ts +27 -0
  127. package/src/lib/format-utils.ts +3 -0
  128. package/src/lib/image-utils.ts +12 -0
  129. package/src/lib/json-ld.ts +236 -0
  130. package/src/lib/markdown.ts +62 -7
  131. package/src/lib/rehype-image-metadata.test.ts +124 -0
  132. package/src/lib/rehype-image-metadata.ts +21 -5
  133. package/src/lib/scroll-utils.ts +11 -0
  134. package/src/lib/shuffle.ts +21 -0
  135. package/src/lib/urls.ts +51 -0
  136. package/tests/e2e/navigation.test.ts +15 -1
  137. package/tests/integration/books.test.ts +61 -0
  138. package/tests/integration/feed-utils.test.ts +145 -0
  139. package/tests/integration/series.test.ts +9 -0
  140. package/tests/unit/static-params.test.ts +59 -2
  141. package/src/app/subscribe/page.tsx +0 -17
  142. package/src/components/SubscribePage.tsx +0 -298
  143. /package/public/{screenshot.png → images/screenshot.png} +0 -0
package/bun.lock CHANGED
@@ -11,6 +11,7 @@
11
11
  "github-slugger": "^2.0.0",
12
12
  "gray-matter": "^4.0.3",
13
13
  "image-size": "^2.0.2",
14
+ "katex": "^0.16.33",
14
15
  "mermaid": "^11.12.3",
15
16
  "next": "16.1.6",
16
17
  "next-image-export-optimizer": "^1.20.1",
@@ -23,8 +24,12 @@
23
24
  "rehype-katex": "^7.0.1",
24
25
  "rehype-raw": "^7.0.0",
25
26
  "rehype-slug": "^6.0.0",
27
+ "rehype-stringify": "^10.0.1",
26
28
  "remark-gfm": "^4.0.1",
27
29
  "remark-math": "^6.0.0",
30
+ "remark-parse": "^11.0.0",
31
+ "remark-rehype": "^11.1.2",
32
+ "unified": "^11.0.5",
28
33
  "unist-util-visit": "^5.1.0",
29
34
  "zod": "^4.3.6",
30
35
  },
@@ -32,7 +37,9 @@
32
37
  "@tailwindcss/postcss": "^4.1.18",
33
38
  "@types/bun": "^1.3.9",
34
39
  "@types/d3": "^7.4.3",
40
+ "@types/hast": "^3.0.4",
35
41
  "@types/image-size": "^0.8.0",
42
+ "@types/mdast": "^4.0.4",
36
43
  "@types/node": "^24.10.13",
37
44
  "@types/react": "^19.2.14",
38
45
  "@types/react-dom": "^19.2.3",
@@ -46,6 +53,13 @@
46
53
  "typescript": "^5.9.3",
47
54
  },
48
55
  },
56
+ "packages/create-amytis": {
57
+ "name": "create-amytis",
58
+ "version": "0.1.0",
59
+ "bin": {
60
+ "create-amytis": "./dist/index.js",
61
+ },
62
+ },
49
63
  },
50
64
  "trustedDependencies": [
51
65
  "sharp",
@@ -558,7 +572,7 @@
558
572
 
559
573
  "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
560
574
 
561
- "commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
575
+ "commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
562
576
 
563
577
  "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
564
578
 
@@ -568,6 +582,8 @@
568
582
 
569
583
  "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="],
570
584
 
585
+ "create-amytis": ["create-amytis@workspace:packages/create-amytis"],
586
+
571
587
  "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
572
588
 
573
589
  "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
@@ -842,6 +858,8 @@
842
858
 
843
859
  "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="],
844
860
 
861
+ "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
862
+
845
863
  "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="],
846
864
 
847
865
  "hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="],
@@ -970,7 +988,7 @@
970
988
 
971
989
  "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="],
972
990
 
973
- "katex": ["katex@0.16.28", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg=="],
991
+ "katex": ["katex@0.16.33", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-q3N5u+1sY9Bu7T4nlXoiRBXWfwSefNGoKeOwekV+gw0cAXQlz2Ww6BLcmBxVDeXBMUDQv6fK5bcNaJLxob3ZQA=="],
974
992
 
975
993
  "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
976
994
 
@@ -1260,6 +1278,8 @@
1260
1278
 
1261
1279
  "rehype-slug": ["rehype-slug@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "github-slugger": "^2.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-to-string": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A=="],
1262
1280
 
1281
+ "rehype-stringify": ["rehype-stringify@10.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-html": "^9.0.0", "unified": "^11.0.0" } }, "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA=="],
1282
+
1263
1283
  "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
1264
1284
 
1265
1285
  "remark-math": ["remark-math@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-math": "^3.0.0", "micromark-extension-math": "^3.0.0", "unified": "^11.0.0" } }, "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA=="],
@@ -1504,6 +1524,8 @@
1504
1524
 
1505
1525
  "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="],
1506
1526
 
1527
+ "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
1528
+
1507
1529
  "d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="],
1508
1530
 
1509
1531
  "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="],
@@ -1520,16 +1542,20 @@
1520
1542
 
1521
1543
  "is-bun-module/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
1522
1544
 
1523
- "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
1524
-
1525
1545
  "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
1526
1546
 
1547
+ "mermaid/katex": ["katex@0.16.28", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg=="],
1548
+
1549
+ "micromark-extension-math/katex": ["katex@0.16.28", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg=="],
1550
+
1527
1551
  "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
1528
1552
 
1529
1553
  "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
1530
1554
 
1531
1555
  "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
1532
1556
 
1557
+ "rehype-katex/katex": ["katex@0.16.28", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg=="],
1558
+
1533
1559
  "sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
1534
1560
 
1535
1561
  "@eslint/eslintrc/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
@@ -0,0 +1,25 @@
1
+ <svg width="800" height="400" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="800" height="400" rx="12" fill="#f0fdf4"/>
3
+ <rect x="1" y="1" width="798" height="398" rx="12" fill="none" stroke="#86efac" stroke-width="2"/>
4
+
5
+ <!-- Decorative circles -->
6
+ <circle cx="120" cy="200" r="60" fill="#bbf7d0" opacity="0.6"/>
7
+ <circle cx="680" cy="200" r="60" fill="#bbf7d0" opacity="0.6"/>
8
+ <circle cx="400" cy="100" r="40" fill="#86efac" opacity="0.4"/>
9
+
10
+ <!-- Simple tree/plant shapes -->
11
+ <polygon points="120,220 95,270 145,270" fill="#4ade80"/>
12
+ <rect x="113" y="268" width="14" height="20" fill="#166534"/>
13
+ <polygon points="120,190 90,240 150,240" fill="#22c55e"/>
14
+
15
+ <polygon points="680,220 655,270 705,270" fill="#4ade80"/>
16
+ <rect x="673" y="268" width="14" height="20" fill="#166534"/>
17
+ <polygon points="680,190 650,240 710,240" fill="#22c55e"/>
18
+
19
+ <!-- Title text -->
20
+ <text x="400" y="175" text-anchor="middle" font-family="Georgia, serif" font-size="42" font-weight="bold" fill="#14532d">The Digital Garden</text>
21
+ <text x="400" y="225" text-anchor="middle" font-family="Georgia, serif" font-size="22" font-style="italic" fill="#166534">A place for ideas to grow</text>
22
+
23
+ <!-- Subtitle -->
24
+ <text x="400" y="310" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" fill="#4ade80">✦ cultivate · connect · contribute ✦</text>
25
+ </svg>
@@ -2,7 +2,7 @@
2
2
  title: "The Digital Garden Handbook"
3
3
  excerpt: "A comprehensive guide to building and maintaining your own digital garden, from initial setup to advanced techniques."
4
4
  date: "2026-01-15"
5
- coverImage: "text:DG"
5
+ coverImage: "/images/flowers.jpg"
6
6
  featured: true
7
7
  draft: false
8
8
  authors: ["Amytis"]
@@ -3,6 +3,8 @@ title: "Introduction to Digital Gardens"
3
3
  excerpt: "Understanding the philosophy and history behind digital gardens."
4
4
  ---
5
5
 
6
+ ![The Digital Garden](./images/digital-garden.svg)
7
+
6
8
  ## What is a Digital Garden?
7
9
 
8
10
  A digital garden is a personal online space where ideas are cultivated over time. Unlike a traditional blog with polished, finished posts, a digital garden embraces the concept of learning in public.
@@ -7,7 +7,7 @@ tags: ["features", "test", "demo"]
7
7
  authors: ["Amytis Team", "Tester"]
8
8
  layout: "post"
9
9
  latex: true
10
- toc: false
10
+ toc: true
11
11
  ---
12
12
 
13
13
  # Heading Level 1
@@ -128,6 +128,31 @@ sequenceDiagram
128
128
  ### Raw HTML (Quoted)
129
129
  <img src="./assets/test.svg" width="100" alt="Small HTML Image" style="border: 2px solid #ccc; border-radius: 8px;" />
130
130
 
131
+ ### Photos from Public Directory
132
+
133
+ ![Antelope Canyon](/images/antelope-canyon.jpg)
134
+
135
+ ![Mountain Landscape](/images/mountains.jpg)
136
+
137
+ ![Lake View](/images/lake.jpg)
138
+
139
+ ![Flowers](/images/flowers.jpg)
140
+
141
+ ![Cappadocia](/images/cappadocia.jpg)
142
+
143
+ ![Galaxy](/images/galaxy.jpg)
144
+
145
+ ### Vibrant Waves (AVIF format)
146
+
147
+ ![Vibrant Waves](/images/vibrant-waves.avif)
148
+
149
+ ### Side by Side (Raw HTML)
150
+
151
+ <div style="display: flex; gap: 1rem; flex-wrap: wrap;">
152
+ <img src="/images/antelope-canyon.jpg" alt="Antelope Canyon" style="width: 48%; border-radius: 8px;" />
153
+ <img src="/images/cappadocia.jpg" alt="Cappadocia" style="width: 48%; border-radius: 8px;" />
154
+ </div>
155
+
131
156
  ---
132
157
 
133
158
  ## Tables
@@ -5,8 +5,6 @@ excerpt: "Demonstrating support for standard .md files."
5
5
  category: "Meta"
6
6
  tags: ["markdown", "legacy"]
7
7
  author: "Old Timer"
8
- featured: true
9
- coverImage: "text:Legacy Markdown"
10
8
  ---
11
9
 
12
10
  # Legacy Markdown Support
@@ -5,8 +5,8 @@ excerpt: "Testing tables, task lists, and other GFM features."
5
5
  category: "Test"
6
6
  tags: ["markdown", "gfm"]
7
7
  author: "Tester"
8
- coverImage: "text:Markdown"
9
8
  featured: true
9
+ coverImage: "/images/cappadocia.jpg"
10
10
  ---
11
11
 
12
12
  # Extended Markdown Features
@@ -6,7 +6,6 @@ category: "测试"
6
6
  tags: ["chinese", "multilingual", "test"]
7
7
  authors: ["Amytis Team"]
8
8
  latex: true
9
- featured: true
10
9
  coverImage: "text:Multilingual"
11
10
  ---
12
11
 
@@ -6,6 +6,7 @@ category: "Showcase"
6
6
  tags: ["test", "code", "syntax-highlighting"]
7
7
  authors: ["Amytis Team"]
8
8
  featured: true
9
+ coverImage: "/images/antelope-canyon.jpg"
9
10
  toc: true
10
11
  externalLinks:
11
12
  - name: "Prism.js"
@@ -6,6 +6,7 @@ category: "Engineering"
6
6
  tags: ["react", "hooks", "frontend"]
7
7
  author: "Code Gardener"
8
8
  featured: true
9
+ coverImage: "/images/antelope-canyon.jpg"
9
10
  ---
10
11
 
11
12
  React Hooks revolutionized how we write components. Let's explore the core hooks.
@@ -2,7 +2,7 @@
2
2
  title: "AI Nexus Weekly"
3
3
  excerpt: "A curated weekly digest exploring the rapidly evolving landscape of Artificial Intelligence, from large language models to robotics."
4
4
  date: "2026-02-17"
5
- coverImage: "text:AI Nexus"
5
+ coverImage: "/images/galaxy.jpg"
6
6
  featured: true
7
7
  sort: "date-desc"
8
8
  ---
@@ -5,6 +5,8 @@ excerpt: "How ideas evolve from raw daily flows into articles, series, and books
5
5
  category: "Philosophy"
6
6
  tags: ["pkm", "knowledge management", "learning"]
7
7
  authors: ["Amytis Team"]
8
+ featured: true
9
+ coverImage: "/images/mountains.jpg"
8
10
  ---
9
11
 
10
12
  # The Philosophy of Digital Gardening
@@ -2,7 +2,7 @@
2
2
  title: "The Digital Garden"
3
3
  excerpt: "Exploring the philosophy and architecture of modern digital gardens. A journey from linear blogs to non-linear knowledge systems."
4
4
  date: "2026-02-11"
5
- coverImage: "text:Digital Garden"
5
+ coverImage: "/images/mountains.jpg"
6
6
  featured: true
7
7
  sort: "manual"
8
8
  posts: ["01-philosophy", "02-architecture"]
@@ -2,7 +2,7 @@
2
2
  title: "Markdown Showcase"
3
3
  excerpt: "A comprehensive tour of the rich content features supported by Amytis, from Mermaid diagrams to LaTeX math."
4
4
  date: "2026-02-13"
5
- coverImage: "text:MDX Showcase"
5
+ coverImage: "/images/lake.jpg"
6
6
  featured: true
7
7
  sort: "manual"
8
8
  posts: ["visuals-and-diagrams", "mathematical-notation", "syntax-highlighting"]
@@ -3,6 +3,8 @@ title: "Syntax Highlighting"
3
3
  date: "2026-02-15"
4
4
  category: "Engineering"
5
5
  tags: ["code", "typescript"]
6
+ featured: true
7
+ coverImage: "/images/vibrant-waves.avif"
6
8
  ---
7
9
 
8
10
  Amytis provides beautiful syntax highlighting for dozens of programming languages. Here are a few examples within the series context.
@@ -2,7 +2,8 @@
2
2
  title: "Part 1: Getting Started with Next.js 15"
3
3
  date: "2026-01-30"
4
4
  excerpt: "Setting up the environment and understanding the core philosophy."
5
- coverImage: "text:Part 1"
5
+ featured: true
6
+ coverImage: "/images/vibrant-waves.avif"
6
7
  ---
7
8
 
8
9
  # Getting Started
@@ -2,8 +2,8 @@
2
2
  title: "Next.js Deep Dive"
3
3
  excerpt: "A comprehensive guide to mastering Next.js 15, including advanced routing, data fetching, and performance optimization."
4
4
  date: "2026-01-30"
5
- coverImage: "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?auto=format&fit=crop&w=800&q=80"
6
5
  sort: "manual"
6
+ coverImage: "/images/cappadocia.jpg"
7
7
  posts:
8
8
  - "01-getting-started"
9
9
  - "02-routing-mastery"
@@ -0,0 +1,27 @@
1
+ ---
2
+ title: "Subscribe"
3
+ excerpt: "Stay updated with new posts and notes."
4
+ ---
5
+
6
+ Choose how you'd like to follow along.
7
+
8
+ ## RSS Feed
9
+
10
+ RSS is an open standard — no account required. Paste the feed URL into any reader app, or use one of the quick-subscribe links below.
11
+
12
+ <rss-feed></rss-feed>
13
+
14
+ **Popular readers:** [Follow](https://app.follow.is) · [Feedly](https://feedly.com) · [Inoreader](https://www.inoreader.com) · [NetNewsWire](https://netnewswire.com) · [NewsBlur](https://newsblur.com)
15
+
16
+ ## Newsletter
17
+
18
+ Subscribe via email to get new posts delivered to your inbox.
19
+
20
+ [Subscribe →](https://example.com)
21
+
22
+ ## Social
23
+
24
+ Follow for updates and discussions.
25
+
26
+ - [X (Twitter)](https://twitter.com)
27
+ - [GitHub](https://github.com)
@@ -1,120 +1,100 @@
1
1
  # Architecture Overview
2
2
 
3
- Amytis is a static site generator built with **Next.js 16 (App Router)**, designed to render a "digital garden" of Markdown/MDX content.
3
+ Amytis is a static-export-first Next.js 16 App Router project for Markdown/MDX publishing across posts, series, books, flows, and notes.
4
4
 
5
5
  ## Core Stack
6
6
 
7
- - **Framework:** Next.js 16.1.6 (App Router) with React 19
8
- - **Runtime:** Bun
9
- - **Styling:** Tailwind CSS v4 with CSS variables for theming
10
- - **Content:** Local `.md`/`.mdx` files parsed at build time
11
- - **Validation:** Zod for frontmatter schema validation
12
- - **Testing:** Bun Test
7
+ - Framework: Next.js 16.1.6 + React 19
8
+ - Runtime/tooling: Bun
9
+ - Styling: Tailwind CSS v4 + CSS variables + `next-themes`
10
+ - Content parsing: `gray-matter` + Zod validation in `src/lib/markdown.ts`
11
+ - Search: Pagefind (`/pagefind/pagefind.js` loaded at runtime)
12
+ - Tests: Bun test suites in `src/` and `tests/`
13
13
 
14
- ## Data Flow
14
+ ## Content Model
15
15
 
16
- 1. **Source:** Content lives in `content/posts/`, `content/series/`, `content/books/`, and `content/flows/`.
17
- 2. **Parsing:** `src/lib/markdown.ts` reads the file system using Node.js `fs`. It uses `gray-matter` to parse frontmatter, `zod` to validate the schema, and `image-size` to extract image dimensions.
18
- 3. **Filtering:** Draft content (`draft: true`) is excluded in production. Future-dated posts are hidden unless `showFuturePosts` is enabled.
19
- 4. **Rendering:**
20
- - **Lists:** `getAllPosts()` / `getAllBooks()` / `getAllFlows()` returns sorted metadata for listing pages.
21
- - **Single Post/Book:** `getPostBySlug(slug)` / `getBookData(slug)` reads a specific file with full content.
22
- - **Series:** `getSeriesPosts(slug)` fetches posts for a series.
23
- - **Book Chapters:** `getBookChapter(bookSlug, chapterSlug)` fetches individual chapter content.
24
- - **Flows:** `getFlowBySlug(slug)` fetches individual flow entries.
25
- - **Static Generation:** `generateStaticParams` is implemented in dynamic routes (`[slug]`, `[page]`, `[tag]`, `[author]`, `[year]`) to pre-render all pages at build time.
16
+ - `content/posts/`: standalone posts (`.md/.mdx`) and folder posts (`index.mdx`)
17
+ - `content/series/<slug>/`: series metadata (`index.mdx`) + series posts
18
+ - `content/books/<slug>/`: book metadata + chapter files
19
+ - `content/flows/YYYY/MM/DD.(md|mdx)`: daily flow entries
20
+ - `content/notes/`: evergreen notes
21
+ - `content/*.mdx`: static pages (about, links, subscribe, privacy, etc.)
26
22
 
27
- ## Route Structure
23
+ ## Runtime Data Flow
28
24
 
29
- ```
25
+ 1. Source files are read from disk by `src/lib/markdown.ts`.
26
+ 2. Frontmatter is parsed and validated (invalid frontmatter throws at build time).
27
+ 3. Draft/future filtering and sorting are applied (based on `site.config.ts`).
28
+ 4. Route files consume typed helpers (`getAllPosts`, `getBookData`, `getAllFlows`, `getAllNotes`, etc.).
29
+ 5. `generateStaticParams()` precomputes dynamic routes for static export.
30
+
31
+ ## Route Map (App Router)
32
+
33
+ ```text
30
34
  src/app/
31
- page.tsx # Homepage (hero, featured, latest)
32
- layout.tsx # Root layout (providers, navbar, footer)
33
- posts/
34
- [slug]/page.tsx # Individual post
35
- page.tsx # Posts listing
36
- series/
37
- page.tsx # All series overview
38
- [slug]/page.tsx # Single series
39
- books/
40
- page.tsx # All books overview
41
- [slug]/page.tsx # Book landing page
42
- [slug]/[chapter]/page.tsx # Individual book chapter
43
- notes/
44
- page.tsx # All notes list
45
- [slug]/page.tsx # Individual note
46
- graph/
47
- page.tsx # Knowledge graph visualization
48
- flows/
49
- page.tsx # Flows stream/listing
50
- [year]/[month]/[day]/page.tsx # Individual flow entry (optional if modal/stream used)
51
- tags/
52
- page.tsx # Tag cloud
53
- [tag]/page.tsx # Posts by tag
54
- authors/
55
- [author]/page.tsx # Posts by author
56
- archive/
57
- page.tsx # Chronological archive
58
- subscribe/
59
- page.tsx # Subscription options
60
- search.json/
61
- route.ts # Supplementary structured search data
62
- [slug]/page.tsx # Static pages (about, etc.)
35
+ page.tsx # Homepage
36
+ page/[page]/page.tsx # Homepage pagination
37
+ layout.tsx # Root layout/providers
38
+ posts/page.tsx # Posts index
39
+ posts/page/[page]/page.tsx # Posts pagination
40
+ posts/[slug]/page.tsx # Canonical post route
41
+ series/page.tsx # Series index
42
+ series/[slug]/page.tsx # Series detail
43
+ series/[slug]/page/[page]/page.tsx
44
+ books/page.tsx # Books index
45
+ books/[slug]/page.tsx # Book landing
46
+ books/[slug]/[chapter]/page.tsx # Book chapter
47
+ flows/page.tsx # Flow index
48
+ flows/page/[page]/page.tsx # Flow pagination
49
+ flows/[year]/page.tsx
50
+ flows/[year]/[month]/page.tsx
51
+ flows/[year]/[month]/[day]/page.tsx
52
+ notes/page.tsx # Notes index
53
+ notes/page/[page]/page.tsx
54
+ notes/[slug]/page.tsx
55
+ tags/page.tsx
56
+ tags/[tag]/page.tsx
57
+ authors/[author]/page.tsx
58
+ archive/page.tsx
59
+ graph/page.tsx
60
+ feed.xml/route.ts
61
+ search.json/route.ts
62
+ sitemap.ts
63
+ [slug]/page.tsx # Static pages + custom series listing path
64
+ [slug]/page/[page]/page.tsx # Custom series listing pagination
65
+ [slug]/[postSlug]/page.tsx # Custom post basePath/series post path
63
66
  ```
64
67
 
65
- ## Component Architecture
66
-
67
- ### Layout Components
68
- - **`Navbar`** - Config-driven navigation with series dropdown, search trigger, theme toggle, and language switch.
69
- - **`Footer`** - Social links, copyright, site metadata, and language switch.
70
- - **`Hero`** - Homepage hero with collapsible intro.
71
- - **`BookLayout`** - Dedicated layout for book chapters with sidebar navigation.
72
- - **`PageHeader`** - Consistent header for simple pages (Archive, Tags, About).
73
-
74
- ### Content Display
75
- - **`PostList`** - Card-based post listing.
76
- - **`FlowContent`** - Stream-based display for daily notes with timeline grouping.
77
- - **`SeriesCatalog`** - Timeline-style post listing for series.
78
- - **`PostCard`** - Individual post preview card.
79
- - **`CoverImage`** - Optimized image component using `next-image-export-optimizer` with dynamic desaturated gradients.
80
- - **`ShareBar`** - Social sharing buttons for posts and books.
81
- - **`TagContentTabs`** - Tabbed interface for filtering posts vs flows by tag.
82
-
83
- ### Content Rendering
84
- - **`MarkdownRenderer`** - Core rendering component using `react-markdown` with plugins (GFM, Math, Raw HTML).
85
- - **`CodeBlock`** - Syntax-highlighted code blocks with copy button.
86
- - **`Mermaid`** - Client-side Mermaid diagram rendering.
87
-
88
- ### Navigation & Discovery
89
- - **`TableOfContents`** - Sticky TOC with scroll-based tracking.
90
- - **`Search`** - Full-text search modal (Cmd/Ctrl+K) powered by Pagefind. Supports type filter tabs (All/Post/Flow/Book), recent searches, keyboard navigation, debounced input, focus trap, and ARIA accessibility. Search syntax: `"exact phrase"`, `word1 word2` (AND), `-exclude`.
91
- - **`Pagination`** - Previous/Next page navigation.
92
- - **`ReadingProgressBar`** - Top-of-page progress bar for book chapters.
93
- - **`SubscribePage`** - Centralized page for all subscription options.
94
-
95
- ## Data Access Layer (`src/lib/markdown.ts`)
96
-
97
- | Function | Returns | Purpose |
98
- |----------|---------|---------|
99
- | `getAllPosts()` | `PostData[]` | All posts, filtered and sorted |
100
- | `getAllFlows()` | `FlowData[]` | All daily notes, sorted by date |
101
- | `getAllBooks()` | `BookData[]` | All books metadata |
102
- | `getBookData(slug)` | `BookData \| null` | Single book metadata & TOC |
103
- | `getBookChapter(...)` | `BookChapterData` | Single chapter content |
104
- | `getAllNotes()` | `NoteData[]` | All notes, sorted by date |
105
- | `getNoteBySlug(slug)` | `NoteData \| null` | Single note content |
106
- | `getBacklinks(slug)` | `BacklinkSource[]` | Inbound links for a page |
107
- | `getFlowBySlug(slug)` | `FlowData \| null` | Single flow entry |
108
- | `getSeriesPosts(slug)` | `PostData[]` | Posts in a series |
109
- | `calculateReadingTime(content)` | `string` | Estimated reading time (multilingual) |
110
-
111
- ## Theming
112
-
113
- Theming is handled by `next-themes` and Tailwind CSS v4. `src/app/globals.css` defines CSS variables for palettes (`default`, `blue`, `rose`, `amber`).
68
+ ## URL Routing Rules
69
+
70
+ - `next.config.ts` sets `output: "export"` and `trailingSlash: true`.
71
+ - Post URLs use `getPostUrl()` in `src/lib/urls.ts`:
72
+ - Default: `/<posts.basePath>/<post.slug>` (basePath defaults to `posts`)
73
+ - Series override: `/<series.customPaths[seriesSlug]>/<post.slug>`
74
+ - Dynamic route params should return raw segment values from `generateStaticParams()` (do not pre-encode values).
75
+
76
+ ## Key Components
77
+
78
+ - Layout/navigation: `Navbar`, `Footer`, `Hero`, `FlowHubTabs`
79
+ - Content renderers: `MarkdownRenderer`, `CodeBlock`, `Mermaid`
80
+ - Post surfaces: `PostLayout`, `PostSidebar`, `PostCard`, `RelatedPosts`, `ShareBar`
81
+ - Notes/flows discovery: `NoteSidebar`, `FlowContent`, `FlowCalendarSidebar`, `TagContentTabs`
82
+ - Search/discovery: `Search`, `Pagination`, `KnowledgeGraph`
83
+
84
+ ## Data Layer Highlights (`src/lib/markdown.ts`)
85
+
86
+ - Posts/series: `getAllPosts`, `getListingPosts`, `getPostBySlug`, `getSeriesPosts`, `getSeriesData`
87
+ - Books: `getAllBooks`, `getBookData`, `getBookChapter`
88
+ - Flows: `getAllFlows`, `getFlowBySlug`, `getFlowsByYear`, `getFlowsByMonth`
89
+ - Notes: `getAllNotes`, `getNoteBySlug`, `getNotesByTag`
90
+ - Discovery: `buildSlugRegistry`, `getBacklinks`, `getAllTags`, `getAllAuthors`
114
91
 
115
92
  ## Build Pipeline
116
93
 
117
- 1. **`scripts/copy-assets.ts`** - Copies images from content directories (`posts/`, `series/`, `books/`, `flows/`) to `public/posts/` for static hosting.
118
- 2. **`next build`** - Next.js static export to `out/`.
119
- 3. **`next-image-export-optimizer`** - Generates optimized WebP variants (production build only).
120
- 4. **`pagefind --site out`** - Crawls the exported HTML and builds a full-text search index in `out/pagefind/`. The index is loaded at runtime by `src/components/Search.tsx` via a dynamic import. Pure utility functions used by the search component and the search index route live in `src/lib/search-utils.ts`.
94
+ 1. `bun scripts/copy-assets.ts`: copy co-located media into `public/`
95
+ 2. `bun run build:graph`: generate graph data
96
+ 3. `next build`: static export to `out/`
97
+ 4. Production only (`bun run build`): `next-image-export-optimizer`
98
+ 5. Pagefind indexing:
99
+ - Production: `pagefind --site out` (writes to `out/pagefind`)
100
+ - Dev build: `pagefind --site out --output-path public/pagefind`
@@ -62,9 +62,6 @@ Notes live in `content/notes/`.
62
62
  ```bash
63
63
  # Create a new note using the dedicated script
64
64
  bun run new-note "Zettelkasten Method"
65
-
66
- # Or using the general new script
67
- bun run new "Zettelkasten Method" --note
68
65
  ```
69
66
 
70
67
  ### Importing Content
@@ -96,7 +93,9 @@ bun run series-draft "my-series" --undraft
96
93
  ```bash
97
94
  bun test # Run all tests
98
95
  bun run test:unit # Run unit tests
96
+ bun run test:int # Run integration tests
99
97
  bun run test:e2e # Run end-to-end tests
98
+ bun run validate # Lint + test + build:dev
100
99
  ```
101
100
 
102
101
  ## Building
@@ -106,6 +105,15 @@ bun run build # Production build (includes image optimization and P
106
105
  bun run build:dev # Development build (faster, no image optimization; also generates Pagefind index)
107
106
  ```
108
107
 
108
+ ## Static Export Routing Rules
109
+
110
+ Amytis relies on static export (`output: "export"`) with `trailingSlash: true`.
111
+
112
+ - In `generateStaticParams()`, return raw segment values and let Next.js handle encoding.
113
+ - Do not pre-encode route params with `encodeURIComponent`.
114
+ - Do not link to placeholder routes such as `/posts/[slug]`; always link to concrete URLs.
115
+ - When touching dynamic routes, verify both ASCII and Unicode slugs.
116
+
109
117
  ## Code Style
110
118
 
111
119
  - **Linting:** `bun run lint`