@nextsparkjs/theme-default 0.1.0-beta.1

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 (333) hide show
  1. package/about/business.md +49 -0
  2. package/about/features.json +302 -0
  3. package/about/team.md +79 -0
  4. package/api/ai/chat/stream/route.ts +212 -0
  5. package/api/ai/orchestrator/route.ts +226 -0
  6. package/api/ai/single-agent/route.ts +291 -0
  7. package/api/ai/usage/route.ts +122 -0
  8. package/blocks/benefits/component.tsx +100 -0
  9. package/blocks/benefits/config.ts +11 -0
  10. package/blocks/benefits/examples.ts +85 -0
  11. package/blocks/benefits/fields.ts +156 -0
  12. package/blocks/benefits/schema.ts +33 -0
  13. package/blocks/cta-section/component.tsx +100 -0
  14. package/blocks/cta-section/config.ts +11 -0
  15. package/blocks/cta-section/examples.ts +41 -0
  16. package/blocks/cta-section/fields.ts +89 -0
  17. package/blocks/cta-section/index.ts +6 -0
  18. package/blocks/cta-section/schema.ts +32 -0
  19. package/blocks/cta-section/thumbnail.png +1 -0
  20. package/blocks/faq-accordion/component.tsx +156 -0
  21. package/blocks/faq-accordion/config.ts +11 -0
  22. package/blocks/faq-accordion/examples.ts +77 -0
  23. package/blocks/faq-accordion/fields.ts +119 -0
  24. package/blocks/faq-accordion/index.ts +6 -0
  25. package/blocks/faq-accordion/schema.ts +45 -0
  26. package/blocks/features-grid/component.tsx +112 -0
  27. package/blocks/features-grid/config.ts +11 -0
  28. package/blocks/features-grid/examples.ts +63 -0
  29. package/blocks/features-grid/fields.ts +97 -0
  30. package/blocks/features-grid/index.ts +6 -0
  31. package/blocks/features-grid/schema.ts +40 -0
  32. package/blocks/features-grid/thumbnail.png +1 -0
  33. package/blocks/hero/component.tsx +100 -0
  34. package/blocks/hero/config.ts +11 -0
  35. package/blocks/hero/examples.ts +35 -0
  36. package/blocks/hero/fields.ts +60 -0
  37. package/blocks/hero/index.ts +6 -0
  38. package/blocks/hero/schema.ts +32 -0
  39. package/blocks/hero/thumbnail.png +1 -0
  40. package/blocks/hero/thumbnail.png.txt +6 -0
  41. package/blocks/hero-with-form/component.tsx +232 -0
  42. package/blocks/hero-with-form/config.ts +11 -0
  43. package/blocks/hero-with-form/examples.ts +16 -0
  44. package/blocks/hero-with-form/fields.ts +207 -0
  45. package/blocks/hero-with-form/index.ts +6 -0
  46. package/blocks/hero-with-form/schema.ts +54 -0
  47. package/blocks/jumbotron/component.tsx +136 -0
  48. package/blocks/jumbotron/config.ts +11 -0
  49. package/blocks/jumbotron/examples.ts +36 -0
  50. package/blocks/jumbotron/fields.ts +202 -0
  51. package/blocks/jumbotron/index.ts +6 -0
  52. package/blocks/jumbotron/schema.ts +55 -0
  53. package/blocks/logo-cloud/component.tsx +154 -0
  54. package/blocks/logo-cloud/config.ts +11 -0
  55. package/blocks/logo-cloud/examples.ts +34 -0
  56. package/blocks/logo-cloud/fields.ts +133 -0
  57. package/blocks/logo-cloud/index.ts +6 -0
  58. package/blocks/logo-cloud/schema.ts +46 -0
  59. package/blocks/post-content/component.tsx +197 -0
  60. package/blocks/post-content/config.ts +11 -0
  61. package/blocks/post-content/examples.ts +33 -0
  62. package/blocks/post-content/fields.ts +165 -0
  63. package/blocks/post-content/index.ts +4 -0
  64. package/blocks/post-content/schema.ts +46 -0
  65. package/blocks/pricing-table/component.tsx +154 -0
  66. package/blocks/pricing-table/config.ts +11 -0
  67. package/blocks/pricing-table/examples.ts +96 -0
  68. package/blocks/pricing-table/fields.ts +161 -0
  69. package/blocks/pricing-table/index.ts +4 -0
  70. package/blocks/pricing-table/schema.ts +50 -0
  71. package/blocks/split-content/component.tsx +135 -0
  72. package/blocks/split-content/config.ts +11 -0
  73. package/blocks/split-content/examples.ts +38 -0
  74. package/blocks/split-content/fields.ts +198 -0
  75. package/blocks/split-content/index.ts +6 -0
  76. package/blocks/split-content/schema.ts +67 -0
  77. package/blocks/stats-counter/component.tsx +124 -0
  78. package/blocks/stats-counter/config.ts +11 -0
  79. package/blocks/stats-counter/examples.ts +61 -0
  80. package/blocks/stats-counter/fields.ts +134 -0
  81. package/blocks/stats-counter/index.ts +6 -0
  82. package/blocks/stats-counter/schema.ts +47 -0
  83. package/blocks/testimonials/component.tsx +114 -0
  84. package/blocks/testimonials/config.ts +11 -0
  85. package/blocks/testimonials/examples.ts +65 -0
  86. package/blocks/testimonials/fields.ts +105 -0
  87. package/blocks/testimonials/index.ts +6 -0
  88. package/blocks/testimonials/schema.ts +41 -0
  89. package/blocks/testimonials/thumbnail.png +1 -0
  90. package/blocks/text-content/component.tsx +97 -0
  91. package/blocks/text-content/config.ts +11 -0
  92. package/blocks/text-content/examples.ts +30 -0
  93. package/blocks/text-content/fields.ts +88 -0
  94. package/blocks/text-content/index.ts +6 -0
  95. package/blocks/text-content/schema.ts +30 -0
  96. package/blocks/text-content/thumbnail.png +1 -0
  97. package/blocks/timeline/component.tsx +267 -0
  98. package/blocks/timeline/config.ts +11 -0
  99. package/blocks/timeline/examples.ts +68 -0
  100. package/blocks/timeline/fields.ts +147 -0
  101. package/blocks/timeline/index.ts +6 -0
  102. package/blocks/timeline/schema.ts +49 -0
  103. package/blocks/video-hero/component.tsx +270 -0
  104. package/blocks/video-hero/config.ts +11 -0
  105. package/blocks/video-hero/examples.ts +24 -0
  106. package/blocks/video-hero/fields.ts +98 -0
  107. package/blocks/video-hero/index.ts +6 -0
  108. package/blocks/video-hero/schema.ts +39 -0
  109. package/components/ai-chat/ChatPanel.tsx +575 -0
  110. package/components/ai-chat/ConversationItem.tsx +266 -0
  111. package/components/ai-chat/ConversationSidebar.tsx +99 -0
  112. package/components/ai-chat/MarkdownRenderer.tsx +15 -0
  113. package/components/ai-chat/Message.tsx +42 -0
  114. package/components/ai-chat/MessageInput.tsx +49 -0
  115. package/components/ai-chat/MessageList.tsx +46 -0
  116. package/components/ai-chat/TypingIndicator.tsx +11 -0
  117. package/config/app.config.ts +367 -0
  118. package/config/billing.config.ts +349 -0
  119. package/config/dashboard.config.ts +506 -0
  120. package/config/dev.config.ts +104 -0
  121. package/config/features.config.ts +203 -0
  122. package/config/flows.config.ts +129 -0
  123. package/config/permissions.config.ts +245 -0
  124. package/config/theme.config.ts +74 -0
  125. package/docs/01-overview/01-introduction.md +335 -0
  126. package/docs/01-overview/02-customization.md +671 -0
  127. package/docs/02-features/01-components.md +155 -0
  128. package/docs/02-features/02-styling.md +139 -0
  129. package/docs/02-features/03-tasks-entity.md +407 -0
  130. package/docs/03-ai/01-overview.md +211 -0
  131. package/docs/03-ai/02-customization.md +436 -0
  132. package/entities/customers/customers.config.ts +75 -0
  133. package/entities/customers/customers.fields.ts +165 -0
  134. package/entities/customers/customers.service.ts +516 -0
  135. package/entities/customers/customers.types.ts +83 -0
  136. package/entities/customers/messages/en.json +66 -0
  137. package/entities/customers/messages/es.json +66 -0
  138. package/entities/customers/migrations/001_customers_table.sql +102 -0
  139. package/entities/customers/migrations/002_customers_metas.sql +92 -0
  140. package/entities/pages/messages/en.json +41 -0
  141. package/entities/pages/messages/es.json +41 -0
  142. package/entities/pages/migrations/001_pages_table.sql +112 -0
  143. package/entities/pages/migrations/002_pages_metas.sql +56 -0
  144. package/entities/pages/migrations/003_add_status.sql +50 -0
  145. package/entities/pages/pages-management.service.ts +610 -0
  146. package/entities/pages/pages.config.ts +94 -0
  147. package/entities/pages/pages.fields.ts +101 -0
  148. package/entities/pages/pages.service.ts +290 -0
  149. package/entities/pages/pages.types.ts +124 -0
  150. package/entities/posts/components/post-header.tsx +97 -0
  151. package/entities/posts/messages/en.json +55 -0
  152. package/entities/posts/messages/es.json +55 -0
  153. package/entities/posts/migrations/001_posts_table.sql +115 -0
  154. package/entities/posts/migrations/003_add_status.sql +44 -0
  155. package/entities/posts/migrations/004_entity_taxonomy_relations.sql +129 -0
  156. package/entities/posts/migrations/006_posts_metas.sql +56 -0
  157. package/entities/posts/posts.config.ts +101 -0
  158. package/entities/posts/posts.fields.ts +116 -0
  159. package/entities/posts/posts.service.ts +376 -0
  160. package/entities/posts/posts.types.ts +74 -0
  161. package/entities/tasks/messages/en.json +204 -0
  162. package/entities/tasks/messages/es.json +204 -0
  163. package/entities/tasks/migrations/001_tasks_table.sql +105 -0
  164. package/entities/tasks/migrations/002_task_metas.sql +85 -0
  165. package/entities/tasks/migrations/sample_data.json +77 -0
  166. package/entities/tasks/tasks.config.ts +79 -0
  167. package/entities/tasks/tasks.fields.ts +196 -0
  168. package/entities/tasks/tasks.service.ts +541 -0
  169. package/entities/tasks/tasks.types.ts +56 -0
  170. package/lib/hooks/useAiChat.ts +114 -0
  171. package/lib/hooks/useConversations.ts +376 -0
  172. package/lib/hooks/useOrchestratorChat.ts +122 -0
  173. package/lib/hooks/usePersistentChat.ts +315 -0
  174. package/lib/hooks/useStreamingChat.ts +127 -0
  175. package/lib/hooks/useTokenUsage.ts +63 -0
  176. package/lib/langchain/agents/customer-assistant.md +69 -0
  177. package/lib/langchain/agents/index.ts +61 -0
  178. package/lib/langchain/agents/orchestrator.md +59 -0
  179. package/lib/langchain/agents/page-assistant.md +85 -0
  180. package/lib/langchain/agents/single-agent.md +46 -0
  181. package/lib/langchain/agents/task-assistant.md +55 -0
  182. package/lib/langchain/config.ts +45 -0
  183. package/lib/langchain/handlers/customer-handler.ts +338 -0
  184. package/lib/langchain/handlers/page-handler.ts +232 -0
  185. package/lib/langchain/handlers/task-handler.ts +323 -0
  186. package/lib/langchain/langchain.config.ts +223 -0
  187. package/lib/langchain/observability.config.ts +30 -0
  188. package/lib/langchain/orchestrator.ts +562 -0
  189. package/lib/langchain/tools/customers.ts +176 -0
  190. package/lib/langchain/tools/index.ts +10 -0
  191. package/lib/langchain/tools/orchestrator.ts +92 -0
  192. package/lib/langchain/tools/pages.ts +289 -0
  193. package/lib/langchain/tools/tasks.ts +167 -0
  194. package/lib/scheduled-actions/billing.ts +149 -0
  195. package/lib/scheduled-actions/index.ts +170 -0
  196. package/lib/scheduled-actions/webhook.ts +231 -0
  197. package/lib/selectors.ts +197 -0
  198. package/messages/de/admin.json +219 -0
  199. package/messages/de/aiUsage.json +36 -0
  200. package/messages/de/buttons.json +19 -0
  201. package/messages/de/categories.json +35 -0
  202. package/messages/de/common.json +16 -0
  203. package/messages/de/dev.json +101 -0
  204. package/messages/de/docs.json +27 -0
  205. package/messages/de/entities.json +7 -0
  206. package/messages/de/features.json +119 -0
  207. package/messages/de/footer.json +22 -0
  208. package/messages/de/home.json +57 -0
  209. package/messages/de/index.ts +39 -0
  210. package/messages/de/mobileNav.json +13 -0
  211. package/messages/de/navigation.json +8 -0
  212. package/messages/de/observability.json +74 -0
  213. package/messages/de/posts.json +54 -0
  214. package/messages/de/pricing.json +102 -0
  215. package/messages/de/support.json +9 -0
  216. package/messages/de/teams.json +8 -0
  217. package/messages/en/admin.json +219 -0
  218. package/messages/en/aiUsage.json +36 -0
  219. package/messages/en/buttons.json +19 -0
  220. package/messages/en/categories.json +35 -0
  221. package/messages/en/common.json +16 -0
  222. package/messages/en/dev.json +106 -0
  223. package/messages/en/docs.json +27 -0
  224. package/messages/en/entities.json +7 -0
  225. package/messages/en/features.json +119 -0
  226. package/messages/en/footer.json +22 -0
  227. package/messages/en/home.json +57 -0
  228. package/messages/en/index.ts +39 -0
  229. package/messages/en/mobileNav.json +13 -0
  230. package/messages/en/navigation.json +8 -0
  231. package/messages/en/observability.json +74 -0
  232. package/messages/en/posts.json +54 -0
  233. package/messages/en/pricing.json +102 -0
  234. package/messages/en/support.json +9 -0
  235. package/messages/en/teams.json +8 -0
  236. package/messages/es/admin.json +219 -0
  237. package/messages/es/aiUsage.json +36 -0
  238. package/messages/es/buttons.json +19 -0
  239. package/messages/es/categories.json +35 -0
  240. package/messages/es/common.json +16 -0
  241. package/messages/es/dev.json +101 -0
  242. package/messages/es/docs.json +27 -0
  243. package/messages/es/entities.json +7 -0
  244. package/messages/es/features.json +119 -0
  245. package/messages/es/footer.json +22 -0
  246. package/messages/es/home.json +57 -0
  247. package/messages/es/index.ts +39 -0
  248. package/messages/es/mobileNav.json +13 -0
  249. package/messages/es/navigation.json +8 -0
  250. package/messages/es/observability.json +74 -0
  251. package/messages/es/posts.json +54 -0
  252. package/messages/es/pricing.json +102 -0
  253. package/messages/es/support.json +9 -0
  254. package/messages/es/teams.json +8 -0
  255. package/messages/fr/admin.json +219 -0
  256. package/messages/fr/aiUsage.json +36 -0
  257. package/messages/fr/buttons.json +19 -0
  258. package/messages/fr/categories.json +35 -0
  259. package/messages/fr/common.json +16 -0
  260. package/messages/fr/dev.json +101 -0
  261. package/messages/fr/docs.json +27 -0
  262. package/messages/fr/entities.json +7 -0
  263. package/messages/fr/features.json +119 -0
  264. package/messages/fr/footer.json +22 -0
  265. package/messages/fr/home.json +57 -0
  266. package/messages/fr/index.ts +39 -0
  267. package/messages/fr/mobileNav.json +13 -0
  268. package/messages/fr/navigation.json +8 -0
  269. package/messages/fr/observability.json +74 -0
  270. package/messages/fr/posts.json +54 -0
  271. package/messages/fr/pricing.json +102 -0
  272. package/messages/fr/support.json +9 -0
  273. package/messages/fr/teams.json +8 -0
  274. package/messages/it/admin.json +219 -0
  275. package/messages/it/aiUsage.json +36 -0
  276. package/messages/it/buttons.json +19 -0
  277. package/messages/it/categories.json +35 -0
  278. package/messages/it/common.json +16 -0
  279. package/messages/it/dev.json +101 -0
  280. package/messages/it/docs.json +27 -0
  281. package/messages/it/entities.json +7 -0
  282. package/messages/it/features.json +119 -0
  283. package/messages/it/footer.json +22 -0
  284. package/messages/it/home.json +57 -0
  285. package/messages/it/index.ts +39 -0
  286. package/messages/it/mobileNav.json +13 -0
  287. package/messages/it/navigation.json +8 -0
  288. package/messages/it/observability.json +74 -0
  289. package/messages/it/posts.json +54 -0
  290. package/messages/it/pricing.json +102 -0
  291. package/messages/it/support.json +9 -0
  292. package/messages/it/teams.json +8 -0
  293. package/messages/pt/admin.json +219 -0
  294. package/messages/pt/aiUsage.json +36 -0
  295. package/messages/pt/buttons.json +19 -0
  296. package/messages/pt/categories.json +35 -0
  297. package/messages/pt/common.json +16 -0
  298. package/messages/pt/dev.json +101 -0
  299. package/messages/pt/docs.json +27 -0
  300. package/messages/pt/entities.json +7 -0
  301. package/messages/pt/features.json +119 -0
  302. package/messages/pt/footer.json +22 -0
  303. package/messages/pt/home.json +57 -0
  304. package/messages/pt/index.ts +39 -0
  305. package/messages/pt/mobileNav.json +13 -0
  306. package/messages/pt/navigation.json +8 -0
  307. package/messages/pt/observability.json +74 -0
  308. package/messages/pt/posts.json +54 -0
  309. package/messages/pt/pricing.json +102 -0
  310. package/messages/pt/support.json +9 -0
  311. package/messages/pt/teams.json +8 -0
  312. package/migrations/089_add_editor_team_role.sql +39 -0
  313. package/migrations/090_demo_users_teams.sql +540 -0
  314. package/migrations/091_greek_teams_billing.sql +523 -0
  315. package/migrations/092_billing_sample_data.sql +774 -0
  316. package/migrations/093_pages_sample_data.sql +1158 -0
  317. package/migrations/094_posts_sample_data.sql +278 -0
  318. package/migrations/095_tasks_sample_data.sql +440 -0
  319. package/migrations/096_customers_sample_data.sql +358 -0
  320. package/migrations/097_scheduled_actions_sample_data.sql +111 -0
  321. package/package.json +22 -0
  322. package/public/docs/desktop-layout-example.png +0 -0
  323. package/styles/components.css +11 -0
  324. package/styles/globals.css +179 -0
  325. package/templates/(public)/blog/[slug]/page.tsx +65 -0
  326. package/templates/(public)/layout.tsx +25 -0
  327. package/templates/(public)/page.tsx +200 -0
  328. package/templates/(public)/support/page.tsx +321 -0
  329. package/templates/dashboard/(main)/agent-multi/page.tsx +63 -0
  330. package/templates/dashboard/(main)/agent-single/page.tsx +142 -0
  331. package/templates/dashboard/(main)/settings/ai-usage/page.tsx +157 -0
  332. package/templates/superadmin/ai-observability/[traceId]/page.tsx +27 -0
  333. package/templates/superadmin/ai-observability/page.tsx +17 -0
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod'
2
+ import { baseBlockSchema, ctaSchema } from '@nextsparkjs/core/types/blocks'
3
+
4
+ /**
5
+ * Primary CTA Schema for Jumbotron
6
+ * Extends standard CTA with variant options
7
+ */
8
+ const primaryCtaSchema = ctaSchema.extend({
9
+ variant: z.enum(['default', 'outline', 'secondary']).default('default'),
10
+ })
11
+
12
+ /**
13
+ * Secondary CTA Schema for Jumbotron
14
+ * Uses same structure as primary CTA
15
+ */
16
+ const secondaryCtaSchema = ctaSchema.extend({
17
+ variant: z.enum(['default', 'outline', 'secondary']).default('outline'),
18
+ })
19
+
20
+ export type PrimaryCta = z.infer<typeof primaryCtaSchema>
21
+ export type SecondaryCta = z.infer<typeof secondaryCtaSchema>
22
+
23
+ /**
24
+ * Jumbotron Block Schema
25
+ *
26
+ * Extends base schema with:
27
+ * - subtitle: Supporting text/description (separate from content)
28
+ * - primaryCta: Primary CTA button with variant
29
+ * - secondaryCta: Optional secondary CTA button with variant
30
+ * - fullscreen: Toggle for 100vh height
31
+ * - backgroundImage: Optional background image
32
+ * - textColor: Light/dark for contrast
33
+ * - textAlign: Content alignment (center, left, right)
34
+ *
35
+ * Note: Uses base schema title, backgroundColor, className, id
36
+ */
37
+ export const jumbotronSpecificSchema = z.object({
38
+ // Content fields
39
+ subtitle: z.string().optional(),
40
+ primaryCta: primaryCtaSchema.optional(),
41
+ secondaryCta: secondaryCtaSchema.optional(),
42
+
43
+ // Design fields
44
+ fullscreen: z.boolean().default(false),
45
+ backgroundImage: z.string().url('Must be a valid URL').optional(),
46
+ textColor: z.enum(['light', 'dark']).default('light'),
47
+ textAlign: z.enum(['center', 'left', 'right']).default('center'),
48
+ })
49
+
50
+ /**
51
+ * Complete Jumbotron Block Schema
52
+ */
53
+ export const schema = baseBlockSchema.merge(jumbotronSpecificSchema)
54
+
55
+ export type JumbotronBlockProps = z.infer<typeof schema>
@@ -0,0 +1,154 @@
1
+ import React from 'react'
2
+ import Image from 'next/image'
3
+ import { cn } from '@nextsparkjs/core/lib/utils'
4
+ import { buildSectionClasses } from '@nextsparkjs/core/types/blocks'
5
+ import { sel } from '../../lib/selectors'
6
+ import type { LogoCloudBlockProps, LogoItem } from './schema'
7
+
8
+ /**
9
+ * Logo Cloud Block Component
10
+ *
11
+ * Props from 3-tab structure:
12
+ * - Content: title, content, cta, logos
13
+ * - Design: backgroundColor, layout, columns, grayscale, size
14
+ * - Advanced: className, id
15
+ */
16
+ export function LogoCloudBlock({
17
+ // Base content props
18
+ title,
19
+ content,
20
+ cta,
21
+ // Logo Cloud-specific content
22
+ logos,
23
+ // Base design props
24
+ backgroundColor,
25
+ // Logo Cloud-specific design
26
+ layout = 'grid',
27
+ columns = '5',
28
+ grayscale = true,
29
+ size = 'md',
30
+ // Base advanced props
31
+ className,
32
+ id,
33
+ }: LogoCloudBlockProps) {
34
+ // Ensure logos is an array
35
+ const safeLogos = Array.isArray(logos) ? logos : []
36
+
37
+ // Build layout classes based on layout prop
38
+ const layoutClasses: Record<string, string> = {
39
+ grid: 'grid',
40
+ row: 'flex flex-wrap',
41
+ 'row-scroll': 'flex overflow-x-auto',
42
+ }
43
+
44
+ // Build column classes for grid layout
45
+ const columnClasses: Record<string, string> = {
46
+ '3': 'grid-cols-2 sm:grid-cols-3',
47
+ '4': 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-4',
48
+ '5': 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-5',
49
+ '6': 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6',
50
+ }
51
+
52
+ // Build size classes for logo containers
53
+ const sizeClasses: Record<string, string> = {
54
+ sm: 'h-12 w-24',
55
+ md: 'h-16 w-32',
56
+ lg: 'h-20 w-40',
57
+ }
58
+
59
+ // Container classes based on layout
60
+ const containerClasses = cn(
61
+ layoutClasses[layout],
62
+ layout === 'grid' && cn('gap-8', columnClasses[columns]),
63
+ layout === 'row' && 'gap-6 justify-center items-center',
64
+ layout === 'row-scroll' && 'gap-6 overflow-x-auto pb-4',
65
+ )
66
+
67
+ // Build section classes with background and custom className
68
+ const sectionClasses = buildSectionClasses(
69
+ 'py-12 px-4 md:py-16',
70
+ { backgroundColor, className }
71
+ )
72
+
73
+ return (
74
+ <section id={id} className={sectionClasses} data-cy={sel('blocks.logoCloud.container')}>
75
+ <div className="container mx-auto max-w-6xl">
76
+ {/* Section Header */}
77
+ {(title || content) && (
78
+ <div className="mb-10 text-center">
79
+ {title && (
80
+ <h2 className="mb-3 text-2xl font-semibold md:text-3xl">
81
+ {title}
82
+ </h2>
83
+ )}
84
+ {content && (
85
+ <p className="mx-auto max-w-2xl text-muted-foreground">
86
+ {content}
87
+ </p>
88
+ )}
89
+ </div>
90
+ )}
91
+
92
+ {/* Logos Container */}
93
+ <div className={containerClasses}>
94
+ {safeLogos.map((logo: LogoItem, index: number) => {
95
+ const LogoImage = (
96
+ <div
97
+ className={cn(
98
+ 'relative flex items-center justify-center transition-all duration-300',
99
+ sizeClasses[size],
100
+ grayscale && 'grayscale hover:grayscale-0 opacity-70 hover:opacity-100'
101
+ )}
102
+ >
103
+ <Image
104
+ src={logo.image}
105
+ alt={logo.alt}
106
+ fill
107
+ className="object-contain"
108
+ sizes="(max-width: 768px) 50vw, (max-width: 1200px) 33vw, 20vw"
109
+ />
110
+ </div>
111
+ )
112
+
113
+ return (
114
+ <div
115
+ key={index}
116
+ className="flex items-center justify-center"
117
+ >
118
+ {logo.url ? (
119
+ <a
120
+ href={logo.url}
121
+ target="_blank"
122
+ rel="noopener noreferrer"
123
+ className="block"
124
+ data-cy={sel('blocks.logoCloud.link', { index: String(index) })}
125
+ >
126
+ {LogoImage}
127
+ </a>
128
+ ) : (
129
+ <div data-cy={sel('blocks.logoCloud.item', { index: String(index) })}>
130
+ {LogoImage}
131
+ </div>
132
+ )}
133
+ </div>
134
+ )
135
+ })}
136
+ </div>
137
+
138
+ {/* Optional CTA */}
139
+ {cta && (
140
+ <div className="mt-10 text-center">
141
+ <a
142
+ href={cta.link}
143
+ target={cta.target}
144
+ rel={cta.target === '_blank' ? 'noopener noreferrer' : undefined}
145
+ className="inline-flex items-center justify-center rounded-md bg-primary px-6 py-2.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
146
+ >
147
+ {cta.text}
148
+ </a>
149
+ </div>
150
+ )}
151
+ </div>
152
+ </section>
153
+ )
154
+ }
@@ -0,0 +1,11 @@
1
+ import type { BlockConfig } from '@nextsparkjs/core/types/blocks'
2
+
3
+ export const config: Omit<BlockConfig, 'schema' | 'fieldDefinitions' | 'Component' | 'examples'> = {
4
+ slug: 'logo-cloud',
5
+ name: 'Logo Cloud',
6
+ description: 'A section displaying client/partner logos in a grid or row layout ("Trusted by...", "As seen in...")',
7
+ category: 'content',
8
+ icon: 'Building2',
9
+ thumbnail: '/theme/blocks/logo-cloud/thumbnail.png',
10
+ scope: ['pages']
11
+ }
@@ -0,0 +1,34 @@
1
+ import type { BlockExample } from '@nextsparkjs/core/types/blocks'
2
+
3
+ export const examples: BlockExample[] = [
4
+ {
5
+ name: 'Default',
6
+ description: 'Logo cloud showing trusted brands',
7
+ props: {
8
+ title: 'Trusted by Leading Companies',
9
+ backgroundColor: 'gray-50',
10
+ logos: [
11
+ {
12
+ name: 'Vercel',
13
+ imageUrl: 'https://assets.vercel.com/image/upload/front/favicon/vercel/180x180.png',
14
+ url: 'https://vercel.com',
15
+ },
16
+ {
17
+ name: 'Next.js',
18
+ imageUrl: 'https://assets.vercel.com/image/upload/v1662130559/nextjs/Icon_dark_background.png',
19
+ url: 'https://nextjs.org',
20
+ },
21
+ {
22
+ name: 'Supabase',
23
+ imageUrl: 'https://supabase.com/brand-assets/supabase-logo-icon.png',
24
+ url: 'https://supabase.com',
25
+ },
26
+ {
27
+ name: 'Stripe',
28
+ imageUrl: 'https://images.ctfassets.net/fzn2n1nzq965/HTTOloNPhisV9P4hlMPNA/cacf1bb88b9fc492dfad34378d844280/Stripe_icon_-_square.svg',
29
+ url: 'https://stripe.com',
30
+ },
31
+ ],
32
+ },
33
+ },
34
+ ]
@@ -0,0 +1,133 @@
1
+ import type { FieldDefinition } from '@nextsparkjs/core/types/blocks'
2
+ import {
3
+ baseContentFields,
4
+ baseDesignFields,
5
+ baseAdvancedFields,
6
+ } from '@nextsparkjs/core/types/blocks'
7
+
8
+ /**
9
+ * Logo Cloud Block Field Definitions
10
+ *
11
+ * Organized into 3 tabs:
12
+ * - Content: title, content, cta (from base) + logos array
13
+ * - Design: backgroundColor (from base) + layout, columns, grayscale, size
14
+ * - Advanced: className, id (from base)
15
+ */
16
+
17
+ // Logo Cloud-specific content fields
18
+ const logoCloudContentFields: FieldDefinition[] = [
19
+ {
20
+ name: 'logos',
21
+ label: 'Logos',
22
+ type: 'array',
23
+ tab: 'content',
24
+ required: true,
25
+ description: 'List of client/partner logos to display',
26
+ helpText: 'Add up to 20 logos with image, alt text, and optional link',
27
+ minItems: 1,
28
+ maxItems: 20,
29
+ itemFields: [
30
+ {
31
+ name: 'image',
32
+ label: 'Logo Image',
33
+ type: 'image',
34
+ tab: 'content',
35
+ required: true,
36
+ helpText: 'Logo image URL (recommended: transparent PNG, 200x100px)',
37
+ },
38
+ {
39
+ name: 'alt',
40
+ label: 'Alt Text',
41
+ type: 'text',
42
+ tab: 'content',
43
+ required: true,
44
+ placeholder: 'Company Name',
45
+ helpText: 'Descriptive alt text for accessibility',
46
+ },
47
+ {
48
+ name: 'url',
49
+ label: 'Link URL (Optional)',
50
+ type: 'url',
51
+ tab: 'content',
52
+ required: false,
53
+ placeholder: 'https://example.com',
54
+ helpText: 'Optional link when logo is clicked',
55
+ },
56
+ ],
57
+ },
58
+ ]
59
+
60
+ // Logo Cloud-specific design fields
61
+ const logoCloudDesignFields: FieldDefinition[] = [
62
+ {
63
+ name: 'layout',
64
+ label: 'Layout',
65
+ type: 'select',
66
+ tab: 'design',
67
+ required: false,
68
+ default: 'grid',
69
+ description: 'Layout style for logos',
70
+ options: [
71
+ { label: 'Grid', value: 'grid' },
72
+ { label: 'Row (Wrap)', value: 'row' },
73
+ { label: 'Row (Scroll)', value: 'row-scroll' },
74
+ ],
75
+ },
76
+ {
77
+ name: 'columns',
78
+ label: 'Grid Columns',
79
+ type: 'select',
80
+ tab: 'design',
81
+ required: false,
82
+ default: '5',
83
+ description: 'Number of columns in grid layout (desktop)',
84
+ options: [
85
+ { label: '3 Columns', value: '3' },
86
+ { label: '4 Columns', value: '4' },
87
+ { label: '5 Columns', value: '5' },
88
+ { label: '6 Columns', value: '6' },
89
+ ],
90
+ },
91
+ {
92
+ name: 'grayscale',
93
+ label: 'Grayscale Effect',
94
+ type: 'checkbox',
95
+ tab: 'design',
96
+ required: false,
97
+ default: true,
98
+ description: 'Show logos in grayscale (color on hover)',
99
+ },
100
+ {
101
+ name: 'size',
102
+ label: 'Logo Size',
103
+ type: 'select',
104
+ tab: 'design',
105
+ required: false,
106
+ default: 'md',
107
+ description: 'Size of logos',
108
+ options: [
109
+ { label: 'Small', value: 'sm' },
110
+ { label: 'Medium', value: 'md' },
111
+ { label: 'Large', value: 'lg' },
112
+ ],
113
+ },
114
+ ]
115
+
116
+ /**
117
+ * Complete field definitions organized by tab
118
+ */
119
+ export const fieldDefinitions: FieldDefinition[] = [
120
+ // Content tab: base fields + logo-cloud-specific
121
+ ...baseContentFields,
122
+ ...logoCloudContentFields,
123
+
124
+ // Design tab: base fields + logo-cloud-specific
125
+ ...baseDesignFields,
126
+ ...logoCloudDesignFields,
127
+
128
+ // Advanced tab: base fields only
129
+ ...baseAdvancedFields,
130
+ ]
131
+
132
+ // Alias for compatibility
133
+ export const fields = fieldDefinitions
@@ -0,0 +1,6 @@
1
+ export { config } from './config'
2
+ export { schema } from './schema'
3
+ export { fields } from './fields'
4
+ export { LogoCloudBlock as Component } from './component'
5
+
6
+ export type { LogoCloudBlockProps } from './schema'
@@ -0,0 +1,46 @@
1
+ import { z } from 'zod'
2
+ import { baseBlockSchema } from '@nextsparkjs/core/types/blocks'
3
+
4
+ /**
5
+ * Logo Item Schema
6
+ * Individual logo in the cloud
7
+ */
8
+ const logoItemSchema = z.object({
9
+ image: z.string().url('Must be a valid URL'),
10
+ alt: z.string().min(1, 'Alt text is required for accessibility'),
11
+ url: z.string().url('Must be a valid URL').optional(),
12
+ })
13
+
14
+ export type LogoItem = z.infer<typeof logoItemSchema>
15
+
16
+ /**
17
+ * Logo Cloud Block Schema
18
+ *
19
+ * Extends base schema with:
20
+ * - logos: Array of logo items (image, alt, optional url)
21
+ * - layout: Layout style (grid, row, row-scroll)
22
+ * - columns: Grid columns (3, 4, 5, 6)
23
+ * - grayscale: Show logos in grayscale with hover effect
24
+ * - size: Logo size (sm, md, lg)
25
+ *
26
+ * Note: Uses base schema title, content, cta, backgroundColor, className, id
27
+ */
28
+ export const logoCloudSpecificSchema = z.object({
29
+ // Content: array of logo items
30
+ logos: z.array(logoItemSchema)
31
+ .min(1, 'At least one logo is required')
32
+ .max(20, 'Maximum 20 logos allowed'),
33
+
34
+ // Design: layout and styling
35
+ layout: z.enum(['grid', 'row', 'row-scroll']).default('grid'),
36
+ columns: z.enum(['3', '4', '5', '6']).default('5'),
37
+ grayscale: z.boolean().default(true),
38
+ size: z.enum(['sm', 'md', 'lg']).default('md'),
39
+ })
40
+
41
+ /**
42
+ * Complete Logo Cloud Block Schema
43
+ */
44
+ export const schema = baseBlockSchema.merge(logoCloudSpecificSchema)
45
+
46
+ export type LogoCloudBlockProps = z.infer<typeof schema>
@@ -0,0 +1,197 @@
1
+ import React from 'react'
2
+ import { cn } from '@nextsparkjs/core/lib/utils'
3
+ import { buildSectionClasses } from '@nextsparkjs/core/types/blocks'
4
+ import { sel } from '../../lib/selectors'
5
+ import type { PostContentBlockProps } from './schema'
6
+
7
+ /**
8
+ * Post Content Block Component
9
+ *
10
+ * Beautiful editorial typography optimized for long-form reading.
11
+ * Inspired by Medium, The New York Times, and other premium publishers.
12
+ *
13
+ * Props from 3-tab structure:
14
+ * - Content: title, content (rich-text article body), cta
15
+ * - Design: backgroundColor, drop cap, typography, dividers
16
+ * - Advanced: className, id
17
+ *
18
+ * Features:
19
+ * - Optimal reading width (680-900px)
20
+ * - Beautiful line height (1.7-1.8) for readability
21
+ * - Optional decorative drop cap
22
+ * - Configurable typography scale
23
+ * - Section dividers for visual breaks
24
+ */
25
+ export function PostContentBlock({
26
+ // Base content props
27
+ title,
28
+ content,
29
+ cta,
30
+ // Base design props
31
+ backgroundColor,
32
+ // Post-content-specific design
33
+ showDropCap = false,
34
+ dropCapStyle = 'serif',
35
+ maxWidth = 'narrow',
36
+ fontSize = 'medium',
37
+ lineHeight = 'relaxed',
38
+ paragraphSpacing = 'normal',
39
+ showDividers = false,
40
+ dividerStyle = 'line',
41
+ // Base advanced props
42
+ className,
43
+ id,
44
+ }: PostContentBlockProps) {
45
+ // Max width classes for optimal reading
46
+ const maxWidthClasses: Record<string, string> = {
47
+ narrow: 'max-w-[680px]', // Optimal for reading
48
+ medium: 'max-w-[768px]',
49
+ wide: 'max-w-[900px]',
50
+ }
51
+
52
+ // Font size classes
53
+ const fontSizeClasses: Record<string, string> = {
54
+ small: 'text-base', // 16px
55
+ medium: 'text-lg', // 18px
56
+ large: 'text-xl', // 20px
57
+ }
58
+
59
+ // Line height classes
60
+ const lineHeightClasses: Record<string, string> = {
61
+ compact: 'leading-relaxed', // 1.6
62
+ normal: 'leading-loose', // 1.7
63
+ relaxed: 'leading-[1.8]', // 1.8
64
+ }
65
+
66
+ // Paragraph spacing classes
67
+ const paragraphSpacingClasses: Record<string, string> = {
68
+ tight: '[&_p]:mb-4',
69
+ normal: '[&_p]:mb-6',
70
+ loose: '[&_p]:mb-8',
71
+ }
72
+
73
+ // Drop cap style classes
74
+ const dropCapClasses: Record<string, string> = {
75
+ serif: 'font-serif',
76
+ 'sans-serif': 'font-sans',
77
+ decorative: 'font-serif italic',
78
+ }
79
+
80
+ // Build section classes with background
81
+ const sectionClasses = buildSectionClasses(
82
+ 'py-16 px-4 md:py-24',
83
+ { backgroundColor, className }
84
+ )
85
+
86
+ // Drop cap CSS (applied via first-letter pseudo-element)
87
+ const contentWithDropCap = showDropCap
88
+ ? `[&>p:first-of-type]:first-letter:float-left [&>p:first-of-type]:first-letter:text-7xl [&>p:first-of-type]:first-letter:font-bold [&>p:first-of-type]:first-letter:mr-3 [&>p:first-of-type]:first-letter:mt-1 [&>p:first-of-type]:first-letter:leading-none [&>p:first-of-type]:first-letter:${dropCapClasses[dropCapStyle]}`
89
+ : ''
90
+
91
+ // Divider component
92
+ const Divider = () => {
93
+ if (!showDividers) return null
94
+
95
+ const dividerContent: Record<string, React.ReactNode> = {
96
+ line: <hr className="border-t border-gray-300 dark:border-gray-700" />,
97
+ dots: (
98
+ <div className="text-center text-gray-400 dark:text-gray-600 text-2xl tracking-[0.5em]">
99
+ •••
100
+ </div>
101
+ ),
102
+ asterisks: (
103
+ <div className="text-center text-gray-400 dark:text-gray-600 text-xl tracking-widest">
104
+ * * *
105
+ </div>
106
+ ),
107
+ }
108
+
109
+ return (
110
+ <div className="my-12" data-cy={sel('blocks.postContent.divider')}>
111
+ {dividerContent[dividerStyle]}
112
+ </div>
113
+ )
114
+ }
115
+
116
+ return (
117
+ <section id={id} className={sectionClasses} data-cy={sel('blocks.postContent.container')}>
118
+ <div className="container mx-auto">
119
+ {/* Optional Section Title */}
120
+ {title && (
121
+ <div className={cn('mb-12 text-center', maxWidthClasses[maxWidth], 'mx-auto')}>
122
+ <h2 className="text-4xl font-bold md:text-5xl lg:text-6xl tracking-tight">
123
+ {title}
124
+ </h2>
125
+ </div>
126
+ )}
127
+
128
+ {/* Rich Text Article Content - Editorial Typography */}
129
+ <article
130
+ className={cn(
131
+ // Base typography
132
+ 'prose prose-lg dark:prose-invert',
133
+ // Max width for optimal reading
134
+ maxWidthClasses[maxWidth],
135
+ 'mx-auto',
136
+ // Font size
137
+ fontSizeClasses[fontSize],
138
+ // Line height
139
+ lineHeightClasses[lineHeight],
140
+ // Paragraph spacing
141
+ paragraphSpacingClasses[paragraphSpacing],
142
+ // Drop cap
143
+ contentWithDropCap,
144
+ // Enhanced prose styling for editorial content
145
+ 'prose-headings:font-bold prose-headings:tracking-tight',
146
+ 'prose-h2:text-3xl prose-h2:mt-12 prose-h2:mb-6',
147
+ 'prose-h3:text-2xl prose-h3:mt-10 prose-h3:mb-4',
148
+ 'prose-h4:text-xl prose-h4:mt-8 prose-h4:mb-3',
149
+ // Links
150
+ 'prose-a:text-primary prose-a:no-underline prose-a:font-medium hover:prose-a:underline',
151
+ // Blockquotes (pull quotes)
152
+ 'prose-blockquote:border-l-4 prose-blockquote:border-primary',
153
+ 'prose-blockquote:pl-6 prose-blockquote:italic',
154
+ 'prose-blockquote:text-xl prose-blockquote:leading-relaxed',
155
+ 'prose-blockquote:my-8',
156
+ // Lists
157
+ 'prose-ul:my-6 prose-ol:my-6',
158
+ 'prose-li:my-2',
159
+ // Images
160
+ 'prose-img:rounded-lg prose-img:shadow-lg',
161
+ 'prose-img:my-8',
162
+ // Code
163
+ 'prose-code:bg-gray-100 dark:prose-code:bg-gray-800',
164
+ 'prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded',
165
+ 'prose-code:text-sm',
166
+ // Pre/code blocks
167
+ 'prose-pre:bg-gray-900 prose-pre:text-gray-100',
168
+ 'prose-pre:rounded-lg prose-pre:my-6',
169
+ // Strong/bold
170
+ 'prose-strong:font-semibold prose-strong:text-gray-900 dark:prose-strong:text-gray-100',
171
+ // Em/italic
172
+ 'prose-em:italic'
173
+ )}
174
+ dangerouslySetInnerHTML={{ __html: content }}
175
+ />
176
+
177
+ {/* Optional Divider after content */}
178
+ {showDividers && <Divider />}
179
+
180
+ {/* Optional CTA */}
181
+ {cta && (
182
+ <div className={cn('mt-12 text-center', maxWidthClasses[maxWidth], 'mx-auto')}>
183
+ <a
184
+ href={cta.link}
185
+ target={cta.target}
186
+ rel={cta.target === '_blank' ? 'noopener noreferrer' : undefined}
187
+ className="inline-flex items-center justify-center rounded-md bg-primary px-8 py-3 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors shadow-md hover:shadow-lg"
188
+ data-cy={sel('blocks.postContent.cta')}
189
+ >
190
+ {cta.text}
191
+ </a>
192
+ </div>
193
+ )}
194
+ </div>
195
+ </section>
196
+ )
197
+ }
@@ -0,0 +1,11 @@
1
+ import type { BlockConfig } from '@nextsparkjs/core/types/blocks'
2
+
3
+ export const config: Omit<BlockConfig, 'schema' | 'fieldDefinitions' | 'Component' | 'examples'> = {
4
+ slug: 'post-content',
5
+ name: 'Post Content',
6
+ description: 'A rich text content block designed for long-form blog posts with editorial styling similar to Medium. Features beautiful typography, proper spacing, and optional elements like drop caps, pull quotes, and images.',
7
+ category: 'content',
8
+ icon: 'FileText',
9
+ thumbnail: '/theme/blocks/post-content/thumbnail.png',
10
+ scope: ['posts']
11
+ }
@@ -0,0 +1,33 @@
1
+ import type { BlockExample } from '@nextsparkjs/core/types/blocks'
2
+
3
+ export const examples: BlockExample[] = [
4
+ {
5
+ name: 'Default',
6
+ description: 'Standard blog post content',
7
+ props: {
8
+ content: `
9
+ <h2>Introduction</h2>
10
+ <p>This is a sample blog post demonstrating the post content block. It supports rich text formatting including headings, paragraphs, lists, and more.</p>
11
+
12
+ <h3>Key Features</h3>
13
+ <ul>
14
+ <li>Rich text editing with markdown support</li>
15
+ <li>Automatic table of contents generation</li>
16
+ <li>Code syntax highlighting</li>
17
+ <li>Image optimization</li>
18
+ </ul>
19
+
20
+ <h3>Getting Started</h3>
21
+ <p>To use this block, simply add it to your page and start writing. The editor supports all standard formatting options and will automatically handle styling to match your theme.</p>
22
+
23
+ <blockquote>
24
+ <p>"This platform has transformed how we create and manage content. The simplicity is unmatched." - Happy Customer</p>
25
+ </blockquote>
26
+
27
+ <p>Learn more by exploring our <a href="/docs">documentation</a> or reaching out to our support team.</p>
28
+ `,
29
+ backgroundColor: 'white',
30
+ maxWidth: '4xl',
31
+ },
32
+ },
33
+ ]