create-smoodly-app 0.0.45 → 0.0.47

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 (395) hide show
  1. package/README.md +15 -10
  2. package/dist/index.js +75 -33
  3. package/package.json +2 -2
  4. package/templates/astro-blank/AGENTS.md +55 -0
  5. package/templates/astro-blank/CLAUDE.md +1 -0
  6. package/templates/astro-blank/README.md +88 -0
  7. package/templates/astro-blank/_gitignore +5 -0
  8. package/templates/astro-blank/astro.config.ts +15 -0
  9. package/templates/astro-blank/package.json +24 -0
  10. package/templates/astro-blank/scripts/seed.ts +80 -0
  11. package/templates/astro-blank/src/components/LocaleNav.astro +15 -0
  12. package/templates/astro-blank/src/components/elements/Button.schema.ts +22 -0
  13. package/templates/astro-blank/src/components/pages/Home.schema.ts +17 -0
  14. package/templates/astro-blank/src/components/pages/Page.schema.ts +14 -0
  15. package/templates/astro-blank/src/components/sections/Text.schema.ts +24 -0
  16. package/templates/astro-blank/src/elements/button.astro +10 -0
  17. package/templates/astro-blank/src/entry-pages/posts.astro +23 -0
  18. package/templates/astro-blank/src/layouts/Layout.astro +25 -0
  19. package/templates/astro-blank/src/live.config.ts +13 -0
  20. package/templates/astro-blank/src/page-types/home.astro +15 -0
  21. package/templates/astro-blank/src/page-types/page.astro +15 -0
  22. package/templates/astro-blank/src/pages/404.astro +7 -0
  23. package/templates/astro-blank/src/sections/text.astro +18 -0
  24. package/templates/astro-blank/src/smoodly/collections.ts +24 -0
  25. package/templates/astro-blank/src/smoodly/config.ts +28 -0
  26. package/templates/astro-blank/src/smoodly/elements.ts +5 -0
  27. package/templates/astro-blank/src/smoodly/globals.ts +18 -0
  28. package/templates/astro-blank/src/smoodly/sections.ts +5 -0
  29. package/templates/astro-blank/src/smoodly/toolsets.ts +14 -0
  30. package/templates/astro-blank/src/styles/global.css +57 -0
  31. package/templates/astro-blank/supabase/migrations/20260926120000_smoodly_v1.sql +535 -0
  32. package/templates/astro-blank/supabase/migrations/20260926130000_smoodly_v2.sql +26 -0
  33. package/templates/astro-blank/supabase/migrations/20260926140000_smoodly_v3.sql +38 -0
  34. package/templates/astro-blank/supabase/migrations/20260926150000_smoodly_v4.sql +41 -0
  35. package/templates/astro-blank/supabase/migrations/20260929120000_smoodly_v5.sql +56 -0
  36. package/templates/astro-blank/supabase/migrations/20260929135523_smoodly_v6.sql +200 -0
  37. package/templates/astro-blank/supabase/migrations/20260930040956_smoodly_v7.sql +84 -0
  38. package/templates/astro-blank/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
  39. package/templates/astro-blank/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
  40. package/templates/astro-blank/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
  41. package/templates/{blank/supabase/migrations/20260930041056_smoodly_views.sql → astro-blank/supabase/migrations/20261008120100_smoodly_views.sql} +1 -1
  42. package/templates/astro-blank/tsconfig.json +17 -0
  43. package/templates/astro-blank-intl/AGENTS.md +55 -0
  44. package/templates/astro-blank-intl/CLAUDE.md +1 -0
  45. package/templates/astro-blank-intl/README.md +89 -0
  46. package/templates/astro-blank-intl/_gitignore +5 -0
  47. package/templates/astro-blank-intl/astro.config.ts +17 -0
  48. package/templates/astro-blank-intl/package.json +24 -0
  49. package/templates/astro-blank-intl/scripts/seed.ts +80 -0
  50. package/templates/astro-blank-intl/src/components/LocaleNav.astro +15 -0
  51. package/templates/astro-blank-intl/src/components/elements/Button.schema.ts +22 -0
  52. package/templates/astro-blank-intl/src/components/pages/Home.schema.ts +17 -0
  53. package/templates/astro-blank-intl/src/components/pages/Page.schema.ts +14 -0
  54. package/templates/astro-blank-intl/src/components/sections/Text.schema.ts +24 -0
  55. package/templates/astro-blank-intl/src/elements/button.astro +10 -0
  56. package/templates/astro-blank-intl/src/entry-pages/posts.astro +23 -0
  57. package/templates/astro-blank-intl/src/layouts/Layout.astro +25 -0
  58. package/templates/astro-blank-intl/src/live.config.ts +13 -0
  59. package/templates/astro-blank-intl/src/page-types/home.astro +15 -0
  60. package/templates/astro-blank-intl/src/page-types/page.astro +15 -0
  61. package/templates/astro-blank-intl/src/pages/404.astro +7 -0
  62. package/templates/astro-blank-intl/src/sections/text.astro +18 -0
  63. package/templates/astro-blank-intl/src/smoodly/collections.ts +24 -0
  64. package/templates/astro-blank-intl/src/smoodly/config.ts +29 -0
  65. package/templates/astro-blank-intl/src/smoodly/elements.ts +5 -0
  66. package/templates/astro-blank-intl/src/smoodly/globals.ts +18 -0
  67. package/templates/astro-blank-intl/src/smoodly/sections.ts +5 -0
  68. package/templates/astro-blank-intl/src/smoodly/toolsets.ts +14 -0
  69. package/templates/astro-blank-intl/src/styles/global.css +57 -0
  70. package/templates/astro-blank-intl/supabase/migrations/20260926120000_smoodly_v1.sql +535 -0
  71. package/templates/astro-blank-intl/supabase/migrations/20260926130000_smoodly_v2.sql +26 -0
  72. package/templates/astro-blank-intl/supabase/migrations/20260926140000_smoodly_v3.sql +38 -0
  73. package/templates/astro-blank-intl/supabase/migrations/20260926150000_smoodly_v4.sql +41 -0
  74. package/templates/astro-blank-intl/supabase/migrations/20260929120000_smoodly_v5.sql +56 -0
  75. package/templates/astro-blank-intl/supabase/migrations/20260929135523_smoodly_v6.sql +200 -0
  76. package/templates/astro-blank-intl/supabase/migrations/20260930040956_smoodly_v7.sql +84 -0
  77. package/templates/astro-blank-intl/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
  78. package/templates/astro-blank-intl/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
  79. package/templates/astro-blank-intl/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
  80. package/templates/{blank-intl/supabase/migrations/20260930041056_smoodly_views.sql → astro-blank-intl/supabase/migrations/20261008120100_smoodly_views.sql} +1 -1
  81. package/templates/astro-blank-intl/tsconfig.json +17 -0
  82. package/templates/astro-blank-intl.tsconfig.json +24 -0
  83. package/templates/astro-blank.tsconfig.json +24 -0
  84. package/templates/astro-lorem/AGENTS.md +55 -0
  85. package/templates/astro-lorem/CLAUDE.md +1 -0
  86. package/templates/astro-lorem/README.md +60 -0
  87. package/templates/astro-lorem/_gitignore +5 -0
  88. package/templates/astro-lorem/astro.config.ts +15 -0
  89. package/templates/astro-lorem/package.json +24 -0
  90. package/templates/astro-lorem/public/images/about_main.jpg +0 -0
  91. package/templates/astro-lorem/public/images/article_photo.jpg +0 -0
  92. package/templates/astro-lorem/public/images/hero_image.jpg +0 -0
  93. package/templates/astro-lorem/public/images/home_section_2.jpg +0 -0
  94. package/templates/astro-lorem/scripts/seed.ts +405 -0
  95. package/templates/astro-lorem/src/components/ArticleCard.astro +19 -0
  96. package/templates/astro-lorem/src/components/articles.ts +42 -0
  97. package/templates/astro-lorem/src/components/elements/Callout.schema.ts +24 -0
  98. package/templates/astro-lorem/src/components/elements/Cta.schema.ts +21 -0
  99. package/templates/astro-lorem/src/components/news.ts +13 -0
  100. package/templates/astro-lorem/src/components/pages/Articles.schema.ts +20 -0
  101. package/templates/astro-lorem/src/components/pages/Home.schema.ts +26 -0
  102. package/templates/astro-lorem/src/components/pages/News.schema.ts +17 -0
  103. package/templates/astro-lorem/src/components/pages/Page.schema.ts +24 -0
  104. package/templates/astro-lorem/src/components/sections/FavouriteArticles.schema.ts +17 -0
  105. package/templates/astro-lorem/src/components/sections/Footer.schema.ts +31 -0
  106. package/templates/astro-lorem/src/components/sections/Header.schema.ts +21 -0
  107. package/templates/astro-lorem/src/components/sections/Hero.schema.ts +24 -0
  108. package/templates/astro-lorem/src/components/sections/ImageText.schema.ts +30 -0
  109. package/templates/astro-lorem/src/components/sections/PageHero.schema.ts +21 -0
  110. package/templates/astro-lorem/src/components/sections/People.schema.ts +15 -0
  111. package/templates/astro-lorem/src/components/sections/Points.schema.ts +21 -0
  112. package/templates/astro-lorem/src/components/sections/Quote.schema.ts +23 -0
  113. package/templates/astro-lorem/src/elements/callout.astro +13 -0
  114. package/templates/astro-lorem/src/elements/cta.astro +15 -0
  115. package/templates/astro-lorem/src/entry-pages/articles.astro +71 -0
  116. package/templates/astro-lorem/src/entry-pages/news.astro +29 -0
  117. package/templates/astro-lorem/src/layouts/Layout.astro +39 -0
  118. package/templates/astro-lorem/src/live.config.ts +20 -0
  119. package/templates/astro-lorem/src/messages/en.json +7 -0
  120. package/templates/astro-lorem/src/messages/fi.json +7 -0
  121. package/templates/astro-lorem/src/page-types/articlesIndex.astro +24 -0
  122. package/templates/astro-lorem/src/page-types/home.astro +14 -0
  123. package/templates/astro-lorem/src/page-types/newsIndex.astro +28 -0
  124. package/templates/astro-lorem/src/page-types/page.astro +27 -0
  125. package/templates/astro-lorem/src/pages/404.astro +7 -0
  126. package/templates/astro-lorem/src/sections/favouriteArticles.astro +26 -0
  127. package/templates/astro-lorem/src/sections/footer.astro +37 -0
  128. package/templates/astro-lorem/src/sections/header.astro +27 -0
  129. package/templates/astro-lorem/src/sections/hero.astro +28 -0
  130. package/templates/astro-lorem/src/sections/imageText.astro +22 -0
  131. package/templates/astro-lorem/src/sections/pageHero.astro +20 -0
  132. package/templates/astro-lorem/src/sections/peopleSection.astro +24 -0
  133. package/templates/astro-lorem/src/sections/points.astro +32 -0
  134. package/templates/astro-lorem/src/sections/quote.astro +16 -0
  135. package/templates/astro-lorem/src/smoodly/collections.ts +63 -0
  136. package/templates/astro-lorem/src/smoodly/config.ts +48 -0
  137. package/templates/astro-lorem/src/smoodly/elements.ts +6 -0
  138. package/templates/astro-lorem/src/smoodly/globals.ts +29 -0
  139. package/templates/astro-lorem/src/smoodly/sections.ts +18 -0
  140. package/templates/astro-lorem/src/smoodly/toolsets.ts +16 -0
  141. package/templates/astro-lorem/src/strings.ts +17 -0
  142. package/templates/astro-lorem/src/styles/global.css +228 -0
  143. package/templates/astro-lorem/supabase/migrations/20260926120000_smoodly_v1.sql +535 -0
  144. package/templates/astro-lorem/supabase/migrations/20260926130000_smoodly_v2.sql +26 -0
  145. package/templates/astro-lorem/supabase/migrations/20260926140000_smoodly_v3.sql +38 -0
  146. package/templates/astro-lorem/supabase/migrations/20260926150000_smoodly_v4.sql +41 -0
  147. package/templates/astro-lorem/supabase/migrations/20260929120000_smoodly_v5.sql +56 -0
  148. package/templates/astro-lorem/supabase/migrations/20260929135523_smoodly_v6.sql +200 -0
  149. package/templates/astro-lorem/supabase/migrations/20260930040956_smoodly_v7.sql +84 -0
  150. package/templates/astro-lorem/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
  151. package/templates/astro-lorem/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
  152. package/templates/astro-lorem/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
  153. package/templates/{lorem/supabase/migrations/20260930041056_smoodly_views.sql → astro-lorem/supabase/migrations/20261008120100_smoodly_views.sql} +1 -1
  154. package/templates/astro-lorem/tsconfig.json +17 -0
  155. package/templates/astro-lorem-intl/AGENTS.md +55 -0
  156. package/templates/astro-lorem-intl/CLAUDE.md +1 -0
  157. package/templates/astro-lorem-intl/README.md +62 -0
  158. package/templates/astro-lorem-intl/_gitignore +5 -0
  159. package/templates/astro-lorem-intl/astro.config.ts +17 -0
  160. package/templates/astro-lorem-intl/package.json +24 -0
  161. package/templates/astro-lorem-intl/public/images/about_main.jpg +0 -0
  162. package/templates/astro-lorem-intl/public/images/article_photo.jpg +0 -0
  163. package/templates/astro-lorem-intl/public/images/hero_image.jpg +0 -0
  164. package/templates/astro-lorem-intl/public/images/home_section_2.jpg +0 -0
  165. package/templates/astro-lorem-intl/scripts/seed.ts +405 -0
  166. package/templates/astro-lorem-intl/src/components/ArticleCard.astro +19 -0
  167. package/templates/astro-lorem-intl/src/components/articles.ts +42 -0
  168. package/templates/astro-lorem-intl/src/components/elements/Callout.schema.ts +24 -0
  169. package/templates/astro-lorem-intl/src/components/elements/Cta.schema.ts +21 -0
  170. package/templates/astro-lorem-intl/src/components/news.ts +13 -0
  171. package/templates/astro-lorem-intl/src/components/pages/Articles.schema.ts +20 -0
  172. package/templates/astro-lorem-intl/src/components/pages/Home.schema.ts +26 -0
  173. package/templates/astro-lorem-intl/src/components/pages/News.schema.ts +17 -0
  174. package/templates/astro-lorem-intl/src/components/pages/Page.schema.ts +24 -0
  175. package/templates/astro-lorem-intl/src/components/sections/FavouriteArticles.schema.ts +17 -0
  176. package/templates/astro-lorem-intl/src/components/sections/Footer.schema.ts +31 -0
  177. package/templates/astro-lorem-intl/src/components/sections/Header.schema.ts +21 -0
  178. package/templates/astro-lorem-intl/src/components/sections/Hero.schema.ts +24 -0
  179. package/templates/astro-lorem-intl/src/components/sections/ImageText.schema.ts +30 -0
  180. package/templates/astro-lorem-intl/src/components/sections/PageHero.schema.ts +21 -0
  181. package/templates/astro-lorem-intl/src/components/sections/People.schema.ts +15 -0
  182. package/templates/astro-lorem-intl/src/components/sections/Points.schema.ts +21 -0
  183. package/templates/astro-lorem-intl/src/components/sections/Quote.schema.ts +23 -0
  184. package/templates/astro-lorem-intl/src/elements/callout.astro +13 -0
  185. package/templates/astro-lorem-intl/src/elements/cta.astro +15 -0
  186. package/templates/astro-lorem-intl/src/entry-pages/articles.astro +71 -0
  187. package/templates/astro-lorem-intl/src/entry-pages/news.astro +29 -0
  188. package/templates/astro-lorem-intl/src/layouts/Layout.astro +39 -0
  189. package/templates/astro-lorem-intl/src/live.config.ts +20 -0
  190. package/templates/astro-lorem-intl/src/messages/en.json +7 -0
  191. package/templates/astro-lorem-intl/src/messages/fi.json +7 -0
  192. package/templates/astro-lorem-intl/src/page-types/articlesIndex.astro +24 -0
  193. package/templates/astro-lorem-intl/src/page-types/home.astro +14 -0
  194. package/templates/astro-lorem-intl/src/page-types/newsIndex.astro +28 -0
  195. package/templates/astro-lorem-intl/src/page-types/page.astro +27 -0
  196. package/templates/astro-lorem-intl/src/pages/404.astro +7 -0
  197. package/templates/astro-lorem-intl/src/sections/favouriteArticles.astro +26 -0
  198. package/templates/astro-lorem-intl/src/sections/footer.astro +37 -0
  199. package/templates/astro-lorem-intl/src/sections/header.astro +27 -0
  200. package/templates/astro-lorem-intl/src/sections/hero.astro +28 -0
  201. package/templates/astro-lorem-intl/src/sections/imageText.astro +22 -0
  202. package/templates/astro-lorem-intl/src/sections/pageHero.astro +20 -0
  203. package/templates/astro-lorem-intl/src/sections/peopleSection.astro +24 -0
  204. package/templates/astro-lorem-intl/src/sections/points.astro +32 -0
  205. package/templates/astro-lorem-intl/src/sections/quote.astro +16 -0
  206. package/templates/astro-lorem-intl/src/smoodly/collections.ts +63 -0
  207. package/templates/astro-lorem-intl/src/smoodly/config.ts +48 -0
  208. package/templates/astro-lorem-intl/src/smoodly/elements.ts +6 -0
  209. package/templates/astro-lorem-intl/src/smoodly/globals.ts +29 -0
  210. package/templates/astro-lorem-intl/src/smoodly/sections.ts +18 -0
  211. package/templates/astro-lorem-intl/src/smoodly/toolsets.ts +16 -0
  212. package/templates/astro-lorem-intl/src/strings.ts +17 -0
  213. package/templates/astro-lorem-intl/src/styles/global.css +228 -0
  214. package/templates/astro-lorem-intl/supabase/migrations/20260926120000_smoodly_v1.sql +535 -0
  215. package/templates/astro-lorem-intl/supabase/migrations/20260926130000_smoodly_v2.sql +26 -0
  216. package/templates/astro-lorem-intl/supabase/migrations/20260926140000_smoodly_v3.sql +38 -0
  217. package/templates/astro-lorem-intl/supabase/migrations/20260926150000_smoodly_v4.sql +41 -0
  218. package/templates/astro-lorem-intl/supabase/migrations/20260929120000_smoodly_v5.sql +56 -0
  219. package/templates/astro-lorem-intl/supabase/migrations/20260929135523_smoodly_v6.sql +200 -0
  220. package/templates/astro-lorem-intl/supabase/migrations/20260930040956_smoodly_v7.sql +84 -0
  221. package/templates/astro-lorem-intl/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
  222. package/templates/astro-lorem-intl/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
  223. package/templates/astro-lorem-intl/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
  224. package/templates/{lorem-intl/supabase/migrations/20260930041056_smoodly_views.sql → astro-lorem-intl/supabase/migrations/20261008120100_smoodly_views.sql} +1 -1
  225. package/templates/astro-lorem-intl/tsconfig.json +17 -0
  226. package/templates/astro-lorem-intl.tsconfig.json +24 -0
  227. package/templates/astro-lorem.tsconfig.json +24 -0
  228. package/templates/blank/AGENTS.md +23 -16
  229. package/templates/blank/README.md +20 -15
  230. package/templates/blank/app/(smoodly)/admin/[[...segments]]/route.ts +8 -0
  231. package/templates/blank/components/elements/Button.schema.ts +22 -0
  232. package/templates/blank/components/elements/Button.tsx +2 -20
  233. package/templates/blank/components/pages/Home.schema.ts +17 -0
  234. package/templates/blank/components/pages/Home.tsx +2 -16
  235. package/templates/blank/components/pages/Page.schema.ts +14 -0
  236. package/templates/blank/components/pages/Page.tsx +2 -14
  237. package/templates/blank/components/pages/Post.tsx +5 -4
  238. package/templates/blank/components/sections/Text.schema.ts +24 -0
  239. package/templates/blank/components/sections/Text.tsx +7 -27
  240. package/templates/blank/package.json +1 -1
  241. package/templates/blank/scripts/seed.ts +2 -2
  242. package/templates/blank/smoodly/config.ts +8 -7
  243. package/templates/blank/smoodly/elements.ts +4 -4
  244. package/templates/blank/smoodly/globals.ts +4 -2
  245. package/templates/blank/smoodly/richtext.tsx +4 -5
  246. package/templates/blank/smoodly/sections.ts +4 -4
  247. package/templates/blank/smoodly/server.ts +22 -44
  248. package/templates/blank/smoodly/toolsets.ts +2 -2
  249. package/templates/blank/smoodly/views.ts +15 -0
  250. package/templates/blank/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
  251. package/templates/blank/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
  252. package/templates/blank/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
  253. package/templates/blank/supabase/migrations/20261008120100_smoodly_views.sql +20 -0
  254. package/templates/blank-intl/AGENTS.md +23 -16
  255. package/templates/blank-intl/README.md +20 -15
  256. package/templates/blank-intl/app/(smoodly)/admin/[[...segments]]/route.ts +8 -0
  257. package/templates/blank-intl/components/elements/Button.schema.ts +22 -0
  258. package/templates/blank-intl/components/elements/Button.tsx +2 -20
  259. package/templates/blank-intl/components/pages/Home.schema.ts +17 -0
  260. package/templates/blank-intl/components/pages/Home.tsx +2 -16
  261. package/templates/blank-intl/components/pages/Page.schema.ts +14 -0
  262. package/templates/blank-intl/components/pages/Page.tsx +2 -14
  263. package/templates/blank-intl/components/pages/Post.tsx +5 -4
  264. package/templates/blank-intl/components/sections/Text.schema.ts +24 -0
  265. package/templates/blank-intl/components/sections/Text.tsx +7 -27
  266. package/templates/blank-intl/package.json +1 -1
  267. package/templates/blank-intl/scripts/seed.ts +2 -2
  268. package/templates/blank-intl/smoodly/config.ts +8 -7
  269. package/templates/blank-intl/smoodly/elements.ts +4 -4
  270. package/templates/blank-intl/smoodly/globals.ts +4 -2
  271. package/templates/blank-intl/smoodly/richtext.tsx +4 -5
  272. package/templates/blank-intl/smoodly/sections.ts +4 -4
  273. package/templates/blank-intl/smoodly/server.ts +22 -44
  274. package/templates/blank-intl/smoodly/toolsets.ts +2 -2
  275. package/templates/blank-intl/smoodly/views.ts +15 -0
  276. package/templates/blank-intl/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
  277. package/templates/blank-intl/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
  278. package/templates/blank-intl/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
  279. package/templates/blank-intl/supabase/migrations/20261008120100_smoodly_views.sql +20 -0
  280. package/templates/lorem/AGENTS.md +23 -16
  281. package/templates/lorem/README.md +3 -3
  282. package/templates/lorem/app/(smoodly)/admin/[[...segments]]/route.ts +8 -0
  283. package/templates/lorem/components/elements/Callout.schema.ts +24 -0
  284. package/templates/lorem/components/elements/Callout.tsx +2 -24
  285. package/templates/lorem/components/elements/Cta.schema.ts +21 -0
  286. package/templates/lorem/components/elements/Cta.tsx +2 -21
  287. package/templates/lorem/components/pages/Article.tsx +1 -1
  288. package/templates/lorem/components/pages/Articles.schema.ts +20 -0
  289. package/templates/lorem/components/pages/Articles.tsx +2 -20
  290. package/templates/lorem/components/pages/Home.schema.ts +26 -0
  291. package/templates/lorem/components/pages/Home.tsx +2 -26
  292. package/templates/lorem/components/pages/News.schema.ts +17 -0
  293. package/templates/lorem/components/pages/News.tsx +2 -17
  294. package/templates/lorem/components/pages/NewsPost.tsx +1 -1
  295. package/templates/lorem/components/pages/Page.schema.ts +24 -0
  296. package/templates/lorem/components/pages/Page.tsx +2 -24
  297. package/templates/lorem/components/sections/FavouriteArticles.schema.ts +17 -0
  298. package/templates/lorem/components/sections/FavouriteArticles.tsx +2 -17
  299. package/templates/lorem/components/sections/Footer.schema.ts +31 -0
  300. package/templates/lorem/components/sections/Footer.tsx +4 -32
  301. package/templates/lorem/components/sections/Header.schema.ts +21 -0
  302. package/templates/lorem/components/sections/Header.tsx +2 -21
  303. package/templates/lorem/components/sections/Hero.schema.ts +24 -0
  304. package/templates/lorem/components/sections/Hero.tsx +2 -24
  305. package/templates/lorem/components/sections/ImageText.schema.ts +30 -0
  306. package/templates/lorem/components/sections/ImageText.tsx +2 -30
  307. package/templates/lorem/components/sections/PageHero.schema.ts +21 -0
  308. package/templates/lorem/components/sections/PageHero.tsx +2 -21
  309. package/templates/lorem/components/sections/People.schema.ts +15 -0
  310. package/templates/lorem/components/sections/People.tsx +2 -14
  311. package/templates/lorem/components/sections/Points.schema.ts +21 -0
  312. package/templates/lorem/components/sections/Points.tsx +2 -21
  313. package/templates/lorem/components/sections/Quote.schema.ts +23 -0
  314. package/templates/lorem/components/sections/Quote.tsx +2 -23
  315. package/templates/lorem/package.json +1 -1
  316. package/templates/lorem/scripts/seed.ts +18 -18
  317. package/templates/lorem/smoodly/collections.ts +2 -2
  318. package/templates/lorem/smoodly/config.ts +14 -15
  319. package/templates/lorem/smoodly/elements.ts +5 -5
  320. package/templates/lorem/smoodly/globals.ts +10 -7
  321. package/templates/lorem/smoodly/richtext.tsx +5 -5
  322. package/templates/lorem/smoodly/sections.ts +12 -13
  323. package/templates/lorem/smoodly/server.ts +22 -44
  324. package/templates/lorem/smoodly/toolsets.ts +3 -3
  325. package/templates/lorem/smoodly/views.ts +33 -0
  326. package/templates/lorem/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
  327. package/templates/lorem/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
  328. package/templates/lorem/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
  329. package/templates/lorem/supabase/migrations/20261008120100_smoodly_views.sql +58 -0
  330. package/templates/lorem-intl/AGENTS.md +23 -16
  331. package/templates/lorem-intl/README.md +3 -3
  332. package/templates/lorem-intl/app/(smoodly)/admin/[[...segments]]/route.ts +8 -0
  333. package/templates/lorem-intl/components/elements/Callout.schema.ts +24 -0
  334. package/templates/lorem-intl/components/elements/Callout.tsx +2 -24
  335. package/templates/lorem-intl/components/elements/Cta.schema.ts +21 -0
  336. package/templates/lorem-intl/components/elements/Cta.tsx +2 -21
  337. package/templates/lorem-intl/components/pages/Article.tsx +1 -1
  338. package/templates/lorem-intl/components/pages/Articles.schema.ts +20 -0
  339. package/templates/lorem-intl/components/pages/Articles.tsx +2 -20
  340. package/templates/lorem-intl/components/pages/Home.schema.ts +26 -0
  341. package/templates/lorem-intl/components/pages/Home.tsx +2 -26
  342. package/templates/lorem-intl/components/pages/News.schema.ts +17 -0
  343. package/templates/lorem-intl/components/pages/News.tsx +2 -17
  344. package/templates/lorem-intl/components/pages/NewsPost.tsx +1 -1
  345. package/templates/lorem-intl/components/pages/Page.schema.ts +24 -0
  346. package/templates/lorem-intl/components/pages/Page.tsx +2 -24
  347. package/templates/lorem-intl/components/sections/FavouriteArticles.schema.ts +17 -0
  348. package/templates/lorem-intl/components/sections/FavouriteArticles.tsx +2 -17
  349. package/templates/lorem-intl/components/sections/Footer.schema.ts +31 -0
  350. package/templates/lorem-intl/components/sections/Footer.tsx +4 -32
  351. package/templates/lorem-intl/components/sections/Header.schema.ts +21 -0
  352. package/templates/lorem-intl/components/sections/Header.tsx +2 -21
  353. package/templates/lorem-intl/components/sections/Hero.schema.ts +24 -0
  354. package/templates/lorem-intl/components/sections/Hero.tsx +2 -24
  355. package/templates/lorem-intl/components/sections/ImageText.schema.ts +30 -0
  356. package/templates/lorem-intl/components/sections/ImageText.tsx +2 -30
  357. package/templates/lorem-intl/components/sections/PageHero.schema.ts +21 -0
  358. package/templates/lorem-intl/components/sections/PageHero.tsx +2 -21
  359. package/templates/lorem-intl/components/sections/People.schema.ts +15 -0
  360. package/templates/lorem-intl/components/sections/People.tsx +2 -14
  361. package/templates/lorem-intl/components/sections/Points.schema.ts +21 -0
  362. package/templates/lorem-intl/components/sections/Points.tsx +2 -21
  363. package/templates/lorem-intl/components/sections/Quote.schema.ts +23 -0
  364. package/templates/lorem-intl/components/sections/Quote.tsx +2 -23
  365. package/templates/lorem-intl/package.json +1 -1
  366. package/templates/lorem-intl/scripts/seed.ts +18 -18
  367. package/templates/lorem-intl/smoodly/collections.ts +2 -2
  368. package/templates/lorem-intl/smoodly/config.ts +14 -15
  369. package/templates/lorem-intl/smoodly/elements.ts +5 -5
  370. package/templates/lorem-intl/smoodly/globals.ts +10 -7
  371. package/templates/lorem-intl/smoodly/richtext.tsx +5 -5
  372. package/templates/lorem-intl/smoodly/sections.ts +12 -13
  373. package/templates/lorem-intl/smoodly/server.ts +22 -44
  374. package/templates/lorem-intl/smoodly/toolsets.ts +3 -3
  375. package/templates/lorem-intl/smoodly/views.ts +33 -0
  376. package/templates/lorem-intl/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
  377. package/templates/lorem-intl/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
  378. package/templates/lorem-intl/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
  379. package/templates/lorem-intl/supabase/migrations/20261008120100_smoodly_views.sql +58 -0
  380. package/templates/blank/app/(smoodly)/admin/[[...segments]]/page.tsx +0 -1
  381. package/templates/blank/app/(smoodly)/admin/layout.tsx +0 -15
  382. package/templates/blank/smoodly/admin.tsx +0 -6
  383. package/templates/blank/smoodly/ops.ts +0 -11
  384. package/templates/blank-intl/app/(smoodly)/admin/[[...segments]]/page.tsx +0 -1
  385. package/templates/blank-intl/app/(smoodly)/admin/layout.tsx +0 -15
  386. package/templates/blank-intl/smoodly/admin.tsx +0 -6
  387. package/templates/blank-intl/smoodly/ops.ts +0 -11
  388. package/templates/lorem/app/(smoodly)/admin/[[...segments]]/page.tsx +0 -1
  389. package/templates/lorem/app/(smoodly)/admin/layout.tsx +0 -15
  390. package/templates/lorem/smoodly/admin.tsx +0 -6
  391. package/templates/lorem/smoodly/ops.ts +0 -11
  392. package/templates/lorem-intl/app/(smoodly)/admin/[[...segments]]/page.tsx +0 -1
  393. package/templates/lorem-intl/app/(smoodly)/admin/layout.tsx +0 -15
  394. package/templates/lorem-intl/smoodly/admin.tsx +0 -6
  395. package/templates/lorem-intl/smoodly/ops.ts +0 -11
package/README.md CHANGED
@@ -2,18 +2,23 @@
2
2
 
3
3
  npx create-smoodly-app my-site
4
4
 
5
- A fresh Next.js app with the Smoodly admin mounted at `/admin`, a Supabase
6
- project holding the schema, `.env.local` written, and a first editor who can
7
- sign in.
5
+ A fresh Astro or Next.js site with the Smoodly admin mounted at `/admin`, a
6
+ Supabase project holding the schema, `.env` (Astro) or `.env.local` (Next)
7
+ written, and a first editor who can sign in.
8
8
 
9
9
  ## What it asks
10
10
 
11
11
  1. Project name (also the positional argument).
12
- 2. **Multilingual?** No (default) scaffolds one language. Yes asks for the
13
- locales, comma-separated, the first being the default (`en, fi`), and
14
- scaffolds the next-intl layout: the site under `app/[locale]/`, `i18n/`,
15
- `proxy.ts`, translated URLs per locale. Flag: `--locales en,fi`.
16
- 3. Where content lives: a **local Supabase stack** (`supabase init` +
12
+ 2. **Framework**: Astro (the default, and what `--yes` takes) or Next.js.
13
+ Flag: `--framework astro|next`. The questions below apply to both; the
14
+ Astro site ships no React, and writes `.env` (its `npm start` reads it)
15
+ with `SITE_URL` on port 4321.
16
+ 3. **Multilingual?** No (default) scaffolds one language. Yes asks for the
17
+ locales, comma-separated, the first being the default (`en, fi`). Next
18
+ gets the next-intl layout: the site under `app/[locale]/`, `i18n/`,
19
+ `proxy.ts`, translated URLs per locale; Astro gets its own i18n config
20
+ (`astro.config.ts` and `src/smoodly/config.ts`). Flag: `--locales en,fi`.
21
+ 4. Where content lives: a **local Supabase stack** (`supabase init` +
17
22
  `supabase start` inside the app, with the services Smoodly never uses
18
23
  excluded — the image proxy, edge functions, analytics, the pooler —
19
24
  so the stack stays small beside any other you run; needs
@@ -29,14 +34,14 @@ sign in.
29
34
  project is Smoodly's own: its tables are created unprefixed in `public`,
30
35
  so an app that keeps its own tables in Supabase gives them a project of
31
36
  their own, or takes Smoodly Cloud for content (DESIGN.md §1).
32
- 4. The first editor: pick an existing Auth user or create one (email +
37
+ 5. The first editor: pick an existing Auth user or create one (email +
33
38
  password). The `members` row is written with role `admin`, the email
34
39
  beside the id.
35
40
  If `@` does not appear when you type it (macOS, `@` on Option+2 with the
36
41
  terminal's "Option as Meta" setting on), paste the address or pass
37
42
  `--editor-email`.
38
43
 
39
- Every prompt has a flag, so CI can run it: `--supabase local|hosted`, `--locales`,
44
+ Every prompt has a flag, so CI can run it: `--framework astro|next`, `--supabase local|hosted`, `--locales`,
40
45
  `--url`, `--publishable-key`, `--secret-key`, `--editor-email` (password from
41
46
  `SMOODLY_EDITOR_PASSWORD`), `--skip-link`, `--yes`. `--help` lists them.
42
47
  A hidden `--smoodly-dep <spec>` overrides the `smoodly` dependency written into
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ function parseFlags(argv) {
32
32
  "smoodly-dep": { type: "string" },
33
33
  locales: { type: "string" },
34
34
  starter: { type: "string" },
35
+ framework: { type: "string" },
35
36
  "skip-link": { type: "boolean", default: false },
36
37
  yes: { type: "boolean", short: "y", default: false },
37
38
  help: { type: "boolean", short: "h", default: false }
@@ -46,6 +47,10 @@ function parseFlags(argv) {
46
47
  if (starter !== void 0 && starter !== "lorem" && starter !== "blank") {
47
48
  throw new Error(`--starter must be "lorem" or "blank", got "${starter}"`);
48
49
  }
50
+ const framework = values.framework;
51
+ if (framework !== void 0 && framework !== "astro" && framework !== "next") {
52
+ throw new Error(`--framework must be "astro" or "next", got "${framework}"`);
53
+ }
49
54
  return {
50
55
  name: positionals[0],
51
56
  supabase,
@@ -58,10 +63,11 @@ function parseFlags(argv) {
58
63
  help: values.help ?? false,
59
64
  smoodlyDep: values["smoodly-dep"],
60
65
  locales: values.locales === void 0 ? void 0 : parseLocales(values.locales),
61
- starter
66
+ starter,
67
+ framework
62
68
  };
63
69
  }
64
- var HELP = `create-smoodly-app \u2014 a fresh Next.js site with Smoodly installed
70
+ var HELP = `create-smoodly-app \u2014 a fresh Astro or Next.js site with Smoodly installed
65
71
 
66
72
  Usage: npx create-smoodly-app [folder] [options]
67
73
 
@@ -69,8 +75,9 @@ Usage: npx create-smoodly-app [folder] [options]
69
75
  the package name: lowercase letters, digits, dots, dashes and underscores.
70
76
 
71
77
  Options:
78
+ --framework astro|next The framework (asked when omitted; astro is the default)
72
79
  --supabase local|hosted Where content lives (asked when omitted; local needs the Supabase CLI)
73
- --locales <a,b,\u2026> Multilingual: the locales, first is the default (scaffolds next-intl; omitted = one language)
80
+ --locales <a,b,\u2026> Multilingual: the locales, first is the default (Astro i18n or next-intl; omitted = one language)
74
81
  --starter lorem|blank lorem: the Lorem Co. tour of every feature (default); blank: one of each, for building your own
75
82
  --url <url> Hosted: the Supabase project URL
76
83
  --publishable-key <key> Hosted: the anon / publishable key
@@ -145,15 +152,15 @@ async function hasCommand(cmd, run2, cwd) {
145
152
 
146
153
  // src/ports-net.ts
147
154
  import { createServer } from "node:net";
148
- function canListen(port, host) {
155
+ function canListen(port2, host) {
149
156
  return new Promise((resolve3) => {
150
157
  const server = createServer();
151
158
  server.once("error", () => resolve3(false));
152
- server.listen(port, host, () => server.close(() => resolve3(true)));
159
+ server.listen(port2, host, () => server.close(() => resolve3(true)));
153
160
  });
154
161
  }
155
- async function isPortFree(port) {
156
- return await canListen(port, "127.0.0.1") && await canListen(port, "0.0.0.0");
162
+ async function isPortFree(port2) {
163
+ return await canListen(port2, "127.0.0.1") && await canListen(port2, "0.0.0.0");
157
164
  }
158
165
 
159
166
  // src/prompts.ts
@@ -161,7 +168,7 @@ import * as p from "@clack/prompts";
161
168
  import { resolve as resolve2 } from "node:path";
162
169
 
163
170
  // src/env.ts
164
- function renderEnv(keys) {
171
+ function renderEnv(keys, siteUrl2 = "http://localhost:3000") {
165
172
  return [
166
173
  "# Smoodly, self-host: the content project's values. The live site reads",
167
174
  "# with SMOODLY_URL and SMOODLY_SECRET_KEY; the admin also signs in with",
@@ -173,7 +180,7 @@ function renderEnv(keys) {
173
180
  "",
174
181
  "# The site's own address: absolute hreflang and social URLs. Set it to",
175
182
  "# the production URL where you deploy.",
176
- "SITE_URL=http://localhost:3000",
183
+ `SITE_URL=${siteUrl2}`,
177
184
  ""
178
185
  ].join("\n");
179
186
  }
@@ -224,6 +231,8 @@ async function isEmptyOrMissing(dir) {
224
231
  }
225
232
  var ROUTING_LOCALES = ' locales: ["en", "fi"],';
226
233
  var ROUTING_DEFAULT = ' defaultLocale: "en",';
234
+ var ASTRO_I18N_LINE = ' i18n: { locales: ["en", "fi"], defaultLocale: "en" },';
235
+ var ASTRO_CONFIG_LOCALES_LINE = ' locales: { default: "en", supported: ["en", "fi"] },';
227
236
  var ARTICLES_PATH_LINE = ' path: { en: "posts", fi: "artikkelit" },';
228
237
  var ARTICLES_SLUG_LINE = ' slug: { en: "posts", fi: "artikkelit" },';
229
238
  var NEWS_PATH_LINE = ' path: { en: "company/news", fi: "yritys/uutiset" },';
@@ -244,7 +253,20 @@ async function scaffold(opts) {
244
253
  pkg.name = opts.name;
245
254
  if (opts.smoodlyDep) pkg.dependencies.smoodly = opts.smoodlyDep;
246
255
  await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
247
- if (opts.locales) {
256
+ const astro = opts.framework === "astro";
257
+ const root = astro ? "src/" : "";
258
+ if (opts.locales && astro) {
259
+ const rewriteLine = async (rel, line, next) => {
260
+ const path = join(opts.dir, rel);
261
+ const source = await readFile(path, "utf8");
262
+ if (!source.includes(line)) throw new Error(`${rel} in the template has drifted; expected the line
263
+ ${line}`);
264
+ await writeFile(path, source.replace(line, next));
265
+ };
266
+ await rewriteLine("astro.config.ts", ASTRO_I18N_LINE, ` i18n: { locales: [${opts.locales.map((l) => JSON.stringify(l)).join(", ")}], defaultLocale: ${JSON.stringify(opts.locales[0])} },`);
267
+ await rewriteLine("src/smoodly/config.ts", ASTRO_CONFIG_LOCALES_LINE, ` locales: { default: ${JSON.stringify(opts.locales[0])}, supported: [${opts.locales.map((l) => JSON.stringify(l)).join(", ")}] },`);
268
+ }
269
+ if (opts.locales && !astro) {
248
270
  const routingPath = join(opts.dir, "i18n/routing.ts");
249
271
  const routing = await readFile(routingPath, "utf8");
250
272
  if (!routing.includes(ROUTING_LOCALES) || !routing.includes(ROUTING_DEFAULT)) {
@@ -254,23 +276,25 @@ async function scaffold(opts) {
254
276
  routingPath,
255
277
  routing.replace(ROUTING_LOCALES, ` locales: [${opts.locales.map((l) => JSON.stringify(l)).join(", ")}],`).replace(ROUTING_DEFAULT, ` defaultLocale: ${JSON.stringify(opts.locales[0])},`)
256
278
  );
257
- const source = await readFile(join(opts.dir, "messages", "en.json"), "utf8").catch(() => null);
279
+ }
280
+ if (opts.locales) {
281
+ const source = await readFile(join(opts.dir, `${root}messages`, "en.json"), "utf8").catch(() => null);
258
282
  if (source !== null) {
259
283
  for (const locale of opts.locales) {
260
- const file = join(opts.dir, "messages", `${locale}.json`);
284
+ const file = join(opts.dir, `${root}messages`, `${locale}.json`);
261
285
  if (await readFile(file, "utf8").catch(() => null) === null) await writeFile(file, source);
262
286
  }
263
287
  }
264
288
  if (opts.starter !== "blank") {
265
289
  const rewrites = [
266
- { map: articlesSegments(opts.locales), from: '{ en: "posts", fi: "artikkelit" }', lines: [["smoodly/collections.ts", ARTICLES_PATH_LINE], ["components/pages/Articles.tsx", ARTICLES_SLUG_LINE]] },
267
- { map: newsSegments(opts.locales), from: '{ en: "company/news", fi: "yritys/uutiset" }', lines: [["smoodly/collections.ts", NEWS_PATH_LINE], ["components/pages/News.tsx", NEWS_SLUG_LINE]] }
290
+ { map: articlesSegments(opts.locales), from: '{ en: "posts", fi: "artikkelit" }', lines: [["smoodly/collections.ts", ARTICLES_PATH_LINE], ["components/pages/Articles.schema.ts", ARTICLES_SLUG_LINE]] },
291
+ { map: newsSegments(opts.locales), from: '{ en: "company/news", fi: "yritys/uutiset" }', lines: [["smoodly/collections.ts", NEWS_PATH_LINE], ["components/pages/News.schema.ts", NEWS_SLUG_LINE]] }
268
292
  ];
269
293
  for (const { map, from, lines } of rewrites) {
270
294
  for (const [rel, line] of lines) {
271
- const path = join(opts.dir, rel);
295
+ const path = join(opts.dir, root + rel);
272
296
  const source2 = await readFile(path, "utf8");
273
- if (!source2.includes(line)) throw new Error(`${rel} in the template has drifted; expected the line
297
+ if (!source2.includes(line)) throw new Error(`${root}${rel} in the template has drifted; expected the line
274
298
  ${line}`);
275
299
  await writeFile(path, source2.replace(line, line.replace(from, map)));
276
300
  }
@@ -302,7 +326,16 @@ async function resolveAnswers(flags2, ctx, env = process.env) {
302
326
  const invalid = validName(name);
303
327
  if (invalid) bail(invalid);
304
328
  if (!await isEmptyOrMissing(dir)) bail(`${dir} exists and is not empty.`);
305
- const locales = flags2.locales ?? (flags2.yes ? void 0 : await answer(p.confirm({ message: "Multilingual site? (next-intl, translated URLs)", initialValue: false })) ? await answer(
329
+ const framework = flags2.framework ?? (flags2.yes ? "astro" : await answer(
330
+ p.select({
331
+ message: "Framework?",
332
+ options: [
333
+ { value: "astro", label: "Astro", hint: "sections as .astro files, pages cached whole and expired by tag" },
334
+ { value: "next", label: "Next.js", hint: "sections as React server components" }
335
+ ]
336
+ })
337
+ ));
338
+ const locales = flags2.locales ?? (flags2.yes ? void 0 : await answer(p.confirm({ message: `Multilingual site? (${framework === "astro" ? "Astro i18n" : "next-intl"}, translated URLs)`, initialValue: false })) ? await answer(
306
339
  p.text({
307
340
  message: "Locales, comma-separated, the first is the default",
308
341
  placeholder: "en, fr",
@@ -336,7 +369,7 @@ async function resolveAnswers(flags2, ctx, env = process.env) {
336
369
  if (supabase === "local") {
337
370
  if (!ctx.hasSupabaseCli) bail("Local mode needs the Supabase CLI: https://supabase.com/docs/guides/cli (brew install supabase/tap/supabase)");
338
371
  if (flags2.editorEmail && !env.SMOODLY_EDITOR_PASSWORD) throw new Error(`Set SMOODLY_EDITOR_PASSWORD to create ${flags2.editorEmail}.`);
339
- return { name, dir, supabase, hasSupabaseCli: true, smoodlyDep: flags2.smoodlyDep, locales, starter };
372
+ return { name, dir, supabase, hasSupabaseCli: true, smoodlyDep: flags2.smoodlyDep, locales, starter, framework };
340
373
  }
341
374
  const badUrl = "Supabase project URL must be an http(s) URL, like https://abcd.supabase.co";
342
375
  const typedUrl = flags2.url ?? await answer(
@@ -355,7 +388,7 @@ async function resolveAnswers(flags2, ctx, env = process.env) {
355
388
  p.log.warn(`No project ref in ${url}; skipping supabase link. Run \`supabase link --project-ref <project-ref>\` and \`supabase db push\` yourself.`);
356
389
  }
357
390
  const linkAndPush = !ctx.hasSupabaseCli ? false : flags2.skipLink || !ref ? false : flags2.yes ? true : await answer(p.confirm({ message: `Link this app to the project and push the migration now? (supabase link --project-ref ${ref}, supabase db push)` }));
358
- return { name, dir, supabase, hasSupabaseCli: ctx.hasSupabaseCli, hosted: { keys: { url, publishableKey, secretKey }, linkAndPush }, smoodlyDep: flags2.smoodlyDep, locales, starter };
391
+ return { name, dir, supabase, hasSupabaseCli: ctx.hasSupabaseCli, hosted: { keys: { url, publishableKey, secretKey }, linkAndPush }, smoodlyDep: flags2.smoodlyDep, locales, starter, framework };
359
392
  }
360
393
  function askEditorWith(flags2, env = process.env) {
361
394
  return async (existing) => {
@@ -388,8 +421,9 @@ function askEditorWith(flags2, env = process.env) {
388
421
  import { join as join2 } from "node:path";
389
422
 
390
423
  // src/plan.ts
391
- function templateName(starter, locales) {
392
- return locales === "intl" ? `${starter}-intl` : starter;
424
+ function templateName(framework, starter, locales) {
425
+ const base = locales === "intl" ? `${starter}-intl` : starter;
426
+ return framework === "astro" ? `astro-${base}` : base;
393
427
  }
394
428
  function plan(a) {
395
429
  if (a.supabase === "local" && !a.hasSupabaseCli) {
@@ -420,8 +454,8 @@ async function findFreeBlock(ports, isFree, bases = BASES) {
420
454
  for (const base of bases) {
421
455
  const shifted = ports.map((p3) => base + (p3 - DEFAULT_BASE));
422
456
  let allFree = true;
423
- for (const port of shifted) {
424
- if (!await isFree(port)) {
457
+ for (const port2 of shifted) {
458
+ if (!await isFree(port2)) {
425
459
  allFree = false;
426
460
  break;
427
461
  }
@@ -465,12 +499,13 @@ async function run(answers2, deps) {
465
499
  switch (step) {
466
500
  case "copyTemplate":
467
501
  await deps.fs.scaffold({
468
- templateDir: deps.templateDirs[templateName(answers2.starter ?? "lorem", answers2.locales ? "intl" : "single")],
502
+ templateDir: deps.templateDirs[templateName(answers2.framework, answers2.starter ?? "lorem", answers2.locales ? "intl" : "single")],
469
503
  dir: cwd,
470
504
  name: answers2.name,
471
505
  smoodlyDep: answers2.smoodlyDep,
472
506
  locales: answers2.locales,
473
- starter: answers2.starter
507
+ starter: answers2.starter,
508
+ framework: answers2.framework
474
509
  });
475
510
  break;
476
511
  case "install":
@@ -478,7 +513,7 @@ async function run(answers2, deps) {
478
513
  await sh("npm", ["install"]);
479
514
  break;
480
515
  case "gitInit":
481
- await deps.agentFiles?.(cwd).catch(() => {
516
+ if (answers2.framework === "next") await deps.agentFiles?.(cwd).catch(() => {
482
517
  });
483
518
  await sh("git", ["init"], { capture: true });
484
519
  await sh("git", ["add", "-A"], { capture: true });
@@ -520,7 +555,7 @@ async function run(answers2, deps) {
520
555
  await sh("supabase", ["db", "push"]);
521
556
  break;
522
557
  case "writeEnv":
523
- await deps.fs.writeFile(join2(cwd, ".env.local"), renderEnv(ctx.keys));
558
+ await deps.fs.writeFile(join2(cwd, envFile(answers2)), renderEnv(ctx.keys, siteUrl(answers2)));
524
559
  break;
525
560
  case "ensureEditor":
526
561
  ctx.editor = await ensureEditor(deps.editorClient(ctx.keys), deps.askEditor);
@@ -548,10 +583,12 @@ async function run(answers2, deps) {
548
583
  return { ok: true, keys: ctx.keys, editor: ctx.editor };
549
584
  }
550
585
  var withoutSeedSql = (toml) => toml.replace(/(\[db\.seed\]\n(?:#.*\n)*enabled = )true/, "$1false");
586
+ var envFile = (answers2) => answers2.framework === "astro" ? ".env" : ".env.local";
587
+ var siteUrl = (answers2) => answers2.framework === "astro" ? "http://localhost:4321" : "http://localhost:3000";
551
588
  function fallbackFor(step, answers2, keys) {
552
- const envLines = keys ? `Write ${answers2.dir}/.env.local:
589
+ const envLines = keys ? `Write ${answers2.dir}/${envFile(answers2)}:
553
590
 
554
- ${renderEnv(keys).split("\n").filter((l) => !l.startsWith("#")).join("\n").trimEnd()}` : `Write ${answers2.dir}/.env.local with SMOODLY_URL, SMOODLY_SECRET_KEY and SMOODLY_PUBLISHABLE_KEY (from \`supabase status\`), and SITE_URL=http://localhost:3000.`;
591
+ ${renderEnv(keys, siteUrl(answers2)).split("\n").filter((l) => !l.startsWith("#")).join("\n").trimEnd()}` : `Write ${answers2.dir}/${envFile(answers2)} with SMOODLY_URL, SMOODLY_SECRET_KEY and SMOODLY_PUBLISHABLE_KEY (from \`supabase status\`), and SITE_URL=${siteUrl(answers2)}.`;
555
592
  const editorLines = [
556
593
  "Create the first editor, then the starter content:",
557
594
  "",
@@ -597,7 +634,11 @@ var TEMPLATE_DIRS = {
597
634
  lorem: fileURLToPath(new URL("../templates/lorem/", import.meta.url)),
598
635
  "lorem-intl": fileURLToPath(new URL("../templates/lorem-intl/", import.meta.url)),
599
636
  blank: fileURLToPath(new URL("../templates/blank/", import.meta.url)),
600
- "blank-intl": fileURLToPath(new URL("../templates/blank-intl/", import.meta.url))
637
+ "blank-intl": fileURLToPath(new URL("../templates/blank-intl/", import.meta.url)),
638
+ "astro-lorem": fileURLToPath(new URL("../templates/astro-lorem/", import.meta.url)),
639
+ "astro-lorem-intl": fileURLToPath(new URL("../templates/astro-lorem-intl/", import.meta.url)),
640
+ "astro-blank": fileURLToPath(new URL("../templates/astro-blank/", import.meta.url)),
641
+ "astro-blank-intl": fileURLToPath(new URL("../templates/astro-blank-intl/", import.meta.url))
601
642
  };
602
643
  var flags;
603
644
  try {
@@ -649,15 +690,16 @@ if (!outcome.ok) {
649
690
  if (outcome.fallback) p2.note(outcome.fallback, "Finish by hand");
650
691
  process.exit(1);
651
692
  }
693
+ var port = answers.framework === "astro" ? 4321 : 3e3;
652
694
  p2.note(
653
695
  [
654
696
  ...relative(process.cwd(), answers.dir) ? [`cd ${relative(process.cwd(), answers.dir)}`] : [],
655
697
  "npm run dev",
656
698
  "",
657
- answers.starter === "blank" ? "Site: http://localhost:3000 (a blank site \u2014 build it in components/ and at /admin)" : "Site: http://localhost:3000 (the Lorem Co. starter \u2014 edit it at /admin)",
658
- "Admin: http://localhost:3000/admin",
699
+ answers.starter === "blank" ? `Site: http://localhost:${port} (a blank site \u2014 build it in ${answers.framework === "astro" ? "src/sections/, src/components/" : "components/"} and at /admin)` : `Site: http://localhost:${port} (the Lorem Co. starter \u2014 edit it at /admin)`,
700
+ `Admin: http://localhost:${port}/admin`,
659
701
  `Editor: ${outcome.editor.email ?? outcome.editor.id}`,
660
- ...answers.locales ? [`Locales: ${answers.locales.join(", ")} (i18n/routing.ts)`] : []
702
+ ...answers.locales ? [`Locales: ${answers.locales.join(", ")} (${answers.framework === "astro" ? "astro.config.ts and src/smoodly/config.ts" : "i18n/routing.ts"})`] : []
661
703
  ].join("\n"),
662
704
  "Ready"
663
705
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-smoodly-app",
3
- "version": "0.0.45",
3
+ "version": "0.0.47",
4
4
  "description": "Create a Smoodly site: a fresh Next.js app with the admin mounted, a Supabase schema, an env file and a first editor.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -26,7 +26,7 @@
26
26
  "prepublishOnly": "npm run build",
27
27
  "test": "vitest run",
28
28
  "typecheck": "tsc --noEmit && npm run typecheck:templates",
29
- "typecheck:templates": "npm run build:template && tsc -p templates/lorem.tsconfig.json && tsc -p templates/lorem-intl.tsconfig.json && tsc -p templates/blank.tsconfig.json && tsc -p templates/blank-intl.tsconfig.json"
29
+ "typecheck:templates": "npm run build:template && tsc -p templates/lorem.tsconfig.json && tsc -p templates/lorem-intl.tsconfig.json && tsc -p templates/blank.tsconfig.json && tsc -p templates/blank-intl.tsconfig.json && tsc -p templates/astro-lorem.tsconfig.json && tsc -p templates/astro-lorem-intl.tsconfig.json && tsc -p templates/astro-blank.tsconfig.json && tsc -p templates/astro-blank-intl.tsconfig.json"
30
30
  },
31
31
  "dependencies": {
32
32
  "@clack/prompts": "^1.7.0",
@@ -0,0 +1,55 @@
1
+ # Working in this Smoodly site
2
+
3
+ This is an Astro site with Smoodly installed: content lives in Supabase, the admin is mounted at /admin by the `smoodly/astro` integration in `astro.config.ts`, and the site reads content through live collections (`src/live.config.ts`). Read README.md first: it says where things are.
4
+
5
+ ## Rules
6
+
7
+ - **Register schemas in the config; views are files by name.** A section's view is
8
+ `src/sections/<name>.astro`, an element's `src/elements/<name>.astro`, a page
9
+ type's `src/page-types/<name>.astro`, an entry page's
10
+ `src/entry-pages/<collection>.astro`, where <name> is the schema's `name`.
11
+ The schema is listed in its barrel (`src/smoodly/sections.ts`, `elements.ts`,
12
+ the config's `pages`; collections and global items in `collections.ts`,
13
+ `globals.ts`) and `src/smoodly/config.ts` is the one registry. The server
14
+ names a schema with no file, and a file with no schema, at the first request.
15
+ - **A `.schema.ts` file imports no component.** Zones, toolsets, global items and
16
+ `node()` take schemas (`z.locked(heroSchema)`), never views.
17
+ - **After a new collection or a `smoodly` upgrade, run `npx smoodly migrate`**
18
+ and apply what it writes (`supabase db push` hosted, `supabase migration up`
19
+ local). It copies the core schema files the package ships and writes a
20
+ read view and order index per collection. Never edit a file it wrote.
21
+ The admin reads the base tables, so it lists a collection either way;
22
+ the view is for SQL and PostgREST readers, the index for `order`.
23
+ - **Content from code goes through `createContent(config)`** from `smoodly/astro`:
24
+ `scripts/seed.ts` is starter content written with it — the admin's own
25
+ operations, so validation and publish checks apply and a failure throws. Never
26
+ write rows or call the stores directly. The seed is idempotent; `npm run seed`.
27
+ - **Reads are live collections.** `src/live.config.ts` names one loader per
28
+ collection, global item, the pages and the page tree; a view reads with
29
+ `getLiveCollection("articles", { locale })` or `getLiveEntry("footer", { locale })`
30
+ from `astro:content`. Every read tags the response, and a publish expires it.
31
+ - **The admin, the preview address and the page shell are injected** by
32
+ `smoodly()` in `astro.config.ts`; the project has no route file for them. A page
33
+ file of your own under `src/pages` wins its address. `site` in `astro.config.ts`
34
+ is the site's own address (from `SITE_URL`).
35
+ - **A link an editor typed goes through `safeHref`** from "smoodly" in an `.astro`
36
+ view: `<a href={safeHref(link.href)}>`. Astro writes attributes as given, so a
37
+ `javascript:` URL would reach the page; the template build refuses a shipped
38
+ view that skips it.
39
+ - **Section styles are inline.** `<Section>` from `smoodly/astro/components` renders
40
+ the root element with Smoodly's styles set and the node id; marks
41
+ (`smoodly.marks(schema)`) tie rendered elements to fields.
42
+ - **Multilingual sites.** The locale set is `locales` in `src/smoodly/config.ts`
43
+ and `i18n` in `astro.config.ts`; the two must agree. UI strings of the site's
44
+ own go through `src/strings.ts` and `src/messages/<locale>.json`.
45
+ - **Fixed versus open pages.** A page type with a `slug` is fixed: one
46
+ record, created for you, never deleted. Without one it is open: editors
47
+ create as many as they like.
48
+
49
+ ## Commands
50
+
51
+ npm run dev # http://localhost:4321, /admin is the editor
52
+ npm run build # checks the schema files (smoodly migrate --check), then astro build
53
+ npm run start # the built server (dist/server/entry.mjs)
54
+ npm run seed # starter content, idempotent
55
+ npm run typecheck # astro check
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,88 @@
1
+ # Smoodly site
2
+
3
+ Created by `create-smoodly-app`. Content lives in Supabase; the admin is
4
+ mounted at `/admin` by the `smoodly/astro` integration. AGENTS.md holds the
5
+ rules for an agent (or a person) building the site.
6
+
7
+ npm run dev # http://localhost:4321 — /admin is the editor
8
+
9
+ ## Where things are
10
+
11
+ - `astro.config.ts` — the `smoodly()` integration (it mounts the admin, the preview address and the page shell), the node adapter, the cache and `site` (from `SITE_URL`).
12
+ - `src/page-types/` — one view per page type: `home.astro` (fixed at /, one freeform zone) and `page.astro` (open, editors create many).
13
+ - `src/entry-pages/posts.astro` — the entry page of the posts collection, at /posts/<slug>.
14
+ - `src/sections/text.astro` and `src/components/sections/Text.schema.ts` — the one section: a title and a richtext body.
15
+ - `src/elements/button.astro` and `src/components/elements/Button.schema.ts` — the one element: a link styled as a button, embedded in richtext through `src/smoodly/toolsets.ts`.
16
+ - `src/components/LocaleNav.astro` — the language links every page view renders from its `locales` map; nothing with one locale.
17
+ - `src/layouts/Layout.astro` — the document every view renders: the head, the footer line from the `site` global item, `src/styles/global.css`.
18
+ - `src/smoodly/` — the wiring: `config.ts` (the registry of schemas and the settings), `collections.ts` (posts), `globals.ts` (the site item), `sections.ts` and `elements.ts` (the schemas the config registers), `toolsets.ts`.
19
+ - `src/live.config.ts` — the live collections the views read with `getLiveCollection` and `getLiveEntry`.
20
+ - `scripts/seed.ts` — the home page and the site item, written through `createContent(config)`. `npm run seed` is idempotent.
21
+ - `src/styles/global.css` — a reset, a container, the two tones. Replace it.
22
+ - `.env` — `SMOODLY_URL`, `SMOODLY_SECRET_KEY`, `SMOODLY_PUBLISHABLE_KEY`.
23
+
24
+ ## Add a section
25
+
26
+ Copy `src/components/sections/Text.schema.ts`, give the schema a new
27
+ `name` and `title` and declare its fields. List it in `sections` in
28
+ `src/smoodly/sections.ts` and in the `sections` of every zone that may
29
+ hold it in `src/components/pages/*.schema.ts`. Then write its view,
30
+ `src/sections/<name>.astro`, by copying `text.astro`: render the fields
31
+ inside `<Section>` with marks on each. It appears in the editor's "+"
32
+ picker. A schema with no view, and a view with no schema, are named at
33
+ the first request.
34
+
35
+ ## Add an element
36
+
37
+ Copy `src/components/elements/Button.schema.ts` (relative imports only).
38
+ Add the schema to `elements` in `src/smoodly/elements.ts` and to the
39
+ `elements` of the toolset in `src/smoodly/toolsets.ts` that may embed
40
+ it, and write `src/elements/<name>.astro`. A link an editor types goes
41
+ through `safeHref`, as in `button.astro`. It appears in the richtext
42
+ editor's "/" menu.
43
+
44
+ ## Add a collection
45
+
46
+ Copy the `posts` block in `src/smoodly/collections.ts`, add the new
47
+ collection to `collections` and a loader for it in `src/live.config.ts`,
48
+ and write its entry page, `src/entry-pages/<collection>.astro`, by
49
+ copying `posts.astro`. Then write its read view and order index and
50
+ apply them:
51
+
52
+ npx smoodly migrate
53
+ supabase db push # hosted; `supabase migration up` for the local stack (keeps the content)
54
+
55
+ The same command brings the core schema forward after a `smoodly` upgrade.
56
+
57
+ ## Add a global item
58
+
59
+ Copy the `site` block in `src/smoodly/globals.ts`, add it to `globals`
60
+ and give it a loader in `src/live.config.ts`. A view reads it with
61
+ `getLiveEntry("name", { locale })`. Edit it under Globals in the admin.
62
+
63
+ Reseed an empty database any time with `npm run seed`.
64
+
65
+ ## Moving the admin
66
+
67
+ The admin is at `/admin`. To serve it somewhere else, say `/studio`:
68
+
69
+ 1. set `smoodly({ adminPath: "/studio" })` in `astro.config.ts`,
70
+ 2. set `admin: { path: "/studio" }` in `src/smoodly/config.ts`.
71
+
72
+ The two must agree; the admin says so when they do not.
73
+
74
+ ## Editors
75
+
76
+ Anyone who may open `/admin` is a Supabase Auth user with a row in the
77
+ `editors` table. To add one: create the user in Supabase Studio
78
+ (Authentication → Users), then in the SQL editor:
79
+
80
+ insert into editors (user_id, role) values ('<the user id>', 'editor');
81
+
82
+ ## Hosted Supabase
83
+
84
+ supabase init # if the CLI has not been set up here yet
85
+ supabase link # pick the project
86
+ supabase db push # applies supabase/migrations/
87
+
88
+ Then put the project's URL and keys in `.env`.
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ dist/
3
+ .astro/
4
+ .env
5
+ smoodly-export-*
@@ -0,0 +1,15 @@
1
+ import { defineConfig, memoryCache } from "astro/config";
2
+ import node from "@astrojs/node";
3
+ import smoodly from "smoodly/astro";
4
+
5
+ export default defineConfig({
6
+ output: "server",
7
+ adapter: node({ mode: "standalone" }),
8
+ // Pages are cached whole and expire by tag when an editor publishes (one process, one cache).
9
+ cache: { provider: memoryCache() },
10
+ // The site's own address: canonical, hreflang and social links are absolute on it. Astro reads astro.config
11
+ // before .env, so this sees only a SITE_URL set in the shell; when it is unset, smoodly() sets `site` from
12
+ // SITE_URL in .env. Set SITE_URL where you deploy: `site` is fixed at `astro build`.
13
+ site: process.env.SITE_URL,
14
+ integrations: [smoodly({ config: "src/smoodly/config.ts" })],
15
+ });
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "smoodly-site",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "astro dev",
7
+ "build": "smoodly migrate --check && astro build",
8
+ "start": "node --env-file-if-exists=.env dist/server/entry.mjs",
9
+ "seed": "tsx scripts/seed.ts",
10
+ "typecheck": "astro check"
11
+ },
12
+ "dependencies": {
13
+ "@astrojs/node": "^11.1.6",
14
+ "@supabase/supabase-js": "^2.113.0",
15
+ "astro": "^7.3.5",
16
+ "smoodly": "0.0.47"
17
+ },
18
+ "devDependencies": {
19
+ "@astrojs/check": "^0.9.10",
20
+ "@types/node": "^26.4.1",
21
+ "typescript": "^5",
22
+ "tsx": "^4"
23
+ }
24
+ }
@@ -0,0 +1,80 @@
1
+ // The starter's content, written through the content API (`createContent(config)`
2
+ // from smoodly/astro): the admin's own operations, so validation and
3
+ // the publish checks apply and a failure throws. It is also the example
4
+ // of writing content from code. Ships in the scaffold; create-smoodly-app
5
+ // runs `npm run seed` after the first editor. Idempotent: exits once the
6
+ // home page has content in the default locale. Writes the same copy into every configured locale, whatever
7
+ // their codes, so every locale's "/" resolves.
8
+ // Run: npm run seed (needs .env and a Supabase stack).
9
+ import type { PageTree, TreeNode } from "smoodly";
10
+
11
+ const { config } = await import("../src/smoodly/config");
12
+ const { node } = await import("smoodly");
13
+ const { createContent } = await import("smoodly/astro");
14
+ const { content } = createContent(config);
15
+ const { textSchema } = await import("../src/components/sections/Text.schema");
16
+ const c = content;
17
+
18
+ const DEFAULT = config.locales.default;
19
+ const OTHERS = config.locales.supported.filter((l) => l !== DEFAULT);
20
+
21
+ // The home page is code-owned: `find` returns its record, creating it when
22
+ // the database is new. It is seeded once somebody has given it sections.
23
+ const home = (await c.pages.find(DEFAULT, "/"))!;
24
+ if (Object.values((await c.pages.getTree(home.id, DEFAULT))?.zones ?? {}).some((nodes) => nodes.length > 0)) {
25
+ console.log(`Already seeded — the home page has content in "${DEFAULT}".`);
26
+ process.exit(0);
27
+ }
28
+
29
+ // Richtext is TipTap JSON, the stored format; RichText renders it.
30
+ type Node = Record<string, unknown>;
31
+ const p = (s: string): Node => ({ type: "paragraph", content: [{ type: "text", text: s }] });
32
+ const button = (label: string, href: string): Node => ({ type: "element", attrs: { name: "button", fields: { label, href }, settings: {} } });
33
+ const doc = (...content: Node[]) => ({ type: "doc", content });
34
+
35
+ // --- the site item: name and tagline, published in every locale ------
36
+
37
+ for (const locale of [DEFAULT, ...OTHERS]) {
38
+ await c.globals.set("site", locale, { name: "My site", tagline: "Built with Smoodly" });
39
+ await c.globals.publish("site", locale);
40
+ }
41
+
42
+ // --- the home page: one Text section and one custom section ----------
43
+
44
+ // The custom section is not a registered section, so it is a plain node object.
45
+ const custom: TreeNode = {
46
+ id: "node-custom",
47
+ type: "custom",
48
+ fields: { rows: [
49
+ { spans: [6, 6], gap: "md", align: "top", reverse: false, columns: [
50
+ doc(p("A custom section holds rows of columns. Drag the width bar in the inspector to resize them.")),
51
+ doc(p("Each column is richtext. Columns stack on small screens; the developer's stylesheet needs nothing.")),
52
+ ] },
53
+ ] },
54
+ };
55
+
56
+ const tree: PageTree = {
57
+ meta: { description: "A new site, built with Smoodly." },
58
+ zones: {
59
+ body: [
60
+ node(textSchema, {
61
+ title: "Hello",
62
+ body: doc(
63
+ p("This is your home page. Open the admin to edit this text, add sections, create pages and write posts."),
64
+ p("Everything you see is defined in components/ and registered in smoodly/. The README says where things are."),
65
+ button("Open the admin", "/admin"),
66
+ ),
67
+ }, { id: "node-hello", settings: { background: "paper" } }),
68
+ // The custom section: the editor's own rows of columns, rendered by Smoodly.
69
+ custom,
70
+ ],
71
+ },
72
+ };
73
+
74
+ // The home page exists in every locale already (its registration says so): write the tree and publish.
75
+ for (const locale of [DEFAULT, ...OTHERS]) {
76
+ await c.pages.setTree(home.id, locale, tree);
77
+ await c.pages.publish(home.id, locale);
78
+ }
79
+
80
+ console.log(`Seeded the blank starter in "${[DEFAULT, ...OTHERS].join('", "')}": the home page and the site item, published.`);
@@ -0,0 +1,15 @@
1
+ ---
2
+ // src/components/LocaleNav.astro — the language links, rendered by every page view from its own `locales` map:
3
+ // the same page in the other language where it is published there, that language's home otherwise. Nothing
4
+ // with one locale, so the single-language scaffold shares the file.
5
+ import { href } from "smoodly/astro/runtime";
6
+
7
+ type Props = { locale: string; locales: Record<string, { path: string | null } | null> };
8
+ const { locale, locales } = Astro.props;
9
+ const codes = Object.keys(locales);
10
+ ---
11
+ {codes.length > 1 && (
12
+ <nav class="container locales" aria-label="Language">
13
+ {codes.map((code) => <a href={href(locales[code]?.path ?? "/", code)} aria-current={code === locale ? "page" : undefined}>{code}</a>)}
14
+ </nav>
15
+ )}