create-agntcms-app 0.2.1

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 (197) hide show
  1. package/README.md +39 -0
  2. package/dist/index.mjs +297 -0
  3. package/dist/template/.claude/settings.json +6 -0
  4. package/dist/template/.claude/skills/.gitkeep +0 -0
  5. package/dist/template/.claude-plugin/channel/server.mjs +254 -0
  6. package/dist/template/.claude-plugin/channel/server.ts +369 -0
  7. package/dist/template/.claude-plugin/plugin.json +17 -0
  8. package/dist/template/.mcp.json +8 -0
  9. package/dist/template/BRAND.md +49 -0
  10. package/dist/template/CLAUDE.md +157 -0
  11. package/dist/template/agntcms/config.ts +49 -0
  12. package/dist/template/agntcms/sections/ArticleBody/component.tsx +32 -0
  13. package/dist/template/agntcms/sections/ArticleBody/index.ts +10 -0
  14. package/dist/template/agntcms/sections/ArticleBody/schema.ts +5 -0
  15. package/dist/template/agntcms/sections/ArticleHero/component.tsx +87 -0
  16. package/dist/template/agntcms/sections/ArticleHero/index.ts +10 -0
  17. package/dist/template/agntcms/sections/ArticleHero/schema.ts +12 -0
  18. package/dist/template/agntcms/sections/Banner/component.tsx +83 -0
  19. package/dist/template/agntcms/sections/Banner/index.ts +10 -0
  20. package/dist/template/agntcms/sections/Banner/schema.ts +9 -0
  21. package/dist/template/agntcms/sections/BlogIndex/component.tsx +173 -0
  22. package/dist/template/agntcms/sections/BlogIndex/index.ts +10 -0
  23. package/dist/template/agntcms/sections/BlogIndex/schema.ts +33 -0
  24. package/dist/template/agntcms/sections/BlogIndexHeader/component.tsx +44 -0
  25. package/dist/template/agntcms/sections/BlogIndexHeader/index.ts +10 -0
  26. package/dist/template/agntcms/sections/BlogIndexHeader/schema.ts +8 -0
  27. package/dist/template/agntcms/sections/BlogPostBody/component.tsx +50 -0
  28. package/dist/template/agntcms/sections/BlogPostBody/index.ts +10 -0
  29. package/dist/template/agntcms/sections/BlogPostBody/schema.ts +10 -0
  30. package/dist/template/agntcms/sections/BlogPostHero/component.tsx +88 -0
  31. package/dist/template/agntcms/sections/BlogPostHero/index.ts +10 -0
  32. package/dist/template/agntcms/sections/BlogPostHero/schema.ts +35 -0
  33. package/dist/template/agntcms/sections/CaseStudies/component.tsx +92 -0
  34. package/dist/template/agntcms/sections/CaseStudies/index.ts +10 -0
  35. package/dist/template/agntcms/sections/CaseStudies/schema.ts +17 -0
  36. package/dist/template/agntcms/sections/ContactForm/component.tsx +119 -0
  37. package/dist/template/agntcms/sections/ContactForm/index.ts +10 -0
  38. package/dist/template/agntcms/sections/ContactForm/schema.ts +15 -0
  39. package/dist/template/agntcms/sections/DocsArticle/component.tsx +266 -0
  40. package/dist/template/agntcms/sections/DocsArticle/index.ts +10 -0
  41. package/dist/template/agntcms/sections/DocsArticle/schema.ts +33 -0
  42. package/dist/template/agntcms/sections/FAQ/component.tsx +57 -0
  43. package/dist/template/agntcms/sections/FAQ/index.ts +10 -0
  44. package/dist/template/agntcms/sections/FAQ/schema.ts +11 -0
  45. package/dist/template/agntcms/sections/FeatureGrid/component.tsx +117 -0
  46. package/dist/template/agntcms/sections/FeatureGrid/index.ts +10 -0
  47. package/dist/template/agntcms/sections/FeatureGrid/schema.ts +21 -0
  48. package/dist/template/agntcms/sections/FeaturedArticles/component.tsx +99 -0
  49. package/dist/template/agntcms/sections/FeaturedArticles/index.ts +10 -0
  50. package/dist/template/agntcms/sections/FeaturedArticles/schema.ts +17 -0
  51. package/dist/template/agntcms/sections/GettingStarted/component.tsx +116 -0
  52. package/dist/template/agntcms/sections/GettingStarted/index.ts +10 -0
  53. package/dist/template/agntcms/sections/GettingStarted/schema.ts +11 -0
  54. package/dist/template/agntcms/sections/Hero/component.tsx +148 -0
  55. package/dist/template/agntcms/sections/Hero/index.ts +10 -0
  56. package/dist/template/agntcms/sections/Hero/schema.ts +16 -0
  57. package/dist/template/agntcms/sections/HowItWorks/component.tsx +57 -0
  58. package/dist/template/agntcms/sections/HowItWorks/index.ts +10 -0
  59. package/dist/template/agntcms/sections/HowItWorks/schema.ts +11 -0
  60. package/dist/template/agntcms/sections/ImageText/component.tsx +110 -0
  61. package/dist/template/agntcms/sections/ImageText/index.ts +10 -0
  62. package/dist/template/agntcms/sections/ImageText/schema.ts +14 -0
  63. package/dist/template/agntcms/sections/LogoStrip/component.tsx +37 -0
  64. package/dist/template/agntcms/sections/LogoStrip/index.ts +10 -0
  65. package/dist/template/agntcms/sections/LogoStrip/schema.ts +6 -0
  66. package/dist/template/agntcms/sections/Newsletter/component.tsx +48 -0
  67. package/dist/template/agntcms/sections/Newsletter/index.ts +10 -0
  68. package/dist/template/agntcms/sections/Newsletter/schema.ts +8 -0
  69. package/dist/template/agntcms/sections/OpenSource/component.tsx +99 -0
  70. package/dist/template/agntcms/sections/OpenSource/index.ts +10 -0
  71. package/dist/template/agntcms/sections/OpenSource/schema.ts +13 -0
  72. package/dist/template/agntcms/sections/PainAnswer/component.tsx +81 -0
  73. package/dist/template/agntcms/sections/PainAnswer/index.ts +10 -0
  74. package/dist/template/agntcms/sections/PainAnswer/schema.ts +15 -0
  75. package/dist/template/agntcms/sections/PricingPlans/component.tsx +100 -0
  76. package/dist/template/agntcms/sections/PricingPlans/index.ts +10 -0
  77. package/dist/template/agntcms/sections/PricingPlans/schema.ts +13 -0
  78. package/dist/template/agntcms/sections/Problem/component.tsx +49 -0
  79. package/dist/template/agntcms/sections/Problem/index.ts +10 -0
  80. package/dist/template/agntcms/sections/Problem/schema.ts +12 -0
  81. package/dist/template/agntcms/sections/SiteFooter/component.tsx +88 -0
  82. package/dist/template/agntcms/sections/SiteFooter/index.ts +10 -0
  83. package/dist/template/agntcms/sections/SiteFooter/schema.ts +13 -0
  84. package/dist/template/agntcms/sections/SiteHeader/component.tsx +99 -0
  85. package/dist/template/agntcms/sections/SiteHeader/index.ts +10 -0
  86. package/dist/template/agntcms/sections/SiteHeader/schema.ts +14 -0
  87. package/dist/template/agntcms/sections/SiteMeta/component.tsx +26 -0
  88. package/dist/template/agntcms/sections/SiteMeta/index.ts +13 -0
  89. package/dist/template/agntcms/sections/SiteMeta/schema.ts +18 -0
  90. package/dist/template/agntcms/sections/TabbedFeatures/component.tsx +120 -0
  91. package/dist/template/agntcms/sections/TabbedFeatures/index.ts +10 -0
  92. package/dist/template/agntcms/sections/TabbedFeatures/schema.ts +13 -0
  93. package/dist/template/agntcms/sections/TeamGrid/component.tsx +77 -0
  94. package/dist/template/agntcms/sections/TeamGrid/index.ts +10 -0
  95. package/dist/template/agntcms/sections/TeamGrid/schema.ts +14 -0
  96. package/dist/template/agntcms/sections/Testimonials/component.tsx +76 -0
  97. package/dist/template/agntcms/sections/Testimonials/index.ts +10 -0
  98. package/dist/template/agntcms/sections/Testimonials/schema.ts +12 -0
  99. package/dist/template/agntcms/sections/WhatIsBuilt/component.tsx +86 -0
  100. package/dist/template/agntcms/sections/WhatIsBuilt/index.ts +10 -0
  101. package/dist/template/agntcms/sections/WhatIsBuilt/schema.ts +20 -0
  102. package/dist/template/agntcms/site-meta.ts +81 -0
  103. package/dist/template/app/[[...slug]]/page.tsx +123 -0
  104. package/dist/template/app/admin/AdminPageClient.tsx +77 -0
  105. package/dist/template/app/admin/AdminPageDynamic.tsx +24 -0
  106. package/dist/template/app/admin/page.tsx +14 -0
  107. package/dist/template/app/api/agntcms/_shared.ts +80 -0
  108. package/dist/template/app/api/agntcms/assets/route.ts +11 -0
  109. package/dist/template/app/api/agntcms/assets/upload/route.ts +11 -0
  110. package/dist/template/app/api/agntcms/draft/discard/route.ts +12 -0
  111. package/dist/template/app/api/agntcms/draft/list/route.ts +11 -0
  112. package/dist/template/app/api/agntcms/draft/publish/route.ts +11 -0
  113. package/dist/template/app/api/agntcms/draft/reorder/route.ts +10 -0
  114. package/dist/template/app/api/agntcms/draft/save/route.ts +11 -0
  115. package/dist/template/app/api/agntcms/events/route.ts +12 -0
  116. package/dist/template/app/api/agntcms/forms/delete/route.ts +17 -0
  117. package/dist/template/app/api/agntcms/forms/list/route.ts +24 -0
  118. package/dist/template/app/api/agntcms/forms/read/route.ts +23 -0
  119. package/dist/template/app/api/agntcms/forms/submit/route.ts +17 -0
  120. package/dist/template/app/api/agntcms/global/delete/route.ts +13 -0
  121. package/dist/template/app/api/agntcms/global/history/route.ts +10 -0
  122. package/dist/template/app/api/agntcms/global/list/route.ts +14 -0
  123. package/dist/template/app/api/agntcms/global/read/route.ts +11 -0
  124. package/dist/template/app/api/agntcms/global/rollback/route.ts +10 -0
  125. package/dist/template/app/api/agntcms/global/save/route.ts +14 -0
  126. package/dist/template/app/api/agntcms/mcp/route.ts +12 -0
  127. package/dist/template/app/api/agntcms/page/delete/route.ts +10 -0
  128. package/dist/template/app/api/agntcms/page/duplicate/route.ts +11 -0
  129. package/dist/template/app/api/agntcms/page/history/route.ts +10 -0
  130. package/dist/template/app/api/agntcms/page/list/route.ts +10 -0
  131. package/dist/template/app/api/agntcms/page/read/route.ts +11 -0
  132. package/dist/template/app/api/agntcms/page/rename/route.ts +10 -0
  133. package/dist/template/app/api/agntcms/page/rollback/route.ts +10 -0
  134. package/dist/template/app/api/agntcms/page/unpublish/route.ts +11 -0
  135. package/dist/template/app/api/agntcms/preview/enter/route.ts +13 -0
  136. package/dist/template/app/api/agntcms/preview/exit/route.ts +10 -0
  137. package/dist/template/app/api/agntcms/preview/issue/route.ts +12 -0
  138. package/dist/template/app/api/agntcms/template/list/route.ts +15 -0
  139. package/dist/template/app/apple-icon.svg +9 -0
  140. package/dist/template/app/icon.svg +9 -0
  141. package/dist/template/app/layout.tsx +107 -0
  142. package/dist/template/app/not-found.tsx +75 -0
  143. package/dist/template/app/robots.ts +33 -0
  144. package/dist/template/app/sitemap.ts +49 -0
  145. package/dist/template/content/globals/site-footer.json +53 -0
  146. package/dist/template/content/globals/site-header.json +18 -0
  147. package/dist/template/content/globals/site-meta.json +13 -0
  148. package/dist/template/content/pages/404.json +34 -0
  149. package/dist/template/content/pages/about.json +307 -0
  150. package/dist/template/content/pages/article-editor.json +61 -0
  151. package/dist/template/content/pages/article-schemas.json +61 -0
  152. package/dist/template/content/pages/blog.json +162 -0
  153. package/dist/template/content/pages/contact.json +29 -0
  154. package/dist/template/content/pages/home.json +243 -0
  155. package/dist/template/content/pages/pricing.json +219 -0
  156. package/dist/template/content/pages/services.json +177 -0
  157. package/dist/template/fonts/Satoshi-Medium.woff2 +0 -0
  158. package/dist/template/fonts/Satoshi-Regular.woff2 +0 -0
  159. package/dist/template/next.config.ts +6 -0
  160. package/dist/template/package.json +36 -0
  161. package/dist/template/postcss.config.mjs +5 -0
  162. package/dist/template/public/assets/.gitkeep +0 -0
  163. package/dist/template/public/assets/0418d7ed21f57e7b9e0546725c92b8419daeaa355675d9070fab0c2013cf1524.jpg +0 -0
  164. package/dist/template/public/assets/0d0475f21aa96435a8ed3cdb2fddcc6278492e76ae842f569432454f4d33631a.jpg +0 -0
  165. package/dist/template/public/assets/27457a1adee2372030d9876b0d52c44d46be98843999935eaef2526b9b961f12.jpg +0 -0
  166. package/dist/template/public/assets/3855d91192f0c6120b01427b78ef84e52baa9f4b5a17d4271e41c1bfd95a5b0c.jpg +0 -0
  167. package/dist/template/public/assets/3b3b90c5084635b746be673ede92a328f002f5621a42c9a5cb89c5e2435652cb.jpg +0 -0
  168. package/dist/template/public/assets/3e76165a78fd3e7b8ed1e93dee50803ae11110c756c8c1c89229a2dec2bc0abf.jpg +0 -0
  169. package/dist/template/public/assets/4a3e28f85dc850c347ea0fd931696aa936a6bd45f193e7f1c9328b5896fb272c.jpg +0 -0
  170. package/dist/template/public/assets/579f67d5fd4c9106c6cdf2ef29f50df934ad0fc2b7849bac1e1cfb1e3f92303b.jpg +0 -0
  171. package/dist/template/public/assets/5b95209269661bb60fb250f1da682e05b9efa64dd42f350608b299e6bf1f2f35.jpg +0 -0
  172. package/dist/template/public/assets/5e04b46f8317ef95a7ddf85aedfe5c098a755f05056325d0251eccf95ce51172.jpg +0 -0
  173. package/dist/template/public/assets/6167a9164be2cf1183bdfdd4946bf9b908570e79e92a2380c25f0bb702422bbd.jpg +0 -0
  174. package/dist/template/public/assets/75e723ec316de28247924e5dfb73a4b266e10de605e749f150883d280ed8ed16.jpg +0 -0
  175. package/dist/template/public/assets/816a11e6a7245feaf51bbebf09d1bda3f125b334bc24fc3b8f47b5380a7b4294.jpg +0 -0
  176. package/dist/template/public/assets/81eba6f5654b8746a9b0cba1a9521a67f2b4afaaefc7c88d66dfab1461270d8f.jpg +0 -0
  177. package/dist/template/public/assets/82a2ce9e49361098f77a28755779dc5a7c026831cbd135175749c1304e21dacc.jpg +0 -0
  178. package/dist/template/public/assets/8d7b02ba277ba56bdafdbd47b01f7df6d993c714b4dc2305eb65a1307c09647d.jpg +0 -0
  179. package/dist/template/public/assets/b303185b471678e4d62f678a1549ee26022f4745407d08cae44ecb1c25352293.jpg +0 -0
  180. package/dist/template/public/assets/b69b49169c11546100d6dd5280073bc0d84cbbcc6d33fa01ecf6a5866fa42237.jpg +0 -0
  181. package/dist/template/public/assets/c4d2f0d1a310e457ac722a399693652e3c86c55b294243d5ffc679394e12f9d1.jpg +0 -0
  182. package/dist/template/public/assets/cae09f4729f8a348b67267c2f2a550be0f3bfa420689afe1a5cf8b7e2b146238.png +0 -0
  183. package/dist/template/public/assets/cb3acf58b57417a4b26474ba04c096af7103c4320ed2f4f3683f79d7670a055c.jpg +0 -0
  184. package/dist/template/public/assets/d5a0701b2d156284e0ce851cd2534ec632db34f91fbcbee3b8a7784d45ce78d2.jpg +0 -0
  185. package/dist/template/public/assets/d6ef1c3f48b0e488521794fb60701da1fd2c3a1621d6ac5f17ccfd4909d3be60.jpg +0 -0
  186. package/dist/template/public/assets/de249ff9be2539cf0d1ce092de3c57001839b6c3e14fcee3fc31a7b7673ae007.jpg +0 -0
  187. package/dist/template/public/assets/eac45438956be187b010e24b3289757aa00f227c190d49ee99fea510552dd2ba.jpg +0 -0
  188. package/dist/template/public/assets/f8b9200065b5436c6a88361839edc2b89be88d3037c84a80d3ee95c32891510b.jpg +0 -0
  189. package/dist/template/public/assets/placeholder.png +0 -0
  190. package/dist/template/public/brand/mark.svg +6 -0
  191. package/dist/template/public/brand/wordmark-light.svg +6 -0
  192. package/dist/template/public/brand/wordmark.svg +6 -0
  193. package/dist/template/styles/globals.css +69 -0
  194. package/dist/template/styles/theme.css +492 -0
  195. package/dist/template/styles/typography.css +469 -0
  196. package/dist/template/tsconfig.json +30 -0
  197. package/package.json +30 -0
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" fill="#141413"/>
3
+ <g stroke="#E8E6DC" stroke-width="2.6" stroke-linecap="square" fill="none">
4
+ <line x1="8" y1="24" x2="16" y2="6"/>
5
+ <line x1="16" y1="6" x2="24" y2="24"/>
6
+ <line x1="11" y1="18" x2="21" y2="18"/>
7
+ </g>
8
+ <rect x="8" y="27" width="16" height="2" fill="#00C9A7"/>
9
+ </svg>
@@ -0,0 +1,107 @@
1
+ import type { Metadata } from 'next'
2
+ import { Inter_Tight, JetBrains_Mono, Source_Serif_4 } from 'next/font/google'
3
+ import { draftMode } from 'next/headers'
4
+ import { GlobalSlot, createRuntime } from '@agntcms/next/server'
5
+ import config from '@/agntcms/config'
6
+ import { getSiteMeta } from '@/agntcms/site-meta'
7
+ import '../styles/globals.css'
8
+
9
+ // Inter Tight covers both display and body roles per BRAND.md.
10
+ const interTight = Inter_Tight({
11
+ subsets: ['latin', 'cyrillic'],
12
+ weight: ['400', '500', '600', '700'],
13
+ variable: '--font-inter-tight',
14
+ display: 'swap',
15
+ })
16
+
17
+ const jetbrainsMono = JetBrains_Mono({
18
+ subsets: ['latin'],
19
+ variable: '--font-jetbrains-mono',
20
+ display: 'swap',
21
+ })
22
+
23
+ // Source Serif 4 is reserved for manifesto / long-form essay typography.
24
+ const sourceSerif = Source_Serif_4({
25
+ subsets: ['latin'],
26
+ weight: ['400', '500'],
27
+ variable: '--font-source-serif',
28
+ display: 'swap',
29
+ })
30
+
31
+ // The runtime is constructed once per module load. createRuntime is
32
+ // stateless — it holds no request-scoped data — so module-level
33
+ // construction is safe and avoids rebuilding the adapter on every request.
34
+ const runtime = createRuntime({ contentAdapter: config.contentAdapter })
35
+
36
+ // Layout-level metadata provides fallback defaults for every page.
37
+ // Per-page generateMetadata in app/[[...slug]]/page.tsx overrides these
38
+ // with page-specific titles and descriptions. Users who want to customize
39
+ // the default metadata can edit this function — layout.tsx is NOT frozen.
40
+ export async function generateMetadata(): Promise<Metadata> {
41
+ const siteMeta = await getSiteMeta(runtime.getGlobal)
42
+
43
+ // layout.tsx uses a local fallback description rather than the global one
44
+ // because this file is user-editable and the wording belongs to the user.
45
+ const description =
46
+ siteMeta.defaultDescription !== ''
47
+ ? siteMeta.defaultDescription
48
+ : 'An opinionated, AI-native CMS framework for content sites.'
49
+
50
+ const baseUrl = siteMeta.baseUrl ?? ''
51
+ const ogImageUrl =
52
+ siteMeta.defaultOgImage && baseUrl
53
+ ? `${baseUrl}/assets/${siteMeta.defaultOgImage.filename}`
54
+ : undefined
55
+
56
+ return {
57
+ // `metadataBase` anchors all relative URLs in Open Graph / Twitter
58
+ // image fields. Without it, Next.js cannot resolve /assets/ URLs.
59
+ ...(baseUrl ? { metadataBase: new URL(baseUrl) } : {}),
60
+ // `default` is shown when a route has no own title (admin, error pages).
61
+ // `template: '%s'` is a pass-through — seo.title in page content becomes
62
+ // the literal <title> without any brand suffix appended.
63
+ // Authors include the brand in seo.title when they want it.
64
+ title: {
65
+ default: siteMeta.siteName,
66
+ template: '%s',
67
+ },
68
+ description,
69
+ openGraph: {
70
+ siteName: siteMeta.siteName,
71
+ type: 'website',
72
+ ...(ogImageUrl ? { images: [{ url: ogImageUrl }] } : {}),
73
+ },
74
+ twitter: {
75
+ card: 'summary_large_image',
76
+ ...(ogImageUrl ? { images: [ogImageUrl] } : {}),
77
+ },
78
+ }
79
+ }
80
+
81
+ export default async function RootLayout({ children }: { children: React.ReactNode }) {
82
+ // Read draft mode at the request level. GlobalSlot mirrors the same
83
+ // preview/published split as getContent so globals get editable
84
+ // affordances in preview and bare data in production.
85
+ const { isEnabled: isPreview } = await draftMode()
86
+ const mode = isPreview ? 'preview' : 'published'
87
+
88
+ return (
89
+ <html lang="en" className={`${interTight.variable} ${jetbrainsMono.variable} ${sourceSerif.variable}`}>
90
+ <body className="bg-paper text-ink font-body antialiased">
91
+ <GlobalSlot
92
+ name="site-header"
93
+ getGlobal={runtime.getGlobal}
94
+ definitions={config.sections}
95
+ mode={mode}
96
+ />
97
+ {children}
98
+ <GlobalSlot
99
+ name="site-footer"
100
+ getGlobal={runtime.getGlobal}
101
+ definitions={config.sections}
102
+ mode={mode}
103
+ />
104
+ </body>
105
+ </html>
106
+ )
107
+ }
@@ -0,0 +1,75 @@
1
+ // FROZEN — do not edit. Framework file managed by agntcms.
2
+ //
3
+ // Renders the actual Next.js not-found route from the canonical CMS page
4
+ // slug `404`. Editors change the 404 experience by editing that page's
5
+ // content, not by creating ad hoc slugs like `error-404`.
6
+
7
+ import type { Metadata } from 'next'
8
+ import { cookies } from 'next/headers'
9
+ import {
10
+ PageRenderer,
11
+ PreviewProvider,
12
+ PreviewToolbar,
13
+ SectionEditControls,
14
+ } from '@agntcms/next/client'
15
+ import { createRuntime, NOT_FOUND_PAGE_SLUG } from '@agntcms/next/server'
16
+ import config from '@/agntcms/config'
17
+
18
+ const runtime = createRuntime({ contentAdapter: config.contentAdapter })
19
+
20
+ export async function generateMetadata(): Promise<Metadata> {
21
+ const page = await runtime.getContent({ slug: NOT_FOUND_PAGE_SLUG, mode: 'published' })
22
+
23
+ return {
24
+ title: page?.seo.title ?? 'Page not found',
25
+ description:
26
+ page?.seo.description ??
27
+ 'The page you requested could not be found.',
28
+ robots: {
29
+ index: false,
30
+ follow: false,
31
+ },
32
+ }
33
+ }
34
+
35
+ export default async function NotFoundPage() {
36
+ const cookieStore = await cookies()
37
+ const isPreview = cookieStore.get('__agntcms_preview')?.value === '1'
38
+ const mode = isPreview ? 'preview' : 'published'
39
+
40
+ const page = await runtime.getContent({ slug: NOT_FOUND_PAGE_SLUG, mode })
41
+
42
+ if (!page) {
43
+ return (
44
+ <main className="flex min-h-[60vh] items-center justify-center px-8 py-24 text-center">
45
+ <div className="max-w-xl">
46
+ <p className="mb-3 text-[11px] font-medium uppercase tracking-[0.12em] text-text-brand-primary">
47
+ 404
48
+ </p>
49
+ <h1 className="font-display text-[clamp(36px,6vw,64px)] font-medium tracking-[-0.04em] text-text-primary">
50
+ Page not found
51
+ </h1>
52
+ <p className="mt-5 text-[18px] leading-[1.7] text-text-secondary">
53
+ The page you requested does not exist.
54
+ </p>
55
+ </div>
56
+ </main>
57
+ )
58
+ }
59
+
60
+ return (
61
+ <PreviewProvider mode={mode}>
62
+ {isPreview ? (
63
+ <div data-agntcms-page={NOT_FOUND_PAGE_SLUG}>
64
+ <SectionEditControls
65
+ page={page}
66
+ definitions={config.sections}
67
+ />
68
+ </div>
69
+ ) : (
70
+ <PageRenderer page={page} definitions={config.sections} />
71
+ )}
72
+ {isPreview && <PreviewToolbar definitions={config.sections} />}
73
+ </PreviewProvider>
74
+ )
75
+ }
@@ -0,0 +1,33 @@
1
+ // FROZEN — do not edit. Framework file managed by agntcms.
2
+ //
3
+ // Emits a basic allow-all robots.txt. The `sitemap` URL is derived from
4
+ // the `site-meta` global so it stays in sync with the configured canonical
5
+ // origin. If `site-meta` is absent or `baseUrl` is empty the sitemap
6
+ // directive is omitted rather than emitting a malformed URL.
7
+
8
+ import type { MetadataRoute } from 'next'
9
+ import { createRuntime } from '@agntcms/next/server'
10
+ import config from '@/agntcms/config'
11
+ import { getSiteMeta } from '@/agntcms/site-meta'
12
+
13
+ const runtime = createRuntime({ contentAdapter: config.contentAdapter })
14
+
15
+ export default async function robots(): Promise<MetadataRoute.Robots> {
16
+ const siteMeta = await getSiteMeta(runtime.getGlobal)
17
+
18
+ // baseUrl is null when absent or empty; getSiteMeta already strips trailing slashes.
19
+ const origin = siteMeta.baseUrl ?? undefined
20
+
21
+ const result: MetadataRoute.Robots = {
22
+ rules: {
23
+ userAgent: '*',
24
+ allow: '/',
25
+ },
26
+ }
27
+
28
+ if (origin) {
29
+ result.sitemap = `${origin}/sitemap.xml`
30
+ }
31
+
32
+ return result
33
+ }
@@ -0,0 +1,49 @@
1
+ // FROZEN — do not edit. Framework file managed by agntcms.
2
+ //
3
+ // Generates the XML sitemap for all published pages. Reads the base URL
4
+ // from the `site-meta` global so the sitemap stays in sync with the
5
+ // configured canonical origin. If `site-meta` is missing or `baseUrl` is
6
+ // empty, the sitemap entry is omitted for that page rather than emitting
7
+ // a malformed URL.
8
+ //
9
+ // `listPages` returns published pages only (ARCHITECTURE.md §12: draft-
10
+ // only pages are intentionally excluded from the sitemap). Each entry
11
+ // carries `lastModified` from `page.publishedAt` when present; the
12
+ // framework does not fill in mtime here because it would require a second
13
+ // adapter round-trip per page.
14
+
15
+ import type { MetadataRoute } from 'next'
16
+ import { createRuntime, isSitemapEligibleSlug } from '@agntcms/next/server'
17
+ import config from '@/agntcms/config'
18
+ import { getSiteMeta } from '@/agntcms/site-meta'
19
+
20
+ const runtime = createRuntime({ contentAdapter: config.contentAdapter })
21
+
22
+ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
23
+ const [siteMeta, pages] = await Promise.all([
24
+ getSiteMeta(runtime.getGlobal),
25
+ runtime.listPages(),
26
+ ])
27
+
28
+ // If site-meta is absent or baseUrl is not configured, we cannot build
29
+ // absolute URLs — return an empty sitemap rather than relative URLs,
30
+ // which the Sitemap Protocol disallows.
31
+ if (siteMeta.baseUrl === null) return []
32
+
33
+ // baseUrl is already normalised (no trailing slash) by getSiteMeta.
34
+ const origin = siteMeta.baseUrl
35
+
36
+ return pages.filter((page) => isSitemapEligibleSlug(page.slug)).map((page) => {
37
+ // The "home" slug maps to the root path; every other slug becomes a
38
+ // sub-path. Both cases use the absolute URL format required by the
39
+ // Sitemap Protocol.
40
+ const path = page.slug === 'home' ? '' : `/${page.slug}`
41
+ const entry: MetadataRoute.Sitemap[number] = {
42
+ url: `${origin}${path}`,
43
+ }
44
+ if (page.publishedAt) {
45
+ entry.lastModified = new Date(page.publishedAt)
46
+ }
47
+ return entry
48
+ })
49
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "site-footer",
3
+ "type": "SiteFooter",
4
+ "data": {
5
+ "brandName": "Servicely™ · demo",
6
+ "showCaret": true,
7
+ "tagline": "This is a demo site. Servicely™ isn't a real platform — the content here is for showcasing the agntcms template.",
8
+ "columns": [
9
+ {
10
+ "_id": "col-product",
11
+ "heading": "Product",
12
+ "links": [
13
+ { "_id": "l-1", "label": "Overview", "link": { "type": "internal", "slug": "/", "label": "Overview" } },
14
+ { "_id": "l-2", "label": "Pricing", "link": { "type": "internal", "slug": "/pricing", "label": "Pricing" } },
15
+ { "_id": "l-3", "label": "Changelog", "link": { "type": "internal", "slug": "/", "label": "Changelog" } },
16
+ { "_id": "l-4", "label": "Roadmap", "link": { "type": "internal", "slug": "/", "label": "Roadmap" } }
17
+ ]
18
+ },
19
+ {
20
+ "_id": "col-docs",
21
+ "heading": "Docs",
22
+ "links": [
23
+ { "_id": "l-1", "label": "Quickstart", "link": { "type": "internal", "slug": "/", "label": "Quickstart" } },
24
+ { "_id": "l-2", "label": "Editor", "link": { "type": "internal", "slug": "/", "label": "Editor" } },
25
+ { "_id": "l-3", "label": "CLI reference", "link": { "type": "internal", "slug": "/", "label": "CLI reference" } },
26
+ { "_id": "l-4", "label": "Hosting", "link": { "type": "internal", "slug": "/", "label": "Hosting" } }
27
+ ]
28
+ },
29
+ {
30
+ "_id": "col-company",
31
+ "heading": "Company",
32
+ "links": [
33
+ { "_id": "l-1", "label": "About", "link": { "type": "internal", "slug": "/about", "label": "About" } },
34
+ { "_id": "l-2", "label": "Blog", "link": { "type": "internal", "slug": "/blog", "label": "Blog" } },
35
+ { "_id": "l-3", "label": "Contact", "link": { "type": "internal", "slug": "/contact", "label": "Contact" } },
36
+ { "_id": "l-4", "label": "Security", "link": { "type": "internal", "slug": "/", "label": "Security" } }
37
+ ]
38
+ },
39
+ {
40
+ "_id": "col-legal",
41
+ "heading": "Legal",
42
+ "links": [
43
+ { "_id": "l-1", "label": "Terms", "link": { "type": "internal", "slug": "/", "label": "Terms" } },
44
+ { "_id": "l-2", "label": "Privacy", "link": { "type": "internal", "slug": "/", "label": "Privacy" } },
45
+ { "_id": "l-3", "label": "DPA", "link": { "type": "internal", "slug": "/", "label": "DPA" } },
46
+ { "_id": "l-4", "label": "License", "link": { "type": "internal", "slug": "/", "label": "License" } }
47
+ ]
48
+ }
49
+ ],
50
+ "copyright": "© 2026 Servicely™ · this is a demo, not a real service",
51
+ "versionLine": "v0.0.1-demo · all systems nominal (because there aren't any)"
52
+ }
53
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "site-header",
3
+ "type": "SiteHeader",
4
+ "data": {
5
+ "brandName": "Servicely™ · demo",
6
+ "showCaret": true,
7
+ "navItems": [
8
+ { "_id": "nav-1", "label": "Home", "link": { "type": "internal", "slug": "/", "label": "Home" } },
9
+ { "_id": "nav-2", "label": "Services", "link": { "type": "internal", "slug": "/services", "label": "Services" } },
10
+ { "_id": "nav-3", "label": "About", "link": { "type": "internal", "slug": "/about", "label": "About" } },
11
+ { "_id": "nav-4", "label": "Pricing", "link": { "type": "internal", "slug": "/pricing", "label": "Pricing" } },
12
+ { "_id": "nav-5", "label": "Blog", "link": { "type": "internal", "slug": "/blog", "label": "Blog" } },
13
+ { "_id": "nav-6", "label": "Contact", "link": { "type": "internal", "slug": "/contact", "label": "Contact" } }
14
+ ],
15
+ "signInCta": { "type": "internal", "slug": "/", "label": "Sign in" },
16
+ "primaryCta": { "type": "internal", "slug": "/", "label": "Start selling" }
17
+ }
18
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "site-meta",
3
+ "type": "SiteMeta",
4
+ "data": {
5
+ "siteName": "Servicely™ · demo",
6
+ "baseUrl": "https://servicely.demo",
7
+ "defaultOgImage": {
8
+ "filename": "placeholder.png",
9
+ "alt": "Servicely™ — a demo platform for selling any service"
10
+ },
11
+ "defaultDescription": "Demo site for the fictional Servicely™ platform — a service that sells any service. This is an agntcms template, not a real company."
12
+ }
13
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "slug": "404",
3
+ "seo": {
4
+ "title": "Page not found · Servicely™ (demo)",
5
+ "description": "The URL exists in our heads, but not in this deploy. Likely culprits: a typo, a stale link, or a redirect we forgot to set up."
6
+ },
7
+ "sections": [
8
+ {
9
+ "id": "imagetext-1",
10
+ "type": "ImageText",
11
+ "data": {
12
+ "eyebrow": "404 · page not found",
13
+ "headline": "## This page is *working from home.* ☕",
14
+ "body": "The URL exists in our heads, but not in this deploy. Likely culprits: a typo, a stale link, or a redirect we forgot to set up (Friday afternoon, naturally).\n\nTry the homepage or just tell us — we read every report and usually fix the link the same day. Reminder: this is a demo site, so the page count is deliberately small — a real site would have many more landing pages to land on.",
15
+ "imagePosition": "left",
16
+ "background": "paper",
17
+ "primaryCta": {
18
+ "type": "internal",
19
+ "slug": "/",
20
+ "label": "Back to home"
21
+ },
22
+ "secondaryCta": {
23
+ "type": "internal",
24
+ "slug": "/contact",
25
+ "label": "Report broken link"
26
+ },
27
+ "image": {
28
+ "filename": "0d0475f21aa96435a8ed3cdb2fddcc6278492e76ae842f569432454f4d33631a.jpg",
29
+ "alt": "Coffee cup beside a notebook on a warm desk"
30
+ }
31
+ }
32
+ }
33
+ ]
34
+ }