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,175 +1,175 @@
1
- import type { CSSProperties } from 'react';
2
-
3
- /**
4
- * Text-colour model shared by the block renderers and the CMS previews.
5
- *
6
- * A colour is stored as EITHER a theme token keyword (`"primary"`) — which keeps
7
- * following the active theme and dark mode — OR a literal CSS colour emitted by
8
- * the colour picker (`"#FF8800"`, `"rgba(...)"`, `"hsl(...)"`). Tokens render as
9
- * a static Tailwind class; literals render as an inline style.
10
- *
11
- * Keep this the single source of truth: HeadingBlockRenderer, EditableBlock and
12
- * ColumnEditor previously each carried their own map and had drifted apart
13
- * (`muted` rendered as the near-invisible `text-muted` on the live site while
14
- * both previews used `text-muted-foreground`).
15
- */
16
-
17
- export const TEXT_COLOR_TOKENS = [
18
- 'foreground',
19
- 'primary',
20
- 'secondary',
21
- 'accent',
22
- 'muted',
23
- 'destructive',
24
- 'background',
25
- ] as const;
26
-
27
- export type TextColorToken = (typeof TEXT_COLOR_TOKENS)[number];
28
-
29
- /**
30
- * Token -> Tailwind class.
31
- *
32
- * These must be written as complete literals, never interpolated. Tailwind v4
33
- * ignores the `safelist` key in the legacy JS config (libs/ui/tailwind.config.js
34
- * still carries one, but v4 never reads it) — a class exists only if the scanner
35
- * finds the whole string in a source file. Listing them here is what guarantees
36
- * they are emitted.
37
- */
38
- export const TEXT_COLOR_CLASSES: Record<TextColorToken, string> = {
39
- foreground: 'text-foreground',
40
- primary: 'text-primary',
41
- secondary: 'text-secondary',
42
- accent: 'text-accent',
43
- // `--muted` is a near-white surface colour; the readable pair is its foreground.
44
- muted: 'text-muted-foreground',
45
- destructive: 'text-destructive',
46
- background: 'text-background',
47
- };
48
-
49
- /** Token -> CSS colour, for painting swatches in the editor. */
50
- export const TEXT_COLOR_SWATCHES: Record<TextColorToken, string> = {
51
- foreground: 'hsl(var(--foreground))',
52
- primary: 'hsl(var(--primary))',
53
- secondary: 'hsl(var(--secondary))',
54
- accent: 'hsl(var(--accent))',
55
- muted: 'hsl(var(--muted-foreground))',
56
- destructive: 'hsl(var(--destructive))',
57
- background: 'hsl(var(--background))',
58
- };
59
-
60
- export const TEXT_COLOR_LABELS: Record<TextColorToken, string> = {
61
- foreground: 'Default text',
62
- primary: 'Primary',
63
- secondary: 'Secondary',
64
- accent: 'Accent',
65
- muted: 'Muted',
66
- destructive: 'Destructive',
67
- background: 'Background',
68
- };
69
-
70
- /** Token options in the shape `ColorField` expects. */
71
- export const TEXT_COLOR_TOKEN_OPTIONS = TEXT_COLOR_TOKENS.map((token) => ({
72
- value: token,
73
- label: TEXT_COLOR_LABELS[token],
74
- cssColor: TEXT_COLOR_SWATCHES[token],
75
- }));
76
-
77
- /**
78
- * Accepts what the colour picker can emit. Deliberately permissive on the inner
79
- * grammar — the picker only produces well-formed values, and being strict here
80
- * would reject legitimate colours authored by hand or by Cortex AI.
81
- */
82
- export const CSS_COLOR_PATTERN =
83
- /^(#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})|rgba?\([^)]+\)|hsla?\([^)]+\))$/i;
84
-
85
- /**
86
- * CSS named colours, which the picker never emits but hand-authored and AI-authored
87
- * content does.
88
- *
89
- * The seeded contact page sets `textColor: "white"` on its heading. That is perfectly
90
- * valid CSS, but it matched neither a theme token nor the literal pattern above, so
91
- * `resolveTextColor` returned `{}` and the heading quietly inherited the dark default —
92
- * a white-on-dark hero rendering dark-on-dark, with nothing anywhere reporting a problem.
93
- *
94
- * Spelled out as a set rather than folded into the pattern as `[a-z]+`, because
95
- * `isCustomCssColor` has to keep answering false for a theme token like `accent`: the
96
- * two are stored in the same field and told apart only by these predicates.
97
- */
98
- const CSS_NAMED_COLORS = new Set(
99
- (
100
- 'aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue ' +
101
- 'blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk ' +
102
- 'crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki ' +
103
- 'darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen ' +
104
- 'darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue ' +
105
- 'dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite ' +
106
- 'gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki ' +
107
- 'lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan ' +
108
- 'lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen ' +
109
- 'lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen ' +
110
- 'magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen ' +
111
- 'mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream ' +
112
- 'mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid ' +
113
- 'palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum ' +
114
- 'powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown ' +
115
- 'seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen ' +
116
- 'steelblue tan teal thistle tomato turquoise violet wheat white whitesmoke yellow ' +
117
- 'yellowgreen transparent currentcolor'
118
- ).split(' ')
119
- );
120
-
121
- export function isCssNamedColor(value: unknown): value is string {
122
- return typeof value === 'string' && CSS_NAMED_COLORS.has(value.trim().toLowerCase());
123
- }
124
-
125
- export function isTextColorToken(value: unknown): value is TextColorToken {
126
- return typeof value === 'string' && (TEXT_COLOR_TOKENS as readonly string[]).includes(value);
127
- }
128
-
129
- export function isCustomCssColor(value: unknown): value is string {
130
- return (
131
- (typeof value === 'string' && CSS_COLOR_PATTERN.test(value.trim())) ||
132
- isCssNamedColor(value)
133
- );
134
- }
135
-
136
- export interface ResolvedTextColor {
137
- className?: string;
138
- style?: CSSProperties;
139
- }
140
-
141
- /**
142
- * Turn a stored colour into render props. Unknown values resolve to `{}` so a
143
- * stale or hand-edited value degrades to the inherited colour rather than
144
- * emitting a bogus Tailwind class.
145
- */
146
- export function resolveTextColor(value: string | null | undefined): ResolvedTextColor {
147
- if (!value) return {};
148
- const trimmed = value.trim();
149
- if (isTextColorToken(trimmed)) {
150
- return { className: TEXT_COLOR_CLASSES[trimmed] };
151
- }
152
- if (isCustomCssColor(trimmed)) {
153
- return { style: { color: trimmed } };
154
- }
155
- return {};
156
- }
157
-
158
- /**
159
- * Text alignment, also as literals. `text-justify` currently only survives
160
- * because the string happens to appear in a regex in libs/editor/src/lib/kit.ts;
161
- * pinning it here removes that accidental dependency.
162
- */
163
- export const TEXT_ALIGN_CLASSES = {
164
- left: 'text-left',
165
- center: 'text-center',
166
- right: 'text-right',
167
- justify: 'text-justify',
168
- } as const;
169
-
170
- export type TextAlign = keyof typeof TEXT_ALIGN_CLASSES;
171
-
172
- export function resolveTextAlign(value: string | null | undefined): string | undefined {
173
- if (!value) return undefined;
174
- return TEXT_ALIGN_CLASSES[value as TextAlign];
175
- }
1
+ import type { CSSProperties } from 'react';
2
+
3
+ /**
4
+ * Text-colour model shared by the block renderers and the CMS previews.
5
+ *
6
+ * A colour is stored as EITHER a theme token keyword (`"primary"`) — which keeps
7
+ * following the active theme and dark mode — OR a literal CSS colour emitted by
8
+ * the colour picker (`"#FF8800"`, `"rgba(...)"`, `"hsl(...)"`). Tokens render as
9
+ * a static Tailwind class; literals render as an inline style.
10
+ *
11
+ * Keep this the single source of truth: HeadingBlockRenderer, EditableBlock and
12
+ * ColumnEditor previously each carried their own map and had drifted apart
13
+ * (`muted` rendered as the near-invisible `text-muted` on the live site while
14
+ * both previews used `text-muted-foreground`).
15
+ */
16
+
17
+ export const TEXT_COLOR_TOKENS = [
18
+ 'foreground',
19
+ 'primary',
20
+ 'secondary',
21
+ 'accent',
22
+ 'muted',
23
+ 'destructive',
24
+ 'background',
25
+ ] as const;
26
+
27
+ export type TextColorToken = (typeof TEXT_COLOR_TOKENS)[number];
28
+
29
+ /**
30
+ * Token -> Tailwind class.
31
+ *
32
+ * These must be written as complete literals, never interpolated. Tailwind v4
33
+ * ignores the `safelist` key in the legacy JS config (libs/ui/tailwind.config.js
34
+ * still carries one, but v4 never reads it) — a class exists only if the scanner
35
+ * finds the whole string in a source file. Listing them here is what guarantees
36
+ * they are emitted.
37
+ */
38
+ export const TEXT_COLOR_CLASSES: Record<TextColorToken, string> = {
39
+ foreground: 'text-foreground',
40
+ primary: 'text-primary',
41
+ secondary: 'text-secondary',
42
+ accent: 'text-accent',
43
+ // `--muted` is a near-white surface colour; the readable pair is its foreground.
44
+ muted: 'text-muted-foreground',
45
+ destructive: 'text-destructive',
46
+ background: 'text-background',
47
+ };
48
+
49
+ /** Token -> CSS colour, for painting swatches in the editor. */
50
+ export const TEXT_COLOR_SWATCHES: Record<TextColorToken, string> = {
51
+ foreground: 'hsl(var(--foreground))',
52
+ primary: 'hsl(var(--primary))',
53
+ secondary: 'hsl(var(--secondary))',
54
+ accent: 'hsl(var(--accent))',
55
+ muted: 'hsl(var(--muted-foreground))',
56
+ destructive: 'hsl(var(--destructive))',
57
+ background: 'hsl(var(--background))',
58
+ };
59
+
60
+ export const TEXT_COLOR_LABELS: Record<TextColorToken, string> = {
61
+ foreground: 'Default text',
62
+ primary: 'Primary',
63
+ secondary: 'Secondary',
64
+ accent: 'Accent',
65
+ muted: 'Muted',
66
+ destructive: 'Destructive',
67
+ background: 'Background',
68
+ };
69
+
70
+ /** Token options in the shape `ColorField` expects. */
71
+ export const TEXT_COLOR_TOKEN_OPTIONS = TEXT_COLOR_TOKENS.map((token) => ({
72
+ value: token,
73
+ label: TEXT_COLOR_LABELS[token],
74
+ cssColor: TEXT_COLOR_SWATCHES[token],
75
+ }));
76
+
77
+ /**
78
+ * Accepts what the colour picker can emit. Deliberately permissive on the inner
79
+ * grammar — the picker only produces well-formed values, and being strict here
80
+ * would reject legitimate colours authored by hand or by Cortex AI.
81
+ */
82
+ export const CSS_COLOR_PATTERN =
83
+ /^(#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})|rgba?\([^)]+\)|hsla?\([^)]+\))$/i;
84
+
85
+ /**
86
+ * CSS named colours, which the picker never emits but hand-authored and AI-authored
87
+ * content does.
88
+ *
89
+ * The seeded contact page sets `textColor: "white"` on its heading. That is perfectly
90
+ * valid CSS, but it matched neither a theme token nor the literal pattern above, so
91
+ * `resolveTextColor` returned `{}` and the heading quietly inherited the dark default —
92
+ * a white-on-dark hero rendering dark-on-dark, with nothing anywhere reporting a problem.
93
+ *
94
+ * Spelled out as a set rather than folded into the pattern as `[a-z]+`, because
95
+ * `isCustomCssColor` has to keep answering false for a theme token like `accent`: the
96
+ * two are stored in the same field and told apart only by these predicates.
97
+ */
98
+ const CSS_NAMED_COLORS = new Set(
99
+ (
100
+ 'aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue ' +
101
+ 'blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk ' +
102
+ 'crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki ' +
103
+ 'darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen ' +
104
+ 'darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue ' +
105
+ 'dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite ' +
106
+ 'gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki ' +
107
+ 'lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan ' +
108
+ 'lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen ' +
109
+ 'lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen ' +
110
+ 'magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen ' +
111
+ 'mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream ' +
112
+ 'mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid ' +
113
+ 'palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum ' +
114
+ 'powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown ' +
115
+ 'seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen ' +
116
+ 'steelblue tan teal thistle tomato turquoise violet wheat white whitesmoke yellow ' +
117
+ 'yellowgreen transparent currentcolor'
118
+ ).split(' ')
119
+ );
120
+
121
+ export function isCssNamedColor(value: unknown): value is string {
122
+ return typeof value === 'string' && CSS_NAMED_COLORS.has(value.trim().toLowerCase());
123
+ }
124
+
125
+ export function isTextColorToken(value: unknown): value is TextColorToken {
126
+ return typeof value === 'string' && (TEXT_COLOR_TOKENS as readonly string[]).includes(value);
127
+ }
128
+
129
+ export function isCustomCssColor(value: unknown): value is string {
130
+ return (
131
+ (typeof value === 'string' && CSS_COLOR_PATTERN.test(value.trim())) ||
132
+ isCssNamedColor(value)
133
+ );
134
+ }
135
+
136
+ export interface ResolvedTextColor {
137
+ className?: string;
138
+ style?: CSSProperties;
139
+ }
140
+
141
+ /**
142
+ * Turn a stored colour into render props. Unknown values resolve to `{}` so a
143
+ * stale or hand-edited value degrades to the inherited colour rather than
144
+ * emitting a bogus Tailwind class.
145
+ */
146
+ export function resolveTextColor(value: string | null | undefined): ResolvedTextColor {
147
+ if (!value) return {};
148
+ const trimmed = value.trim();
149
+ if (isTextColorToken(trimmed)) {
150
+ return { className: TEXT_COLOR_CLASSES[trimmed] };
151
+ }
152
+ if (isCustomCssColor(trimmed)) {
153
+ return { style: { color: trimmed } };
154
+ }
155
+ return {};
156
+ }
157
+
158
+ /**
159
+ * Text alignment, also as literals. `text-justify` currently only survives
160
+ * because the string happens to appear in a regex in libs/editor/src/lib/kit.ts;
161
+ * pinning it here removes that accidental dependency.
162
+ */
163
+ export const TEXT_ALIGN_CLASSES = {
164
+ left: 'text-left',
165
+ center: 'text-center',
166
+ right: 'text-right',
167
+ justify: 'text-justify',
168
+ } as const;
169
+
170
+ export type TextAlign = keyof typeof TEXT_ALIGN_CLASSES;
171
+
172
+ export function resolveTextAlign(value: string | null | undefined): string | undefined {
173
+ if (!value) return undefined;
174
+ return TEXT_ALIGN_CLASSES[value as TextAlign];
175
+ }