@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.
- package/.entire/settings.json +4 -0
- package/AGENTS.md +13 -7
- package/CHANGELOG.md +41 -0
- package/CLAUDE.md +30 -2
- package/README.md +84 -58
- package/README.zh.md +195 -0
- package/TODO.md +6 -4
- package/bun.lock +30 -4
- package/content/books/sample-book/images/digital-garden.svg +25 -0
- package/content/books/sample-book/index.mdx +1 -1
- package/content/books/sample-book/introduction.mdx +2 -0
- package/content/posts/2026-01-21-kitchen-sink/index.mdx +26 -1
- package/content/posts/legacy-markdown.md +0 -2
- package/content/posts/markdown-features.mdx +1 -1
- package/content/posts/multilingual-test.mdx +0 -1
- package/content/posts/syntax-highlighting-showcase.mdx +1 -0
- package/content/posts/understanding-react-hooks.mdx +1 -0
- package/content/series/ai-nexus-weekly/index.mdx +1 -1
- package/content/series/digital-garden/01-philosophy.mdx +2 -0
- package/content/series/digital-garden/index.mdx +1 -1
- package/content/series/markdown-showcase/index.mdx +1 -1
- package/content/series/markdown-showcase/syntax-highlighting.mdx +2 -0
- package/content/series/nextjs-deep-dive/01-getting-started.mdx +2 -1
- package/content/series/nextjs-deep-dive/index.mdx +1 -1
- package/content/subscribe.mdx +27 -0
- package/docs/ARCHITECTURE.md +85 -105
- package/docs/CONTRIBUTING.md +11 -3
- package/docs/DIGITAL_GARDEN.md +11 -9
- package/docs/deployment.md +31 -13
- package/eslint.config.mjs +2 -0
- package/package.json +13 -4
- package/packages/create-amytis/package.json +13 -0
- package/packages/create-amytis/src/index.test.ts +187 -0
- package/packages/create-amytis/src/index.ts +230 -0
- package/packages/create-amytis/tsconfig.json +14 -0
- package/public/images/amytis-screenshot.jpg +0 -0
- package/public/images/antelope-canyon.jpg +0 -0
- package/public/images/avatar.jpg +0 -0
- package/public/images/cappadocia.jpg +0 -0
- package/public/images/flowers.jpg +0 -0
- package/public/images/galaxy.jpg +0 -0
- package/public/images/lake.jpg +0 -0
- package/public/images/mountains.jpg +0 -0
- package/public/images/vibrant-waves.avif +0 -0
- package/public/images/wechat-qr.jpg +0 -0
- package/public/next-image-export-optimizer-hashes.json +72 -2
- package/scripts/deploy.ts +77 -0
- package/site.config.example.ts +268 -0
- package/site.config.ts +82 -10
- package/src/app/[slug]/[postSlug]/page.tsx +161 -0
- package/src/app/[slug]/page/[page]/page.tsx +202 -0
- package/src/app/[slug]/page.tsx +162 -7
- package/src/app/archive/page.tsx +5 -8
- package/src/app/books/[slug]/[chapter]/page.tsx +45 -3
- package/src/app/books/[slug]/page.tsx +16 -2
- package/src/app/books/page.tsx +12 -8
- package/src/app/feed.atom/route.ts +62 -0
- package/src/app/feed.xml/route.ts +45 -20
- package/src/app/flows/[year]/[month]/[day]/page.tsx +9 -1
- package/src/app/flows/[year]/page.tsx +1 -1
- package/src/app/flows/page/[page]/page.tsx +2 -0
- package/src/app/flows/page.tsx +4 -0
- package/src/app/globals.css +9 -3
- package/src/app/layout.tsx +22 -1
- package/src/app/notes/[slug]/page.tsx +2 -3
- package/src/app/page.tsx +86 -8
- package/src/app/posts/[slug]/page.tsx +33 -7
- package/src/app/posts/page/[page]/page.tsx +9 -5
- package/src/app/posts/page.tsx +13 -7
- package/src/app/series/[slug]/page/[page]/page.tsx +8 -25
- package/src/app/series/[slug]/page.tsx +21 -19
- package/src/app/series/page.tsx +14 -28
- package/src/app/sitemap.ts +2 -1
- package/src/app/tags/[tag]/page.tsx +1 -1
- package/src/app/tags/page.tsx +1 -1
- package/src/components/Analytics.tsx +34 -36
- package/src/components/AuthorCard.tsx +56 -14
- package/src/components/BookMobileNav.tsx +8 -34
- package/src/components/BookSidebar.tsx +17 -107
- package/src/components/CodeBlock.test.tsx +19 -0
- package/src/components/CodeBlock.tsx +20 -16
- package/src/components/Comments.tsx +4 -3
- package/src/components/CoverImage.tsx +11 -3
- package/src/components/CuratedSeriesSection.tsx +17 -15
- package/src/components/ExternalLinks.tsx +1 -1
- package/src/components/FeaturedStoriesSection.tsx +124 -58
- package/src/components/FlowCalendarSidebar.tsx +10 -7
- package/src/components/FlowContent.tsx +33 -5
- package/src/components/Footer.tsx +28 -6
- package/src/components/Hero.tsx +44 -92
- package/src/components/InlineBookToc.tsx +56 -0
- package/src/components/KnowledgeGraph.tsx +26 -0
- package/src/components/LanguageProvider.tsx +7 -2
- package/src/components/LanguageSwitch.tsx +1 -1
- package/src/components/LatestWritingSection.tsx +12 -22
- package/src/components/MarkdownRenderer.test.tsx +41 -0
- package/src/components/MarkdownRenderer.tsx +43 -8
- package/src/components/Navbar.tsx +24 -15
- package/src/components/NoteSidebar.tsx +6 -76
- package/src/components/Pagination.tsx +2 -2
- package/src/components/PostCard.tsx +2 -1
- package/src/components/PostList.tsx +81 -62
- package/src/components/PostNavigation.tsx +8 -7
- package/src/components/PostSidebar.tsx +12 -107
- package/src/components/PrevNextNav.tsx +63 -0
- package/src/components/RecentNotesSection.tsx +2 -2
- package/src/components/RelatedPosts.tsx +3 -2
- package/src/components/RssFeedWidget.tsx +55 -0
- package/src/components/SectionLabel.tsx +16 -0
- package/src/components/SelectedBooksSection.tsx +12 -15
- package/src/components/SeriesCatalog.tsx +74 -69
- package/src/components/SeriesList.tsx +11 -36
- package/src/components/SeriesSidebar.tsx +4 -2
- package/src/components/TagContentTabs.tsx +17 -12
- package/src/components/TagPageHeader.tsx +12 -2
- package/src/components/TagSidebar.tsx +3 -2
- package/src/components/TagsIndexClient.tsx +1 -1
- package/src/components/TocPanel.tsx +67 -0
- package/src/hooks/useActiveHeading.ts +32 -0
- package/src/hooks/useSidebarAutoScroll.ts +19 -0
- package/src/i18n/translations.ts +26 -4
- package/src/layouts/BookLayout.tsx +10 -40
- package/src/layouts/PostLayout.tsx +37 -26
- package/src/layouts/SimpleLayout.tsx +5 -5
- package/src/lib/feed-utils.ts +68 -0
- package/src/lib/format-utils.test.ts +27 -0
- package/src/lib/format-utils.ts +3 -0
- package/src/lib/image-utils.ts +12 -0
- package/src/lib/json-ld.ts +236 -0
- package/src/lib/markdown.ts +62 -7
- package/src/lib/rehype-image-metadata.test.ts +124 -0
- package/src/lib/rehype-image-metadata.ts +21 -5
- package/src/lib/scroll-utils.ts +11 -0
- package/src/lib/shuffle.ts +21 -0
- package/src/lib/urls.ts +51 -0
- package/tests/e2e/navigation.test.ts +15 -1
- package/tests/integration/books.test.ts +61 -0
- package/tests/integration/feed-utils.test.ts +145 -0
- package/tests/integration/series.test.ts +9 -0
- package/tests/unit/static-params.test.ts +59 -2
- package/src/app/subscribe/page.tsx +0 -17
- package/src/components/SubscribePage.tsx +0 -298
- /package/public/{screenshot.png → images/screenshot.png} +0 -0
|
@@ -1,4 +1,74 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"/
|
|
2
|
+
"books/agentic-design-patterns/images/appendix-a/image1.png": "k8G1J92r2m-rIbN+EDXjtOtXL3Im9pfIF9E+N4gR6+o=",
|
|
3
|
+
"books/agentic-design-patterns/images/appendix-b/image1.png": "j7jY3StmUJFaDcCc37qmkMNApRPw-hqtxo6xGMEvvSI=",
|
|
4
|
+
"books/agentic-design-patterns/images/appendix-d/image1.png": "Jz-nZ-kZPaM1XYPAzWY8jJRrmZHH3TOZKMVX6hD-G0I=",
|
|
5
|
+
"books/agentic-design-patterns/images/appendix-d/image2.png": "flQCnHTEIqAftdXjvY3gwc+OYbcZDaqwr3rtxDU7PjE=",
|
|
6
|
+
"books/agentic-design-patterns/images/appendix-d/image3.png": "FPPSaVvnYJhlRplp25grIwI0+vmtc9NHl+6EcpXIAic=",
|
|
7
|
+
"books/agentic-design-patterns/images/appendix-d/image4.png": "TvxBNTKaZCho+W8eTm67fiYmSwDJzwj1AOOguIV+o04=",
|
|
8
|
+
"books/agentic-design-patterns/images/appendix-d/image5.png": "EPlVRP7VlPQqkF9fawSV43kQg7JKVsjy-dHLFH6LIBY=",
|
|
9
|
+
"books/agentic-design-patterns/images/appendix-d/image6.png": "s7zF5TgNEQfvkakIHhdHXQ7S5imi4+AOtaD+PG7w0xM=",
|
|
10
|
+
"books/agentic-design-patterns/images/appendix-g/image1.png": "r-KvQvaqGSwZrN+30Dg6KZx4dEUzKZwPJ+eezw-KH70=",
|
|
11
|
+
"books/agentic-design-patterns/images/chapter-1/image1.png": "yfUwbocNorvSCdvdxTqQZmmiZsoRWbsn9iby6iKJYFQ=",
|
|
12
|
+
"books/agentic-design-patterns/images/chapter-1/image2.png": "pisZVOmTdn1Y+qs7sAqTqn04BeR3UngCd17kApWgehk=",
|
|
13
|
+
"books/agentic-design-patterns/images/chapter-10/image1.png": "aKJwlDxhZkAglLFTGgT8qpjIAlNQ5gV5ovCAnBjwx7M=",
|
|
14
|
+
"books/agentic-design-patterns/images/chapter-11/image1.png": "n9ou0BpTuPt3kcTEnCJEQRnMKFd9lI666DBxWDJzd3A=",
|
|
15
|
+
"books/agentic-design-patterns/images/chapter-11/image2.png": "30uw9sGMGnE+jyt++vVURBUQNXe5ty+8i8h+PMw4GfM=",
|
|
16
|
+
"books/agentic-design-patterns/images/chapter-12/image1.png": "4e4AD2MJFSB3GByGxGVr4VCQ4H+eYkR3oq39KuZhzw8=",
|
|
17
|
+
"books/agentic-design-patterns/images/chapter-12/image2.png": "1MyjXYovLBeKJmUGnUnx38T-UgkhRfV5opzOcP+OEdw=",
|
|
18
|
+
"books/agentic-design-patterns/images/chapter-13/image1.png": "PB3msIGhOmq1Oyrv6ubGJZJCvO+dD7XNxZiw5Ck-834=",
|
|
19
|
+
"books/agentic-design-patterns/images/chapter-14/image1.png": "p-06-D2Vp2x7HMXwmkTNLneS9sjsfSvmTsLTlVUjY5k=",
|
|
20
|
+
"books/agentic-design-patterns/images/chapter-14/image2.png": "XvXKYbuijYChfq4+MlZYXLBpxTg11ddJ0AFnzvntLPw=",
|
|
21
|
+
"books/agentic-design-patterns/images/chapter-14/image3.png": "YhmOWyJo3pDKVbKW7RMgTbNrbmsE89RkmaJ2z76vhos=",
|
|
22
|
+
"books/agentic-design-patterns/images/chapter-14/image4.png": "QBNe+Wt65qdLfkC5MJqaiNBJGMa-DbCm3gCOqfB7ehk=",
|
|
23
|
+
"books/agentic-design-patterns/images/chapter-15/image1.png": "I90CSqCb8g-h1F0a3sDusDC+LmHdjJTBBk6iFw4ZnJY=",
|
|
24
|
+
"books/agentic-design-patterns/images/chapter-15/image2.png": "HaxOSZ1f67BhEPa3KXtYAlK0cNm3G4H24dnLXpcgyA0=",
|
|
25
|
+
"books/agentic-design-patterns/images/chapter-16/image1.png": "Blc0vDhlie6PM3f6QSUX3argbfezTNDOLYhmT9uuS6E=",
|
|
26
|
+
"books/agentic-design-patterns/images/chapter-16/image2.png": "9-KZ0VOekDwFVL1dEzB9aleyCYF+WU6bx1h3aVNoOAM=",
|
|
27
|
+
"books/agentic-design-patterns/images/chapter-17/image1.png": "jjGM2Ll5bdszl9xFThVxxJzcq76iYDM1zz8qq2MVVYE=",
|
|
28
|
+
"books/agentic-design-patterns/images/chapter-17/image2.png": "O1KLKrodQew2Pdd7k3vBZhE4dCiK9u6JLvXSXy7h1do=",
|
|
29
|
+
"books/agentic-design-patterns/images/chapter-17/image3.png": "Sd9qFn-U5g8dhinNPJEk5LaeaN8DFH2FCus1Wcm6oSg=",
|
|
30
|
+
"books/agentic-design-patterns/images/chapter-17/image4.png": "eV38Dg78Y2Qo-0sqlsvcK8-p19fgRJqwx42sBKTzC+s=",
|
|
31
|
+
"books/agentic-design-patterns/images/chapter-17/image5.png": "bgKPC1TSVlvfkCG5vILT-4j9gJU7LyeGFkXO+8OFdXE=",
|
|
32
|
+
"books/agentic-design-patterns/images/chapter-17/image6.png": "qMku-C8gxZEFQEl8Qq8Y1ld5zUntH1Q-2izcb4QxLA0=",
|
|
33
|
+
"books/agentic-design-patterns/images/chapter-17/image7.png": "20wopLVN59okB+C-F+U5vi79Lg0uH+5P6wIj72gV7wU=",
|
|
34
|
+
"books/agentic-design-patterns/images/chapter-18/image1.png": "kjCIoNU60icXMOHmYNXWNMjdi5nIdJZYDSp0WFnG2Es=",
|
|
35
|
+
"books/agentic-design-patterns/images/chapter-19/image1.png": "XrQVFAWWsMcwKHgcqx8jGLy4q+6RbiGZ5CuKpRlk4Y0=",
|
|
36
|
+
"books/agentic-design-patterns/images/chapter-19/image2.png": "URHY+cH5AR4KvA6HPnXPhZd5fwwGdlwvFvxgCdTkWjg=",
|
|
37
|
+
"books/agentic-design-patterns/images/chapter-19/image3.png": "VLp1qFNcaj126-RuATV6ikSsow8lrXsn3EuZotvR5N4=",
|
|
38
|
+
"books/agentic-design-patterns/images/chapter-19/image4.png": "CnyNMtPiFSdzqrewwLgPu2X1CKdgMqCl8T8+d7lw2Fw=",
|
|
39
|
+
"books/agentic-design-patterns/images/chapter-2/image1.png": "C6odPDB9zhN28YxfU909JAoQWe0mRm3lUsyG8NzHBWk=",
|
|
40
|
+
"books/agentic-design-patterns/images/chapter-20/image1.png": "qkzjKCUjVFD8IWyoZQXuBuZ4zFJ0Y0CQfKepVcrnEN0=",
|
|
41
|
+
"books/agentic-design-patterns/images/chapter-21/image1.png": "3KpW6uLQDru7DFDdgi6ntD5WhuGS383aYwlQLWoVX+w=",
|
|
42
|
+
"books/agentic-design-patterns/images/chapter-21/image2.png": "wSXMrRuAnhQcEM5fg-sJ5ZikM3olj0vz6FDKWKIUyW8=",
|
|
43
|
+
"books/agentic-design-patterns/images/chapter-3/image1.png": "iHnUE2kckwzB0xiIySSe4O34c6bIs3VlO7pUQBmV8iI=",
|
|
44
|
+
"books/agentic-design-patterns/images/chapter-3/image2.png": "9-G6F+aBymQVTnDQy4zwsXDZMkLaYKBe4xw26tC24R8=",
|
|
45
|
+
"books/agentic-design-patterns/images/chapter-4/image1.png": "8h0PBlqt+JFo7Y803cA9MgfTe8TZeZd6te9l+WEsIss=",
|
|
46
|
+
"books/agentic-design-patterns/images/chapter-4/image2.png": "p-arzpUPFdnKmzTOKaDQys5Eml0jHA7VuUV0FyLYpqM=",
|
|
47
|
+
"books/agentic-design-patterns/images/chapter-5/image1.png": "XE5qhAklrW-7eVf7T9x9vo4xQX3WesZjZ9IjuyeSUkc=",
|
|
48
|
+
"books/agentic-design-patterns/images/chapter-5/image2.png": "MqUdsG5aXQECft+lsCRTeMIJP-YzxWFHC8HdNzio6aw=",
|
|
49
|
+
"books/agentic-design-patterns/images/chapter-6/image1.png": "t6mFrbVSNJYF73t+Q+YydXdwRQr1c4yIUkySvCelw1I=",
|
|
50
|
+
"books/agentic-design-patterns/images/chapter-6/image2.png": "zvwqWis6Ad7Iv1-wgYtTgz2ItXsno2PetcHRHon0-wY=",
|
|
51
|
+
"books/agentic-design-patterns/images/chapter-6/image3.png": "al-H2S7UznxQzP3KT3N0lCTNd83B45bqbPppuhSphy0=",
|
|
52
|
+
"books/agentic-design-patterns/images/chapter-6/image4.png": "u2k2jHjTOABLTlJ3OsxkGyCoOR-fwHeDmTXMACbtQ+4=",
|
|
53
|
+
"books/agentic-design-patterns/images/chapter-7/image1.png": "izX9YjrJ+gHGOiR9RHbuMIupCE2zyB01w-gwJAJr4Is=",
|
|
54
|
+
"books/agentic-design-patterns/images/chapter-7/image2.png": "-0zrZR8CNZqtuyaBRwUbZSfthKwuqrBu4fLar9L4Clg=",
|
|
55
|
+
"books/agentic-design-patterns/images/chapter-7/image3.png": "dKr7VICWOTc-ZRhm4Aaokz+xPZ8IXS0SiDOJeF1Y03c=",
|
|
56
|
+
"books/agentic-design-patterns/images/chapter-8/image1.png": "GWG47Om+9GwRvl8x-X269Sa-Mv3rqkW2xkx1WIB-I-E=",
|
|
57
|
+
"books/agentic-design-patterns/images/chapter-9/image1.png": "fIZ4QE7EIQSAv0K2GmU99OmTBAkpvnYUsFFScCv6VPI=",
|
|
58
|
+
"books/agentic-design-patterns/images/chapter-9/image2.png": "6y-8OjY7oEedk5ZhUUU2YIU3tjMxIzpnxcDxIRWPGpI=",
|
|
59
|
+
"books/agentic-design-patterns/images/chapter-9/image3.png": "69nSkOOPfX9FkIJGhKIrhSuCOZt29L5UAukVXfMXN2E=",
|
|
60
|
+
"books/agentic-design-patterns/images/chapter-9/image4.png": "GQCpjBMz5DRxsHIA8-73IMsxeBPc5ZTMaIsx2iihMME=",
|
|
61
|
+
"books/agentic-design-patterns/images/cover.png": "R47dqw-ws79m6VBfzhWuLi9ihoCaU7JcezquBAB1-bQ=",
|
|
62
|
+
"images/amytis-screenshot.jpg": "t9tzciqJPsNz1hlixAFLrrvD4Fsaih2Kbd1Jd2C-fqs=",
|
|
63
|
+
"images/antelope-canyon.jpg": "xuP3HlbwqA4z1Hsq7s1u2u61yN3SHzIMUd6Wl5yqgK8=",
|
|
64
|
+
"images/avatar.jpg": "SUGxOhDUD96YiVxZJkVJ6ou4W5MdCJ9hTBQMSQanZOM=",
|
|
65
|
+
"images/cappadocia.jpg": "EE2Mt8d9ERKd40SfwyhjFsejZLhFx1SfSAQmlu+TQOo=",
|
|
66
|
+
"images/flowers.jpg": "Lq9dhpZvAIgkYnijDsFIzrKiv8trdM-cUnl-5Y86PuM=",
|
|
67
|
+
"images/galaxy.jpg": "-3YwMkU3PGsm4P2yNsA2S02XRL1j0KbBh6nWKwy0hYc=",
|
|
68
|
+
"images/lake.jpg": "imAORQhxpmoU3jzKBMNFJuFSa0UgiSf2Dmea5Rj8-8M=",
|
|
69
|
+
"images/mountains.jpg": "FsrkZws9EKMqHCk1Hc6i6nEIcTRcrMBa4ddgqR6oRaI=",
|
|
70
|
+
"images/screenshot.png": "FAqbAgLRbWbYq9yJ4iggq2aKxRD8hdeDICc3DI14yhg=",
|
|
71
|
+
"images/vibrant-waves.avif": "pP6wNch0dvCr+HGjkA8zIT6ZuZCSSofiVkG922udk54=",
|
|
72
|
+
"images/wechat-qr.jpg": "DNIzz0Wcl8WP0h-jHHgZ9LEvf3ZKOXHgxlvpw3gK2ME=",
|
|
73
|
+
"posts/02-routing-mastery/assets/m-p-model.png": "fDmvlEkZnE-UCvPK4gmDkJD7SU8coOTl4iw5hpsmcWI="
|
|
4
74
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { spawnSync } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
|
|
4
|
+
// ─── Argument parsing ─────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
function flag(name: string): string | undefined {
|
|
7
|
+
const idx = process.argv.indexOf(`--${name}`);
|
|
8
|
+
if (idx === -1) return undefined;
|
|
9
|
+
const value = process.argv[idx + 1];
|
|
10
|
+
// Treat missing or another flag as absent
|
|
11
|
+
return value && !value.startsWith('--') ? value : undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Flags take priority over .env / .env.local values (loaded automatically by Bun)
|
|
15
|
+
const host = flag('host') ?? process.env.DEPLOY_HOST;
|
|
16
|
+
const user = flag('user') ?? process.env.DEPLOY_USER;
|
|
17
|
+
const password = flag('password') ?? process.env.DEPLOY_PASSWORD;
|
|
18
|
+
const path = flag('path') ?? process.env.DEPLOY_PATH ?? '/var/www/html';
|
|
19
|
+
|
|
20
|
+
// ─── Validation ───────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
const missing = (['host', 'user', 'password'] as const).filter(k => !({ host, user, password }[k]));
|
|
23
|
+
if (missing.length > 0) {
|
|
24
|
+
console.error(`Missing required value(s): ${missing.map(k => `--${k}`).join(', ')}`);
|
|
25
|
+
console.error('');
|
|
26
|
+
console.error('Provide them as flags or set in .env.local:');
|
|
27
|
+
console.error(' DEPLOY_HOST=<ip>');
|
|
28
|
+
console.error(' DEPLOY_USER=<user>');
|
|
29
|
+
console.error(' DEPLOY_PASSWORD=<password>');
|
|
30
|
+
console.error(' DEPLOY_PATH=<remote-path> (optional, default: /var/www/html)');
|
|
31
|
+
console.error('');
|
|
32
|
+
console.error('Usage: bun run deploy [--host <ip>] [--user <user>] [--password <pass>] [--path <path>]');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!fs.existsSync('out')) {
|
|
37
|
+
console.error('Error: out/ directory not found. Run `bun run build` first.');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const check = spawnSync('which', ['sshpass']);
|
|
42
|
+
if (check.status !== 0) {
|
|
43
|
+
console.error('Error: sshpass not found. Install it first:');
|
|
44
|
+
console.error(' macOS: brew install hudochenkov/sshpass/sshpass');
|
|
45
|
+
console.error(' Linux: apt install sshpass');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─── Deploy ───────────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
const destination = `${user}@${host}:${path}/`;
|
|
52
|
+
console.log(`Deploying out/ → ${destination}`);
|
|
53
|
+
|
|
54
|
+
const rsync = spawnSync(
|
|
55
|
+
'sshpass',
|
|
56
|
+
['-p', password!, 'rsync', '-avz', '--delete', '-e', 'ssh -o StrictHostKeyChecking=accept-new', 'out/', destination],
|
|
57
|
+
{ stdio: 'inherit' }
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (rsync.status !== 0) {
|
|
61
|
+
console.error('\nDeploy failed.');
|
|
62
|
+
process.exit(rsync.status ?? 1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log('\nReloading nginx...');
|
|
66
|
+
|
|
67
|
+
const reload = spawnSync(
|
|
68
|
+
'sshpass',
|
|
69
|
+
['-p', password!, 'ssh', '-o', 'StrictHostKeyChecking=accept-new', `${user}@${host}`, 'nginx -s reload'],
|
|
70
|
+
{ stdio: 'inherit' }
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (reload.status !== 0) {
|
|
74
|
+
console.warn('Warning: nginx reload failed (site was still deployed).');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log('Done.');
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
// Single-language starter template for Amytis.
|
|
2
|
+
// Copy this file to site.config.ts and customize it for your site.
|
|
3
|
+
//
|
|
4
|
+
// This template has i18n disabled. All locale-aware fields use plain strings.
|
|
5
|
+
// To add multi-language support later, switch to { en: '...', zh: '...' } objects
|
|
6
|
+
// and set i18n.enabled: true with your locales.
|
|
7
|
+
|
|
8
|
+
export interface NavChildItem {
|
|
9
|
+
name: string;
|
|
10
|
+
url: string;
|
|
11
|
+
external?: boolean;
|
|
12
|
+
dividerBefore?: boolean; // render a separator line before this item
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface NavItem {
|
|
16
|
+
name: string;
|
|
17
|
+
url: string;
|
|
18
|
+
weight: number;
|
|
19
|
+
external?: boolean;
|
|
20
|
+
dropdown?: string[];
|
|
21
|
+
children?: NavChildItem[]; // static sub-links rendered as a dropdown
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Defined up-front so nav and posts config stay in sync — change once here
|
|
25
|
+
const postsBasePath = 'posts'; // Change to e.g. 'articles' to serve all posts at /articles/[slug]
|
|
26
|
+
|
|
27
|
+
// Defined up-front so footer.connect can reference these URLs without duplication
|
|
28
|
+
const social = {
|
|
29
|
+
github: "https://github.com/your-username",
|
|
30
|
+
twitter: "https://twitter.com/your-username",
|
|
31
|
+
email: "mailto:you@example.com",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const siteConfig = {
|
|
35
|
+
|
|
36
|
+
// ── Site identity ─────────────────────────────────────────────────────────
|
|
37
|
+
title: "My Garden",
|
|
38
|
+
logo: {
|
|
39
|
+
// Path to navbar logo image served from public/ (leave empty for the default built-in icon)
|
|
40
|
+
// Accepts SVG, PNG, etc. — e.g. "/logo.svg" or "/images/my-logo.png"
|
|
41
|
+
src: "",
|
|
42
|
+
// Path to favicon served from public/ (defaults to /icon.svg)
|
|
43
|
+
favicon: "/icon.svg",
|
|
44
|
+
},
|
|
45
|
+
description: "A personal digital garden — notes, articles, and ideas.",
|
|
46
|
+
baseUrl: "https://example.com", // Replace with your actual domain
|
|
47
|
+
ogImage: "/og-image.png", // Default OG/social preview image — place a 1200×630 PNG at public/og-image.png
|
|
48
|
+
footerText: `© ${new Date().getFullYear()} My Garden. All rights reserved.`,
|
|
49
|
+
|
|
50
|
+
// ── i18n ──────────────────────────────────────────────────────────────────
|
|
51
|
+
i18n: {
|
|
52
|
+
// i18n is disabled: the language switcher is hidden and defaultLocale is always used.
|
|
53
|
+
// To enable multi-language support, set enabled: true, add more locales, and
|
|
54
|
+
// convert plain string fields to locale maps: e.g. title: { en: '...', zh: '...' }
|
|
55
|
+
enabled: false,
|
|
56
|
+
defaultLocale: 'en',
|
|
57
|
+
locales: ['en'],
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// ── Navigation ────────────────────────────────────────────────────────────
|
|
61
|
+
nav: [
|
|
62
|
+
{ name: "Posts", url: `/${postsBasePath}`, weight: 1 },
|
|
63
|
+
{ name: "Series", url: "/series", weight: 2 },
|
|
64
|
+
{ name: "About", url: "/about", weight: 3 },
|
|
65
|
+
{ name: "More", url: "", weight: 4, children: [
|
|
66
|
+
{ name: "Archive", url: "/archive" },
|
|
67
|
+
{ name: "Tags", url: "/tags" },
|
|
68
|
+
{ name: "Subscribe", url: "/subscribe", dividerBefore: true },
|
|
69
|
+
]},
|
|
70
|
+
] as NavItem[],
|
|
71
|
+
|
|
72
|
+
// ── Footer ────────────────────────────────────────────────────────────────
|
|
73
|
+
footer: {
|
|
74
|
+
explore: [
|
|
75
|
+
{ name: "Archive", url: "/archive", weight: 1 },
|
|
76
|
+
{ name: "Tags", url: "/tags", weight: 2 },
|
|
77
|
+
{ name: "About", url: "/about", weight: 3 },
|
|
78
|
+
],
|
|
79
|
+
connect: [
|
|
80
|
+
{ name: "GitHub", url: social.github, weight: 1 },
|
|
81
|
+
{ name: "X (Twitter)", url: social.twitter, weight: 2 },
|
|
82
|
+
{ name: "RSS Feed", url: "/feed.xml", weight: 3 },
|
|
83
|
+
{ name: "Subscribe", url: "/subscribe", weight: 4 },
|
|
84
|
+
],
|
|
85
|
+
builtWith: {
|
|
86
|
+
show: true,
|
|
87
|
+
url: "https://github.com/hutusi/amytis",
|
|
88
|
+
text: "Built with Amytis",
|
|
89
|
+
},
|
|
90
|
+
// bottomLinks: [
|
|
91
|
+
// { text: '京ICP备12345678号', url: 'https://beian.miit.gov.cn/' },
|
|
92
|
+
// ],
|
|
93
|
+
bottomLinks: [],
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// ── Social & sharing ──────────────────────────────────────────────────────
|
|
97
|
+
social,
|
|
98
|
+
share: {
|
|
99
|
+
enabled: true,
|
|
100
|
+
// Supported: twitter, facebook, linkedin, weibo, reddit, hackernews,
|
|
101
|
+
// telegram, bluesky, mastodon, douban, zhihu, copy
|
|
102
|
+
platforms: ['twitter', 'facebook', 'linkedin', 'copy'],
|
|
103
|
+
},
|
|
104
|
+
subscribe: {
|
|
105
|
+
substack: '', // Substack publication URL, e.g., 'https://yourname.substack.com'
|
|
106
|
+
telegram: '', // Telegram channel URL, e.g., 'https://t.me/yourchannel'
|
|
107
|
+
wechat: {
|
|
108
|
+
qrCode: '', // Path to QR image in public/, e.g., '/images/wechat-qr.png'
|
|
109
|
+
account: '', // WeChat official account ID/name shown below QR
|
|
110
|
+
},
|
|
111
|
+
email: '', // Newsletter/mailing list URL (distinct from social.email contact address)
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// ── Features ──────────────────────────────────────────────────────────────
|
|
115
|
+
features: {
|
|
116
|
+
posts: {
|
|
117
|
+
enabled: true,
|
|
118
|
+
name: "Articles",
|
|
119
|
+
},
|
|
120
|
+
series: {
|
|
121
|
+
enabled: true,
|
|
122
|
+
name: "Series",
|
|
123
|
+
},
|
|
124
|
+
books: {
|
|
125
|
+
enabled: false,
|
|
126
|
+
name: "Books",
|
|
127
|
+
},
|
|
128
|
+
flow: {
|
|
129
|
+
enabled: false,
|
|
130
|
+
name: "Flow",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// ── Homepage ──────────────────────────────────────────────────────────────
|
|
135
|
+
hero: {
|
|
136
|
+
tagline: "My Personal Digital Garden",
|
|
137
|
+
title: "A place for ideas to grow.",
|
|
138
|
+
subtitle: "Notes, articles, and explorations — written for curiosity, shared for connection.",
|
|
139
|
+
},
|
|
140
|
+
homepage: {
|
|
141
|
+
sections: [
|
|
142
|
+
{ id: 'hero', enabled: true, weight: 1 },
|
|
143
|
+
{ id: 'featured-posts', enabled: true, weight: 2, maxItems: 4 },
|
|
144
|
+
{ id: 'latest-posts', enabled: true, weight: 3, maxItems: 3 },
|
|
145
|
+
{ id: 'recent-flows', enabled: false, weight: 4, maxItems: 8 },
|
|
146
|
+
{ id: 'featured-series', enabled: true, weight: 5, maxItems: 6, scrollThreshold: 2 },
|
|
147
|
+
{ id: 'featured-books', enabled: false, weight: 6, maxItems: 4 },
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// ── Content ───────────────────────────────────────────────────────────────
|
|
152
|
+
pagination: {
|
|
153
|
+
posts: 10,
|
|
154
|
+
series: 5,
|
|
155
|
+
flows: 20,
|
|
156
|
+
notes: 20,
|
|
157
|
+
},
|
|
158
|
+
posts: {
|
|
159
|
+
basePath: postsBasePath,
|
|
160
|
+
toc: true,
|
|
161
|
+
showFuturePosts: false,
|
|
162
|
+
includeDateInUrl: false,
|
|
163
|
+
// trailingSlash is configured in next.config.ts (Next.js handles URL normalization)
|
|
164
|
+
authors: {
|
|
165
|
+
// Default author names applied when a post has no author in its frontmatter.
|
|
166
|
+
// Falls back to series authors first, then to this list.
|
|
167
|
+
default: ["Your Name"] as string[],
|
|
168
|
+
showInHeader: true, // Show author byline below the post title
|
|
169
|
+
showAuthorCard: true, // Show author bio card at the end of the post
|
|
170
|
+
},
|
|
171
|
+
// Series slugs whose posts are hidden from the main posts listing.
|
|
172
|
+
// Posts remain accessible via their series page and direct URLs.
|
|
173
|
+
excludeFromListing: [] as string[],
|
|
174
|
+
archive: {
|
|
175
|
+
showAuthors: true,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
series: {
|
|
179
|
+
// Per-series custom URL prefix for posts within that series
|
|
180
|
+
// e.g., { 'weeklies': 'weeklies' } → posts served at /weeklies/[slug]
|
|
181
|
+
customPaths: {} as Record<string, string>,
|
|
182
|
+
},
|
|
183
|
+
flows: {
|
|
184
|
+
recentCount: 5,
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
feed: {
|
|
188
|
+
maxItems: 20, // Max items per feed (0 = no limit)
|
|
189
|
+
format: 'rss' as 'rss' | 'atom' | 'both', // Format(s) to serve and advertise
|
|
190
|
+
content: 'full' as 'excerpt' | 'full', // Full post content or excerpt only
|
|
191
|
+
includeFlows: false, // Include flow notes alongside posts
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
// ── Images ────────────────────────────────────────────────────────────────
|
|
195
|
+
images: {
|
|
196
|
+
// CDN base URL for serving images (leave empty to serve locally)
|
|
197
|
+
// e.g., "https://cdn.example.com" or "https://your-bucket.r2.dev"
|
|
198
|
+
// When set, local image paths like /posts/slug/images/cover.jpg are rewritten
|
|
199
|
+
// to https://cdn.example.com/posts/slug/images/cover.jpg at render time.
|
|
200
|
+
cdnBaseUrl: "",
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
// ── Appearance ────────────────────────────────────────────────────────────
|
|
204
|
+
themeColor: 'default', // 'default' | 'blue' | 'rose' | 'amber'
|
|
205
|
+
|
|
206
|
+
// ── Browser compatibility warning ─────────────────────────────────────────
|
|
207
|
+
browserCheck: {
|
|
208
|
+
// URL shown in the outdated-browser banner. Set to '' to hide the link
|
|
209
|
+
// (useful for corporate/intranet deployments where IT manages upgrades).
|
|
210
|
+
updateUrl: 'https://browsehappy.com/',
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
// ── Analytics ─────────────────────────────────────────────────────────────
|
|
214
|
+
analytics: {
|
|
215
|
+
providers: [], // enable one or many: 'umami' | 'plausible' | 'google'
|
|
216
|
+
umami: {
|
|
217
|
+
websiteId: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID || '', // Your Umami Website ID
|
|
218
|
+
src: process.env.NEXT_PUBLIC_UMAMI_URL || 'https://us.umami.is/script.js', // Default or self-hosted URL
|
|
219
|
+
},
|
|
220
|
+
plausible: {
|
|
221
|
+
domain: '', // Your domain
|
|
222
|
+
src: 'https://plausible.io/js/script.js',
|
|
223
|
+
},
|
|
224
|
+
google: {
|
|
225
|
+
measurementId: '', // G-XXXXXXXXXX
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
// ── Comments ──────────────────────────────────────────────────────────────
|
|
230
|
+
comments: {
|
|
231
|
+
provider: null, // 'giscus' | 'disqus' | null
|
|
232
|
+
giscus: {
|
|
233
|
+
repo: 'your-username/your-repo', // username/repo
|
|
234
|
+
repoId: '',
|
|
235
|
+
category: 'Announcements',
|
|
236
|
+
categoryId: '',
|
|
237
|
+
},
|
|
238
|
+
disqus: {
|
|
239
|
+
shortname: '',
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
// ── Authors ───────────────────────────────────────────────────────────────
|
|
244
|
+
authors: {
|
|
245
|
+
// Map display name (as used in post frontmatter) to author profile.
|
|
246
|
+
// Example:
|
|
247
|
+
// "Author Name": {
|
|
248
|
+
// bio: "Short bio shown in author card below each post.",
|
|
249
|
+
// avatar: "/images/authors/author-name.jpg", // path under public/
|
|
250
|
+
// social: [
|
|
251
|
+
// { image: "/images/authors/wechat-qr.jpg", description: "WeChat Official Account" },
|
|
252
|
+
// ],
|
|
253
|
+
// },
|
|
254
|
+
"Your Name": {
|
|
255
|
+
bio: "Write a short bio here.",
|
|
256
|
+
avatar: "/images/avatar.jpg",
|
|
257
|
+
social: [],
|
|
258
|
+
},
|
|
259
|
+
} as Record<string, {
|
|
260
|
+
bio?: string;
|
|
261
|
+
avatar?: string; // Avatar image path served from public/
|
|
262
|
+
social?: Array<{
|
|
263
|
+
image: string; // Social image (e.g. QR code) path served from public/
|
|
264
|
+
description: string; // Label shown below the image
|
|
265
|
+
}>;
|
|
266
|
+
}>,
|
|
267
|
+
|
|
268
|
+
};
|
package/site.config.ts
CHANGED
|
@@ -25,6 +25,13 @@ export const siteConfig = {
|
|
|
25
25
|
|
|
26
26
|
// ── Site identity ─────────────────────────────────────────────────────────
|
|
27
27
|
title: { en: "Amytis", zh: "Amytis" },
|
|
28
|
+
logo: {
|
|
29
|
+
// Path to navbar logo image served from public/ (leave empty for the default built-in icon)
|
|
30
|
+
// Accepts SVG, PNG, etc. — e.g. "/logo.svg" or "/images/my-logo.png"
|
|
31
|
+
src: "",
|
|
32
|
+
// Path to favicon served from public/ (defaults to /icon.svg)
|
|
33
|
+
favicon: "/icon.svg",
|
|
34
|
+
},
|
|
28
35
|
description: { en: "Amytis — an elegant open-source framework for building your personal digital garden.", zh: "Amytis — 优雅的开源数字花园框架。" },
|
|
29
36
|
baseUrl: "https://example.com", // Replace with your actual domain
|
|
30
37
|
ogImage: "/og-image.png", // Default OG/social preview image — place a 1200×630 PNG at public/og-image.png
|
|
@@ -32,6 +39,11 @@ export const siteConfig = {
|
|
|
32
39
|
|
|
33
40
|
// ── i18n ──────────────────────────────────────────────────────────────────
|
|
34
41
|
i18n: {
|
|
42
|
+
// Set enabled: false to disable multi-language support entirely.
|
|
43
|
+
// The language switcher will be hidden and the defaultLocale is always used.
|
|
44
|
+
// When disabled, all locale-aware fields (title, description, hero, etc.)
|
|
45
|
+
// accept plain strings instead of { en: '...', zh: '...' } objects.
|
|
46
|
+
enabled: true,
|
|
35
47
|
defaultLocale: 'en',
|
|
36
48
|
locales: ['en', 'zh'],
|
|
37
49
|
},
|
|
@@ -61,7 +73,7 @@ export const siteConfig = {
|
|
|
61
73
|
],
|
|
62
74
|
connect: [
|
|
63
75
|
{ name: "GitHub", url: social.github, weight: 1 },
|
|
64
|
-
{ name: "Twitter", url: social.twitter, weight: 2 },
|
|
76
|
+
{ name: "X (Twitter)", url: social.twitter, weight: 2 },
|
|
65
77
|
{ name: "RSS Feed", url: "/feed.xml", weight: 3 },
|
|
66
78
|
{ name: "Subscribe", url: "/subscribe", weight: 4 },
|
|
67
79
|
],
|
|
@@ -70,6 +82,14 @@ export const siteConfig = {
|
|
|
70
82
|
url: "https://github.com/hutusi/amytis",
|
|
71
83
|
text: { en: "Built with Amytis", zh: "基于 Amytis 构建" },
|
|
72
84
|
},
|
|
85
|
+
// Optional custom links shown in the footer bottom bar.
|
|
86
|
+
// Common uses: ICP registration (China), PSB registration, cookie policy, sitemap, etc.
|
|
87
|
+
// Example:
|
|
88
|
+
// bottomLinks: [
|
|
89
|
+
// { text: '京ICP备12345678号', url: 'https://beian.miit.gov.cn/' },
|
|
90
|
+
// { text: 'Cookie Policy' }, // url is optional — renders as plain text
|
|
91
|
+
// ],
|
|
92
|
+
bottomLinks: [] as { text: string | Record<string, string>; url?: string }[],
|
|
73
93
|
},
|
|
74
94
|
|
|
75
95
|
// ── Social & sharing ──────────────────────────────────────────────────────
|
|
@@ -119,11 +139,11 @@ export const siteConfig = {
|
|
|
119
139
|
homepage: {
|
|
120
140
|
sections: [
|
|
121
141
|
{ id: 'hero', enabled: true, weight: 1 },
|
|
122
|
-
{ id: 'featured-
|
|
123
|
-
{ id: '
|
|
124
|
-
{ id: '
|
|
125
|
-
{ id: '
|
|
126
|
-
{ id: '
|
|
142
|
+
{ id: 'featured-posts', enabled: true, weight: 2, maxItems: 4 },
|
|
143
|
+
{ id: 'latest-posts', enabled: true, weight: 3, maxItems: 3 },
|
|
144
|
+
{ id: 'recent-flows', enabled: true, weight: 4, maxItems: 8 },
|
|
145
|
+
{ id: 'featured-series', enabled: true, weight: 5, maxItems: 6, scrollThreshold: 2 },
|
|
146
|
+
{ id: 'featured-books', enabled: true, weight: 6, maxItems: 4 },
|
|
127
147
|
],
|
|
128
148
|
},
|
|
129
149
|
|
|
@@ -135,17 +155,48 @@ export const siteConfig = {
|
|
|
135
155
|
notes: 20,
|
|
136
156
|
},
|
|
137
157
|
posts: {
|
|
158
|
+
basePath: 'posts', // Change to e.g. 'articles' to serve all posts at /articles/[slug]
|
|
138
159
|
toc: true,
|
|
139
160
|
showFuturePosts: false,
|
|
140
161
|
includeDateInUrl: false,
|
|
141
162
|
// trailingSlash is configured in next.config.ts (Next.js handles URL normalization)
|
|
163
|
+
authors: {
|
|
164
|
+
// Default author names applied when a post has no author in its frontmatter.
|
|
165
|
+
// Falls back to series authors first, then to this list.
|
|
166
|
+
default: ["John Hu"] as string[],
|
|
167
|
+
showInHeader: true, // Show author byline below the post title
|
|
168
|
+
showAuthorCard: true, // Show author bio card at the end of the post
|
|
169
|
+
},
|
|
170
|
+
// Series slugs whose posts are hidden from the main posts listing.
|
|
171
|
+
// Posts remain accessible via their series page and direct URLs.
|
|
172
|
+
excludeFromListing: [] as string[],
|
|
142
173
|
archive: {
|
|
143
174
|
showAuthors: true,
|
|
144
175
|
},
|
|
145
176
|
},
|
|
177
|
+
series: {
|
|
178
|
+
// Per-series custom URL prefix for posts within that series
|
|
179
|
+
// e.g., { 'weeklies': 'weeklies' } → posts served at /weeklies/[slug]
|
|
180
|
+
customPaths: {} as Record<string, string>,
|
|
181
|
+
},
|
|
146
182
|
flows: {
|
|
147
183
|
recentCount: 5,
|
|
148
184
|
},
|
|
185
|
+
feed: {
|
|
186
|
+
maxItems: 20, // Max items per feed (0 = no limit)
|
|
187
|
+
format: 'rss' as 'rss' | 'atom' | 'both', // Format(s) to serve and advertise
|
|
188
|
+
content: 'full' as 'excerpt' | 'full', // Full post content or excerpt only
|
|
189
|
+
includeFlows: false, // Include flow notes alongside posts
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
// ── Images ────────────────────────────────────────────────────────────────
|
|
193
|
+
images: {
|
|
194
|
+
// CDN base URL for serving images (leave empty to serve locally)
|
|
195
|
+
// e.g., "https://cdn.example.com" or "https://your-bucket.r2.dev"
|
|
196
|
+
// When set, local image paths like /posts/slug/images/cover.jpg are rewritten
|
|
197
|
+
// to https://cdn.example.com/posts/slug/images/cover.jpg at render time.
|
|
198
|
+
cdnBaseUrl: "",
|
|
199
|
+
},
|
|
149
200
|
|
|
150
201
|
// ── Appearance ────────────────────────────────────────────────────────────
|
|
151
202
|
themeColor: 'default', // 'default' | 'blue' | 'rose' | 'amber'
|
|
@@ -159,7 +210,7 @@ export const siteConfig = {
|
|
|
159
210
|
|
|
160
211
|
// ── Analytics ─────────────────────────────────────────────────────────────
|
|
161
212
|
analytics: {
|
|
162
|
-
|
|
213
|
+
providers: ['umami'] as ('umami' | 'plausible' | 'google')[], // enable one or many; [] disables analytics
|
|
163
214
|
umami: {
|
|
164
215
|
websiteId: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID || '', // Your Umami Website ID
|
|
165
216
|
src: process.env.NEXT_PUBLIC_UMAMI_URL || 'https://us.umami.is/script.js', // Default or self-hosted URL
|
|
@@ -189,8 +240,29 @@ export const siteConfig = {
|
|
|
189
240
|
|
|
190
241
|
// ── Authors ───────────────────────────────────────────────────────────────
|
|
191
242
|
authors: {
|
|
192
|
-
// Map display name (as used in post frontmatter) to author profile
|
|
193
|
-
//
|
|
194
|
-
|
|
243
|
+
// Map display name (as used in post frontmatter) to author profile.
|
|
244
|
+
// Example:
|
|
245
|
+
// "Author Name": {
|
|
246
|
+
// bio: "Short bio shown in author card below each post.",
|
|
247
|
+
// avatar: "/images/authors/author-name.jpg", // path under public/
|
|
248
|
+
// social: [
|
|
249
|
+
// { image: "/images/authors/wechat-qr.jpg", description: "WeChat Official Account" },
|
|
250
|
+
// ],
|
|
251
|
+
// },
|
|
252
|
+
"John Hu": {
|
|
253
|
+
bio: "Coder, Writer, Creator.",
|
|
254
|
+
avatar: "/images/avatar.jpg",
|
|
255
|
+
social: [
|
|
256
|
+
{ image: "/images/wechat-qr.jpg", description: "Follow on WeChat" },
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
} as Record<string, {
|
|
260
|
+
bio?: string;
|
|
261
|
+
avatar?: string; // Avatar image path served from public/
|
|
262
|
+
social?: Array<{
|
|
263
|
+
image: string; // Social image (e.g. QR code) path served from public/
|
|
264
|
+
description: string; // Label shown below the image
|
|
265
|
+
}>;
|
|
266
|
+
}>,
|
|
195
267
|
|
|
196
268
|
};
|