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.
- package/README.md +15 -10
- package/dist/index.js +75 -33
- package/package.json +2 -2
- package/templates/astro-blank/AGENTS.md +55 -0
- package/templates/astro-blank/CLAUDE.md +1 -0
- package/templates/astro-blank/README.md +88 -0
- package/templates/astro-blank/_gitignore +5 -0
- package/templates/astro-blank/astro.config.ts +15 -0
- package/templates/astro-blank/package.json +24 -0
- package/templates/astro-blank/scripts/seed.ts +80 -0
- package/templates/astro-blank/src/components/LocaleNav.astro +15 -0
- package/templates/astro-blank/src/components/elements/Button.schema.ts +22 -0
- package/templates/astro-blank/src/components/pages/Home.schema.ts +17 -0
- package/templates/astro-blank/src/components/pages/Page.schema.ts +14 -0
- package/templates/astro-blank/src/components/sections/Text.schema.ts +24 -0
- package/templates/astro-blank/src/elements/button.astro +10 -0
- package/templates/astro-blank/src/entry-pages/posts.astro +23 -0
- package/templates/astro-blank/src/layouts/Layout.astro +25 -0
- package/templates/astro-blank/src/live.config.ts +13 -0
- package/templates/astro-blank/src/page-types/home.astro +15 -0
- package/templates/astro-blank/src/page-types/page.astro +15 -0
- package/templates/astro-blank/src/pages/404.astro +7 -0
- package/templates/astro-blank/src/sections/text.astro +18 -0
- package/templates/astro-blank/src/smoodly/collections.ts +24 -0
- package/templates/astro-blank/src/smoodly/config.ts +28 -0
- package/templates/astro-blank/src/smoodly/elements.ts +5 -0
- package/templates/astro-blank/src/smoodly/globals.ts +18 -0
- package/templates/astro-blank/src/smoodly/sections.ts +5 -0
- package/templates/astro-blank/src/smoodly/toolsets.ts +14 -0
- package/templates/astro-blank/src/styles/global.css +57 -0
- package/templates/astro-blank/supabase/migrations/20260926120000_smoodly_v1.sql +535 -0
- package/templates/astro-blank/supabase/migrations/20260926130000_smoodly_v2.sql +26 -0
- package/templates/astro-blank/supabase/migrations/20260926140000_smoodly_v3.sql +38 -0
- package/templates/astro-blank/supabase/migrations/20260926150000_smoodly_v4.sql +41 -0
- package/templates/astro-blank/supabase/migrations/20260929120000_smoodly_v5.sql +56 -0
- package/templates/astro-blank/supabase/migrations/20260929135523_smoodly_v6.sql +200 -0
- package/templates/astro-blank/supabase/migrations/20260930040956_smoodly_v7.sql +84 -0
- package/templates/astro-blank/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
- package/templates/astro-blank/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
- package/templates/astro-blank/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
- package/templates/{blank/supabase/migrations/20260930041056_smoodly_views.sql → astro-blank/supabase/migrations/20261008120100_smoodly_views.sql} +1 -1
- package/templates/astro-blank/tsconfig.json +17 -0
- package/templates/astro-blank-intl/AGENTS.md +55 -0
- package/templates/astro-blank-intl/CLAUDE.md +1 -0
- package/templates/astro-blank-intl/README.md +89 -0
- package/templates/astro-blank-intl/_gitignore +5 -0
- package/templates/astro-blank-intl/astro.config.ts +17 -0
- package/templates/astro-blank-intl/package.json +24 -0
- package/templates/astro-blank-intl/scripts/seed.ts +80 -0
- package/templates/astro-blank-intl/src/components/LocaleNav.astro +15 -0
- package/templates/astro-blank-intl/src/components/elements/Button.schema.ts +22 -0
- package/templates/astro-blank-intl/src/components/pages/Home.schema.ts +17 -0
- package/templates/astro-blank-intl/src/components/pages/Page.schema.ts +14 -0
- package/templates/astro-blank-intl/src/components/sections/Text.schema.ts +24 -0
- package/templates/astro-blank-intl/src/elements/button.astro +10 -0
- package/templates/astro-blank-intl/src/entry-pages/posts.astro +23 -0
- package/templates/astro-blank-intl/src/layouts/Layout.astro +25 -0
- package/templates/astro-blank-intl/src/live.config.ts +13 -0
- package/templates/astro-blank-intl/src/page-types/home.astro +15 -0
- package/templates/astro-blank-intl/src/page-types/page.astro +15 -0
- package/templates/astro-blank-intl/src/pages/404.astro +7 -0
- package/templates/astro-blank-intl/src/sections/text.astro +18 -0
- package/templates/astro-blank-intl/src/smoodly/collections.ts +24 -0
- package/templates/astro-blank-intl/src/smoodly/config.ts +29 -0
- package/templates/astro-blank-intl/src/smoodly/elements.ts +5 -0
- package/templates/astro-blank-intl/src/smoodly/globals.ts +18 -0
- package/templates/astro-blank-intl/src/smoodly/sections.ts +5 -0
- package/templates/astro-blank-intl/src/smoodly/toolsets.ts +14 -0
- package/templates/astro-blank-intl/src/styles/global.css +57 -0
- package/templates/astro-blank-intl/supabase/migrations/20260926120000_smoodly_v1.sql +535 -0
- package/templates/astro-blank-intl/supabase/migrations/20260926130000_smoodly_v2.sql +26 -0
- package/templates/astro-blank-intl/supabase/migrations/20260926140000_smoodly_v3.sql +38 -0
- package/templates/astro-blank-intl/supabase/migrations/20260926150000_smoodly_v4.sql +41 -0
- package/templates/astro-blank-intl/supabase/migrations/20260929120000_smoodly_v5.sql +56 -0
- package/templates/astro-blank-intl/supabase/migrations/20260929135523_smoodly_v6.sql +200 -0
- package/templates/astro-blank-intl/supabase/migrations/20260930040956_smoodly_v7.sql +84 -0
- package/templates/astro-blank-intl/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
- package/templates/astro-blank-intl/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
- package/templates/astro-blank-intl/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
- package/templates/{blank-intl/supabase/migrations/20260930041056_smoodly_views.sql → astro-blank-intl/supabase/migrations/20261008120100_smoodly_views.sql} +1 -1
- package/templates/astro-blank-intl/tsconfig.json +17 -0
- package/templates/astro-blank-intl.tsconfig.json +24 -0
- package/templates/astro-blank.tsconfig.json +24 -0
- package/templates/astro-lorem/AGENTS.md +55 -0
- package/templates/astro-lorem/CLAUDE.md +1 -0
- package/templates/astro-lorem/README.md +60 -0
- package/templates/astro-lorem/_gitignore +5 -0
- package/templates/astro-lorem/astro.config.ts +15 -0
- package/templates/astro-lorem/package.json +24 -0
- package/templates/astro-lorem/public/images/about_main.jpg +0 -0
- package/templates/astro-lorem/public/images/article_photo.jpg +0 -0
- package/templates/astro-lorem/public/images/hero_image.jpg +0 -0
- package/templates/astro-lorem/public/images/home_section_2.jpg +0 -0
- package/templates/astro-lorem/scripts/seed.ts +405 -0
- package/templates/astro-lorem/src/components/ArticleCard.astro +19 -0
- package/templates/astro-lorem/src/components/articles.ts +42 -0
- package/templates/astro-lorem/src/components/elements/Callout.schema.ts +24 -0
- package/templates/astro-lorem/src/components/elements/Cta.schema.ts +21 -0
- package/templates/astro-lorem/src/components/news.ts +13 -0
- package/templates/astro-lorem/src/components/pages/Articles.schema.ts +20 -0
- package/templates/astro-lorem/src/components/pages/Home.schema.ts +26 -0
- package/templates/astro-lorem/src/components/pages/News.schema.ts +17 -0
- package/templates/astro-lorem/src/components/pages/Page.schema.ts +24 -0
- package/templates/astro-lorem/src/components/sections/FavouriteArticles.schema.ts +17 -0
- package/templates/astro-lorem/src/components/sections/Footer.schema.ts +31 -0
- package/templates/astro-lorem/src/components/sections/Header.schema.ts +21 -0
- package/templates/astro-lorem/src/components/sections/Hero.schema.ts +24 -0
- package/templates/astro-lorem/src/components/sections/ImageText.schema.ts +30 -0
- package/templates/astro-lorem/src/components/sections/PageHero.schema.ts +21 -0
- package/templates/astro-lorem/src/components/sections/People.schema.ts +15 -0
- package/templates/astro-lorem/src/components/sections/Points.schema.ts +21 -0
- package/templates/astro-lorem/src/components/sections/Quote.schema.ts +23 -0
- package/templates/astro-lorem/src/elements/callout.astro +13 -0
- package/templates/astro-lorem/src/elements/cta.astro +15 -0
- package/templates/astro-lorem/src/entry-pages/articles.astro +71 -0
- package/templates/astro-lorem/src/entry-pages/news.astro +29 -0
- package/templates/astro-lorem/src/layouts/Layout.astro +39 -0
- package/templates/astro-lorem/src/live.config.ts +20 -0
- package/templates/astro-lorem/src/messages/en.json +7 -0
- package/templates/astro-lorem/src/messages/fi.json +7 -0
- package/templates/astro-lorem/src/page-types/articlesIndex.astro +24 -0
- package/templates/astro-lorem/src/page-types/home.astro +14 -0
- package/templates/astro-lorem/src/page-types/newsIndex.astro +28 -0
- package/templates/astro-lorem/src/page-types/page.astro +27 -0
- package/templates/astro-lorem/src/pages/404.astro +7 -0
- package/templates/astro-lorem/src/sections/favouriteArticles.astro +26 -0
- package/templates/astro-lorem/src/sections/footer.astro +37 -0
- package/templates/astro-lorem/src/sections/header.astro +27 -0
- package/templates/astro-lorem/src/sections/hero.astro +28 -0
- package/templates/astro-lorem/src/sections/imageText.astro +22 -0
- package/templates/astro-lorem/src/sections/pageHero.astro +20 -0
- package/templates/astro-lorem/src/sections/peopleSection.astro +24 -0
- package/templates/astro-lorem/src/sections/points.astro +32 -0
- package/templates/astro-lorem/src/sections/quote.astro +16 -0
- package/templates/astro-lorem/src/smoodly/collections.ts +63 -0
- package/templates/astro-lorem/src/smoodly/config.ts +48 -0
- package/templates/astro-lorem/src/smoodly/elements.ts +6 -0
- package/templates/astro-lorem/src/smoodly/globals.ts +29 -0
- package/templates/astro-lorem/src/smoodly/sections.ts +18 -0
- package/templates/astro-lorem/src/smoodly/toolsets.ts +16 -0
- package/templates/astro-lorem/src/strings.ts +17 -0
- package/templates/astro-lorem/src/styles/global.css +228 -0
- package/templates/astro-lorem/supabase/migrations/20260926120000_smoodly_v1.sql +535 -0
- package/templates/astro-lorem/supabase/migrations/20260926130000_smoodly_v2.sql +26 -0
- package/templates/astro-lorem/supabase/migrations/20260926140000_smoodly_v3.sql +38 -0
- package/templates/astro-lorem/supabase/migrations/20260926150000_smoodly_v4.sql +41 -0
- package/templates/astro-lorem/supabase/migrations/20260929120000_smoodly_v5.sql +56 -0
- package/templates/astro-lorem/supabase/migrations/20260929135523_smoodly_v6.sql +200 -0
- package/templates/astro-lorem/supabase/migrations/20260930040956_smoodly_v7.sql +84 -0
- package/templates/astro-lorem/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
- package/templates/astro-lorem/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
- package/templates/astro-lorem/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
- package/templates/{lorem/supabase/migrations/20260930041056_smoodly_views.sql → astro-lorem/supabase/migrations/20261008120100_smoodly_views.sql} +1 -1
- package/templates/astro-lorem/tsconfig.json +17 -0
- package/templates/astro-lorem-intl/AGENTS.md +55 -0
- package/templates/astro-lorem-intl/CLAUDE.md +1 -0
- package/templates/astro-lorem-intl/README.md +62 -0
- package/templates/astro-lorem-intl/_gitignore +5 -0
- package/templates/astro-lorem-intl/astro.config.ts +17 -0
- package/templates/astro-lorem-intl/package.json +24 -0
- package/templates/astro-lorem-intl/public/images/about_main.jpg +0 -0
- package/templates/astro-lorem-intl/public/images/article_photo.jpg +0 -0
- package/templates/astro-lorem-intl/public/images/hero_image.jpg +0 -0
- package/templates/astro-lorem-intl/public/images/home_section_2.jpg +0 -0
- package/templates/astro-lorem-intl/scripts/seed.ts +405 -0
- package/templates/astro-lorem-intl/src/components/ArticleCard.astro +19 -0
- package/templates/astro-lorem-intl/src/components/articles.ts +42 -0
- package/templates/astro-lorem-intl/src/components/elements/Callout.schema.ts +24 -0
- package/templates/astro-lorem-intl/src/components/elements/Cta.schema.ts +21 -0
- package/templates/astro-lorem-intl/src/components/news.ts +13 -0
- package/templates/astro-lorem-intl/src/components/pages/Articles.schema.ts +20 -0
- package/templates/astro-lorem-intl/src/components/pages/Home.schema.ts +26 -0
- package/templates/astro-lorem-intl/src/components/pages/News.schema.ts +17 -0
- package/templates/astro-lorem-intl/src/components/pages/Page.schema.ts +24 -0
- package/templates/astro-lorem-intl/src/components/sections/FavouriteArticles.schema.ts +17 -0
- package/templates/astro-lorem-intl/src/components/sections/Footer.schema.ts +31 -0
- package/templates/astro-lorem-intl/src/components/sections/Header.schema.ts +21 -0
- package/templates/astro-lorem-intl/src/components/sections/Hero.schema.ts +24 -0
- package/templates/astro-lorem-intl/src/components/sections/ImageText.schema.ts +30 -0
- package/templates/astro-lorem-intl/src/components/sections/PageHero.schema.ts +21 -0
- package/templates/astro-lorem-intl/src/components/sections/People.schema.ts +15 -0
- package/templates/astro-lorem-intl/src/components/sections/Points.schema.ts +21 -0
- package/templates/astro-lorem-intl/src/components/sections/Quote.schema.ts +23 -0
- package/templates/astro-lorem-intl/src/elements/callout.astro +13 -0
- package/templates/astro-lorem-intl/src/elements/cta.astro +15 -0
- package/templates/astro-lorem-intl/src/entry-pages/articles.astro +71 -0
- package/templates/astro-lorem-intl/src/entry-pages/news.astro +29 -0
- package/templates/astro-lorem-intl/src/layouts/Layout.astro +39 -0
- package/templates/astro-lorem-intl/src/live.config.ts +20 -0
- package/templates/astro-lorem-intl/src/messages/en.json +7 -0
- package/templates/astro-lorem-intl/src/messages/fi.json +7 -0
- package/templates/astro-lorem-intl/src/page-types/articlesIndex.astro +24 -0
- package/templates/astro-lorem-intl/src/page-types/home.astro +14 -0
- package/templates/astro-lorem-intl/src/page-types/newsIndex.astro +28 -0
- package/templates/astro-lorem-intl/src/page-types/page.astro +27 -0
- package/templates/astro-lorem-intl/src/pages/404.astro +7 -0
- package/templates/astro-lorem-intl/src/sections/favouriteArticles.astro +26 -0
- package/templates/astro-lorem-intl/src/sections/footer.astro +37 -0
- package/templates/astro-lorem-intl/src/sections/header.astro +27 -0
- package/templates/astro-lorem-intl/src/sections/hero.astro +28 -0
- package/templates/astro-lorem-intl/src/sections/imageText.astro +22 -0
- package/templates/astro-lorem-intl/src/sections/pageHero.astro +20 -0
- package/templates/astro-lorem-intl/src/sections/peopleSection.astro +24 -0
- package/templates/astro-lorem-intl/src/sections/points.astro +32 -0
- package/templates/astro-lorem-intl/src/sections/quote.astro +16 -0
- package/templates/astro-lorem-intl/src/smoodly/collections.ts +63 -0
- package/templates/astro-lorem-intl/src/smoodly/config.ts +48 -0
- package/templates/astro-lorem-intl/src/smoodly/elements.ts +6 -0
- package/templates/astro-lorem-intl/src/smoodly/globals.ts +29 -0
- package/templates/astro-lorem-intl/src/smoodly/sections.ts +18 -0
- package/templates/astro-lorem-intl/src/smoodly/toolsets.ts +16 -0
- package/templates/astro-lorem-intl/src/strings.ts +17 -0
- package/templates/astro-lorem-intl/src/styles/global.css +228 -0
- package/templates/astro-lorem-intl/supabase/migrations/20260926120000_smoodly_v1.sql +535 -0
- package/templates/astro-lorem-intl/supabase/migrations/20260926130000_smoodly_v2.sql +26 -0
- package/templates/astro-lorem-intl/supabase/migrations/20260926140000_smoodly_v3.sql +38 -0
- package/templates/astro-lorem-intl/supabase/migrations/20260926150000_smoodly_v4.sql +41 -0
- package/templates/astro-lorem-intl/supabase/migrations/20260929120000_smoodly_v5.sql +56 -0
- package/templates/astro-lorem-intl/supabase/migrations/20260929135523_smoodly_v6.sql +200 -0
- package/templates/astro-lorem-intl/supabase/migrations/20260930040956_smoodly_v7.sql +84 -0
- package/templates/astro-lorem-intl/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
- package/templates/astro-lorem-intl/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
- package/templates/astro-lorem-intl/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
- package/templates/{lorem-intl/supabase/migrations/20260930041056_smoodly_views.sql → astro-lorem-intl/supabase/migrations/20261008120100_smoodly_views.sql} +1 -1
- package/templates/astro-lorem-intl/tsconfig.json +17 -0
- package/templates/astro-lorem-intl.tsconfig.json +24 -0
- package/templates/astro-lorem.tsconfig.json +24 -0
- package/templates/blank/AGENTS.md +23 -16
- package/templates/blank/README.md +20 -15
- package/templates/blank/app/(smoodly)/admin/[[...segments]]/route.ts +8 -0
- package/templates/blank/components/elements/Button.schema.ts +22 -0
- package/templates/blank/components/elements/Button.tsx +2 -20
- package/templates/blank/components/pages/Home.schema.ts +17 -0
- package/templates/blank/components/pages/Home.tsx +2 -16
- package/templates/blank/components/pages/Page.schema.ts +14 -0
- package/templates/blank/components/pages/Page.tsx +2 -14
- package/templates/blank/components/pages/Post.tsx +5 -4
- package/templates/blank/components/sections/Text.schema.ts +24 -0
- package/templates/blank/components/sections/Text.tsx +7 -27
- package/templates/blank/package.json +1 -1
- package/templates/blank/scripts/seed.ts +2 -2
- package/templates/blank/smoodly/config.ts +8 -7
- package/templates/blank/smoodly/elements.ts +4 -4
- package/templates/blank/smoodly/globals.ts +4 -2
- package/templates/blank/smoodly/richtext.tsx +4 -5
- package/templates/blank/smoodly/sections.ts +4 -4
- package/templates/blank/smoodly/server.ts +22 -44
- package/templates/blank/smoodly/toolsets.ts +2 -2
- package/templates/blank/smoodly/views.ts +15 -0
- package/templates/blank/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
- package/templates/blank/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
- package/templates/blank/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
- package/templates/blank/supabase/migrations/20261008120100_smoodly_views.sql +20 -0
- package/templates/blank-intl/AGENTS.md +23 -16
- package/templates/blank-intl/README.md +20 -15
- package/templates/blank-intl/app/(smoodly)/admin/[[...segments]]/route.ts +8 -0
- package/templates/blank-intl/components/elements/Button.schema.ts +22 -0
- package/templates/blank-intl/components/elements/Button.tsx +2 -20
- package/templates/blank-intl/components/pages/Home.schema.ts +17 -0
- package/templates/blank-intl/components/pages/Home.tsx +2 -16
- package/templates/blank-intl/components/pages/Page.schema.ts +14 -0
- package/templates/blank-intl/components/pages/Page.tsx +2 -14
- package/templates/blank-intl/components/pages/Post.tsx +5 -4
- package/templates/blank-intl/components/sections/Text.schema.ts +24 -0
- package/templates/blank-intl/components/sections/Text.tsx +7 -27
- package/templates/blank-intl/package.json +1 -1
- package/templates/blank-intl/scripts/seed.ts +2 -2
- package/templates/blank-intl/smoodly/config.ts +8 -7
- package/templates/blank-intl/smoodly/elements.ts +4 -4
- package/templates/blank-intl/smoodly/globals.ts +4 -2
- package/templates/blank-intl/smoodly/richtext.tsx +4 -5
- package/templates/blank-intl/smoodly/sections.ts +4 -4
- package/templates/blank-intl/smoodly/server.ts +22 -44
- package/templates/blank-intl/smoodly/toolsets.ts +2 -2
- package/templates/blank-intl/smoodly/views.ts +15 -0
- package/templates/blank-intl/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
- package/templates/blank-intl/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
- package/templates/blank-intl/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
- package/templates/blank-intl/supabase/migrations/20261008120100_smoodly_views.sql +20 -0
- package/templates/lorem/AGENTS.md +23 -16
- package/templates/lorem/README.md +3 -3
- package/templates/lorem/app/(smoodly)/admin/[[...segments]]/route.ts +8 -0
- package/templates/lorem/components/elements/Callout.schema.ts +24 -0
- package/templates/lorem/components/elements/Callout.tsx +2 -24
- package/templates/lorem/components/elements/Cta.schema.ts +21 -0
- package/templates/lorem/components/elements/Cta.tsx +2 -21
- package/templates/lorem/components/pages/Article.tsx +1 -1
- package/templates/lorem/components/pages/Articles.schema.ts +20 -0
- package/templates/lorem/components/pages/Articles.tsx +2 -20
- package/templates/lorem/components/pages/Home.schema.ts +26 -0
- package/templates/lorem/components/pages/Home.tsx +2 -26
- package/templates/lorem/components/pages/News.schema.ts +17 -0
- package/templates/lorem/components/pages/News.tsx +2 -17
- package/templates/lorem/components/pages/NewsPost.tsx +1 -1
- package/templates/lorem/components/pages/Page.schema.ts +24 -0
- package/templates/lorem/components/pages/Page.tsx +2 -24
- package/templates/lorem/components/sections/FavouriteArticles.schema.ts +17 -0
- package/templates/lorem/components/sections/FavouriteArticles.tsx +2 -17
- package/templates/lorem/components/sections/Footer.schema.ts +31 -0
- package/templates/lorem/components/sections/Footer.tsx +4 -32
- package/templates/lorem/components/sections/Header.schema.ts +21 -0
- package/templates/lorem/components/sections/Header.tsx +2 -21
- package/templates/lorem/components/sections/Hero.schema.ts +24 -0
- package/templates/lorem/components/sections/Hero.tsx +2 -24
- package/templates/lorem/components/sections/ImageText.schema.ts +30 -0
- package/templates/lorem/components/sections/ImageText.tsx +2 -30
- package/templates/lorem/components/sections/PageHero.schema.ts +21 -0
- package/templates/lorem/components/sections/PageHero.tsx +2 -21
- package/templates/lorem/components/sections/People.schema.ts +15 -0
- package/templates/lorem/components/sections/People.tsx +2 -14
- package/templates/lorem/components/sections/Points.schema.ts +21 -0
- package/templates/lorem/components/sections/Points.tsx +2 -21
- package/templates/lorem/components/sections/Quote.schema.ts +23 -0
- package/templates/lorem/components/sections/Quote.tsx +2 -23
- package/templates/lorem/package.json +1 -1
- package/templates/lorem/scripts/seed.ts +18 -18
- package/templates/lorem/smoodly/collections.ts +2 -2
- package/templates/lorem/smoodly/config.ts +14 -15
- package/templates/lorem/smoodly/elements.ts +5 -5
- package/templates/lorem/smoodly/globals.ts +10 -7
- package/templates/lorem/smoodly/richtext.tsx +5 -5
- package/templates/lorem/smoodly/sections.ts +12 -13
- package/templates/lorem/smoodly/server.ts +22 -44
- package/templates/lorem/smoodly/toolsets.ts +3 -3
- package/templates/lorem/smoodly/views.ts +33 -0
- package/templates/lorem/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
- package/templates/lorem/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
- package/templates/lorem/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
- package/templates/lorem/supabase/migrations/20261008120100_smoodly_views.sql +58 -0
- package/templates/lorem-intl/AGENTS.md +23 -16
- package/templates/lorem-intl/README.md +3 -3
- package/templates/lorem-intl/app/(smoodly)/admin/[[...segments]]/route.ts +8 -0
- package/templates/lorem-intl/components/elements/Callout.schema.ts +24 -0
- package/templates/lorem-intl/components/elements/Callout.tsx +2 -24
- package/templates/lorem-intl/components/elements/Cta.schema.ts +21 -0
- package/templates/lorem-intl/components/elements/Cta.tsx +2 -21
- package/templates/lorem-intl/components/pages/Article.tsx +1 -1
- package/templates/lorem-intl/components/pages/Articles.schema.ts +20 -0
- package/templates/lorem-intl/components/pages/Articles.tsx +2 -20
- package/templates/lorem-intl/components/pages/Home.schema.ts +26 -0
- package/templates/lorem-intl/components/pages/Home.tsx +2 -26
- package/templates/lorem-intl/components/pages/News.schema.ts +17 -0
- package/templates/lorem-intl/components/pages/News.tsx +2 -17
- package/templates/lorem-intl/components/pages/NewsPost.tsx +1 -1
- package/templates/lorem-intl/components/pages/Page.schema.ts +24 -0
- package/templates/lorem-intl/components/pages/Page.tsx +2 -24
- package/templates/lorem-intl/components/sections/FavouriteArticles.schema.ts +17 -0
- package/templates/lorem-intl/components/sections/FavouriteArticles.tsx +2 -17
- package/templates/lorem-intl/components/sections/Footer.schema.ts +31 -0
- package/templates/lorem-intl/components/sections/Footer.tsx +4 -32
- package/templates/lorem-intl/components/sections/Header.schema.ts +21 -0
- package/templates/lorem-intl/components/sections/Header.tsx +2 -21
- package/templates/lorem-intl/components/sections/Hero.schema.ts +24 -0
- package/templates/lorem-intl/components/sections/Hero.tsx +2 -24
- package/templates/lorem-intl/components/sections/ImageText.schema.ts +30 -0
- package/templates/lorem-intl/components/sections/ImageText.tsx +2 -30
- package/templates/lorem-intl/components/sections/PageHero.schema.ts +21 -0
- package/templates/lorem-intl/components/sections/PageHero.tsx +2 -21
- package/templates/lorem-intl/components/sections/People.schema.ts +15 -0
- package/templates/lorem-intl/components/sections/People.tsx +2 -14
- package/templates/lorem-intl/components/sections/Points.schema.ts +21 -0
- package/templates/lorem-intl/components/sections/Points.tsx +2 -21
- package/templates/lorem-intl/components/sections/Quote.schema.ts +23 -0
- package/templates/lorem-intl/components/sections/Quote.tsx +2 -23
- package/templates/lorem-intl/package.json +1 -1
- package/templates/lorem-intl/scripts/seed.ts +18 -18
- package/templates/lorem-intl/smoodly/collections.ts +2 -2
- package/templates/lorem-intl/smoodly/config.ts +14 -15
- package/templates/lorem-intl/smoodly/elements.ts +5 -5
- package/templates/lorem-intl/smoodly/globals.ts +10 -7
- package/templates/lorem-intl/smoodly/richtext.tsx +5 -5
- package/templates/lorem-intl/smoodly/sections.ts +12 -13
- package/templates/lorem-intl/smoodly/server.ts +22 -44
- package/templates/lorem-intl/smoodly/toolsets.ts +3 -3
- package/templates/lorem-intl/smoodly/views.ts +33 -0
- package/templates/lorem-intl/supabase/migrations/20261007120000_smoodly_v8.sql +154 -0
- package/templates/lorem-intl/supabase/migrations/20261007200000_smoodly_v9.sql +30 -0
- package/templates/lorem-intl/supabase/migrations/20261008120000_smoodly_v10.sql +47 -0
- package/templates/lorem-intl/supabase/migrations/20261008120100_smoodly_views.sql +58 -0
- package/templates/blank/app/(smoodly)/admin/[[...segments]]/page.tsx +0 -1
- package/templates/blank/app/(smoodly)/admin/layout.tsx +0 -15
- package/templates/blank/smoodly/admin.tsx +0 -6
- package/templates/blank/smoodly/ops.ts +0 -11
- package/templates/blank-intl/app/(smoodly)/admin/[[...segments]]/page.tsx +0 -1
- package/templates/blank-intl/app/(smoodly)/admin/layout.tsx +0 -15
- package/templates/blank-intl/smoodly/admin.tsx +0 -6
- package/templates/blank-intl/smoodly/ops.ts +0 -11
- package/templates/lorem/app/(smoodly)/admin/[[...segments]]/page.tsx +0 -1
- package/templates/lorem/app/(smoodly)/admin/layout.tsx +0 -15
- package/templates/lorem/smoodly/admin.tsx +0 -6
- package/templates/lorem/smoodly/ops.ts +0 -11
- package/templates/lorem-intl/app/(smoodly)/admin/[[...segments]]/page.tsx +0 -1
- package/templates/lorem-intl/app/(smoodly)/admin/layout.tsx +0 -15
- package/templates/lorem-intl/smoodly/admin.tsx +0 -6
- package/templates/lorem-intl/smoodly/ops.ts +0 -11
|
@@ -1,32 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { smoodly, type Props } from "smoodly/react";
|
|
2
2
|
import { RichText } from "@/smoodly/richtext";
|
|
3
|
-
import {
|
|
3
|
+
import { textSchema } from "./Text.schema";
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// reads — here `background` — is the view's own data attribute.
|
|
11
|
-
export const textSchema = smoodly.schema.section({
|
|
12
|
-
name: "text",
|
|
13
|
-
title: "Text",
|
|
14
|
-
description: "A title and a body of text.",
|
|
15
|
-
fields: {
|
|
16
|
-
title: f.text(),
|
|
17
|
-
body: f.richtext({ toolset: text }),
|
|
18
|
-
},
|
|
19
|
-
settings: {
|
|
20
|
-
background: f.radio([{ value: "paper", label: "Paper" }, { value: "ink", label: "Ink" }]).default("paper"),
|
|
21
|
-
},
|
|
22
|
-
sample: {
|
|
23
|
-
title: "Hello",
|
|
24
|
-
body: { type: "doc", content: [{ type: "paragraph", content: [{ type: "text", text: "Write something here." }] }] },
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Marks tie the rendered elements to their fields, so the editor can
|
|
29
|
-
// select and inline-edit them on the canvas.
|
|
5
|
+
// The section's component. The section wrapper renders the root element,
|
|
6
|
+
// Smoodly's styles set inline (spacing, width, align) and the node id; a
|
|
7
|
+
// setting the CSS reads — here `background` — is the view's own data
|
|
8
|
+
// attribute. Marks tie the rendered elements to their fields, so the
|
|
9
|
+
// editor can select and inline-edit them on the canvas.
|
|
30
10
|
const m = smoodly.marks(textSchema);
|
|
31
11
|
|
|
32
12
|
function TextView({ title, body, settings, styles }: Props<typeof textSchema>) {
|
|
@@ -14,7 +14,7 @@ loadEnvConfig(process.cwd());
|
|
|
14
14
|
|
|
15
15
|
const { config } = await import("../smoodly/config");
|
|
16
16
|
const { content, node } = await import("../smoodly/server");
|
|
17
|
-
const {
|
|
17
|
+
const { textSchema } = await import("../components/sections/Text.schema");
|
|
18
18
|
const c = content();
|
|
19
19
|
|
|
20
20
|
const DEFAULT = config.locales.default;
|
|
@@ -59,7 +59,7 @@ const tree: PageTree = {
|
|
|
59
59
|
meta: { description: "A new site, built with Smoodly." },
|
|
60
60
|
zones: {
|
|
61
61
|
body: [
|
|
62
|
-
node(
|
|
62
|
+
node(textSchema, {
|
|
63
63
|
title: "Hello",
|
|
64
64
|
body: doc(
|
|
65
65
|
p("This is your home page. Open the admin to edit this text, add sections, create pages and write posts."),
|
|
@@ -4,15 +4,15 @@ import { collections } from "./collections";
|
|
|
4
4
|
import { sections } from "./sections";
|
|
5
5
|
import { globals } from "./globals";
|
|
6
6
|
import { elements } from "./elements";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { PostPage } from "@/components/pages/Post";
|
|
7
|
+
import { homeSchema } from "@/components/pages/Home.schema";
|
|
8
|
+
import { pageSchema } from "@/components/pages/Page.schema";
|
|
10
9
|
|
|
11
10
|
export { collections, sections, globals, elements };
|
|
12
11
|
|
|
13
|
-
// The registry and the plain settings, one file.
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// The registry of schemas and the plain settings, one file. It imports
|
|
13
|
+
// no component, no route file and no Next API, so a script and the CLI
|
|
14
|
+
// can load it outside the app; smoodly/views.ts lists the components
|
|
15
|
+
// that render these schemas. The locale set comes from
|
|
16
16
|
// i18n/routing.ts (plain config, no Next inside) — the proxy runs in the
|
|
17
17
|
// edge runtime and could not import this file the other way round.
|
|
18
18
|
export const config = defineConfig({
|
|
@@ -21,7 +21,8 @@ export const config = defineConfig({
|
|
|
21
21
|
elements,
|
|
22
22
|
collections,
|
|
23
23
|
globals,
|
|
24
|
-
|
|
24
|
+
// Page types. The entry page is a view: smoodly/views.ts lists it.
|
|
25
|
+
pages: [homeSchema, pageSchema],
|
|
25
26
|
},
|
|
26
27
|
locales: { default: routing.defaultLocale, supported: [...routing.locales] },
|
|
27
28
|
// The record the site root serves.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// The
|
|
2
|
-
//
|
|
3
|
-
import {
|
|
1
|
+
// The element schemas a richtext body may embed; smoodly/views.ts lists
|
|
2
|
+
// the components that render them.
|
|
3
|
+
import { buttonSchema } from "@/components/elements/Button.schema";
|
|
4
4
|
|
|
5
|
-
export const elements = [
|
|
5
|
+
export const elements = [buttonSchema];
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Global content: code-declared singletons, edited once under Globals
|
|
2
2
|
// and read anywhere. `site` is an object item — fields code consumes;
|
|
3
|
-
// the layout reads it through q().getGlobal("site")
|
|
4
|
-
//
|
|
3
|
+
// the layout reads it through q().getGlobal("site"), so it is a layout
|
|
4
|
+
// global: it comes with the page's own read. A visual item
|
|
5
|
+
// (`section: someSectionSchema`) renders through a section instead.
|
|
5
6
|
import { f, smoodly } from "smoodly";
|
|
6
7
|
|
|
7
8
|
export const site = smoodly.global({
|
|
@@ -11,6 +12,7 @@ export const site = smoodly.global({
|
|
|
11
12
|
name: f.text(),
|
|
12
13
|
tagline: f.text().localized(),
|
|
13
14
|
},
|
|
15
|
+
layout: true,
|
|
14
16
|
});
|
|
15
17
|
|
|
16
18
|
export const globals = [site];
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// The site's
|
|
2
|
-
//
|
|
3
|
-
// entry pages import RichText from here, never from the package root.
|
|
1
|
+
// The site's bound RichText: the element components a body may embed,
|
|
2
|
+
// images through <SmoodlyImage>.
|
|
4
3
|
import { createRichText } from "smoodly/richtext";
|
|
5
|
-
import {
|
|
4
|
+
import { Button } from "@/components/elements/Button";
|
|
6
5
|
|
|
7
|
-
export const RichText = createRichText({ elements, sizes: "(min-width: 900px) 720px, 100vw" });
|
|
6
|
+
export const RichText = createRichText({ elements: [Button], sizes: "(min-width: 900px) 720px, 100vw" });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// The
|
|
2
|
-
//
|
|
3
|
-
import {
|
|
1
|
+
// The section schemas the config registers; smoodly/views.ts lists the
|
|
2
|
+
// components that render them.
|
|
3
|
+
import { textSchema } from "@/components/sections/Text.schema";
|
|
4
4
|
|
|
5
|
-
export const sections = [
|
|
5
|
+
export const sections = [textSchema];
|
|
@@ -5,23 +5,23 @@
|
|
|
5
5
|
// SMOODLY_PUBLISHABLE_KEY (the `smoodlyEnv("admin")` calls below); whether the
|
|
6
6
|
// secret is a service-role key or a cloud write key is invisible here.
|
|
7
7
|
//
|
|
8
|
-
// Lazy on purpose
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// config at module scope.
|
|
8
|
+
// Lazy on purpose: a page component imports this file to read through
|
|
9
|
+
// `q` (components/pages/Articles.tsx), and this file imports the
|
|
10
|
+
// components through ./views. That is a cycle, so nothing here reads
|
|
11
|
+
// views() or the env at module scope.
|
|
13
12
|
import {
|
|
14
|
-
createSmoodlyClient, createSmoodlySite,
|
|
13
|
+
createSmoodlyClient, createSmoodlySite, createSupabaseStores, presenceConfig, siteOptions, smoodlyEnv,
|
|
14
|
+
type PresenceConfig, type SmoodlySite, type SupabaseAssetStore, type SupabaseEntryStore, type SupabaseMemberStore, type SupabasePageStore,
|
|
15
15
|
} from "smoodly";
|
|
16
|
-
// The admin
|
|
17
|
-
//
|
|
18
|
-
// shell in every live page's JavaScript.
|
|
16
|
+
// The admin's server pieces: the sign-in check and the content API. No browser code lives in the package's
|
|
17
|
+
// entries; the admin itself is prebuilt and served by app/(smoodly)/admin/[[...segments]]/route.ts.
|
|
19
18
|
import { createSmoodlyContent, supabaseAdminAuth, type AdminAuth, type AuthConfig, type SmoodlyContent } from "smoodly/admin";
|
|
20
|
-
import { createPageRenderer, createSmoodlyQueries
|
|
19
|
+
import { createPageRenderer, createSmoodlyQueries } from "smoodly/next";
|
|
21
20
|
import { connection } from "next/server";
|
|
22
21
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
23
22
|
import { getPathname } from "@/i18n/navigation";
|
|
24
|
-
import { collections, config,
|
|
23
|
+
import { collections, config, globals } from "./config";
|
|
24
|
+
import { views } from "./views";
|
|
25
25
|
|
|
26
26
|
type Renderer = ReturnType<typeof createPageRenderer>;
|
|
27
27
|
type Queries = ReturnType<typeof createSmoodlyQueries<typeof collections, typeof globals>>;
|
|
@@ -35,49 +35,27 @@ function bound(): Bound {
|
|
|
35
35
|
if (cached) return cached;
|
|
36
36
|
const env = smoodlyEnv();
|
|
37
37
|
const db = createSmoodlyClient(env);
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
const paths = new SupabasePathIndex(db, config.anchors);
|
|
41
|
-
const locales = config.locales;
|
|
42
|
-
const entries = new SupabaseEntryStore(db, collections, { paths, locales, globals, elements });
|
|
43
|
-
const pages = new SupabasePageStore(db, {
|
|
44
|
-
sections, elements, entries, paths, locales,
|
|
45
|
-
// The custom section's columns toolset, so a column's images count as usage (spec 2026-09-14 §6).
|
|
46
|
-
custom: config.custom,
|
|
47
|
-
homePageSlug: config.homePageSlug,
|
|
48
|
-
// Code-owned addresses by owner: a fixed page's or a collection's path (spec 2026-09-29 nested paths §3).
|
|
49
|
-
addresses: config.addresses,
|
|
50
|
-
tree: config.pages?.tree,
|
|
51
|
-
});
|
|
52
|
-
// The media library: rows in `assets`, bytes in the public `media` bucket.
|
|
53
|
-
const assets = new SupabaseAssetStore(db);
|
|
54
|
-
// The admission table, read by the Members page (spec 2026-09-25 §4).
|
|
55
|
-
const members = new SupabaseMemberStore(db);
|
|
38
|
+
// The stores, read off the config: pages, entries, the media library and the admission table.
|
|
39
|
+
const { pages, entries, assets, members, published } = createSupabaseStores(db, config);
|
|
56
40
|
const auth = supabaseAdminAuth({ authUrl: env.authUrl, authKey: env.authKey, db });
|
|
57
|
-
// The site layer's own view of the stores; nothing outside this file reads them.
|
|
58
|
-
const stores = () => ({ pages, entries, assets });
|
|
59
41
|
// The site layer: (locale, path) → page or entry, and the content API
|
|
60
42
|
// `q` wraps below. Not exported — the app reads through `q`, which adds
|
|
61
43
|
// draft awareness and the cache.
|
|
62
|
-
const site = createSmoodlySite(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
sections,
|
|
66
|
-
elements,
|
|
67
|
-
pages: (config.registry.pages ?? []) as PageRegistration[],
|
|
68
|
-
href,
|
|
69
|
-
});
|
|
44
|
+
const site = createSmoodlySite(siteOptions(config, () => ({ pages, entries, assets, published })));
|
|
45
|
+
// The components, checked against the config's schemas here, on first use.
|
|
46
|
+
const renderer = createPageRenderer({ site, ...views(), href });
|
|
70
47
|
// The read API (spec 2026-09-11): typed by the registry, draft-aware, cached under tags the admin expires.
|
|
71
48
|
const q = createSmoodlyQueries({ site, collections, globals });
|
|
72
49
|
cached = { db, pages, entries, assets, members, auth, site, renderer, q };
|
|
73
50
|
return cached;
|
|
74
51
|
}
|
|
75
52
|
|
|
76
|
-
/** The admin's wiring, for
|
|
53
|
+
/** The admin's wiring, for the admin route only. Scripts write through `content()`. */
|
|
77
54
|
export function adminDeps() {
|
|
78
55
|
smoodlyEnv("admin"); // the admin signs in with the publishable key: a missing one is named here, not on a live page
|
|
79
|
-
const { pages, entries, assets, members, auth } = bound();
|
|
80
|
-
|
|
56
|
+
const { pages, entries, assets, members, auth, site } = bound();
|
|
57
|
+
// The projector: a publish writes the finished documents a live page reads.
|
|
58
|
+
return { pages, entries, assets, members, auth, href, projector: site.projector };
|
|
81
59
|
}
|
|
82
60
|
|
|
83
61
|
/** Write content from code — seeds, migrations, route handlers (spec 2026-09-30 tester findings §1).
|
|
@@ -85,8 +63,8 @@ export function adminDeps() {
|
|
|
85
63
|
* throws `SmoodlyContentError`. `content().pages.create(…)`, `.entries.create(…)`, `.globals.set(…)`,
|
|
86
64
|
* `.media.upload(…)`; build tree nodes with `node(Section, fields)`. */
|
|
87
65
|
export function content(): SmoodlyContent {
|
|
88
|
-
const { pages, entries, assets, members } = bound();
|
|
89
|
-
return createSmoodlyContent({ config, pages, entries, assets, members, href });
|
|
66
|
+
const { pages, entries, assets, members, site } = bound();
|
|
67
|
+
return createSmoodlyContent({ config, pages, entries, assets, members, href, projector: site.projector });
|
|
90
68
|
}
|
|
91
69
|
|
|
92
70
|
export { node } from "smoodly";
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
// Relative imports on purpose: collections.ts pulls this file into
|
|
6
6
|
// create-smoodly-app's template build, which runs outside the app.
|
|
7
7
|
import { smoodly } from "smoodly";
|
|
8
|
-
import {
|
|
8
|
+
import { buttonSchema } from "../components/elements/Button.schema";
|
|
9
9
|
|
|
10
10
|
export const text = smoodly.toolset({
|
|
11
11
|
marks: ["bold", "italic", "link"],
|
|
12
12
|
nodes: ["h2", "h3", "bulletList", "orderedList", "blockquote"],
|
|
13
|
-
elements: [
|
|
13
|
+
elements: [buttonSchema],
|
|
14
14
|
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// The components that render the registered schemas. A function, not a
|
|
2
|
+
// value: a page component may import smoodly/server.ts, which imports
|
|
3
|
+
// this file. defineViews names a schema with no component, and a
|
|
4
|
+
// component with no schema, the first time the site renders.
|
|
5
|
+
import { defineViews } from "smoodly/react";
|
|
6
|
+
import { config } from "./config";
|
|
7
|
+
import { RichText } from "./richtext";
|
|
8
|
+
import { Text } from "@/components/sections/Text";
|
|
9
|
+
import { Button } from "@/components/elements/Button";
|
|
10
|
+
import { HomePage } from "@/components/pages/Home";
|
|
11
|
+
import { StandardPage } from "@/components/pages/Page";
|
|
12
|
+
import { PostPage } from "@/components/pages/Post";
|
|
13
|
+
|
|
14
|
+
export const views = () =>
|
|
15
|
+
defineViews(config, { sections: [Text], elements: [Button], pages: [HomePage, StandardPage, PostPage], RichText });
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
-- Smoodly core schema, version 8 (docs/superpowers/specs/2026-10-07-delivery-model-design.md).
|
|
2
|
+
-- Shipped in the smoodly package; `npx smoodly migrate` copies it into supabase/migrations/.
|
|
3
|
+
-- Never edit a published file: the next change is the next file.
|
|
4
|
+
-- The delta over version 7: the delivery model's two derived tables,
|
|
5
|
+
-- published_pages and published_entries, with the space trigger and the
|
|
6
|
+
-- write-key-only policies every content table has (no row cap: they are
|
|
7
|
+
-- derived), smoodly_list_published over the raw published snapshot, and
|
|
8
|
+
-- the published_routes view, one lookup for any address.
|
|
9
|
+
|
|
10
|
+
set client_min_messages = warning;
|
|
11
|
+
|
|
12
|
+
-- The delivery model (spec 2026-10-07): Publish writes the finished document of a page, an entry
|
|
13
|
+
-- or a global here; a live read is one lookup. Derived and rebuildable from the editing tables.
|
|
14
|
+
create table if not exists "published_pages" (
|
|
15
|
+
"space_id" uuid not null default '00000000-0000-0000-0000-000000000000',
|
|
16
|
+
"page_id" uuid not null references "pages" ("id") on delete cascade,
|
|
17
|
+
"locale" text not null,
|
|
18
|
+
"parent_id" uuid,
|
|
19
|
+
"sort" numeric,
|
|
20
|
+
"path" text,
|
|
21
|
+
"title" text not null,
|
|
22
|
+
"has_page" boolean not null,
|
|
23
|
+
"link_fields" jsonb not null default '{}'::jsonb,
|
|
24
|
+
"document" jsonb,
|
|
25
|
+
"tags" text[] not null default '{}',
|
|
26
|
+
"model" text not null,
|
|
27
|
+
"stale" boolean not null default false,
|
|
28
|
+
"published_at" timestamptz not null default now(),
|
|
29
|
+
primary key ("page_id", "locale")
|
|
30
|
+
);
|
|
31
|
+
create unique index if not exists "published_pages_address_idx"
|
|
32
|
+
on "published_pages" ("space_id", "locale", "path") where "path" is not null;
|
|
33
|
+
create index if not exists "published_pages_tree_idx"
|
|
34
|
+
on "published_pages" ("space_id", "locale", "parent_id", "sort");
|
|
35
|
+
|
|
36
|
+
create table if not exists "published_entries" (
|
|
37
|
+
"space_id" uuid not null default '00000000-0000-0000-0000-000000000000',
|
|
38
|
+
"entry_id" uuid not null references "entries" ("id") on delete cascade,
|
|
39
|
+
"locale" text not null,
|
|
40
|
+
"collection" text not null,
|
|
41
|
+
"parent_id" uuid,
|
|
42
|
+
"sort" numeric,
|
|
43
|
+
"slug" text,
|
|
44
|
+
"path" text,
|
|
45
|
+
"created_at" timestamptz not null,
|
|
46
|
+
"updated_at" timestamptz not null,
|
|
47
|
+
"fields" jsonb not null,
|
|
48
|
+
"document" jsonb not null,
|
|
49
|
+
"tags" text[] not null default '{}',
|
|
50
|
+
"model" text not null,
|
|
51
|
+
"stale" boolean not null default false,
|
|
52
|
+
"published_at" timestamptz not null default now(),
|
|
53
|
+
primary key ("entry_id", "locale")
|
|
54
|
+
);
|
|
55
|
+
create index if not exists "published_entries_list_idx"
|
|
56
|
+
on "published_entries" ("space_id", "collection", "locale");
|
|
57
|
+
create unique index if not exists "published_entries_address_idx"
|
|
58
|
+
on "published_entries" ("space_id", "locale", "path") where "path" is not null;
|
|
59
|
+
create index if not exists "published_entries_fields_idx"
|
|
60
|
+
on "published_entries" using gin ("fields");
|
|
61
|
+
|
|
62
|
+
alter table "published_pages" enable row level security;
|
|
63
|
+
alter table "published_entries" enable row level security;
|
|
64
|
+
|
|
65
|
+
-- smoodly_list_entries without the draft branch, over the delivery table (spec 2026-10-07 §3):
|
|
66
|
+
-- `where` and `order` read the raw published snapshot, the row answers with its document.
|
|
67
|
+
-- Invoker rights, so RLS decides what a key sees.
|
|
68
|
+
create or replace function "smoodly_list_published"(
|
|
69
|
+
p_collection text, p_locale text, p_where jsonb, p_order jsonb,
|
|
70
|
+
p_limit integer, p_parent uuid, p_has_parent boolean, p_ids uuid[]
|
|
71
|
+
) returns table ("id" uuid, "document" jsonb, "model" text, "stale" boolean) language sql stable set search_path = public as $$
|
|
72
|
+
with shown as (
|
|
73
|
+
select p."entry_id" as "id", p."sort", p."created_at", p."updated_at", p."fields", p."document", p."model", p."stale"
|
|
74
|
+
from "published_entries" p
|
|
75
|
+
where p."collection" = p_collection and p."locale" = p_locale
|
|
76
|
+
and (not p_has_parent or p."parent_id" is not distinct from p_parent)
|
|
77
|
+
and (p_ids is null or p."entry_id" = any(p_ids))
|
|
78
|
+
)
|
|
79
|
+
select s."id", s."document", s."model", s."stale" from shown s
|
|
80
|
+
where not exists (
|
|
81
|
+
select 1 from jsonb_each(coalesce(p_where, '{}'::jsonb)) w
|
|
82
|
+
where not (case when w.value ? 'in'
|
|
83
|
+
then (s."fields" ->> w.key) = any (select jsonb_array_elements_text(w.value -> 'in'))
|
|
84
|
+
else s."fields" @> jsonb_build_object(w.key, w.value -> 'contains') end))
|
|
85
|
+
order by
|
|
86
|
+
case when p_order ->> 'by' = 'manual' then s."sort" end asc nulls last,
|
|
87
|
+
case when p_order ->> 'direction' = 'asc' then
|
|
88
|
+
case p_order ->> 'by' when 'createdAt' then to_jsonb(s."created_at") when 'updatedAt' then to_jsonb(s."updated_at")
|
|
89
|
+
when 'manual' then null else nullif(nullif(s."fields" -> (p_order ->> 'by'), 'null'::jsonb), '""'::jsonb) end end asc nulls last,
|
|
90
|
+
case when p_order ->> 'direction' = 'desc' then
|
|
91
|
+
case p_order ->> 'by' when 'createdAt' then to_jsonb(s."created_at") when 'updatedAt' then to_jsonb(s."updated_at")
|
|
92
|
+
when 'manual' then null else nullif(nullif(s."fields" -> (p_order ->> 'by'), 'null'::jsonb), '""'::jsonb) end end desc nulls last,
|
|
93
|
+
s."created_at" asc, s."id" asc
|
|
94
|
+
limit p_limit;
|
|
95
|
+
$$;
|
|
96
|
+
|
|
97
|
+
-- One lookup for any address (spec 2026-10-07 §3): a page or an entry, under the caller's own policies.
|
|
98
|
+
create or replace view "published_routes" with (security_invoker = true) as
|
|
99
|
+
select "space_id", "locale", "path", 'page' as "kind", "page_id" as "id", "document", "model", "stale", "tags", null::text as "collection"
|
|
100
|
+
from "published_pages" where "path" is not null and "has_page"
|
|
101
|
+
union all
|
|
102
|
+
select "space_id", "locale", "path", 'entry' as "kind", "entry_id" as "id", "document", "model", "stale", "tags", "collection"
|
|
103
|
+
from "published_entries" where "path" is not null;
|
|
104
|
+
|
|
105
|
+
-- Stamped with the caller's space on insert, as every content table.
|
|
106
|
+
drop trigger if exists "smoodly_a_set_space" on "published_pages";
|
|
107
|
+
create trigger "smoodly_a_set_space" before insert on "published_pages"
|
|
108
|
+
for each row execute function "smoodly_set_space"();
|
|
109
|
+
|
|
110
|
+
drop trigger if exists "smoodly_a_set_space" on "published_entries";
|
|
111
|
+
create trigger "smoodly_a_set_space" before insert on "published_entries"
|
|
112
|
+
for each row execute function "smoodly_set_space"();
|
|
113
|
+
|
|
114
|
+
-- A write key reads and writes its own space; no other key reads (spec 2026-09-29 content API contract §8).
|
|
115
|
+
drop policy if exists "smoodly_write" on "published_pages";
|
|
116
|
+
|
|
117
|
+
drop policy if exists "smoodly_insert" on "published_pages";
|
|
118
|
+
create policy "smoodly_insert" on "published_pages" for insert
|
|
119
|
+
with check ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
120
|
+
|
|
121
|
+
drop policy if exists "smoodly_update" on "published_pages";
|
|
122
|
+
create policy "smoodly_update" on "published_pages" for update
|
|
123
|
+
using ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write')
|
|
124
|
+
with check ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
125
|
+
|
|
126
|
+
drop policy if exists "smoodly_delete" on "published_pages";
|
|
127
|
+
create policy "smoodly_delete" on "published_pages" for delete
|
|
128
|
+
using ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
129
|
+
|
|
130
|
+
drop policy if exists "smoodly_read" on "published_pages";
|
|
131
|
+
create policy "smoodly_read" on "published_pages" for select
|
|
132
|
+
using ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
133
|
+
|
|
134
|
+
drop policy if exists "smoodly_write" on "published_entries";
|
|
135
|
+
|
|
136
|
+
drop policy if exists "smoodly_insert" on "published_entries";
|
|
137
|
+
create policy "smoodly_insert" on "published_entries" for insert
|
|
138
|
+
with check ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
139
|
+
|
|
140
|
+
drop policy if exists "smoodly_update" on "published_entries";
|
|
141
|
+
create policy "smoodly_update" on "published_entries" for update
|
|
142
|
+
using ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write')
|
|
143
|
+
with check ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
144
|
+
|
|
145
|
+
drop policy if exists "smoodly_delete" on "published_entries";
|
|
146
|
+
create policy "smoodly_delete" on "published_entries" for delete
|
|
147
|
+
using ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
148
|
+
|
|
149
|
+
drop policy if exists "smoodly_read" on "published_entries";
|
|
150
|
+
create policy "smoodly_read" on "published_entries" for select
|
|
151
|
+
using ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
152
|
+
|
|
153
|
+
insert into "smoodly_meta" ("key", "value") values ('schema_version', '8')
|
|
154
|
+
on conflict ("key") do update set "value" = excluded."value";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- Smoodly core schema, version 9 (packages/smoodly/MAINTAINERS.md, register 2026-10-07: what the delivery model left).
|
|
2
|
+
-- Shipped in the smoodly package; `npx smoodly migrate` copies it into supabase/migrations/.
|
|
3
|
+
-- Never edit a published file: the next change is the next file.
|
|
4
|
+
-- The delta over version 8: smoodly_lookup_address, the route row and the
|
|
5
|
+
-- locale's global items in one call, so a live page is one database call
|
|
6
|
+
-- instead of the page then its layout's globals.
|
|
7
|
+
|
|
8
|
+
set client_min_messages = warning;
|
|
9
|
+
|
|
10
|
+
-- The address and the locale's global items in one call (schema v9; register 2026-10-07: a miss was two
|
|
11
|
+
-- rounds, the page then the layout's globals). The route row first, kind 'page' or 'entry'; then one row per
|
|
12
|
+
-- named global, kind 'global', the collection its name. A miss answers nothing: the globals ride with a page
|
|
13
|
+
-- only. Invoker rights, so RLS decides what a key sees.
|
|
14
|
+
create or replace function "smoodly_lookup_address"(p_locale text, p_path text, p_globals text[])
|
|
15
|
+
returns table ("kind" text, "id" uuid, "collection" text, "document" jsonb, "model" text, "stale" boolean, "tags" text[])
|
|
16
|
+
language sql stable set search_path = public as $$
|
|
17
|
+
with route as (
|
|
18
|
+
select r."kind", r."id", r."collection", r."document", r."model", r."stale", r."tags"
|
|
19
|
+
from "published_routes" r where r."locale" = p_locale and r."path" = p_path limit 1
|
|
20
|
+
)
|
|
21
|
+
select * from route
|
|
22
|
+
union all
|
|
23
|
+
select 'global' as "kind", e."entry_id", e."collection", e."document", e."model", e."stale", e."tags"
|
|
24
|
+
from "published_entries" e
|
|
25
|
+
where e."locale" = p_locale and e."collection" = any(coalesce(p_globals, '{}'::text[]))
|
|
26
|
+
and exists (select 1 from route);
|
|
27
|
+
$$;
|
|
28
|
+
|
|
29
|
+
insert into "smoodly_meta" ("key", "value") values ('schema_version', '9')
|
|
30
|
+
on conflict ("key") do update set "value" = excluded."value";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
-- Smoodly core schema, version 10 (DESIGN §3, the document stamp, decided 2026-10-08).
|
|
2
|
+
-- Shipped in the smoodly package; `npx smoodly migrate` copies it into supabase/migrations/.
|
|
3
|
+
-- Never edit a published file: the next change is the next file.
|
|
4
|
+
-- The delta over version 9: published_models, one row per space holding the
|
|
5
|
+
-- document stamp the space recorded as current, so only the deployment at that
|
|
6
|
+
-- stamp rewrites delivery rows (a preview deployment on a newer package serves
|
|
7
|
+
-- assembled documents and writes nothing). Additive: no function changes.
|
|
8
|
+
|
|
9
|
+
set client_min_messages = warning;
|
|
10
|
+
|
|
11
|
+
-- The document stamp a space recorded as current: set by `npx smoodly rebuild` and by the first writer
|
|
12
|
+
-- into an empty record; only a reader at this stamp rewrites published_pages and published_entries.
|
|
13
|
+
create table if not exists "published_models" (
|
|
14
|
+
"space_id" uuid primary key default '00000000-0000-0000-0000-000000000000',
|
|
15
|
+
"model" text not null,
|
|
16
|
+
"recorded_at" timestamptz not null default now()
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
alter table "published_models" enable row level security;
|
|
20
|
+
|
|
21
|
+
-- Stamped with the caller's space on insert, as every content table.
|
|
22
|
+
drop trigger if exists "smoodly_a_set_space" on "published_models";
|
|
23
|
+
create trigger "smoodly_a_set_space" before insert on "published_models"
|
|
24
|
+
for each row execute function "smoodly_set_space"();
|
|
25
|
+
|
|
26
|
+
-- A write key reads and writes its own space's record; no other key reads (spec 2026-09-29 content API contract §8).
|
|
27
|
+
drop policy if exists "smoodly_write" on "published_models";
|
|
28
|
+
|
|
29
|
+
drop policy if exists "smoodly_insert" on "published_models";
|
|
30
|
+
create policy "smoodly_insert" on "published_models" for insert
|
|
31
|
+
with check ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
32
|
+
|
|
33
|
+
drop policy if exists "smoodly_update" on "published_models";
|
|
34
|
+
create policy "smoodly_update" on "published_models" for update
|
|
35
|
+
using ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write')
|
|
36
|
+
with check ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
37
|
+
|
|
38
|
+
drop policy if exists "smoodly_delete" on "published_models";
|
|
39
|
+
create policy "smoodly_delete" on "published_models" for delete
|
|
40
|
+
using ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
41
|
+
|
|
42
|
+
drop policy if exists "smoodly_read" on "published_models";
|
|
43
|
+
create policy "smoodly_read" on "published_models" for select
|
|
44
|
+
using ("space_id" = (select "smoodly_current_space"()) and not (select "smoodly_space_key_revoked"()) and (select "smoodly_key_kind"()) = 'write');
|
|
45
|
+
|
|
46
|
+
insert into "smoodly_meta" ("key", "value") values ('schema_version', '10')
|
|
47
|
+
on conflict ("key") do update set "value" = excluded."value";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- Smoodly: read views for this app's collections, written by `npx smoodly migrate`
|
|
2
|
+
-- (2026-10-08T12:01:00.000Z). One block per collection; the marker's hash is the block's SQL, compared on the next run.
|
|
3
|
+
-- Never edit a written file: change the collection and run migrate again.
|
|
4
|
+
|
|
5
|
+
-- smoodly:collection posts sha256:38ba2918f83ab367
|
|
6
|
+
create or replace view "posts" with (security_invoker = true) as
|
|
7
|
+
select
|
|
8
|
+
e."id",
|
|
9
|
+
l."locale",
|
|
10
|
+
l."slug",
|
|
11
|
+
e."sort",
|
|
12
|
+
e."created_at",
|
|
13
|
+
l."updated_at",
|
|
14
|
+
coalesce(v."fields", e."fields" || l."fields")->>'title' as "title",
|
|
15
|
+
(coalesce(v."fields", e."fields" || l."fields")->>'date')::timestamptz as "date",
|
|
16
|
+
coalesce(v."fields", e."fields" || l."fields")->'body' as "body"
|
|
17
|
+
from "entries" e
|
|
18
|
+
join "entry_locales" l on l."entry_id" = e."id"
|
|
19
|
+
left join "entry_versions" v on v."id" = l."published_version_id"
|
|
20
|
+
where e."collection" = 'posts' and l."status" = 'published';
|
|
@@ -7,11 +7,19 @@ where things are.
|
|
|
7
7
|
|
|
8
8
|
## Rules
|
|
9
9
|
|
|
10
|
-
- **Register
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
- **Register schemas in the config, components in `smoodly/views.ts`.**
|
|
11
|
+
Every section, element and page type is two files: `Name.schema.ts`
|
|
12
|
+
and `Name.tsx`. The schema is listed in its barrel (`sections.ts`,
|
|
13
|
+
`elements.ts`, the config's `pages`; collections and global items in
|
|
14
|
+
`collections.ts`, `globals.ts`) and `smoodly/config.ts` is the one
|
|
15
|
+
registry. The component is listed in `smoodly/views.ts`, an element's
|
|
16
|
+
also in `smoodly/richtext.tsx`; an entry page is listed only in
|
|
17
|
+
`smoodly/views.ts`. A schema with no component, or a component with
|
|
18
|
+
no schema, is a named error on the first render.
|
|
19
|
+
- **A `.schema.ts` file imports no component** and nothing from
|
|
20
|
+
`smoodly/server.ts` or `smoodly/richtext.tsx`. Zones, toolsets, global
|
|
21
|
+
items and `node()` take schemas (`z.locked(heroSchema)`), never
|
|
22
|
+
components.
|
|
15
23
|
- **After a new collection or a `smoodly` upgrade, run `npx smoodly migrate`**
|
|
16
24
|
and apply what it writes (`supabase db push` hosted, `supabase migration up`
|
|
17
25
|
local). It copies the core schema files the package ships and writes a
|
|
@@ -23,16 +31,15 @@ where things are.
|
|
|
23
31
|
the admin's own operations, so validation and publish checks apply and
|
|
24
32
|
a failure throws. Never write rows or call the stores directly. The
|
|
25
33
|
seed is idempotent; run it with `npm run seed`.
|
|
26
|
-
- **
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
is a second root layout. Do not add an `app/layout.tsx`.
|
|
34
|
+
- **The admin is one route.** `app/(smoodly)/admin/[[...segments]]/route.ts`
|
|
35
|
+
serves the admin's page, its files and its operations from the
|
|
36
|
+
`smoodly` package; the folder name must match `admin.path` in the
|
|
37
|
+
config. The admin is prebuilt: nothing of it is compiled into the
|
|
38
|
+
site, and it takes no layout. Server-side pieces such as
|
|
39
|
+
`supabaseAdminAuth` and the content API come from `smoodly/admin`.
|
|
40
|
+
- **One root layout.** The site's layout lives in its route group or
|
|
41
|
+
locale segment. Do not add an `app/layout.tsx`: it would wrap that
|
|
42
|
+
layout in a second `<html>`.
|
|
36
43
|
- **Toolsets and elements.** A richtext field names a toolset
|
|
37
44
|
(`smoodly/toolsets.ts`); a toolset embeds elements by value; the config
|
|
38
45
|
must register those elements too. Files the toolset imports use
|
|
@@ -45,7 +52,7 @@ where things are.
|
|
|
45
52
|
- **Multilingual sites.** Content locales are Smoodly's; URL locales are
|
|
46
53
|
next-intl's. The locale set is `i18n/routing.ts`. UI strings of the
|
|
47
54
|
site's own go through next-intl's messages in `messages/<locale>.json`.
|
|
48
|
-
- **Fixed versus open pages.** A page
|
|
55
|
+
- **Fixed versus open pages.** A page type with a `slug` is fixed: one
|
|
49
56
|
record, created for you, never deleted. Without one it is open: editors
|
|
50
57
|
create as many as they like.
|
|
51
58
|
|