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,10 @@
1
+ import { defineSection } from '@agntcms/next'
2
+ import { schema } from './schema'
3
+ import { ArticleBodyComponent } from './component'
4
+
5
+ export const ArticleBody = defineSection({
6
+ name: 'ArticleBody',
7
+ category: 'Blog',
8
+ schema,
9
+ component: ArticleBodyComponent,
10
+ })
@@ -0,0 +1,5 @@
1
+ import { RichTextField } from '@agntcms/next'
2
+
3
+ export const schema = {
4
+ body: RichTextField,
5
+ }
@@ -0,0 +1,87 @@
1
+ 'use client'
2
+
3
+ import { EditableText, EditableImage, EditableLink, read, isSlotInPreview } from '@agntcms/next/client'
4
+ import type { EditableSlot } from '@agntcms/next/client'
5
+ import { hrefOf, isExternalLink } from '@agntcms/next'
6
+ import type { ImageValue, LinkValue } from '@agntcms/next'
7
+
8
+ interface Props {
9
+ readonly backLink: EditableSlot<'link', LinkValue>
10
+ readonly category: EditableSlot<'text', string>
11
+ readonly title: EditableSlot<'text', string>
12
+ readonly authorName: EditableSlot<'text', string>
13
+ readonly authorPhoto: EditableSlot<'image', ImageValue>
14
+ readonly meta: EditableSlot<'text', string>
15
+ readonly coverImage: EditableSlot<'image', ImageValue>
16
+ }
17
+
18
+ export function ArticleHeroComponent({
19
+ backLink,
20
+ category,
21
+ title,
22
+ authorName,
23
+ authorPhoto,
24
+ meta,
25
+ coverImage,
26
+ }: Props) {
27
+ const back = read(backLink)
28
+ const backHref = hrefOf(back)
29
+ const showBack = Boolean(backHref) || isSlotInPreview(backLink)
30
+ const cover = read(coverImage)
31
+ const hasCover = Boolean(cover?.filename) || isSlotInPreview(coverImage)
32
+
33
+ return (
34
+ <section className="bg-paper">
35
+ <div className="mx-auto w-full max-w-[720px] px-8 pt-16 pb-8">
36
+ {showBack && (
37
+ <a
38
+ href={backHref || '#'}
39
+ target={isExternalLink(back) ? '_blank' : undefined}
40
+ rel={isExternalLink(back) ? 'noreferrer' : undefined}
41
+ className="font-mono text-xs tracking-[0.04em] text-ink-3 no-underline"
42
+ >
43
+ ←{' '}
44
+ <EditableLink field={backLink} className="text-ink-3 no-underline" />
45
+ </a>
46
+ )}
47
+ <div className="mt-6">
48
+ <EditableText
49
+ field={category}
50
+ as="span"
51
+ className="font-mono text-[11px] font-medium tracking-[0.07em] uppercase text-ink-3"
52
+ />
53
+ <EditableText
54
+ field={title}
55
+ as="h1"
56
+ className="mt-3 font-display text-[56px] font-semibold leading-[1.05] tracking-[-0.03em] text-ink"
57
+ />
58
+ <div className="mt-5 flex items-center gap-3">
59
+ <EditableImage
60
+ field={authorPhoto}
61
+ className="!block h-9 w-9 rounded-full object-cover grayscale [&_img]:h-9 [&_img]:w-9 [&_img]:rounded-full [&_img]:object-cover [&_img]:grayscale"
62
+ />
63
+ <div className="text-sm text-ink-2">
64
+ <EditableText
65
+ field={authorName}
66
+ as="b"
67
+ className="font-semibold text-ink"
68
+ />{' '}
69
+ ·{' '}
70
+ <EditableText field={meta} as="span" />
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ {hasCover ? (
76
+ <div className="mx-auto mt-8 w-full max-w-[720px] px-8">
77
+ <div className="aspect-[16/9] overflow-hidden border border-hairline bg-paper-2">
78
+ <EditableImage
79
+ field={coverImage}
80
+ className="!block h-full w-full object-cover [&_img]:h-full [&_img]:w-full [&_img]:object-cover"
81
+ />
82
+ </div>
83
+ </div>
84
+ ) : null}
85
+ </section>
86
+ )
87
+ }
@@ -0,0 +1,10 @@
1
+ import { defineSection } from '@agntcms/next'
2
+ import { schema } from './schema'
3
+ import { ArticleHeroComponent } from './component'
4
+
5
+ export const ArticleHero = defineSection({
6
+ name: 'ArticleHero',
7
+ category: 'Blog',
8
+ schema,
9
+ component: ArticleHeroComponent,
10
+ })
@@ -0,0 +1,12 @@
1
+ import { TextField, LinkField, ImageField } from '@agntcms/next'
2
+
3
+ export const schema = {
4
+ // "Back to blog"-style link at the top of the article.
5
+ backLink: LinkField,
6
+ category: TextField,
7
+ title: TextField,
8
+ authorName: TextField,
9
+ authorPhoto: ImageField,
10
+ meta: TextField,
11
+ coverImage: ImageField,
12
+ }
@@ -0,0 +1,83 @@
1
+ 'use client'
2
+
3
+ import { EditableRichText, EditableImage, EditableLink, read, isSlotInPreview } from '@agntcms/next/client'
4
+ import type { EditableSlot } from '@agntcms/next/client'
5
+ import { hrefOf, isExternalLink } from '@agntcms/next'
6
+ import type { ImageValue, LinkValue } from '@agntcms/next'
7
+
8
+ interface Props {
9
+ readonly headline: EditableSlot<'richText', string>
10
+ readonly lead: EditableSlot<'richText', string>
11
+ readonly primaryCta: EditableSlot<'link', LinkValue>
12
+ readonly secondaryCta: EditableSlot<'link', LinkValue>
13
+ readonly image: EditableSlot<'image', ImageValue>
14
+ }
15
+
16
+ function Cta({
17
+ field,
18
+ variant,
19
+ }: {
20
+ field: EditableSlot<'link', LinkValue>
21
+ variant: 'primary' | 'secondary'
22
+ }) {
23
+ const link = read(field)
24
+ const href = hrefOf(link)
25
+ const show = Boolean(href) || isSlotInPreview(field)
26
+ if (!show) return null
27
+ const styles =
28
+ variant === 'primary'
29
+ ? 'bg-paper text-ink border-paper hover:bg-paper-2 hover:border-paper-2'
30
+ : 'bg-transparent text-paper border-paper/25 hover:border-paper'
31
+ return (
32
+ <a
33
+ href={href || '#'}
34
+ target={isExternalLink(link) ? '_blank' : undefined}
35
+ rel={isExternalLink(link) ? 'noreferrer' : undefined}
36
+ >
37
+ <EditableLink
38
+ field={field}
39
+ className={`inline-flex items-center gap-2 whitespace-nowrap rounded-sm border px-[22px] py-[13px] text-[15px] font-medium no-underline transition-colors duration-200 ease-out ${styles}`}
40
+ />
41
+ </a>
42
+ )
43
+ }
44
+
45
+ export function BannerComponent({
46
+ headline,
47
+ lead,
48
+ primaryCta,
49
+ secondaryCta,
50
+ image,
51
+ }: Props) {
52
+ const img = read(image)
53
+ const hasImage = Boolean(img?.filename) || isSlotInPreview(image)
54
+ return (
55
+ <section className="relative overflow-hidden bg-ink text-paper">
56
+ {hasImage ? (
57
+ <div
58
+ aria-hidden="true"
59
+ className="pointer-events-none absolute inset-y-0 right-0 h-full w-[40%] opacity-90 grayscale"
60
+ >
61
+ <EditableImage
62
+ field={image}
63
+ className="!block h-full w-full object-cover [&_img]:h-full [&_img]:w-full [&_img]:object-cover"
64
+ />
65
+ <div className="absolute inset-0 bg-gradient-to-r from-ink to-transparent" />
66
+ </div>
67
+ ) : null}
68
+ <div className="relative mx-auto w-full max-w-[1280px] px-8 py-24 text-center">
69
+ <div className="mx-auto max-w-[820px] [&_h2]:m-0 [&_h2]:font-display [&_h2]:font-semibold [&_h2]:text-paper [&_h2]:text-[56px] [&_h2]:leading-[1.05] [&_h2]:tracking-[-0.03em] [&_p]:m-0 [&_p]:font-display [&_p]:font-semibold [&_p]:text-paper [&_p]:text-[56px] [&_p]:leading-[1.05] [&_p]:tracking-[-0.03em] [&_em]:not-italic [&_em]:text-paper/55 [&_em]:font-semibold">
70
+ <EditableRichText field={headline} />
71
+ </div>
72
+ <EditableRichText
73
+ field={lead}
74
+ className="mx-auto mt-4 max-w-[560px] [&_p]:text-[17px] [&_p]:leading-[1.55] [&_p]:text-paper/65 [&_p]:m-0"
75
+ />
76
+ <div className="mt-8 flex flex-wrap justify-center gap-3">
77
+ <Cta field={primaryCta} variant="primary" />
78
+ <Cta field={secondaryCta} variant="secondary" />
79
+ </div>
80
+ </div>
81
+ </section>
82
+ )
83
+ }
@@ -0,0 +1,10 @@
1
+ import { defineSection } from '@agntcms/next'
2
+ import { schema } from './schema'
3
+ import { BannerComponent } from './component'
4
+
5
+ export const Banner = defineSection({
6
+ name: 'Banner',
7
+ category: 'Calls to action',
8
+ schema,
9
+ component: BannerComponent,
10
+ })
@@ -0,0 +1,9 @@
1
+ import { RichTextField, LinkField, ImageField } from '@agntcms/next'
2
+
3
+ export const schema = {
4
+ headline: RichTextField,
5
+ lead: RichTextField,
6
+ primaryCta: LinkField,
7
+ secondaryCta: LinkField,
8
+ image: ImageField,
9
+ }
@@ -0,0 +1,173 @@
1
+ 'use client'
2
+
3
+ import {
4
+ EditableImage,
5
+ EditableList,
6
+ EditableRichText,
7
+ read,
8
+ } from '@agntcms/next/client'
9
+ import type { EditableSlot, SlotItem } from '@agntcms/next/client'
10
+ import { schema } from './schema'
11
+
12
+ type Post = SlotItem<typeof schema.posts.itemSchema>
13
+
14
+ interface PostMetaProps {
15
+ readonly category: EditableSlot<'richText', string>
16
+ readonly publishedAt: EditableSlot<'richText', string>
17
+ readonly readingTime: EditableSlot<'richText', string>
18
+ }
19
+
20
+ function PostMeta({ category, publishedAt, readingTime }: PostMetaProps) {
21
+ return (
22
+ <div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-[12px] text-text-tertiary">
23
+ <EditableRichText
24
+ field={category}
25
+ as="span"
26
+ className="text-[11px] font-medium uppercase tracking-[0.10em] text-text-brand-primary"
27
+ />
28
+ <EditableRichText field={publishedAt} as="span" />
29
+ {read(readingTime) && (
30
+ <>
31
+ <span aria-hidden="true">·</span>
32
+ <EditableRichText field={readingTime} as="span" />
33
+ </>
34
+ )}
35
+ </div>
36
+ )
37
+ }
38
+
39
+ interface Props {
40
+ readonly eyebrow: EditableSlot<'richText', string>
41
+ readonly headline: EditableSlot<'richText', string>
42
+ readonly intro: EditableSlot<'richText', string>
43
+ readonly posts: EditableSlot<'list', ReadonlyArray<Post>>
44
+ }
45
+
46
+ export function BlogIndexComponent({ eyebrow, headline, intro, posts }: Props) {
47
+ return (
48
+ <section className="bg-bg-primary">
49
+ <div className="mx-auto w-full max-w-[1080px] px-8 pt-24 pb-24">
50
+ <EditableRichText
51
+ field={eyebrow}
52
+ as="div"
53
+ className="mb-4 text-[11px] font-medium uppercase tracking-[0.10em] text-text-brand-primary"
54
+ />
55
+ <EditableRichText
56
+ field={headline}
57
+ className="
58
+ mb-6
59
+ [&_h1]:m-0 [&_h1]:font-display [&_h1]:font-medium [&_h1]:text-text-primary
60
+ [&_h1]:max-w-[18ch]
61
+ [&_h1]:!text-[clamp(40px,6vw,72px)] [&_h1]:!leading-[1.02] [&_h1]:!tracking-[-0.04em]
62
+ "
63
+ />
64
+ <EditableRichText
65
+ field={intro}
66
+ className="
67
+ mb-16 max-w-[60ch]
68
+ [&_p]:m-0 [&_p]:text-[18px] [&_p]:leading-[1.6] [&_p]:text-text-secondary
69
+ "
70
+ />
71
+
72
+ <EditableList
73
+ field={posts}
74
+ itemSchema={schema.posts.itemSchema}
75
+ renderItem={(post, index) => {
76
+ // Static helpers (PostMeta, the article href) need bare values;
77
+ // editable components receive the slot directly. `read()` is
78
+ // the canonical helper for the bare-value path (see
79
+ // EDITABILITY_DESIGN.md author migration).
80
+ const href = read(post.href) || '#'
81
+ if (index === 0) {
82
+ return (
83
+ <a href={href} className="group block mb-16 no-underline">
84
+ <div className="grid grid-cols-1 gap-8 md:grid-cols-2 md:gap-12 md:items-center">
85
+ <div className="overflow-hidden rounded-xl border-[0.5px] border-border-primary bg-bg-secondary">
86
+ <EditableImage
87
+ field={post.cover}
88
+ className="block aspect-[4/3] w-full object-cover"
89
+ />
90
+ </div>
91
+ <div>
92
+ <PostMeta
93
+ category={post.category}
94
+ publishedAt={post.publishedAt}
95
+ readingTime={post.readingTime}
96
+ />
97
+ <EditableRichText
98
+ field={post.title}
99
+ className="
100
+ mt-4 mb-4 font-display font-medium text-text-primary
101
+ [&_h2]:m-0 [&_h2]:font-display [&_h2]:font-medium [&_h2]:text-text-primary
102
+ [&_h2]:text-[clamp(24px,3vw,32px)] [&_h2]:leading-[1.15] [&_h2]:tracking-[-0.02em]
103
+ [&_p]:m-0 [&_p]:font-display [&_p]:font-medium [&_p]:text-text-primary
104
+ [&_p]:text-[clamp(24px,3vw,32px)] [&_p]:leading-[1.15] [&_p]:tracking-[-0.02em]
105
+ group-hover:text-text-brand-primary transition-colors
106
+ "
107
+ />
108
+ <EditableRichText
109
+ field={post.summary}
110
+ className="
111
+ mb-5 max-w-[52ch]
112
+ [&_p]:m-0 [&_p]:text-[16px] [&_p]:leading-[1.6] [&_p]:text-text-secondary
113
+ "
114
+ />
115
+ <EditableRichText
116
+ field={post.author}
117
+ as="div"
118
+ className="text-[13px] text-text-tertiary"
119
+ />
120
+ </div>
121
+ </div>
122
+ </a>
123
+ )
124
+ }
125
+
126
+ const isFirstInGrid = index === 1
127
+ return (
128
+ <div
129
+ className={
130
+ isFirstInGrid
131
+ ? 'border-t-[0.5px] border-border-primary pt-12'
132
+ : ''
133
+ }
134
+ >
135
+ <a href={href} className="group block no-underline">
136
+ <div className="mb-5 overflow-hidden rounded-lg border-[0.5px] border-border-primary bg-bg-secondary">
137
+ <EditableImage
138
+ field={post.cover}
139
+ className="block aspect-[16/9] w-full object-cover"
140
+ />
141
+ </div>
142
+ <PostMeta
143
+ category={post.category}
144
+ publishedAt={post.publishedAt}
145
+ readingTime={post.readingTime}
146
+ />
147
+ <EditableRichText
148
+ field={post.title}
149
+ className="
150
+ mt-3 mb-3 font-display font-medium text-text-primary
151
+ [&_h3]:m-0 [&_h3]:font-display [&_h3]:font-medium [&_h3]:text-text-primary
152
+ [&_h3]:text-[22px] [&_h3]:leading-[1.2] [&_h3]:tracking-[-0.01em]
153
+ [&_p]:m-0 [&_p]:font-display [&_p]:font-medium [&_p]:text-text-primary
154
+ [&_p]:text-[22px] [&_p]:leading-[1.2] [&_p]:tracking-[-0.01em]
155
+ group-hover:text-text-brand-primary transition-colors
156
+ "
157
+ />
158
+ <EditableRichText
159
+ field={post.summary}
160
+ className="
161
+ [&_p]:m-0 [&_p]:text-[15px] [&_p]:leading-[1.6] [&_p]:text-text-secondary
162
+ "
163
+ />
164
+ </a>
165
+ </div>
166
+ )
167
+ }}
168
+ className="grid grid-cols-1 gap-x-8 gap-y-12 md:grid-cols-2 [&>*:first-child]:md:col-span-2"
169
+ />
170
+ </div>
171
+ </section>
172
+ )
173
+ }
@@ -0,0 +1,10 @@
1
+ import { defineSection } from '@agntcms/next'
2
+ import { schema } from './schema'
3
+ import { BlogIndexComponent } from './component'
4
+
5
+ export const BlogIndex = defineSection({
6
+ name: 'BlogIndex',
7
+ category: 'Blog',
8
+ schema,
9
+ component: BlogIndexComponent,
10
+ })
@@ -0,0 +1,33 @@
1
+ import { ImageField, ListField, RichTextField, TextField } from '@agntcms/next'
2
+
3
+ export const schema = {
4
+ // ALL CAPS section label.
5
+ eyebrow: { kind: 'richText' as const, default: 'Writing' },
6
+
7
+ // Page headline.
8
+ headline: {
9
+ kind: 'richText' as const,
10
+ default: '# Blog.',
11
+ },
12
+
13
+ // Standfirst under the headline.
14
+ intro: {
15
+ kind: 'richText' as const,
16
+ default:
17
+ 'Notes on the product, the philosophy, and the open-source progress. ' +
18
+ 'Short pieces, written in the open.',
19
+ },
20
+
21
+ // List of posts. The first item is rendered as the large "featured" card
22
+ // at the top of the index. Remaining items render in a 2-up grid.
23
+ posts: ListField({
24
+ title: RichTextField,
25
+ summary: RichTextField,
26
+ href: TextField,
27
+ cover: ImageField,
28
+ category: RichTextField,
29
+ publishedAt: RichTextField,
30
+ author: RichTextField,
31
+ readingTime: RichTextField,
32
+ }),
33
+ }
@@ -0,0 +1,44 @@
1
+ 'use client'
2
+
3
+ import { EditableRichText, EditableText, EditableList } from '@agntcms/next/client'
4
+ import type { EditableSlot, SlotItem } from '@agntcms/next/client'
5
+ import { schema } from './schema'
6
+
7
+ type Category = SlotItem<typeof schema.categories.itemSchema>
8
+
9
+ interface Props {
10
+ readonly eyebrow: EditableSlot<'richText', string>
11
+ readonly headline: EditableSlot<'richText', string>
12
+ readonly lead: EditableSlot<'richText', string>
13
+ readonly categories: EditableSlot<'list', ReadonlyArray<Category>>
14
+ }
15
+
16
+ export function BlogIndexHeaderComponent({ eyebrow, headline, lead, categories }: Props) {
17
+ return (
18
+ <section className="bg-paper-2 border-b border-hairline">
19
+ <div className="mx-auto w-full max-w-[1280px] px-8 pt-[88px] pb-8">
20
+ <EditableRichText
21
+ field={eyebrow}
22
+ className="[&_p]:font-mono [&_p]:font-medium [&_p]:text-[12px] [&_p]:tracking-[0.07em] [&_p]:uppercase [&_p]:text-ink-3 [&_p]:m-0"
23
+ />
24
+ <div className="mt-3 max-w-[720px] [&_h1]:m-0 [&_h1]:font-display [&_h1]:font-semibold [&_h1]:text-ink [&_h1]:text-[72px] [&_h1]:leading-[1.05] [&_h1]:tracking-[-0.03em] [&_h2]:m-0 [&_h2]:font-display [&_h2]:font-semibold [&_h2]:text-ink [&_h2]:text-[72px] [&_h2]:leading-[1.05] [&_h2]:tracking-[-0.03em] [&_p]:m-0 [&_p]:font-display [&_p]:font-semibold [&_p]:text-ink [&_p]:text-[72px] [&_p]:leading-[1.05] [&_p]:tracking-[-0.03em] [&_em]:not-italic [&_em]:text-ink-3 [&_em]:font-semibold">
25
+ <EditableRichText field={headline} />
26
+ </div>
27
+ <EditableRichText
28
+ field={lead}
29
+ className="mt-5 max-w-[620px] [&_p]:m-0 [&_p]:text-[19px] [&_p]:leading-[1.55] [&_p]:text-ink-2"
30
+ />
31
+ <EditableList
32
+ field={categories}
33
+ itemSchema={schema.categories.itemSchema}
34
+ className="mt-8 flex flex-wrap gap-2"
35
+ renderItem={(c) => (
36
+ <span className="inline-flex items-center border border-hairline bg-transparent px-3.5 py-[7px] font-mono text-[11px] font-medium tracking-[0.07em] uppercase text-ink-2">
37
+ <EditableText field={c.label} as="span" />
38
+ </span>
39
+ )}
40
+ />
41
+ </div>
42
+ </section>
43
+ )
44
+ }
@@ -0,0 +1,10 @@
1
+ import { defineSection } from '@agntcms/next'
2
+ import { schema } from './schema'
3
+ import { BlogIndexHeaderComponent } from './component'
4
+
5
+ export const BlogIndexHeader = defineSection({
6
+ name: 'BlogIndexHeader',
7
+ category: 'Blog',
8
+ schema,
9
+ component: BlogIndexHeaderComponent,
10
+ })
@@ -0,0 +1,8 @@
1
+ import { RichTextField, TextField, ListField } from '@agntcms/next'
2
+
3
+ export const schema = {
4
+ eyebrow: RichTextField,
5
+ headline: RichTextField,
6
+ lead: RichTextField,
7
+ categories: ListField({ label: TextField }),
8
+ }
@@ -0,0 +1,50 @@
1
+ 'use client'
2
+
3
+ import { EditableRichText } from '@agntcms/next/client'
4
+ import type { EditableSlot } from '@agntcms/next/client'
5
+
6
+ interface Props {
7
+ readonly body: EditableSlot<'richText', string>
8
+ }
9
+
10
+ export function BlogPostBodyComponent({ body }: Props) {
11
+ return (
12
+ <section className="bg-bg-primary">
13
+ <div className="mx-auto w-full max-w-[720px] px-8 pb-24">
14
+ <div
15
+ className="
16
+ [&_h2]:font-display [&_h2]:font-medium [&_h2]:text-text-primary
17
+ [&_h2]:!text-[26px] [&_h2]:!leading-[1.2] [&_h2]:!tracking-[-0.02em]
18
+ [&_h2]:mt-14 [&_h2]:mb-4
19
+ [&_h3]:font-display [&_h3]:font-medium [&_h3]:text-text-primary
20
+ [&_h3]:!text-[19px] [&_h3]:!leading-[1.3] [&_h3]:!tracking-[-0.01em]
21
+ [&_h3]:mt-10 [&_h3]:mb-3
22
+ [&_p]:my-4 [&_p]:text-[17px] [&_p]:leading-[1.7] [&_p]:text-text-primary
23
+ [&_a]:text-text-brand-primary [&_a]:underline [&_a]:underline-offset-[3px] hover:[&_a]:no-underline
24
+ [&_strong]:font-medium [&_strong]:text-text-primary
25
+ [&_em]:italic
26
+ [&_ul]:my-4 [&_ul]:list-disc [&_ul]:pl-6
27
+ [&_ol]:my-4 [&_ol]:list-decimal [&_ol]:pl-6
28
+ [&_li]:my-1.5 [&_li]:text-[17px] [&_li]:leading-[1.7] [&_li]:text-text-primary
29
+ [&_li_p]:my-1
30
+ [&_code]:font-mono [&_code]:text-[14px] [&_code]:font-normal
31
+ [&_code]:text-text-brand-primary [&_code]:bg-bg-brand-primary
32
+ [&_code]:px-[6px] [&_code]:py-[2px] [&_code]:rounded-sm
33
+ [&_pre]:my-6 [&_pre]:bg-bg-brand-primary [&_pre]:rounded-lg
34
+ [&_pre]:px-5 [&_pre]:py-4 [&_pre]:overflow-x-auto
35
+ [&_pre]:font-mono [&_pre]:text-[13px] [&_pre]:leading-[1.7]
36
+ [&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_pre_code]:text-text-primary [&_pre_code]:rounded-none
37
+ [&_blockquote]:my-8 [&_blockquote]:not-italic
38
+ [&_blockquote]:bg-bg-secondary [&_blockquote]:border-l-2 [&_blockquote]:border-border-brand
39
+ [&_blockquote]:rounded-r-lg [&_blockquote]:px-5 [&_blockquote]:py-4
40
+ [&_blockquote_p]:m-0 [&_blockquote_p]:text-[16px] [&_blockquote_p]:leading-[1.65] [&_blockquote_p]:text-text-primary
41
+ [&_hr]:my-12 [&_hr]:border-0 [&_hr]:border-t-[0.5px] [&_hr]:border-border-primary
42
+ [&_img]:my-8 [&_img]:rounded-lg [&_img]:border-[0.5px] [&_img]:border-border-primary
43
+ "
44
+ >
45
+ <EditableRichText field={body} />
46
+ </div>
47
+ </div>
48
+ </section>
49
+ )
50
+ }
@@ -0,0 +1,10 @@
1
+ import { defineSection } from '@agntcms/next'
2
+ import { schema } from './schema'
3
+ import { BlogPostBodyComponent } from './component'
4
+
5
+ export const BlogPostBody = defineSection({
6
+ name: 'BlogPostBody',
7
+ category: 'Blog',
8
+ schema,
9
+ component: BlogPostBodyComponent,
10
+ })
@@ -0,0 +1,10 @@
1
+ export const schema = {
2
+ // The full article body. Headings (##, ###), paragraphs, lists,
3
+ // blockquotes, inline code, and code blocks are all supported.
4
+ body: {
5
+ kind: 'richText' as const,
6
+ default:
7
+ '## A heading\n\nWrite the article body here. Use `##` and `###` for sub-headings, ' +
8
+ 'plain paragraphs for prose, and `> ` for pull quotes.\n',
9
+ },
10
+ }