create-nextblock 0.2.78 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (413) hide show
  1. package/bin/create-nextblock.js +740 -459
  2. package/package.json +1 -2
  3. package/scripts/sync-template.js +18 -1
  4. package/templates/nextblock-template/.browserslistrc +11 -0
  5. package/templates/nextblock-template/.swcrc +30 -30
  6. package/templates/nextblock-template/README.md +23 -114
  7. package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -28
  8. package/templates/nextblock-template/app/(auth-pages)/sign-in/page.tsx +50 -25
  9. package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +111 -56
  10. package/templates/nextblock-template/app/(auth-pages)/two-factor/actions.ts +91 -0
  11. package/templates/nextblock-template/app/(auth-pages)/two-factor/components/TwoFactorForm.tsx +118 -0
  12. package/templates/nextblock-template/app/(auth-pages)/two-factor/page.tsx +51 -0
  13. package/templates/nextblock-template/app/.well-known/ucp/route.ts +16 -0
  14. package/templates/nextblock-template/app/[slug]/PageClientContent.tsx +48 -28
  15. package/templates/nextblock-template/app/[slug]/page.tsx +63 -6
  16. package/templates/nextblock-template/app/[slug]/page.utils.ts +374 -157
  17. package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -0
  18. package/templates/nextblock-template/app/actions/consent.ts +57 -0
  19. package/templates/nextblock-template/app/actions/formActions.ts +130 -11
  20. package/templates/nextblock-template/app/actions/languageActions.ts +31 -30
  21. package/templates/nextblock-template/app/actions/package-actions.ts +183 -0
  22. package/templates/nextblock-template/app/actions/postActions.ts +146 -48
  23. package/templates/nextblock-template/app/actions/twoFactorEmail.ts +21 -0
  24. package/templates/nextblock-template/app/actions/visualEditingActions.test.ts +179 -0
  25. package/templates/nextblock-template/app/actions/visualEditingActions.ts +345 -0
  26. package/templates/nextblock-template/app/actions.ts +67 -12
  27. package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +153 -0
  28. package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +96 -0
  29. package/templates/nextblock-template/app/api/ai/global-agent/route.ts +965 -0
  30. package/templates/nextblock-template/app/api/checkout/freemius/sync/route.ts +29 -0
  31. package/templates/nextblock-template/app/api/checkout/route.ts +146 -0
  32. package/templates/nextblock-template/app/api/cms/full-backup/export/route.ts +33 -0
  33. package/templates/nextblock-template/app/api/cms/full-backup/restore/route.ts +63 -0
  34. package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +3413 -17
  35. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +7830 -0
  36. package/templates/nextblock-template/app/api/cron/sync-currencies/route.ts +35 -0
  37. package/templates/nextblock-template/app/api/custom-blocks/db-relations/route.ts +92 -0
  38. package/templates/nextblock-template/app/api/custom-blocks/editor-definitions/route.ts +43 -0
  39. package/templates/nextblock-template/app/api/draft/disable/route.ts +25 -0
  40. package/templates/nextblock-template/app/api/draft/route.ts +93 -0
  41. package/templates/nextblock-template/app/api/draft/start/route.ts +77 -0
  42. package/templates/nextblock-template/app/api/media/library/route.ts +65 -0
  43. package/templates/nextblock-template/app/api/media/r2-presigned/route.ts +53 -0
  44. package/templates/nextblock-template/app/api/media/record/route.ts +160 -0
  45. package/templates/nextblock-template/app/api/search/route.ts +43 -0
  46. package/templates/nextblock-template/app/api/visual-editing/block-draft/route.ts +47 -0
  47. package/templates/nextblock-template/app/api/visual-editing/product-draft/route.ts +47 -0
  48. package/templates/nextblock-template/app/api/webhooks/freemius/route.ts +34 -0
  49. package/templates/nextblock-template/app/api/webhooks/stripe/route.ts +27 -0
  50. package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +392 -128
  51. package/templates/nextblock-template/app/article/[slug]/page.tsx +179 -127
  52. package/templates/nextblock-template/app/article/[slug]/page.utils.ts +262 -77
  53. package/templates/nextblock-template/app/auth/callback/route.ts +31 -58
  54. package/templates/nextblock-template/app/cart/page.tsx +7 -0
  55. package/templates/nextblock-template/app/checkout/UcpCartHydrator.tsx +20 -0
  56. package/templates/nextblock-template/app/checkout/page.tsx +52 -0
  57. package/templates/nextblock-template/app/checkout/success/actions.ts +136 -0
  58. package/templates/nextblock-template/app/checkout/success/page.tsx +186 -0
  59. package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +163 -33
  60. package/templates/nextblock-template/app/cms/blocks/actions.ts +424 -235
  61. package/templates/nextblock-template/app/cms/blocks/components/BackgroundSelector.tsx +212 -151
  62. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorArea.tsx +41 -20
  63. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +152 -19
  64. package/templates/nextblock-template/app/cms/blocks/components/BlockTypeCard.tsx +25 -17
  65. package/templates/nextblock-template/app/cms/blocks/components/BlockTypeSelector.tsx +200 -18
  66. package/templates/nextblock-template/app/cms/blocks/components/ColumnEditor.tsx +33 -16
  67. package/templates/nextblock-template/app/cms/blocks/components/CustomBlockEditorPreview.tsx +160 -0
  68. package/templates/nextblock-template/app/cms/blocks/components/EditableBlock.tsx +37 -18
  69. package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -67
  70. package/templates/nextblock-template/app/cms/blocks/components/SectionConfigPanel.tsx +108 -31
  71. package/templates/nextblock-template/app/cms/blocks/editors/DynamicCustomBlockEditor.tsx +167 -0
  72. package/templates/nextblock-template/app/cms/blocks/editors/FeaturedProductBlockEditor.tsx +31 -0
  73. package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +2 -2
  74. package/templates/nextblock-template/app/cms/blocks/editors/HeadingBlockEditor.tsx +1 -1
  75. package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +29 -29
  76. package/templates/nextblock-template/app/cms/blocks/editors/PostsGridBlockEditor.tsx +14 -18
  77. package/templates/nextblock-template/app/cms/blocks/editors/ProductGridBlockEditor.tsx +41 -0
  78. package/templates/nextblock-template/app/cms/blocks/editors/SectionBlockEditor.tsx +318 -118
  79. package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +98 -21
  80. package/templates/nextblock-template/app/cms/blocks/editors/VideoEmbedBlockEditor.tsx +1 -1
  81. package/templates/nextblock-template/app/cms/components/ContentLanguageSwitcher.tsx +27 -9
  82. package/templates/nextblock-template/app/cms/components/CopyContentFromLanguage.tsx +1 -1
  83. package/templates/nextblock-template/app/cms/components/CortexAiActiveContext.tsx +23 -0
  84. package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -0
  85. package/templates/nextblock-template/app/cms/components/CortexGlobalAgentChat.tsx +1507 -0
  86. package/templates/nextblock-template/app/cms/components/DraftStatusActions.tsx +145 -0
  87. package/templates/nextblock-template/app/cms/components/FeatureImageField.tsx +244 -0
  88. package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +38 -24
  89. package/templates/nextblock-template/app/cms/coupons/[id]/edit/page.tsx +16 -0
  90. package/templates/nextblock-template/app/cms/coupons/page.tsx +16 -0
  91. package/templates/nextblock-template/app/cms/custom-blocks/[id]/edit/page.tsx +66 -0
  92. package/templates/nextblock-template/app/cms/custom-blocks/actions.ts +519 -0
  93. package/templates/nextblock-template/app/cms/custom-blocks/components/BlockComposer.tsx +1522 -0
  94. package/templates/nextblock-template/app/cms/custom-blocks/components/BlocksLibraryTransferControls.tsx +256 -0
  95. package/templates/nextblock-template/app/cms/custom-blocks/components/DBRelationSelect.tsx +384 -0
  96. package/templates/nextblock-template/app/cms/custom-blocks/components/ImageR2Picker.tsx +221 -0
  97. package/templates/nextblock-template/app/cms/custom-blocks/new/page.tsx +12 -0
  98. package/templates/nextblock-template/app/cms/custom-blocks/page.tsx +438 -0
  99. package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -98
  100. package/templates/nextblock-template/app/cms/dashboard/components/DashboardComponents.tsx +200 -0
  101. package/templates/nextblock-template/app/cms/dashboard/page.tsx +182 -154
  102. package/templates/nextblock-template/app/cms/import-export/ContentTransferControls.tsx +391 -0
  103. package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -0
  104. package/templates/nextblock-template/app/cms/layout.tsx +29 -10
  105. package/templates/nextblock-template/app/cms/media/UploadFolderContext.tsx +22 -22
  106. package/templates/nextblock-template/app/cms/media/actions.ts +45 -124
  107. package/templates/nextblock-template/app/cms/media/components/DeleteMediaButtonClient.tsx +1 -1
  108. package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +26 -26
  109. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -64
  110. package/templates/nextblock-template/app/cms/media/components/MediaPickerDialog.tsx +227 -158
  111. package/templates/nextblock-template/app/cms/media/components/MediaUploadForm.tsx +101 -89
  112. package/templates/nextblock-template/app/cms/media/page.tsx +1 -1
  113. package/templates/nextblock-template/app/cms/navigation/components/NavigationItemForm.tsx +2 -2
  114. package/templates/nextblock-template/app/cms/orders/[id]/MarkPaidButton.tsx +44 -0
  115. package/templates/nextblock-template/app/cms/orders/[id]/page.tsx +16 -0
  116. package/templates/nextblock-template/app/cms/orders/actions.ts +201 -0
  117. package/templates/nextblock-template/app/cms/orders/page.tsx +20 -0
  118. package/templates/nextblock-template/app/cms/orders/types.ts +20 -0
  119. package/templates/nextblock-template/app/cms/pages/[id]/edit/EditPageClient.tsx +156 -121
  120. package/templates/nextblock-template/app/cms/pages/[id]/edit/page.tsx +79 -26
  121. package/templates/nextblock-template/app/cms/pages/actions.ts +54 -38
  122. package/templates/nextblock-template/app/cms/pages/components/DeletePageButtonClient.tsx +1 -1
  123. package/templates/nextblock-template/app/cms/pages/components/PageForm.tsx +267 -116
  124. package/templates/nextblock-template/app/cms/pages/page.tsx +25 -18
  125. package/templates/nextblock-template/app/cms/payments/page.tsx +16 -0
  126. package/templates/nextblock-template/app/cms/posts/[id]/edit/page.tsx +132 -90
  127. package/templates/nextblock-template/app/cms/posts/actions.ts +71 -72
  128. package/templates/nextblock-template/app/cms/posts/components/DeletePostButtonClient.tsx +1 -1
  129. package/templates/nextblock-template/app/cms/posts/components/PostForm.tsx +256 -245
  130. package/templates/nextblock-template/app/cms/posts/new/page.tsx +1 -1
  131. package/templates/nextblock-template/app/cms/posts/page.tsx +20 -13
  132. package/templates/nextblock-template/app/cms/products/ClientNotionEditor.tsx +16 -0
  133. package/templates/nextblock-template/app/cms/products/ProductFormClientShell.tsx +56 -0
  134. package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +292 -0
  135. package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -0
  136. package/templates/nextblock-template/app/cms/products/categories/page.tsx +12 -0
  137. package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -0
  138. package/templates/nextblock-template/app/cms/products/new/page.tsx +143 -0
  139. package/templates/nextblock-template/app/cms/products/page.tsx +42 -0
  140. package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -0
  141. package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -0
  142. package/templates/nextblock-template/app/cms/promotions/PromotionsWorkspace.tsx +456 -0
  143. package/templates/nextblock-template/app/cms/promotions/actions.ts +115 -0
  144. package/templates/nextblock-template/app/cms/promotions/page.tsx +31 -0
  145. package/templates/nextblock-template/app/cms/revisions/RevisionHistoryButton.tsx +2 -2
  146. package/templates/nextblock-template/app/cms/revisions/actions.ts +285 -285
  147. package/templates/nextblock-template/app/cms/revisions/service.ts +19 -16
  148. package/templates/nextblock-template/app/cms/revisions/utils.ts +8 -3
  149. package/templates/nextblock-template/app/cms/settings/backup-restore/BackupRestoreWorkspace.tsx +1004 -0
  150. package/templates/nextblock-template/app/cms/settings/backup-restore/page.tsx +29 -0
  151. package/templates/nextblock-template/app/cms/settings/bot-protection/actions.ts +93 -0
  152. package/templates/nextblock-template/app/cms/settings/bot-protection/components/BotProtectionForm.tsx +129 -0
  153. package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -0
  154. package/templates/nextblock-template/app/cms/settings/copyright/actions.ts +1 -1
  155. package/templates/nextblock-template/app/cms/settings/copyright/components/CopyrightForm.tsx +2 -2
  156. package/templates/nextblock-template/app/cms/settings/copyright/page.tsx +1 -1
  157. package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +496 -0
  158. package/templates/nextblock-template/app/cms/settings/cortex-ai/StoredCortexAiSettingsClient.tsx +410 -0
  159. package/templates/nextblock-template/app/cms/settings/cortex-ai/actions.ts +248 -0
  160. package/templates/nextblock-template/app/cms/settings/cortex-ai/page.tsx +80 -0
  161. package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -0
  162. package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -0
  163. package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -0
  164. package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +203 -44
  165. package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -242
  166. package/templates/nextblock-template/app/cms/settings/global-css/actions.ts +65 -0
  167. package/templates/nextblock-template/app/cms/settings/global-css/components/GlobalCssForm.tsx +46 -0
  168. package/templates/nextblock-template/app/cms/settings/global-css/page.tsx +24 -0
  169. package/templates/nextblock-template/app/cms/settings/languages/components/DeleteLanguageButton.tsx +1 -1
  170. package/templates/nextblock-template/app/cms/settings/languages/components/LanguageForm.tsx +2 -2
  171. package/templates/nextblock-template/app/cms/settings/languages/page.tsx +1 -1
  172. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
  173. package/templates/nextblock-template/app/cms/settings/logos/actions.ts +82 -6
  174. package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -0
  175. package/templates/nextblock-template/app/cms/settings/logos/components/DeleteLogoButton.tsx +21 -18
  176. package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +20 -16
  177. package/templates/nextblock-template/app/cms/settings/logos/components/SiteSeoSettingsForm.tsx +133 -0
  178. package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
  179. package/templates/nextblock-template/app/cms/settings/logos/page.tsx +120 -82
  180. package/templates/nextblock-template/app/cms/settings/logos/types.ts +8 -8
  181. package/templates/nextblock-template/app/cms/settings/packages/activation-form.tsx +84 -0
  182. package/templates/nextblock-template/app/cms/settings/packages/package-card.tsx +122 -0
  183. package/templates/nextblock-template/app/cms/settings/packages/page.tsx +49 -0
  184. package/templates/nextblock-template/app/cms/settings/privacy/actions.ts +53 -0
  185. package/templates/nextblock-template/app/cms/settings/privacy/components/PrivacyForm.tsx +196 -0
  186. package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +26 -0
  187. package/templates/nextblock-template/app/cms/settings/security/actions.ts +251 -0
  188. package/templates/nextblock-template/app/cms/settings/security/components/SecurityPanel.tsx +453 -0
  189. package/templates/nextblock-template/app/cms/settings/security/page.tsx +13 -0
  190. package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -0
  191. package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -0
  192. package/templates/nextblock-template/app/cms/users/[id]/edit/page.tsx +28 -23
  193. package/templates/nextblock-template/app/cms/users/actions.ts +105 -40
  194. package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +1 -1
  195. package/templates/nextblock-template/app/cms/users/components/UserForm.tsx +65 -152
  196. package/templates/nextblock-template/app/cms/users/page.tsx +15 -10
  197. package/templates/nextblock-template/app/globals.css +9 -0
  198. package/templates/nextblock-template/app/layout.tsx +372 -120
  199. package/templates/nextblock-template/app/lib/seo.test.ts +52 -0
  200. package/templates/nextblock-template/app/lib/seo.ts +279 -0
  201. package/templates/nextblock-template/app/lib/site-settings.ts +87 -0
  202. package/templates/nextblock-template/app/lib/sitemap-utils.ts +224 -39
  203. package/templates/nextblock-template/app/lib/ucp/protocol.ts +190 -0
  204. package/templates/nextblock-template/app/lib/ucp/server.test.ts +56 -0
  205. package/templates/nextblock-template/app/lib/ucp/server.ts +1914 -0
  206. package/templates/nextblock-template/app/page.tsx +165 -73
  207. package/templates/nextblock-template/app/product/[slug]/page.tsx +433 -0
  208. package/templates/nextblock-template/app/profile/ProfileAccountSidebar.tsx +73 -0
  209. package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -0
  210. package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -0
  211. package/templates/nextblock-template/app/profile/account-data.ts +37 -0
  212. package/templates/nextblock-template/app/profile/account-links.ts +22 -0
  213. package/templates/nextblock-template/app/profile/account-types.ts +11 -0
  214. package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -0
  215. package/templates/nextblock-template/app/profile/orders/[id]/CustomerOrderDetailPageClient.tsx +79 -0
  216. package/templates/nextblock-template/app/profile/orders/[id]/page.tsx +32 -0
  217. package/templates/nextblock-template/app/profile/orders/page.tsx +19 -0
  218. package/templates/nextblock-template/app/profile/page.tsx +51 -0
  219. package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -0
  220. package/templates/nextblock-template/app/profile/password/actions.ts +59 -0
  221. package/templates/nextblock-template/app/profile/password/page.tsx +27 -0
  222. package/templates/nextblock-template/app/providers.tsx +55 -17
  223. package/templates/nextblock-template/app/robots.txt/route.ts +11 -1
  224. package/templates/nextblock-template/app/sitemap.ts +128 -0
  225. package/templates/nextblock-template/app/ucp/v1/carts/[id]/cancel/route.ts +38 -0
  226. package/templates/nextblock-template/app/ucp/v1/carts/[id]/route.ts +68 -0
  227. package/templates/nextblock-template/app/ucp/v1/carts/route.ts +35 -0
  228. package/templates/nextblock-template/app/ucp/v1/catalog/lookup/route.ts +35 -0
  229. package/templates/nextblock-template/app/ucp/v1/catalog/product/route.ts +35 -0
  230. package/templates/nextblock-template/app/ucp/v1/catalog/search/route.ts +34 -0
  231. package/templates/nextblock-template/components/AppShell.tsx +154 -0
  232. package/templates/nextblock-template/components/BlockRenderer.tsx +210 -64
  233. package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -0
  234. package/templates/nextblock-template/components/CartTranslator.tsx +210 -0
  235. package/templates/nextblock-template/components/CurrentContentSetter.tsx +25 -0
  236. package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -0
  237. package/templates/nextblock-template/components/DeferredCartTranslator.tsx +51 -0
  238. package/templates/nextblock-template/components/DeferredGlobalSearch.tsx +68 -0
  239. package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -0
  240. package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -0
  241. package/templates/nextblock-template/components/FeatureImageHero.tsx +47 -0
  242. package/templates/nextblock-template/components/GitHubLoginButton.tsx +36 -0
  243. package/templates/nextblock-template/components/GlobalSearch.tsx +557 -0
  244. package/templates/nextblock-template/components/Header.tsx +49 -41
  245. package/templates/nextblock-template/components/LanguageSwitcher.tsx +55 -32
  246. package/templates/nextblock-template/components/ResponsiveNav.tsx +138 -43
  247. package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -8
  248. package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -55
  249. package/templates/nextblock-template/components/blocks/PostsGridClient.tsx +42 -37
  250. package/templates/nextblock-template/components/blocks/TestimonialBlock.tsx +6 -2
  251. package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -0
  252. package/templates/nextblock-template/components/blocks/publicRendererLoaders.ts +25 -0
  253. package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -84
  254. package/templates/nextblock-template/components/blocks/renderers/CartBlockRenderer.tsx +17 -0
  255. package/templates/nextblock-template/components/blocks/renderers/CheckoutBlockRenderer.tsx +19 -0
  256. package/templates/nextblock-template/components/blocks/renderers/ClientTextBlockRenderer.tsx +262 -8
  257. package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx +22 -0
  258. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +320 -37
  259. package/templates/nextblock-template/components/blocks/renderers/HeadingBlockRenderer.tsx +11 -8
  260. package/templates/nextblock-template/components/blocks/renderers/ImageBlockRenderer.tsx +12 -3
  261. package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +18 -13
  262. package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +90 -0
  263. package/templates/nextblock-template/components/blocks/renderers/ProductGridBlockRenderer.tsx +31 -0
  264. package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +424 -55
  265. package/templates/nextblock-template/components/blocks/renderers/SectionSlider.tsx +137 -0
  266. package/templates/nextblock-template/components/blocks/renderers/TestimonialBlockRenderer.tsx +57 -0
  267. package/templates/nextblock-template/components/blocks/renderers/TextBlockRenderer.tsx +37 -22
  268. package/templates/nextblock-template/components/blocks/renderers/VideoEmbedBlockRenderer.tsx +23 -15
  269. package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +1 -3
  270. package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +1 -3
  271. package/templates/nextblock-template/components/blocks/types.ts +7 -6
  272. package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
  273. package/templates/nextblock-template/components/form-message.tsx +32 -26
  274. package/templates/nextblock-template/components/header-auth.tsx +69 -17
  275. package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +127 -0
  276. package/templates/nextblock-template/components/privacy/ConsentGatedAnalytics.tsx +59 -0
  277. package/templates/nextblock-template/components/renderers/CachedDynamicLayoutEngine.tsx +28 -0
  278. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.test.tsx +166 -0
  279. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.tsx +464 -0
  280. package/templates/nextblock-template/components/theme-switcher.tsx +8 -8
  281. package/templates/nextblock-template/components/visual-editing/DeferredVisualEditing.tsx +21 -0
  282. package/templates/nextblock-template/components/visual-editing/NextblockVisualEditing.tsx +1172 -0
  283. package/templates/nextblock-template/context/AuthContext.tsx +23 -90
  284. package/templates/nextblock-template/context/CurrentContentContext.tsx +10 -4
  285. package/templates/nextblock-template/context/LanguageContext.tsx +16 -16
  286. package/templates/nextblock-template/context/language-rest-client.ts +31 -0
  287. package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -0
  288. package/templates/nextblock-template/docs/02-ECOMMERCE-CAPABILITIES.md +364 -0
  289. package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +202 -0
  290. package/templates/nextblock-template/docs/04-DATABASE-AND-AUTH.md +252 -0
  291. package/templates/nextblock-template/docs/05-DEVELOPER-GUIDE.md +238 -0
  292. package/templates/nextblock-template/docs/06-CLI-AND-SCAFFOLDING.md +125 -0
  293. package/templates/nextblock-template/docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md +146 -0
  294. package/templates/nextblock-template/docs/08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md +1319 -0
  295. package/templates/nextblock-template/docs/09-LIVE-DRAFT-MODE.md +104 -0
  296. package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +222 -0
  297. package/templates/nextblock-template/docs/README.md +34 -0
  298. package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +12507 -0
  299. package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -14
  300. package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -0
  301. package/templates/nextblock-template/index.d.ts +2 -0
  302. package/templates/nextblock-template/lib/ai-block-generation.ts +339 -0
  303. package/templates/nextblock-template/lib/ai-client.ts +247 -0
  304. package/templates/nextblock-template/lib/ai-config.ts +81 -0
  305. package/templates/nextblock-template/lib/ai-cortex-widget-builder.ts +125 -0
  306. package/templates/nextblock-template/lib/ai-global-agent-custom-block-tools.ts +363 -0
  307. package/templates/nextblock-template/lib/ai-global-agent-db-tools.test.ts +405 -0
  308. package/templates/nextblock-template/lib/ai-global-agent-db-tools.ts +1228 -0
  309. package/templates/nextblock-template/lib/ai-global-agent-ecommerce.ts +5 -0
  310. package/templates/nextblock-template/lib/ai-global-agent-tools-stats.test.ts +223 -0
  311. package/templates/nextblock-template/lib/ai-global-agent-tools.test.ts +2183 -0
  312. package/templates/nextblock-template/lib/ai-global-agent-tools.ts +4807 -0
  313. package/templates/nextblock-template/lib/ai-key-crypto.test.ts +70 -0
  314. package/templates/nextblock-template/lib/ai-key-crypto.ts +132 -0
  315. package/templates/nextblock-template/lib/ai-model-catalog.test.ts +49 -0
  316. package/templates/nextblock-template/lib/ai-model-catalog.ts +41 -0
  317. package/templates/nextblock-template/lib/ai-model-registry.test.ts +231 -0
  318. package/templates/nextblock-template/lib/ai-model-registry.ts +522 -0
  319. package/templates/nextblock-template/lib/auth/cookies.ts +47 -0
  320. package/templates/nextblock-template/lib/auth/crypto.ts +42 -0
  321. package/templates/nextblock-template/lib/auth/trustedDevices.ts +92 -0
  322. package/templates/nextblock-template/lib/auth/twoFactor.ts +167 -0
  323. package/templates/nextblock-template/lib/auth-redirects.ts +46 -0
  324. package/templates/nextblock-template/lib/blocks/FeaturedProductBlock.tsx +94 -0
  325. package/templates/nextblock-template/lib/blocks/ProductGridBlock.tsx +137 -0
  326. package/templates/nextblock-template/lib/blocks/README.md +13 -670
  327. package/templates/nextblock-template/lib/blocks/blockRegistry.ts +138 -56
  328. package/templates/nextblock-template/lib/blocks/blockTypes.ts +18 -0
  329. package/templates/nextblock-template/lib/blocks/ecommerce-block-schemas.ts +31 -0
  330. package/templates/nextblock-template/lib/cms-transfer/csv.test.ts +77 -0
  331. package/templates/nextblock-template/lib/cms-transfer/csv.ts +399 -0
  332. package/templates/nextblock-template/lib/cms-transfer/server.ts +2243 -0
  333. package/templates/nextblock-template/lib/cms-transfer/types.ts +145 -0
  334. package/templates/nextblock-template/lib/cortex-widget-registry.test.ts +199 -0
  335. package/templates/nextblock-template/lib/cortex-widget-registry.ts +88 -0
  336. package/templates/nextblock-template/lib/cortex-widget-schema.test.tsx +237 -0
  337. package/templates/nextblock-template/lib/cortex-widget-schema.ts +393 -0
  338. package/templates/nextblock-template/lib/custom-block-definitions.ts +87 -0
  339. package/templates/nextblock-template/lib/custom-block-r2-upload-shared.ts +178 -0
  340. package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +140 -0
  341. package/templates/nextblock-template/lib/custom-block-r2-upload.ts +68 -0
  342. package/templates/nextblock-template/lib/custom-block-relation-registry.ts +256 -0
  343. package/templates/nextblock-template/lib/custom-block-relations.test.ts +227 -0
  344. package/templates/nextblock-template/lib/custom-block-relations.ts +279 -0
  345. package/templates/nextblock-template/lib/custom-block-safelist.ts +14 -0
  346. package/templates/nextblock-template/lib/editor/dynamic-extension-core.test.ts +172 -0
  347. package/templates/nextblock-template/lib/editor/dynamic-extension-core.ts +213 -0
  348. package/templates/nextblock-template/lib/editor/dynamic-extension-loader.ts +22 -0
  349. package/templates/nextblock-template/lib/editor/dynamic-extensions.tsx +193 -0
  350. package/templates/nextblock-template/lib/full-backup/manifest.test.ts +121 -0
  351. package/templates/nextblock-template/lib/full-backup/manifest.ts +206 -0
  352. package/templates/nextblock-template/lib/full-backup/server.ts +743 -0
  353. package/templates/nextblock-template/lib/media/resolveMediaUrl.ts +45 -0
  354. package/templates/nextblock-template/lib/posts/readTime.ts +60 -0
  355. package/templates/nextblock-template/lib/privacy/consent-client.ts +57 -0
  356. package/templates/nextblock-template/lib/privacy/settings.ts +103 -0
  357. package/templates/nextblock-template/lib/privacy/types.ts +67 -0
  358. package/templates/nextblock-template/lib/promotions/server.test.ts +74 -0
  359. package/templates/nextblock-template/lib/promotions/server.ts +741 -0
  360. package/templates/nextblock-template/lib/resolve-block-relations.test.ts +142 -0
  361. package/templates/nextblock-template/lib/resolve-block-relations.ts +255 -0
  362. package/templates/nextblock-template/lib/search/server.ts +585 -0
  363. package/templates/nextblock-template/lib/search/types.ts +27 -0
  364. package/templates/nextblock-template/lib/visual-editing/draft-content.test.ts +105 -0
  365. package/templates/nextblock-template/lib/visual-editing/draft-content.ts +380 -0
  366. package/templates/nextblock-template/lib/visual-editing/draft-route.test.ts +42 -0
  367. package/templates/nextblock-template/lib/visual-editing/draft-route.ts +82 -0
  368. package/templates/nextblock-template/lib/visual-editing/edit-info.test.ts +143 -0
  369. package/templates/nextblock-template/lib/visual-editing/edit-info.ts +94 -0
  370. package/templates/nextblock-template/lib/visual-editing/mutations.ts +190 -0
  371. package/templates/nextblock-template/lib/visual-editing/product-drafts.test.ts +81 -0
  372. package/templates/nextblock-template/lib/visual-editing/product-drafts.ts +511 -0
  373. package/templates/nextblock-template/lib/visual-editing/types.ts +122 -0
  374. package/templates/nextblock-template/lib/zod-config.ts +5 -0
  375. package/templates/nextblock-template/next.config.js +190 -66
  376. package/templates/nextblock-template/package.json +34 -30
  377. package/templates/nextblock-template/proxy.ts +435 -253
  378. package/templates/nextblock-template/public/images/NBcover.webp +0 -0
  379. package/templates/nextblock-template/public/images/cap.webp +0 -0
  380. package/templates/nextblock-template/public/images/commerce-plan.webp +0 -0
  381. package/templates/nextblock-template/public/images/commerce-square.webp +0 -0
  382. package/templates/nextblock-template/public/images/commerce-wide.webp +0 -0
  383. package/templates/nextblock-template/public/images/cortex-ai-square.webp +0 -0
  384. package/templates/nextblock-template/public/images/cortex-ai.webp +0 -0
  385. package/templates/nextblock-template/public/images/extensibility.webp +0 -0
  386. package/templates/nextblock-template/public/images/goals.webp +0 -0
  387. package/templates/nextblock-template/public/images/included.webp +0 -0
  388. package/templates/nextblock-template/public/images/nx-graph.webp +0 -0
  389. package/templates/nextblock-template/public/images/pants.webp +0 -0
  390. package/templates/nextblock-template/public/images/t-shirt.webp +0 -0
  391. package/templates/nextblock-template/scripts/validate-editor-block-schema.ts +112 -0
  392. package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +100 -0
  393. package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +62 -0
  394. package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +537 -0
  395. package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +58 -0
  396. package/templates/nextblock-template/scripts/verify-custom-block-definitions.ts +188 -0
  397. package/templates/nextblock-template/scripts/verify-dynamic-custom-block-extensions.ts +123 -0
  398. package/templates/nextblock-template/scripts/verify-dynamic-layout-engine.tsx +133 -0
  399. package/templates/nextblock-template/scripts/verify-milestone-2-custom-blocks.ts +65 -0
  400. package/templates/nextblock-template/tailwind.config.js +1 -0
  401. package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -0
  402. package/templates/nextblock-template/tools/deploy-supabase.js +69 -71
  403. package/templates/nextblock-template/tsconfig.json +52 -66
  404. package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
  405. package/templates/nextblock-template/types/jsdom.d.ts +6 -0
  406. package/templates/nextblock-template/app/force-styles.tsx +0 -31
  407. package/templates/nextblock-template/app/sitemap.xml/route.ts +0 -63
  408. package/templates/nextblock-template/components/blocks/renderers/HeroBlockRenderer.tsx +0 -273
  409. package/templates/nextblock-template/docs/How to Create a Custom Block.md +0 -149
  410. package/templates/nextblock-template/docs/cms-application-overview.md +0 -56
  411. package/templates/nextblock-template/docs/cms-architecture-overview.md +0 -73
  412. package/templates/nextblock-template/docs/files-structure.md +0 -426
  413. package/templates/nextblock-template/docs/tiptap-bundle-optimization-summary.md +0 -174
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, type ComponentType, Suspense, LazyExoticComponent, useCallback } from "react";
1
+ import { useState, useEffect, type ComponentType, Suspense, LazyExoticComponent, useCallback, type CSSProperties } from "react";
2
2
  import { cn } from "@nextblock-cms/utils";
3
3
  import {
4
4
  Dialog,
@@ -9,7 +9,7 @@ import {
9
9
  DialogDescription,
10
10
  } from "@nextblock-cms/ui";
11
11
  import { Button } from "@nextblock-cms/ui";
12
- import { blockRegistry, type BlockType } from "@/lib/blocks/blockRegistry";
12
+ import { blockRegistry, BlockType } from '../../../../lib/blocks/blockRegistry';
13
13
 
14
14
  // A generic representation of a block object.
15
15
  // The modal primarily needs `type` to get the label and `content` for editing.
@@ -28,6 +28,14 @@ export type BlockEditorProps<T = unknown> = {
28
28
  sectionBackground?: import("@/lib/blocks/blockRegistry").SectionBlockContent['background'];
29
29
  };
30
30
 
31
+ export type EditorSurfaceContext = {
32
+ isDark: boolean;
33
+ style: CSSProperties;
34
+ };
35
+
36
+ export type BlockEditorSaveMode = "manual" | "autosave";
37
+ export type BlockEditorSaveStatus = "idle" | "dirty" | "saving" | "saved" | "error";
38
+
31
39
  type BlockEditorModalProps = {
32
40
  block: Block;
33
41
  isOpen: boolean;
@@ -35,6 +43,22 @@ type BlockEditorModalProps = {
35
43
  onSave: (updatedContent: unknown) => void;
36
44
  EditorComponent: LazyExoticComponent<ComponentType<BlockEditorProps<unknown>>> | ComponentType<BlockEditorProps<unknown>>;
37
45
  sectionBackground?: import("@/lib/blocks/blockRegistry").SectionBlockContent['background'];
46
+ editorSurfaceContext?: EditorSurfaceContext | null;
47
+ titleOverride?: string;
48
+ useContextualSurface?: boolean;
49
+ saveMode?: BlockEditorSaveMode;
50
+ saveStatus?: BlockEditorSaveStatus;
51
+ saveStatusText?: string;
52
+ onAutoChange?: (content: unknown) => void;
53
+ onFlushBeforeClose?: () => Promise<boolean>;
54
+ };
55
+
56
+ const autosaveStatusLabels: Record<BlockEditorSaveStatus, string> = {
57
+ idle: "Saved",
58
+ dirty: "Unsaved",
59
+ saving: "Saving...",
60
+ saved: "Saved",
61
+ error: "Save failed",
38
62
  };
39
63
 
40
64
  export function BlockEditorModal({
@@ -44,42 +68,121 @@ export function BlockEditorModal({
44
68
  onSave,
45
69
  EditorComponent,
46
70
  sectionBackground,
71
+ editorSurfaceContext,
72
+ titleOverride,
73
+ useContextualSurface,
74
+ saveMode = "manual",
75
+ saveStatus = "idle",
76
+ saveStatusText,
77
+ onAutoChange,
78
+ onFlushBeforeClose,
47
79
  }: BlockEditorModalProps) {
48
80
  const [tempContent, setTempContent] = useState(block.content);
49
81
  const [showConfirmClose, setShowConfirmClose] = useState(false);
82
+ const [isFlushingBeforeClose, setIsFlushingBeforeClose] = useState(false);
50
83
  const isValid = true; // Placeholder for future validation logic
84
+ const isAutosaveMode = saveMode === "autosave";
85
+ const [saveShortcutLabel, setSaveShortcutLabel] = useState("CMD+S");
86
+
87
+ useEffect(() => {
88
+ if (typeof window !== "undefined") {
89
+ const isMac = navigator.userAgent.indexOf("Mac") !== -1;
90
+ setSaveShortcutLabel(isMac ? "CMD+S" : "Ctrl+S");
91
+ }
92
+ }, []);
51
93
 
52
94
  useEffect(() => {
53
95
  // When the modal is opened with a new block, reset the temp content
54
96
  if (isOpen) {
55
97
  setTempContent(block.content);
56
98
  setShowConfirmClose(false);
99
+ setIsFlushingBeforeClose(false);
57
100
  }
58
101
  }, [isOpen, block.content]);
59
102
 
60
- const handleSave = () => {
103
+ const handleSave = useCallback(() => {
61
104
  onSave(tempContent);
62
- };
105
+ }, [onSave, tempContent]);
63
106
 
64
107
  const hasUnsavedChanges = useCallback(() => {
65
108
  return JSON.stringify(block.content) !== JSON.stringify(tempContent);
66
109
  }, [block.content, tempContent]);
67
110
 
111
+ const flushAutosave = useCallback(async () => {
112
+ if (!onFlushBeforeClose) {
113
+ return true;
114
+ }
115
+
116
+ setIsFlushingBeforeClose(true);
117
+ try {
118
+ return await onFlushBeforeClose();
119
+ } finally {
120
+ setIsFlushingBeforeClose(false);
121
+ }
122
+ }, [onFlushBeforeClose]);
123
+
68
124
  const handleCloseAttempt = useCallback(() => {
125
+ if (isAutosaveMode) {
126
+ void (async () => {
127
+ const didFlush = await flushAutosave();
128
+ if (didFlush) {
129
+ onClose();
130
+ }
131
+ })();
132
+ return;
133
+ }
134
+
69
135
  if (hasUnsavedChanges()) {
70
136
  setShowConfirmClose(true);
71
137
  } else {
72
138
  onClose();
73
139
  }
74
- }, [hasUnsavedChanges, onClose]);
140
+ }, [flushAutosave, hasUnsavedChanges, isAutosaveMode, onClose]);
141
+
142
+ useEffect(() => {
143
+ if (!isOpen) {
144
+ return;
145
+ }
146
+
147
+ const handleKeyDown = (event: KeyboardEvent) => {
148
+ if (!(event.metaKey || event.ctrlKey) || event.key.toLowerCase() !== "s") {
149
+ return;
150
+ }
151
+
152
+ event.preventDefault();
153
+ if (isAutosaveMode) {
154
+ handleCloseAttempt();
155
+ } else {
156
+ handleSave();
157
+ }
158
+ };
159
+
160
+ window.addEventListener("keydown", handleKeyDown, true);
161
+ return () => {
162
+ window.removeEventListener("keydown", handleKeyDown, true);
163
+ };
164
+ }, [handleCloseAttempt, handleSave, isAutosaveMode, isOpen]);
75
165
 
76
166
  const handleContentChange = (newContent: unknown) => {
77
167
  setTempContent(newContent);
168
+ if (isAutosaveMode) {
169
+ onAutoChange?.(newContent);
170
+ }
78
171
  // Potentially add validation here and set isValid
79
172
  };
80
173
 
81
174
  const blockInfo = blockRegistry[block.type];
82
175
  const displayText = blockInfo?.label || "Block";
176
+ const shouldUseContextualSurface =
177
+ (useContextualSurface ?? true) && block.type === 'text';
178
+ const contextualSurfaceStyle = shouldUseContextualSurface ? editorSurfaceContext?.style : undefined;
179
+ const editorClassName = cn(
180
+ "bg-transparent text-foreground border-none shadow-none focus-within:ring-0 min-h-[60vh]",
181
+ shouldUseContextualSurface &&
182
+ editorSurfaceContext?.isDark &&
183
+ "[&_.ProseMirror]:text-white [&_.ProseMirror.prose]:prose-invert"
184
+ );
185
+ const resolvedSaveStatusText = saveStatusText ?? autosaveStatusLabels[saveStatus];
83
186
 
84
187
  return (
85
188
  <>
@@ -100,15 +203,43 @@ export function BlockEditorModal({
100
203
  }}
101
204
  >
102
205
  {/* Header */}
103
- <div className="flex items-center justify-between p-4 border-b bg-background/95 backdrop-blur z-10">
206
+ <div className="flex items-center justify-between p-4 border-b bg-background/95 text-foreground backdrop-blur z-10">
104
207
  <div className="flex items-center gap-2">
105
- <DialogTitle className="text-lg font-semibold">Edit {displayText}</DialogTitle>
208
+ <DialogTitle className="text-lg font-semibold">
209
+ {titleOverride ?? `Edit ${displayText}`}
210
+ </DialogTitle>
106
211
  </div>
107
212
  <div className="flex items-center gap-2">
108
- <Button variant="ghost" size="sm" onClick={handleCloseAttempt}>Cancel</Button>
109
- <Button onClick={handleSave} disabled={!isValid} size="sm">
110
- Save (CMD+S)
213
+ {isAutosaveMode && (
214
+ <span
215
+ aria-live="polite"
216
+ className={cn(
217
+ "rounded-md px-2 py-1 text-xs font-medium",
218
+ saveStatus === "error"
219
+ ? "bg-destructive/10 text-destructive"
220
+ : saveStatus === "dirty"
221
+ ? "bg-amber-500/10 text-amber-700 dark:text-amber-300"
222
+ : "bg-muted text-muted-foreground",
223
+ "max-w-[360px] truncate"
224
+ )}
225
+ title={resolvedSaveStatusText}
226
+ >
227
+ {resolvedSaveStatusText}
228
+ </span>
229
+ )}
230
+ <Button
231
+ variant={isAutosaveMode ? "default" : "ghost"}
232
+ size="sm"
233
+ onClick={handleCloseAttempt}
234
+ disabled={isAutosaveMode && isFlushingBeforeClose}
235
+ >
236
+ {isAutosaveMode ? "Done" : "Cancel"}
111
237
  </Button>
238
+ {!isAutosaveMode && (
239
+ <Button onClick={handleSave} disabled={!isValid} size="sm">
240
+ Save ({saveShortcutLabel})
241
+ </Button>
242
+ )}
112
243
  </div>
113
244
  </div>
114
245
 
@@ -120,25 +251,27 @@ export function BlockEditorModal({
120
251
  // Conditional Background Logic:
121
252
  // Only apply specific section background to 'text' and 'heading' blocks to allow "Live Preview" of copy.
122
253
  // For complex blocks like Forms, Buttons, etc., keep a neutral background to ensure input field contrast.
123
- (block.type === 'text' || block.type === 'heading') ? (
254
+ shouldUseContextualSurface ? (
124
255
  // If no specific background, use white/dark default
125
256
  (!sectionBackground || sectionBackground.type === 'none') && "bg-muted/10"
126
257
  ) : "bg-muted/10", // Default for non-text blocks
127
258
 
128
259
  // Apply theme classes if present (ONLY for text/heading)
129
- (block.type === 'text' || block.type === 'heading') && sectionBackground?.type === 'theme' && sectionBackground.theme === 'primary' && 'bg-primary text-primary-foreground',
130
- (block.type === 'text' || block.type === 'heading') && sectionBackground?.type === 'theme' && sectionBackground.theme === 'secondary' && 'bg-secondary text-secondary-foreground',
131
- (block.type === 'text' || block.type === 'heading') && sectionBackground?.type === 'theme' && sectionBackground.theme === 'muted' && 'bg-muted text-muted-foreground',
260
+ shouldUseContextualSurface && sectionBackground?.type === 'theme' && sectionBackground.theme === 'primary' && 'bg-primary text-primary-foreground',
261
+ shouldUseContextualSurface && sectionBackground?.type === 'theme' && sectionBackground.theme === 'secondary' && 'bg-secondary text-secondary-foreground',
262
+ shouldUseContextualSurface && sectionBackground?.type === 'theme' && sectionBackground.theme === 'muted' && 'bg-muted text-muted-foreground',
132
263
 
133
264
  // Dark mode prose invert if dark background (approximate check for solid color)
134
- (block.type === 'text' || block.type === 'heading') && (sectionBackground?.type === 'solid' && sectionBackground.solid_color && ['#000', '#111', '#0f172a', 'black'].some(c => sectionBackground.solid_color?.includes(c))) && "[&_.prose]:prose-invert"
265
+ shouldUseContextualSurface && (sectionBackground?.type === 'solid' && sectionBackground.solid_color && ['#000', '#111', '#0f172a', 'black'].some(c => sectionBackground.solid_color?.includes(c))) && "[&_.prose]:prose-invert",
266
+ shouldUseContextualSurface && editorSurfaceContext?.isDark && "[&_.prose]:prose-invert [&_.ProseMirror]:text-white"
135
267
  )}
136
268
  style={{
137
269
  // Only apply custom color/gradient styles for text/heading
138
- backgroundColor: (block.type === 'text' || block.type === 'heading') && sectionBackground?.type === 'solid' ? sectionBackground.solid_color : undefined,
139
- backgroundImage: (block.type === 'text' || block.type === 'heading') && sectionBackground?.type === 'gradient' && sectionBackground.gradient ?
270
+ backgroundColor: shouldUseContextualSurface && sectionBackground?.type === 'solid' ? sectionBackground.solid_color : undefined,
271
+ backgroundImage: shouldUseContextualSurface && sectionBackground?.type === 'gradient' && sectionBackground.gradient ?
140
272
  `${sectionBackground.gradient.type}-gradient(${sectionBackground.gradient.direction}, ${sectionBackground.gradient.stops.map(s => `${s.color} ${s.position}%`).join(', ')})`
141
- : undefined
273
+ : undefined,
274
+ ...contextualSurfaceStyle,
142
275
  }}
143
276
  >
144
277
  <div className="max-w-6xl mx-auto">
@@ -147,7 +280,7 @@ export function BlockEditorModal({
147
280
  block={block}
148
281
  content={tempContent}
149
282
  onChange={handleContentChange}
150
- className="bg-transparent border-none shadow-none focus-within:ring-0 min-h-[60vh]" // Make editor transparent
283
+ className={editorClassName}
151
284
  sectionBackground={sectionBackground} // Pass down if editor supports it
152
285
  />
153
286
  </Suspense>
@@ -1,6 +1,5 @@
1
1
  // app/cms/blocks/components/BlockTypeCard.tsx
2
2
  import React from 'react';
3
- import { Card, CardContent, CardHeader, CardTitle } from "@nextblock-cms/ui";
4
3
  import {
5
4
  FileText,
6
5
  Heading,
@@ -12,6 +11,12 @@ import {
12
11
  LayoutTemplate,
13
12
  NotebookPen,
14
13
  Package,
14
+ ShoppingBag,
15
+ Star,
16
+ ShoppingCart,
17
+ CreditCard,
18
+ Tag,
19
+ MessageSquareQuote,
15
20
  type LucideProps,
16
21
  } from 'lucide-react';
17
22
 
@@ -25,33 +30,36 @@ const iconMap: { [key: string]: React.FC<LucideProps> } = {
25
30
  Columns3,
26
31
  LayoutTemplate,
27
32
  NotebookPen,
33
+ ShoppingBag,
34
+ Star,
35
+ ShoppingCart,
36
+ CreditCard,
37
+ Tag,
38
+ MessageSquareQuote,
28
39
  };
29
40
 
30
41
  interface BlockTypeCardProps {
31
- icon?: string;
42
+ icon?: string | any;
32
43
  name: string;
33
- description?: string;
34
44
  onClick: () => void;
35
45
  }
36
46
 
37
- const BlockTypeCard: React.FC<BlockTypeCardProps> = ({ icon, name, description, onClick }) => {
38
- const IconComponent = icon && iconMap[icon] ? iconMap[icon] : Package;
47
+ const BlockTypeCard: React.FC<BlockTypeCardProps> = ({ icon, name, onClick }) => {
48
+ const IconComponent = typeof icon === 'string'
49
+ ? (iconMap[icon] || Package)
50
+ : (icon || Package);
39
51
 
40
52
  return (
41
- <Card
53
+ <button
54
+ type="button"
42
55
  onClick={onClick}
43
- className="cursor-pointer hover:shadow-lg hover:border-primary transition-all duration-200 ease-in-out"
56
+ className="group w-full flex items-center justify-between p-4 rounded-lg border border-border bg-card text-card-foreground text-left transition-all duration-150 ease-in-out hover:border-primary/50 hover:bg-accent/40 hover:shadow-sm active:scale-[0.98] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 outline-none"
44
57
  >
45
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
46
- <CardTitle className="text-sm font-medium">{name}</CardTitle>
47
- {IconComponent && <IconComponent className="h-4 w-4 text-muted-foreground" />}
48
- </CardHeader>
49
- <CardContent>
50
- <p className="text-xs text-muted-foreground">
51
- {description || 'No description available.'}
52
- </p>
53
- </CardContent>
54
- </Card>
58
+ <span className="text-sm font-medium leading-none truncate pr-2 text-foreground/90 group-hover:text-foreground">
59
+ {name}
60
+ </span>
61
+ <IconComponent className="h-4 w-4 flex-shrink-0 text-muted-foreground group-hover:text-primary transition-colors duration-150" />
62
+ </button>
55
63
  );
56
64
  };
57
65
 
@@ -8,51 +8,233 @@ import {
8
8
  DialogDescription,
9
9
  DialogHeader,
10
10
  DialogTitle,
11
+ Input,
12
+ Tooltip,
13
+ TooltipTrigger,
14
+ TooltipContent,
15
+ TooltipProvider,
11
16
  } from "@nextblock-cms/ui";
12
- import { blockRegistry, BlockType } from '@/lib/blocks/blockRegistry';
17
+ import { Search, X, Package } from 'lucide-react';
18
+ import { blockRegistry, BlockType } from '../../../../lib/blocks/blockRegistry';
13
19
  import BlockTypeCard from './BlockTypeCard';
14
20
 
15
21
  interface BlockTypeSelectorProps {
16
22
  isOpen: boolean;
17
23
  onOpenChange: (isOpen: boolean) => void;
18
- onSelectBlockType: (blockType: BlockType) => void;
24
+ onSelectBlockType: (blockType: any) => void;
19
25
  allowedBlockTypes?: BlockType[];
20
26
  }
21
27
 
28
+ const CATEGORIES = ["All", "Layout", "Content", "Media", "Interactive", "E-commerce", "Custom"];
29
+
30
+ const getBlockCategory = (type: string, isCustomSlug?: boolean): string => {
31
+ if (isCustomSlug) {
32
+ return 'Custom';
33
+ }
34
+ switch (type) {
35
+ case 'section':
36
+ return 'Layout';
37
+ case 'text':
38
+ case 'heading':
39
+ case 'button':
40
+ case 'testimonial':
41
+ return 'Content';
42
+ case 'image':
43
+ case 'video_embed':
44
+ return 'Media';
45
+ case 'form':
46
+ case 'posts_grid':
47
+ return 'Interactive';
48
+ case 'product_grid':
49
+ case 'featured_product':
50
+ case 'cart':
51
+ case 'checkout':
52
+ case 'product_details':
53
+ return 'E-commerce';
54
+ default:
55
+ return 'Content';
56
+ }
57
+ };
58
+
22
59
  const BlockTypeSelector: React.FC<BlockTypeSelectorProps> = ({
23
60
  isOpen,
24
61
  onOpenChange,
25
62
  onSelectBlockType,
26
63
  allowedBlockTypes,
27
64
  }) => {
28
- const handleSelect = (blockType: BlockType) => {
65
+ const [searchQuery, setSearchQuery] = React.useState('');
66
+ const [activeCategory, setActiveCategory] = React.useState('All');
67
+ const [customDefs, setCustomDefs] = React.useState<any[]>([]);
68
+
69
+ // Reset state and fetch custom blocks when modal is opened
70
+ React.useEffect(() => {
71
+ if (isOpen) {
72
+ setSearchQuery('');
73
+ setActiveCategory('All');
74
+
75
+ fetch("/api/custom-blocks/editor-definitions")
76
+ .then((res) => {
77
+ if (res.ok) {
78
+ return res.json();
79
+ }
80
+ throw new Error("Failed to fetch definitions");
81
+ })
82
+ .then((data) => {
83
+ if (data && data.definitions) {
84
+ setCustomDefs(data.definitions);
85
+ }
86
+ })
87
+ .catch((err) => console.error("Error loading custom blocks for selector:", err));
88
+ }
89
+ }, [isOpen]);
90
+
91
+ const handleSelect = (blockType: string) => {
29
92
  onSelectBlockType(blockType);
30
93
  onOpenChange(false);
31
94
  };
32
95
 
33
- const blockDefs = Object.values(blockRegistry).filter(
34
- (blockDef) => !allowedBlockTypes || allowedBlockTypes.includes(blockDef.type)
35
- );
96
+ const blockDefs = React.useMemo(() => {
97
+ const coreDefs = Object.values(blockRegistry).filter(
98
+ (blockDef) =>
99
+ !allowedBlockTypes || allowedBlockTypes.includes(blockDef.type)
100
+ );
101
+
102
+ const mappedCustomDefs = customDefs.map((def) => ({
103
+ type: def.slug,
104
+ label: def.name,
105
+ icon: "LayoutTemplate",
106
+ initialContent: def.fields.reduce((acc: any, field: any) => {
107
+ acc[field.key] = field.type === "image_r2" ? null : field.type === "db_relation" ? (field.multiple ? [] : null) : "";
108
+ return acc;
109
+ }, {}),
110
+ documentation: {
111
+ description: def.description || "Custom user-defined block layout component.",
112
+ useCases: ["Custom page components"],
113
+ },
114
+ }));
115
+
116
+ return [...coreDefs, ...mappedCustomDefs];
117
+ }, [allowedBlockTypes, customDefs]);
118
+
119
+ // Memoized filter and search results to prevent re-calculations during key strokes
120
+ const filteredBlockDefs = React.useMemo(() => {
121
+ return blockDefs
122
+ .filter((blockDef) => {
123
+ const isCustom = customDefs.some((d) => d.slug === blockDef.type);
124
+ const category = getBlockCategory(blockDef.type, isCustom);
125
+
126
+ // Category Filter
127
+ if (activeCategory !== 'All' && category !== activeCategory) {
128
+ return false;
129
+ }
130
+ // Search Filter
131
+ if (searchQuery.trim() !== '') {
132
+ const query = searchQuery.toLowerCase();
133
+ const matchesLabel = blockDef.label.toLowerCase().includes(query);
134
+ const matchesDesc = blockDef.documentation?.description?.toLowerCase().includes(query) || false;
135
+ return matchesLabel || matchesDesc;
136
+ }
137
+ return true;
138
+ })
139
+ .sort((a, b) => a.label.localeCompare(b.label));
140
+ }, [blockDefs, searchQuery, activeCategory, customDefs]);
36
141
 
37
142
  return (
38
143
  <Dialog open={isOpen} onOpenChange={onOpenChange}>
39
- <DialogContent className="sm:max-w-[625px]">
40
- <DialogHeader>
144
+ <DialogContent className="sm:max-w-[700px] max-h-[85vh] flex flex-col gap-0 p-6 overflow-hidden">
145
+ <DialogHeader className="pb-4">
41
146
  <DialogTitle>Add a New Block</DialogTitle>
42
147
  <DialogDescription>
43
148
  Choose a block type from the options below to add it to the page.
44
149
  </DialogDescription>
45
150
  </DialogHeader>
46
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 py-4">
47
- {blockDefs.map((blockDef) => (
48
- <BlockTypeCard
49
- key={blockDef.type}
50
- name={blockDef.label}
51
- description={blockDef.documentation?.description}
52
- icon={blockDef.icon}
53
- onClick={() => handleSelect(blockDef.type)}
54
- />
55
- ))}
151
+
152
+ <div className="flex flex-col gap-4 py-2 overflow-hidden flex-grow">
153
+ {/* Search and Filters Header */}
154
+ <div className="space-y-3 flex-shrink-0">
155
+ <div className="relative flex items-center">
156
+ <Search className="absolute left-3 h-4 w-4 text-muted-foreground pointer-events-none" />
157
+ <Input
158
+ type="text"
159
+ placeholder="Search block types..."
160
+ value={searchQuery}
161
+ onChange={(e) => setSearchQuery(e.target.value)}
162
+ className="pl-9 pr-8 h-9 text-sm bg-muted/40 hover:bg-muted/60 focus:bg-background transition-colors duration-150 rounded-lg border-border"
163
+ />
164
+ {searchQuery && (
165
+ <button
166
+ type="button"
167
+ onClick={() => setSearchQuery('')}
168
+ className="absolute right-3 p-0.5 rounded-full hover:bg-muted text-muted-foreground hover:text-foreground transition-colors duration-150"
169
+ >
170
+ <X className="h-3.5 w-3.5" />
171
+ </button>
172
+ )}
173
+ </div>
174
+
175
+ {/* Category Filter Tabs */}
176
+ <div className="flex flex-wrap gap-1.5 pb-3 border-b border-border">
177
+ {CATEGORIES.map((category) => (
178
+ <button
179
+ key={category}
180
+ type="button"
181
+ onClick={() => setActiveCategory(category)}
182
+ className={`text-xs py-1 px-3 rounded-full transition-all duration-150 border outline-none ${
183
+ activeCategory === category
184
+ ? 'bg-primary text-primary-foreground border-primary font-medium shadow-sm'
185
+ : 'bg-secondary/40 hover:bg-secondary/80 border-transparent text-muted-foreground'
186
+ }`}
187
+ >
188
+ {category}
189
+ </button>
190
+ ))}
191
+ </div>
192
+ </div>
193
+
194
+ {/* Grid Area */}
195
+ <div className="grid grid-cols-2 sm:grid-cols-3 gap-2.5 overflow-y-auto pr-1 py-1 flex-grow">
196
+ <TooltipProvider delayDuration={200}>
197
+ {filteredBlockDefs.map((blockDef) => (
198
+ <Tooltip key={blockDef.type}>
199
+ <TooltipTrigger asChild>
200
+ <div>
201
+ <BlockTypeCard
202
+ name={blockDef.label}
203
+ icon={blockDef.icon}
204
+ onClick={() => handleSelect(blockDef.type)}
205
+ />
206
+ </div>
207
+ </TooltipTrigger>
208
+ <TooltipContent
209
+ side="top"
210
+ align="center"
211
+ className="max-w-[280px] p-3 text-xs bg-popover border border-border shadow-lg rounded-md z-[60]"
212
+ >
213
+ <div className="space-y-1">
214
+ <div className="font-semibold text-foreground">{blockDef.label}</div>
215
+ <div className="text-muted-foreground leading-normal">
216
+ {blockDef.documentation?.description || 'No description available.'}
217
+ </div>
218
+ {blockDef.documentation?.useCases && blockDef.documentation.useCases.length > 0 && (
219
+ <div className="pt-1.5 border-t border-border mt-1.5 text-[10px] text-muted-foreground/80">
220
+ <span className="font-semibold text-foreground/90">Use cases: </span>
221
+ {blockDef.documentation.useCases.join(', ')}
222
+ </div>
223
+ )}
224
+ </div>
225
+ </TooltipContent>
226
+ </Tooltip>
227
+ ))}
228
+ </TooltipProvider>
229
+
230
+ {filteredBlockDefs.length === 0 && (
231
+ <div className="col-span-full py-12 text-center flex flex-col items-center justify-center text-muted-foreground">
232
+ <Package className="h-8 w-8 mb-2 opacity-30 animate-pulse" />
233
+ <p className="text-sm font-medium">No block types found</p>
234
+ <p className="text-xs">Adjust your search query or select another category</p>
235
+ </div>
236
+ )}
237
+ </div>
56
238
  </div>
57
239
  </DialogContent>
58
240
  </Dialog>