create-nextblock 0.16.1 → 0.16.2

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 (258) hide show
  1. package/docker-template/.dockerignore +1 -1
  2. package/libs/db/tsconfig.lib.json +3 -3
  3. package/libs/editor/tsconfig.lib.json +3 -3
  4. package/libs/ui/tsconfig.lib.json +3 -3
  5. package/libs/utils/tsconfig.json +3 -3
  6. package/package.json +50 -50
  7. package/project.json +19 -19
  8. package/templates/nextblock-template/.dockerignore +1 -1
  9. package/templates/nextblock-template/.swcrc +30 -30
  10. package/templates/nextblock-template/AGENTS.md +9 -9
  11. package/templates/nextblock-template/CLAUDE.md +1 -1
  12. package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -9
  13. package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -27
  14. package/templates/nextblock-template/app/.well-known/ucp/route.ts +16 -16
  15. package/templates/nextblock-template/app/ToasterProvider.tsx +26 -26
  16. package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
  17. package/templates/nextblock-template/app/actions/contactSellerActions.test.ts +280 -280
  18. package/templates/nextblock-template/app/actions/contactSellerActions.ts +222 -222
  19. package/templates/nextblock-template/app/actions/email-retry.test.ts +62 -62
  20. package/templates/nextblock-template/app/actions/email.ts +241 -241
  21. package/templates/nextblock-template/app/actions/formActions.ts +245 -245
  22. package/templates/nextblock-template/app/actions/interactions.ts +489 -489
  23. package/templates/nextblock-template/app/actions/threadActions.ts +166 -166
  24. package/templates/nextblock-template/app/actions/visibilityActions.ts +210 -210
  25. package/templates/nextblock-template/app/api/ai/seo/alt-text/route.ts +221 -221
  26. package/templates/nextblock-template/app/api/ai/seo/metadata/route.ts +186 -186
  27. package/templates/nextblock-template/app/api/checkout/freemius/sync/route.ts +29 -29
  28. package/templates/nextblock-template/app/api/checkout/route.ts +162 -162
  29. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +3092 -3092
  30. package/templates/nextblock-template/app/api/mcp/route.ts +415 -415
  31. package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
  32. package/templates/nextblock-template/app/api/search/route.ts +43 -43
  33. package/templates/nextblock-template/app/api/view/route.ts +114 -114
  34. package/templates/nextblock-template/app/api/visual-editing/block-draft/route.ts +47 -47
  35. package/templates/nextblock-template/app/api/visual-editing/product-draft/route.ts +47 -47
  36. package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
  37. package/templates/nextblock-template/app/cart/page.tsx +7 -7
  38. package/templates/nextblock-template/app/checkout/UcpCartHydrator.tsx +20 -20
  39. package/templates/nextblock-template/app/checkout/page.tsx +57 -57
  40. package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +558 -558
  41. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +241 -241
  42. package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
  43. package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +304 -304
  44. package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +406 -406
  45. package/templates/nextblock-template/app/cms/components/ContactReminderBanner.tsx +75 -75
  46. package/templates/nextblock-template/app/cms/components/CortexAiActiveContext.tsx +23 -23
  47. package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
  48. package/templates/nextblock-template/app/cms/components/FeatureImageField.tsx +254 -254
  49. package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
  50. package/templates/nextblock-template/app/cms/components/PaymentsReminderBanner.tsx +58 -58
  51. package/templates/nextblock-template/app/cms/components/VisibilityBadge.tsx +62 -62
  52. package/templates/nextblock-template/app/cms/components/VisibilityControl.tsx +542 -542
  53. package/templates/nextblock-template/app/cms/coupons/[id]/edit/page.tsx +16 -16
  54. package/templates/nextblock-template/app/cms/coupons/page.tsx +16 -16
  55. package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
  56. package/templates/nextblock-template/app/cms/dashboard/components/DashboardComponents.tsx +200 -200
  57. package/templates/nextblock-template/app/cms/inquiries/actions.ts +66 -66
  58. package/templates/nextblock-template/app/cms/inquiries/page.tsx +12 -12
  59. package/templates/nextblock-template/app/cms/interactions/page.tsx +12 -12
  60. package/templates/nextblock-template/app/cms/layout.tsx +101 -101
  61. package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
  62. package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
  63. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
  64. package/templates/nextblock-template/app/cms/messages/MessagesClient.tsx +661 -661
  65. package/templates/nextblock-template/app/cms/messages/actions.ts +404 -404
  66. package/templates/nextblock-template/app/cms/messages/loadInbox.ts +333 -333
  67. package/templates/nextblock-template/app/cms/messages/page.tsx +87 -87
  68. package/templates/nextblock-template/app/cms/messages/require-admin.ts +37 -37
  69. package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
  70. package/templates/nextblock-template/app/cms/pages/components/PageForm.tsx +649 -649
  71. package/templates/nextblock-template/app/cms/posts/components/PostForm.tsx +618 -618
  72. package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +370 -370
  73. package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
  74. package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
  75. package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
  76. package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
  77. package/templates/nextblock-template/app/cms/revisions/actions.ts +332 -332
  78. package/templates/nextblock-template/app/cms/revisions/service.test.ts +498 -498
  79. package/templates/nextblock-template/app/cms/revisions/service.ts +569 -569
  80. package/templates/nextblock-template/app/cms/revisions/utils.ts +304 -304
  81. package/templates/nextblock-template/app/cms/settings/cortex-ai/CortexAiSettingsClient.tsx +948 -948
  82. package/templates/nextblock-template/app/cms/settings/cortex-ai/McpServerSettingsCard.tsx +628 -628
  83. package/templates/nextblock-template/app/cms/settings/cortex-ai/mcp-actions.ts +230 -230
  84. package/templates/nextblock-template/app/cms/settings/cortex-ai/require-admin.ts +34 -34
  85. package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
  86. package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
  87. package/templates/nextblock-template/app/cms/settings/email/components/EmailForm.tsx +227 -227
  88. package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
  89. package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +276 -276
  90. package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
  91. package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeEditor.tsx +382 -382
  92. package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeManager.tsx +267 -267
  93. package/templates/nextblock-template/app/cms/settings/global-css/page.tsx +40 -40
  94. package/templates/nextblock-template/app/cms/settings/global-css/theme-actions.ts +259 -259
  95. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
  96. package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
  97. package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
  98. package/templates/nextblock-template/app/cms/settings/seo/RedirectsCard.tsx +514 -514
  99. package/templates/nextblock-template/app/cms/settings/seo/RobotsCard.tsx +529 -529
  100. package/templates/nextblock-template/app/cms/settings/seo/SeoSettingsClient.tsx +57 -57
  101. package/templates/nextblock-template/app/cms/settings/seo/actions.ts +448 -448
  102. package/templates/nextblock-template/app/cms/settings/seo/mappers.ts +93 -93
  103. package/templates/nextblock-template/app/cms/settings/seo/page.tsx +46 -46
  104. package/templates/nextblock-template/app/cms/settings/seo/require-admin.ts +47 -47
  105. package/templates/nextblock-template/app/cms/settings/site-scripts/page.tsx +51 -51
  106. package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
  107. package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
  108. package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
  109. package/templates/nextblock-template/app/layout.tsx +671 -671
  110. package/templates/nextblock-template/app/lib/seo.ts +319 -319
  111. package/templates/nextblock-template/app/lib/ucp/protocol.ts +190 -190
  112. package/templates/nextblock-template/app/lib/ucp/server.test.ts +56 -56
  113. package/templates/nextblock-template/app/product/[slug]/page.tsx +502 -502
  114. package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
  115. package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
  116. package/templates/nextblock-template/app/profile/account-links.ts +22 -22
  117. package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
  118. package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
  119. package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
  120. package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
  121. package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
  122. package/templates/nextblock-template/app/providers.tsx +96 -96
  123. package/templates/nextblock-template/app/robots.ts +123 -123
  124. package/templates/nextblock-template/app/thread/ThreadView.tsx +164 -164
  125. package/templates/nextblock-template/app/thread/[token]/route.ts +57 -57
  126. package/templates/nextblock-template/app/thread/layout.tsx +15 -15
  127. package/templates/nextblock-template/app/thread/page.tsx +98 -98
  128. package/templates/nextblock-template/app/ucp/v1/carts/[id]/cancel/route.ts +38 -38
  129. package/templates/nextblock-template/app/ucp/v1/carts/[id]/route.ts +68 -68
  130. package/templates/nextblock-template/app/ucp/v1/carts/route.ts +35 -35
  131. package/templates/nextblock-template/app/ucp/v1/catalog/lookup/route.ts +35 -35
  132. package/templates/nextblock-template/app/ucp/v1/catalog/product/route.ts +35 -35
  133. package/templates/nextblock-template/app/ucp/v1/catalog/search/route.ts +34 -34
  134. package/templates/nextblock-template/components/BlockRenderer.tsx +312 -312
  135. package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
  136. package/templates/nextblock-template/components/CartTranslator.tsx +210 -210
  137. package/templates/nextblock-template/components/ContactSellerSection.tsx +188 -188
  138. package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
  139. package/templates/nextblock-template/components/DeferredCartTranslator.tsx +51 -51
  140. package/templates/nextblock-template/components/DeferredGlobalSearch.tsx +68 -68
  141. package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
  142. package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
  143. package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
  144. package/templates/nextblock-template/components/GlobalSearch.tsx +557 -557
  145. package/templates/nextblock-template/components/Header.tsx +38 -38
  146. package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
  147. package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
  148. package/templates/nextblock-template/components/PostCommentsSection.tsx +378 -378
  149. package/templates/nextblock-template/components/ProductReviewsSection.tsx +426 -426
  150. package/templates/nextblock-template/components/SiteScripts.tsx +56 -56
  151. package/templates/nextblock-template/components/StaffReplies.tsx +102 -102
  152. package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
  153. package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
  154. package/templates/nextblock-template/components/blocks/PostsGridClient.tsx +48 -48
  155. package/templates/nextblock-template/components/blocks/TestimonialBlock.tsx +9 -9
  156. package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
  157. package/templates/nextblock-template/components/blocks/publicRendererLoaders.ts +25 -25
  158. package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -92
  159. package/templates/nextblock-template/components/blocks/renderers/CartBlockRenderer.tsx +18 -18
  160. package/templates/nextblock-template/components/blocks/renderers/CheckoutBlockRenderer.tsx +20 -20
  161. package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx +25 -25
  162. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +385 -385
  163. package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +24 -24
  164. package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +157 -157
  165. package/templates/nextblock-template/components/blocks/renderers/ProductGridBlockRenderer.tsx +34 -34
  166. package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +612 -612
  167. package/templates/nextblock-template/components/blocks/renderers/TestimonialBlockRenderer.tsx +57 -57
  168. package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +2 -2
  169. package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +2 -2
  170. package/templates/nextblock-template/components/blocks/types.ts +7 -7
  171. package/templates/nextblock-template/components/commerce/PaymentReadinessBoundary.tsx +32 -32
  172. package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
  173. package/templates/nextblock-template/components/form-message.tsx +32 -32
  174. package/templates/nextblock-template/components/seo/GenerateMetaButton.tsx +137 -137
  175. package/templates/nextblock-template/components/seo/PageSeoAuditSection.tsx +244 -244
  176. package/templates/nextblock-template/components/seo/SeoAuditPanel.tsx +749 -749
  177. package/templates/nextblock-template/components/seo/SeoIssueList.tsx +195 -195
  178. package/templates/nextblock-template/components/seo/SeoScoreDial.tsx +144 -144
  179. package/templates/nextblock-template/components/seo/SocialPreview.tsx +243 -243
  180. package/templates/nextblock-template/components/seo/SocialPreviewDialog.tsx +110 -110
  181. package/templates/nextblock-template/components/submit-button.tsx +23 -23
  182. package/templates/nextblock-template/components/theme-icon.tsx +78 -78
  183. package/templates/nextblock-template/components/theme-switcher.tsx +85 -85
  184. package/templates/nextblock-template/context/AuthContext.tsx +23 -23
  185. package/templates/nextblock-template/context/ThemeCatalogContext.tsx +44 -44
  186. package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
  187. package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +77 -77
  188. package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +372 -372
  189. package/templates/nextblock-template/docs/14-MESSAGES-INBOX.md +309 -309
  190. package/templates/nextblock-template/docs/README.md +42 -42
  191. package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +12506 -12506
  192. package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
  193. package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
  194. package/templates/nextblock-template/index.d.ts +7 -7
  195. package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
  196. package/templates/nextblock-template/lib/blocks/blockColors.test.ts +134 -134
  197. package/templates/nextblock-template/lib/blocks/blockColors.ts +175 -175
  198. package/templates/nextblock-template/lib/blocks/blockRegistry.ts +761 -761
  199. package/templates/nextblock-template/lib/blocks/inlineScriptNonce.ts +20 -20
  200. package/templates/nextblock-template/lib/cms/contact-reminder.ts +64 -64
  201. package/templates/nextblock-template/lib/cms/payments-reminder.test.ts +135 -0
  202. package/templates/nextblock-template/lib/cms/payments-reminder.ts +30 -23
  203. package/templates/nextblock-template/lib/cms/unread-messages.ts +42 -42
  204. package/templates/nextblock-template/lib/commerce/seller-contact.ts +162 -162
  205. package/templates/nextblock-template/lib/config/email-settings.ts +323 -323
  206. package/templates/nextblock-template/lib/config/email-tls.test.ts +57 -57
  207. package/templates/nextblock-template/lib/cortex-ai/alt-text-request.ts +86 -86
  208. package/templates/nextblock-template/lib/cortex-ai/sandbox-headers.ts +60 -60
  209. package/templates/nextblock-template/lib/email/placeholder-address.test.ts +59 -59
  210. package/templates/nextblock-template/lib/email/placeholder-address.ts +39 -39
  211. package/templates/nextblock-template/lib/messages/thread-reference.test.ts +70 -70
  212. package/templates/nextblock-template/lib/messages/thread-token.test.ts +93 -93
  213. package/templates/nextblock-template/lib/messages/thread-token.ts +157 -157
  214. package/templates/nextblock-template/lib/messages/threads.ts +579 -579
  215. package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
  216. package/templates/nextblock-template/lib/publishing/viewUrl.ts +26 -26
  217. package/templates/nextblock-template/lib/search/types.ts +27 -27
  218. package/templates/nextblock-template/lib/seo/alt-text-write-back.test.ts +154 -154
  219. package/templates/nextblock-template/lib/seo/alt-text-write-back.ts +109 -109
  220. package/templates/nextblock-template/lib/seo/block-content.ts +123 -123
  221. package/templates/nextblock-template/lib/seo/fix-prompts.test.ts +242 -242
  222. package/templates/nextblock-template/lib/seo/fix-prompts.ts +204 -204
  223. package/templates/nextblock-template/lib/seo/page-audit-context.tsx +140 -140
  224. package/templates/nextblock-template/lib/seo/page-document.test.ts +350 -350
  225. package/templates/nextblock-template/lib/seo/page-document.ts +412 -412
  226. package/templates/nextblock-template/lib/seo/redirect-store.test.ts +479 -479
  227. package/templates/nextblock-template/lib/seo/redirect-store.ts +466 -466
  228. package/templates/nextblock-template/lib/seo/robots-settings-signature.test.ts +102 -102
  229. package/templates/nextblock-template/lib/seo/robots-settings-signature.ts +41 -41
  230. package/templates/nextblock-template/lib/seo/robots-txt.test.ts +370 -370
  231. package/templates/nextblock-template/lib/seo/robots-txt.ts +510 -510
  232. package/templates/nextblock-template/lib/setup/migrations-bundle.ts +177 -177
  233. package/templates/nextblock-template/lib/site-scripts/revisions.ts +71 -71
  234. package/templates/nextblock-template/lib/site-scripts/types.ts +46 -46
  235. package/templates/nextblock-template/lib/site-url.test.ts +89 -89
  236. package/templates/nextblock-template/lib/site-url.ts +102 -102
  237. package/templates/nextblock-template/lib/themes/buildThemeCss.ts +124 -124
  238. package/templates/nextblock-template/lib/themes/tokenColor.ts +31 -31
  239. package/templates/nextblock-template/lib/themes/tokens.ts +143 -143
  240. package/templates/nextblock-template/lib/visual-editing/draft-content.test.ts +105 -105
  241. package/templates/nextblock-template/lib/visual-editing/draft-route.test.ts +42 -42
  242. package/templates/nextblock-template/lib/visual-editing/edit-info.test.ts +143 -143
  243. package/templates/nextblock-template/lib/visual-editing/edit-info.ts +94 -94
  244. package/templates/nextblock-template/lib/visual-editing/product-drafts.test.ts +81 -81
  245. package/templates/nextblock-template/lib/zod-config.ts +5 -5
  246. package/templates/nextblock-template/next-env.d.ts +1 -2
  247. package/templates/nextblock-template/package.json +1 -1
  248. package/templates/nextblock-template/postcss.config.js +6 -6
  249. package/templates/nextblock-template/scripts/backup.js +115 -115
  250. package/templates/nextblock-template/scripts/restore.js +385 -385
  251. package/templates/nextblock-template/scripts/validate-editor-block-schema.ts +112 -112
  252. package/templates/nextblock-template/tailwind.config.js +25 -25
  253. package/templates/nextblock-template/tools/build-migrate.mjs +102 -102
  254. package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
  255. package/templates/nextblock-template/tools/deploy-supabase.js +159 -159
  256. package/templates/nextblock-template/tools/lib/migrate-core.mjs +569 -569
  257. package/templates/nextblock-template/tools/update.mjs +1303 -1303
  258. package/tsconfig.base.json +3 -3
@@ -1,5 +1,5 @@
1
- import { useState, useEffect, type ComponentType, Suspense, LazyExoticComponent, useCallback, type CSSProperties } from "react";
2
- import { cn } from "@nextblock-cms/utils";
1
+ import { useState, useEffect, type ComponentType, Suspense, LazyExoticComponent, useCallback, type CSSProperties } from "react";
2
+ import { cn } from "@nextblock-cms/utils";
3
3
  import {
4
4
  Dialog,
5
5
  DialogContent,
@@ -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, 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.
@@ -20,171 +20,171 @@ export type Block<T = unknown> = {
20
20
  };
21
21
 
22
22
  // Props that every block editor component must accept.
23
- export type BlockEditorProps<T = unknown> = {
24
- block: Block<T>;
25
- content: T;
26
- onChange: (newContent: T) => void;
27
- className?: string; // Added for editor component styling
28
- sectionBackground?: import("@/lib/blocks/blockRegistry").SectionBlockContent['background'];
29
- };
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
-
39
- type BlockEditorModalProps = {
40
- block: Block;
41
- isOpen: boolean;
42
- onClose: () => void;
43
- onSave: (updatedContent: unknown) => void;
44
- EditorComponent: LazyExoticComponent<ComponentType<BlockEditorProps<unknown>>> | ComponentType<BlockEditorProps<unknown>>;
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",
62
- };
63
-
64
- export function BlockEditorModal({
65
- block,
66
- isOpen,
67
- onClose,
68
- onSave,
69
- EditorComponent,
70
- sectionBackground,
71
- editorSurfaceContext,
72
- titleOverride,
73
- useContextualSurface,
74
- saveMode = "manual",
75
- saveStatus = "idle",
76
- saveStatusText,
77
- onAutoChange,
78
- onFlushBeforeClose,
79
- }: BlockEditorModalProps) {
80
- const [tempContent, setTempContent] = useState(block.content);
81
- const [showConfirmClose, setShowConfirmClose] = useState(false);
82
- const [isFlushingBeforeClose, setIsFlushingBeforeClose] = useState(false);
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
- }, []);
93
-
94
- useEffect(() => {
95
- // When the modal is opened with a new block, reset the temp content
96
- if (isOpen) {
97
- setTempContent(block.content);
98
- setShowConfirmClose(false);
99
- setIsFlushingBeforeClose(false);
100
- }
101
- }, [isOpen, block.content]);
102
-
103
- const handleSave = useCallback(() => {
104
- onSave(tempContent);
105
- }, [onSave, tempContent]);
106
-
107
- const hasUnsavedChanges = useCallback(() => {
108
- return JSON.stringify(block.content) !== JSON.stringify(tempContent);
109
- }, [block.content, tempContent]);
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
-
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
-
135
- if (hasUnsavedChanges()) {
136
- setShowConfirmClose(true);
137
- } else {
138
- onClose();
139
- }
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]);
165
-
166
- const handleContentChange = (newContent: unknown) => {
167
- setTempContent(newContent);
168
- if (isAutosaveMode) {
169
- onAutoChange?.(newContent);
170
- }
171
- // Potentially add validation here and set isValid
172
- };
173
-
174
- const blockInfo = blockRegistry[block.type];
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];
186
-
187
- return (
23
+ export type BlockEditorProps<T = unknown> = {
24
+ block: Block<T>;
25
+ content: T;
26
+ onChange: (newContent: T) => void;
27
+ className?: string; // Added for editor component styling
28
+ sectionBackground?: import("@/lib/blocks/blockRegistry").SectionBlockContent['background'];
29
+ };
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
+
39
+ type BlockEditorModalProps = {
40
+ block: Block;
41
+ isOpen: boolean;
42
+ onClose: () => void;
43
+ onSave: (updatedContent: unknown) => void;
44
+ EditorComponent: LazyExoticComponent<ComponentType<BlockEditorProps<unknown>>> | ComponentType<BlockEditorProps<unknown>>;
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",
62
+ };
63
+
64
+ export function BlockEditorModal({
65
+ block,
66
+ isOpen,
67
+ onClose,
68
+ onSave,
69
+ EditorComponent,
70
+ sectionBackground,
71
+ editorSurfaceContext,
72
+ titleOverride,
73
+ useContextualSurface,
74
+ saveMode = "manual",
75
+ saveStatus = "idle",
76
+ saveStatusText,
77
+ onAutoChange,
78
+ onFlushBeforeClose,
79
+ }: BlockEditorModalProps) {
80
+ const [tempContent, setTempContent] = useState(block.content);
81
+ const [showConfirmClose, setShowConfirmClose] = useState(false);
82
+ const [isFlushingBeforeClose, setIsFlushingBeforeClose] = useState(false);
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
+ }, []);
93
+
94
+ useEffect(() => {
95
+ // When the modal is opened with a new block, reset the temp content
96
+ if (isOpen) {
97
+ setTempContent(block.content);
98
+ setShowConfirmClose(false);
99
+ setIsFlushingBeforeClose(false);
100
+ }
101
+ }, [isOpen, block.content]);
102
+
103
+ const handleSave = useCallback(() => {
104
+ onSave(tempContent);
105
+ }, [onSave, tempContent]);
106
+
107
+ const hasUnsavedChanges = useCallback(() => {
108
+ return JSON.stringify(block.content) !== JSON.stringify(tempContent);
109
+ }, [block.content, tempContent]);
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
+
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
+
135
+ if (hasUnsavedChanges()) {
136
+ setShowConfirmClose(true);
137
+ } else {
138
+ onClose();
139
+ }
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]);
165
+
166
+ const handleContentChange = (newContent: unknown) => {
167
+ setTempContent(newContent);
168
+ if (isAutosaveMode) {
169
+ onAutoChange?.(newContent);
170
+ }
171
+ // Potentially add validation here and set isValid
172
+ };
173
+
174
+ const blockInfo = blockRegistry[block.type];
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];
186
+
187
+ return (
188
188
  <>
189
189
  <Dialog open={isOpen} onOpenChange={(open) => {
190
190
  if (!open) {
@@ -192,56 +192,56 @@ export function BlockEditorModal({
192
192
  }
193
193
  }}>
194
194
  <DialogContent
195
- className="max-w-6xl h-[90vh] flex flex-col p-0 gap-0 overflow-hidden"
196
- onInteractOutside={(e) => {
197
- e.preventDefault();
198
- handleCloseAttempt();
199
- }}
195
+ className="max-w-6xl h-[90vh] flex flex-col p-0 gap-0 overflow-hidden"
196
+ onInteractOutside={(e) => {
197
+ e.preventDefault();
198
+ handleCloseAttempt();
199
+ }}
200
200
  onEscapeKeyDown={(e) => {
201
201
  e.preventDefault();
202
202
  handleCloseAttempt();
203
203
  }}
204
204
  >
205
205
  {/* Header */}
206
- <div className="flex items-center justify-between p-4 border-b bg-background/95 text-foreground backdrop-blur z-10">
207
- <div className="flex items-center gap-2">
208
- <DialogTitle className="text-lg font-semibold">
209
- {titleOverride ?? `Edit ${displayText}`}
210
- </DialogTitle>
211
- </div>
212
- <div className="flex items-center gap-2">
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"}
237
- </Button>
238
- {!isAutosaveMode && (
239
- <Button onClick={handleSave} disabled={!isValid} size="sm">
240
- Save ({saveShortcutLabel})
241
- </Button>
242
- )}
243
- </div>
244
- </div>
206
+ <div className="flex items-center justify-between p-4 border-b bg-background/95 text-foreground backdrop-blur z-10">
207
+ <div className="flex items-center gap-2">
208
+ <DialogTitle className="text-lg font-semibold">
209
+ {titleOverride ?? `Edit ${displayText}`}
210
+ </DialogTitle>
211
+ </div>
212
+ <div className="flex items-center gap-2">
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"}
237
+ </Button>
238
+ {!isAutosaveMode && (
239
+ <Button onClick={handleSave} disabled={!isValid} size="sm">
240
+ Save ({saveShortcutLabel})
241
+ </Button>
242
+ )}
243
+ </div>
244
+ </div>
245
245
 
246
246
  {/* Editor Area with Contextual Background */}
247
247
  <div
@@ -251,38 +251,38 @@ export function BlockEditorModal({
251
251
  // Conditional Background Logic:
252
252
  // Only apply specific section background to 'text' and 'heading' blocks to allow "Live Preview" of copy.
253
253
  // For complex blocks like Forms, Buttons, etc., keep a neutral background to ensure input field contrast.
254
- shouldUseContextualSurface ? (
255
- // If no specific background, use white/dark default
256
- (!sectionBackground || sectionBackground.type === 'none') && "bg-muted/10"
257
- ) : "bg-muted/10", // Default for non-text blocks
258
-
259
- // Apply theme classes if present (ONLY for text/heading)
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',
263
-
264
- // Dark mode prose invert if dark background (approximate check for solid color)
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"
267
- )}
268
- style={{
269
- // Only apply custom color/gradient styles for text/heading
270
- backgroundColor: shouldUseContextualSurface && sectionBackground?.type === 'solid' ? sectionBackground.solid_color : undefined,
271
- backgroundImage: shouldUseContextualSurface && sectionBackground?.type === 'gradient' && sectionBackground.gradient ?
272
- `${sectionBackground.gradient.type}-gradient(${sectionBackground.gradient.direction}, ${sectionBackground.gradient.stops.map(s => `${s.color} ${s.position}%`).join(', ')})`
273
- : undefined,
274
- ...contextualSurfaceStyle,
275
- }}
276
- >
254
+ shouldUseContextualSurface ? (
255
+ // If no specific background, use white/dark default
256
+ (!sectionBackground || sectionBackground.type === 'none') && "bg-muted/10"
257
+ ) : "bg-muted/10", // Default for non-text blocks
258
+
259
+ // Apply theme classes if present (ONLY for text/heading)
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',
263
+
264
+ // Dark mode prose invert if dark background (approximate check for solid color)
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"
267
+ )}
268
+ style={{
269
+ // Only apply custom color/gradient styles for text/heading
270
+ backgroundColor: shouldUseContextualSurface && sectionBackground?.type === 'solid' ? sectionBackground.solid_color : undefined,
271
+ backgroundImage: shouldUseContextualSurface && sectionBackground?.type === 'gradient' && sectionBackground.gradient ?
272
+ `${sectionBackground.gradient.type}-gradient(${sectionBackground.gradient.direction}, ${sectionBackground.gradient.stops.map(s => `${s.color} ${s.position}%`).join(', ')})`
273
+ : undefined,
274
+ ...contextualSurfaceStyle,
275
+ }}
276
+ >
277
277
  <div className="max-w-6xl mx-auto">
278
278
  <Suspense fallback={<div className="flex justify-center items-center h-32">Loading editor...</div>}>
279
279
  <EditorComponent
280
- block={block}
281
- content={tempContent}
282
- onChange={handleContentChange}
283
- className={editorClassName}
284
- sectionBackground={sectionBackground} // Pass down if editor supports it
285
- />
280
+ block={block}
281
+ content={tempContent}
282
+ onChange={handleContentChange}
283
+ className={editorClassName}
284
+ sectionBackground={sectionBackground} // Pass down if editor supports it
285
+ />
286
286
  </Suspense>
287
287
  </div>
288
288
  </div>