@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.
- package/about/business.md +49 -0
- package/about/features.json +302 -0
- package/about/team.md +79 -0
- package/api/ai/chat/stream/route.ts +212 -0
- package/api/ai/orchestrator/route.ts +226 -0
- package/api/ai/single-agent/route.ts +291 -0
- package/api/ai/usage/route.ts +122 -0
- package/blocks/benefits/component.tsx +100 -0
- package/blocks/benefits/config.ts +11 -0
- package/blocks/benefits/examples.ts +85 -0
- package/blocks/benefits/fields.ts +156 -0
- package/blocks/benefits/schema.ts +33 -0
- package/blocks/cta-section/component.tsx +100 -0
- package/blocks/cta-section/config.ts +11 -0
- package/blocks/cta-section/examples.ts +41 -0
- package/blocks/cta-section/fields.ts +89 -0
- package/blocks/cta-section/index.ts +6 -0
- package/blocks/cta-section/schema.ts +32 -0
- package/blocks/cta-section/thumbnail.png +1 -0
- package/blocks/faq-accordion/component.tsx +156 -0
- package/blocks/faq-accordion/config.ts +11 -0
- package/blocks/faq-accordion/examples.ts +77 -0
- package/blocks/faq-accordion/fields.ts +119 -0
- package/blocks/faq-accordion/index.ts +6 -0
- package/blocks/faq-accordion/schema.ts +45 -0
- package/blocks/features-grid/component.tsx +112 -0
- package/blocks/features-grid/config.ts +11 -0
- package/blocks/features-grid/examples.ts +63 -0
- package/blocks/features-grid/fields.ts +97 -0
- package/blocks/features-grid/index.ts +6 -0
- package/blocks/features-grid/schema.ts +40 -0
- package/blocks/features-grid/thumbnail.png +1 -0
- package/blocks/hero/component.tsx +100 -0
- package/blocks/hero/config.ts +11 -0
- package/blocks/hero/examples.ts +35 -0
- package/blocks/hero/fields.ts +60 -0
- package/blocks/hero/index.ts +6 -0
- package/blocks/hero/schema.ts +32 -0
- package/blocks/hero/thumbnail.png +1 -0
- package/blocks/hero/thumbnail.png.txt +6 -0
- package/blocks/hero-with-form/component.tsx +232 -0
- package/blocks/hero-with-form/config.ts +11 -0
- package/blocks/hero-with-form/examples.ts +16 -0
- package/blocks/hero-with-form/fields.ts +207 -0
- package/blocks/hero-with-form/index.ts +6 -0
- package/blocks/hero-with-form/schema.ts +54 -0
- package/blocks/jumbotron/component.tsx +136 -0
- package/blocks/jumbotron/config.ts +11 -0
- package/blocks/jumbotron/examples.ts +36 -0
- package/blocks/jumbotron/fields.ts +202 -0
- package/blocks/jumbotron/index.ts +6 -0
- package/blocks/jumbotron/schema.ts +55 -0
- package/blocks/logo-cloud/component.tsx +154 -0
- package/blocks/logo-cloud/config.ts +11 -0
- package/blocks/logo-cloud/examples.ts +34 -0
- package/blocks/logo-cloud/fields.ts +133 -0
- package/blocks/logo-cloud/index.ts +6 -0
- package/blocks/logo-cloud/schema.ts +46 -0
- package/blocks/post-content/component.tsx +197 -0
- package/blocks/post-content/config.ts +11 -0
- package/blocks/post-content/examples.ts +33 -0
- package/blocks/post-content/fields.ts +165 -0
- package/blocks/post-content/index.ts +4 -0
- package/blocks/post-content/schema.ts +46 -0
- package/blocks/pricing-table/component.tsx +154 -0
- package/blocks/pricing-table/config.ts +11 -0
- package/blocks/pricing-table/examples.ts +96 -0
- package/blocks/pricing-table/fields.ts +161 -0
- package/blocks/pricing-table/index.ts +4 -0
- package/blocks/pricing-table/schema.ts +50 -0
- package/blocks/split-content/component.tsx +135 -0
- package/blocks/split-content/config.ts +11 -0
- package/blocks/split-content/examples.ts +38 -0
- package/blocks/split-content/fields.ts +198 -0
- package/blocks/split-content/index.ts +6 -0
- package/blocks/split-content/schema.ts +67 -0
- package/blocks/stats-counter/component.tsx +124 -0
- package/blocks/stats-counter/config.ts +11 -0
- package/blocks/stats-counter/examples.ts +61 -0
- package/blocks/stats-counter/fields.ts +134 -0
- package/blocks/stats-counter/index.ts +6 -0
- package/blocks/stats-counter/schema.ts +47 -0
- package/blocks/testimonials/component.tsx +114 -0
- package/blocks/testimonials/config.ts +11 -0
- package/blocks/testimonials/examples.ts +65 -0
- package/blocks/testimonials/fields.ts +105 -0
- package/blocks/testimonials/index.ts +6 -0
- package/blocks/testimonials/schema.ts +41 -0
- package/blocks/testimonials/thumbnail.png +1 -0
- package/blocks/text-content/component.tsx +97 -0
- package/blocks/text-content/config.ts +11 -0
- package/blocks/text-content/examples.ts +30 -0
- package/blocks/text-content/fields.ts +88 -0
- package/blocks/text-content/index.ts +6 -0
- package/blocks/text-content/schema.ts +30 -0
- package/blocks/text-content/thumbnail.png +1 -0
- package/blocks/timeline/component.tsx +267 -0
- package/blocks/timeline/config.ts +11 -0
- package/blocks/timeline/examples.ts +68 -0
- package/blocks/timeline/fields.ts +147 -0
- package/blocks/timeline/index.ts +6 -0
- package/blocks/timeline/schema.ts +49 -0
- package/blocks/video-hero/component.tsx +270 -0
- package/blocks/video-hero/config.ts +11 -0
- package/blocks/video-hero/examples.ts +24 -0
- package/blocks/video-hero/fields.ts +98 -0
- package/blocks/video-hero/index.ts +6 -0
- package/blocks/video-hero/schema.ts +39 -0
- package/components/ai-chat/ChatPanel.tsx +575 -0
- package/components/ai-chat/ConversationItem.tsx +266 -0
- package/components/ai-chat/ConversationSidebar.tsx +99 -0
- package/components/ai-chat/MarkdownRenderer.tsx +15 -0
- package/components/ai-chat/Message.tsx +42 -0
- package/components/ai-chat/MessageInput.tsx +49 -0
- package/components/ai-chat/MessageList.tsx +46 -0
- package/components/ai-chat/TypingIndicator.tsx +11 -0
- package/config/app.config.ts +367 -0
- package/config/billing.config.ts +349 -0
- package/config/dashboard.config.ts +506 -0
- package/config/dev.config.ts +104 -0
- package/config/features.config.ts +203 -0
- package/config/flows.config.ts +129 -0
- package/config/permissions.config.ts +245 -0
- package/config/theme.config.ts +74 -0
- package/docs/01-overview/01-introduction.md +335 -0
- package/docs/01-overview/02-customization.md +671 -0
- package/docs/02-features/01-components.md +155 -0
- package/docs/02-features/02-styling.md +139 -0
- package/docs/02-features/03-tasks-entity.md +407 -0
- package/docs/03-ai/01-overview.md +211 -0
- package/docs/03-ai/02-customization.md +436 -0
- package/entities/customers/customers.config.ts +75 -0
- package/entities/customers/customers.fields.ts +165 -0
- package/entities/customers/customers.service.ts +516 -0
- package/entities/customers/customers.types.ts +83 -0
- package/entities/customers/messages/en.json +66 -0
- package/entities/customers/messages/es.json +66 -0
- package/entities/customers/migrations/001_customers_table.sql +102 -0
- package/entities/customers/migrations/002_customers_metas.sql +92 -0
- package/entities/pages/messages/en.json +41 -0
- package/entities/pages/messages/es.json +41 -0
- package/entities/pages/migrations/001_pages_table.sql +112 -0
- package/entities/pages/migrations/002_pages_metas.sql +56 -0
- package/entities/pages/migrations/003_add_status.sql +50 -0
- package/entities/pages/pages-management.service.ts +610 -0
- package/entities/pages/pages.config.ts +94 -0
- package/entities/pages/pages.fields.ts +101 -0
- package/entities/pages/pages.service.ts +290 -0
- package/entities/pages/pages.types.ts +124 -0
- package/entities/posts/components/post-header.tsx +97 -0
- package/entities/posts/messages/en.json +55 -0
- package/entities/posts/messages/es.json +55 -0
- package/entities/posts/migrations/001_posts_table.sql +115 -0
- package/entities/posts/migrations/003_add_status.sql +44 -0
- package/entities/posts/migrations/004_entity_taxonomy_relations.sql +129 -0
- package/entities/posts/migrations/006_posts_metas.sql +56 -0
- package/entities/posts/posts.config.ts +101 -0
- package/entities/posts/posts.fields.ts +116 -0
- package/entities/posts/posts.service.ts +376 -0
- package/entities/posts/posts.types.ts +74 -0
- package/entities/tasks/messages/en.json +204 -0
- package/entities/tasks/messages/es.json +204 -0
- package/entities/tasks/migrations/001_tasks_table.sql +105 -0
- package/entities/tasks/migrations/002_task_metas.sql +85 -0
- package/entities/tasks/migrations/sample_data.json +77 -0
- package/entities/tasks/tasks.config.ts +79 -0
- package/entities/tasks/tasks.fields.ts +196 -0
- package/entities/tasks/tasks.service.ts +541 -0
- package/entities/tasks/tasks.types.ts +56 -0
- package/lib/hooks/useAiChat.ts +114 -0
- package/lib/hooks/useConversations.ts +376 -0
- package/lib/hooks/useOrchestratorChat.ts +122 -0
- package/lib/hooks/usePersistentChat.ts +315 -0
- package/lib/hooks/useStreamingChat.ts +127 -0
- package/lib/hooks/useTokenUsage.ts +63 -0
- package/lib/langchain/agents/customer-assistant.md +69 -0
- package/lib/langchain/agents/index.ts +61 -0
- package/lib/langchain/agents/orchestrator.md +59 -0
- package/lib/langchain/agents/page-assistant.md +85 -0
- package/lib/langchain/agents/single-agent.md +46 -0
- package/lib/langchain/agents/task-assistant.md +55 -0
- package/lib/langchain/config.ts +45 -0
- package/lib/langchain/handlers/customer-handler.ts +338 -0
- package/lib/langchain/handlers/page-handler.ts +232 -0
- package/lib/langchain/handlers/task-handler.ts +323 -0
- package/lib/langchain/langchain.config.ts +223 -0
- package/lib/langchain/observability.config.ts +30 -0
- package/lib/langchain/orchestrator.ts +562 -0
- package/lib/langchain/tools/customers.ts +176 -0
- package/lib/langchain/tools/index.ts +10 -0
- package/lib/langchain/tools/orchestrator.ts +92 -0
- package/lib/langchain/tools/pages.ts +289 -0
- package/lib/langchain/tools/tasks.ts +167 -0
- package/lib/scheduled-actions/billing.ts +149 -0
- package/lib/scheduled-actions/index.ts +170 -0
- package/lib/scheduled-actions/webhook.ts +231 -0
- package/lib/selectors.ts +197 -0
- package/messages/de/admin.json +219 -0
- package/messages/de/aiUsage.json +36 -0
- package/messages/de/buttons.json +19 -0
- package/messages/de/categories.json +35 -0
- package/messages/de/common.json +16 -0
- package/messages/de/dev.json +101 -0
- package/messages/de/docs.json +27 -0
- package/messages/de/entities.json +7 -0
- package/messages/de/features.json +119 -0
- package/messages/de/footer.json +22 -0
- package/messages/de/home.json +57 -0
- package/messages/de/index.ts +39 -0
- package/messages/de/mobileNav.json +13 -0
- package/messages/de/navigation.json +8 -0
- package/messages/de/observability.json +74 -0
- package/messages/de/posts.json +54 -0
- package/messages/de/pricing.json +102 -0
- package/messages/de/support.json +9 -0
- package/messages/de/teams.json +8 -0
- package/messages/en/admin.json +219 -0
- package/messages/en/aiUsage.json +36 -0
- package/messages/en/buttons.json +19 -0
- package/messages/en/categories.json +35 -0
- package/messages/en/common.json +16 -0
- package/messages/en/dev.json +106 -0
- package/messages/en/docs.json +27 -0
- package/messages/en/entities.json +7 -0
- package/messages/en/features.json +119 -0
- package/messages/en/footer.json +22 -0
- package/messages/en/home.json +57 -0
- package/messages/en/index.ts +39 -0
- package/messages/en/mobileNav.json +13 -0
- package/messages/en/navigation.json +8 -0
- package/messages/en/observability.json +74 -0
- package/messages/en/posts.json +54 -0
- package/messages/en/pricing.json +102 -0
- package/messages/en/support.json +9 -0
- package/messages/en/teams.json +8 -0
- package/messages/es/admin.json +219 -0
- package/messages/es/aiUsage.json +36 -0
- package/messages/es/buttons.json +19 -0
- package/messages/es/categories.json +35 -0
- package/messages/es/common.json +16 -0
- package/messages/es/dev.json +101 -0
- package/messages/es/docs.json +27 -0
- package/messages/es/entities.json +7 -0
- package/messages/es/features.json +119 -0
- package/messages/es/footer.json +22 -0
- package/messages/es/home.json +57 -0
- package/messages/es/index.ts +39 -0
- package/messages/es/mobileNav.json +13 -0
- package/messages/es/navigation.json +8 -0
- package/messages/es/observability.json +74 -0
- package/messages/es/posts.json +54 -0
- package/messages/es/pricing.json +102 -0
- package/messages/es/support.json +9 -0
- package/messages/es/teams.json +8 -0
- package/messages/fr/admin.json +219 -0
- package/messages/fr/aiUsage.json +36 -0
- package/messages/fr/buttons.json +19 -0
- package/messages/fr/categories.json +35 -0
- package/messages/fr/common.json +16 -0
- package/messages/fr/dev.json +101 -0
- package/messages/fr/docs.json +27 -0
- package/messages/fr/entities.json +7 -0
- package/messages/fr/features.json +119 -0
- package/messages/fr/footer.json +22 -0
- package/messages/fr/home.json +57 -0
- package/messages/fr/index.ts +39 -0
- package/messages/fr/mobileNav.json +13 -0
- package/messages/fr/navigation.json +8 -0
- package/messages/fr/observability.json +74 -0
- package/messages/fr/posts.json +54 -0
- package/messages/fr/pricing.json +102 -0
- package/messages/fr/support.json +9 -0
- package/messages/fr/teams.json +8 -0
- package/messages/it/admin.json +219 -0
- package/messages/it/aiUsage.json +36 -0
- package/messages/it/buttons.json +19 -0
- package/messages/it/categories.json +35 -0
- package/messages/it/common.json +16 -0
- package/messages/it/dev.json +101 -0
- package/messages/it/docs.json +27 -0
- package/messages/it/entities.json +7 -0
- package/messages/it/features.json +119 -0
- package/messages/it/footer.json +22 -0
- package/messages/it/home.json +57 -0
- package/messages/it/index.ts +39 -0
- package/messages/it/mobileNav.json +13 -0
- package/messages/it/navigation.json +8 -0
- package/messages/it/observability.json +74 -0
- package/messages/it/posts.json +54 -0
- package/messages/it/pricing.json +102 -0
- package/messages/it/support.json +9 -0
- package/messages/it/teams.json +8 -0
- package/messages/pt/admin.json +219 -0
- package/messages/pt/aiUsage.json +36 -0
- package/messages/pt/buttons.json +19 -0
- package/messages/pt/categories.json +35 -0
- package/messages/pt/common.json +16 -0
- package/messages/pt/dev.json +101 -0
- package/messages/pt/docs.json +27 -0
- package/messages/pt/entities.json +7 -0
- package/messages/pt/features.json +119 -0
- package/messages/pt/footer.json +22 -0
- package/messages/pt/home.json +57 -0
- package/messages/pt/index.ts +39 -0
- package/messages/pt/mobileNav.json +13 -0
- package/messages/pt/navigation.json +8 -0
- package/messages/pt/observability.json +74 -0
- package/messages/pt/posts.json +54 -0
- package/messages/pt/pricing.json +102 -0
- package/messages/pt/support.json +9 -0
- package/messages/pt/teams.json +8 -0
- package/migrations/089_add_editor_team_role.sql +39 -0
- package/migrations/090_demo_users_teams.sql +540 -0
- package/migrations/091_greek_teams_billing.sql +523 -0
- package/migrations/092_billing_sample_data.sql +774 -0
- package/migrations/093_pages_sample_data.sql +1158 -0
- package/migrations/094_posts_sample_data.sql +278 -0
- package/migrations/095_tasks_sample_data.sql +440 -0
- package/migrations/096_customers_sample_data.sql +358 -0
- package/migrations/097_scheduled_actions_sample_data.sql +111 -0
- package/package.json +22 -0
- package/public/docs/desktop-layout-example.png +0 -0
- package/styles/components.css +11 -0
- package/styles/globals.css +179 -0
- package/templates/(public)/blog/[slug]/page.tsx +65 -0
- package/templates/(public)/layout.tsx +25 -0
- package/templates/(public)/page.tsx +200 -0
- package/templates/(public)/support/page.tsx +321 -0
- package/templates/dashboard/(main)/agent-multi/page.tsx +63 -0
- package/templates/dashboard/(main)/agent-single/page.tsx +142 -0
- package/templates/dashboard/(main)/settings/ai-usage/page.tsx +157 -0
- package/templates/superadmin/ai-observability/[traceId]/page.tsx +27 -0
- package/templates/superadmin/ai-observability/page.tsx +17 -0
|
@@ -0,0 +1,147 @@
|
|
|
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
|
+
* Timeline Block Field Definitions
|
|
10
|
+
*
|
|
11
|
+
* Organized into 3 tabs:
|
|
12
|
+
* - Content: title (from base), subtitle, items array
|
|
13
|
+
* - Design: backgroundColor (from base), layout, alternating, showConnector, variant
|
|
14
|
+
* - Advanced: className, id (from base)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Timeline-specific content fields
|
|
18
|
+
const timelineContentFields: FieldDefinition[] = [
|
|
19
|
+
{
|
|
20
|
+
name: 'subtitle',
|
|
21
|
+
label: 'Subtitle',
|
|
22
|
+
type: 'textarea',
|
|
23
|
+
tab: 'content',
|
|
24
|
+
required: false,
|
|
25
|
+
placeholder: 'A brief description of the timeline...',
|
|
26
|
+
helpText: 'Optional section description displayed below the title',
|
|
27
|
+
rows: 2,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'items',
|
|
31
|
+
label: 'Timeline Items',
|
|
32
|
+
type: 'array',
|
|
33
|
+
tab: 'content',
|
|
34
|
+
required: true,
|
|
35
|
+
description: 'Events or steps to display in the timeline',
|
|
36
|
+
helpText: 'Add up to 20 timeline items with date, title, description, and optional icon',
|
|
37
|
+
minItems: 1,
|
|
38
|
+
maxItems: 20,
|
|
39
|
+
itemFields: [
|
|
40
|
+
{
|
|
41
|
+
name: 'date',
|
|
42
|
+
label: 'Date / Step',
|
|
43
|
+
type: 'text',
|
|
44
|
+
tab: 'content',
|
|
45
|
+
required: true,
|
|
46
|
+
placeholder: '2024, Step 1, Q1 2024',
|
|
47
|
+
helpText: 'Date, step number, or time period label',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'title',
|
|
51
|
+
label: 'Title',
|
|
52
|
+
type: 'text',
|
|
53
|
+
tab: 'content',
|
|
54
|
+
required: true,
|
|
55
|
+
placeholder: 'Event or milestone title',
|
|
56
|
+
maxLength: 100,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'description',
|
|
60
|
+
label: 'Description',
|
|
61
|
+
type: 'textarea',
|
|
62
|
+
tab: 'content',
|
|
63
|
+
required: false,
|
|
64
|
+
placeholder: 'Describe this event or step...',
|
|
65
|
+
rows: 3,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'icon',
|
|
69
|
+
label: 'Icon Name (optional)',
|
|
70
|
+
type: 'text',
|
|
71
|
+
tab: 'content',
|
|
72
|
+
required: false,
|
|
73
|
+
placeholder: 'Rocket, Check, Star, Calendar',
|
|
74
|
+
helpText: 'Lucide icon name (optional)',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
// Timeline-specific design fields
|
|
81
|
+
const timelineDesignFields: FieldDefinition[] = [
|
|
82
|
+
{
|
|
83
|
+
name: 'layout',
|
|
84
|
+
label: 'Layout Direction',
|
|
85
|
+
type: 'select',
|
|
86
|
+
tab: 'design',
|
|
87
|
+
required: false,
|
|
88
|
+
default: 'vertical',
|
|
89
|
+
description: 'Timeline orientation',
|
|
90
|
+
helpText: 'Horizontal layout becomes vertical on mobile',
|
|
91
|
+
options: [
|
|
92
|
+
{ label: 'Vertical', value: 'vertical' },
|
|
93
|
+
{ label: 'Horizontal', value: 'horizontal' },
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'alternating',
|
|
98
|
+
label: 'Alternating Sides',
|
|
99
|
+
type: 'checkbox',
|
|
100
|
+
tab: 'design',
|
|
101
|
+
required: false,
|
|
102
|
+
default: true,
|
|
103
|
+
helpText: 'Alternate items between left and right (vertical layout only)',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'showConnector',
|
|
107
|
+
label: 'Show Connector Line',
|
|
108
|
+
type: 'checkbox',
|
|
109
|
+
tab: 'design',
|
|
110
|
+
required: false,
|
|
111
|
+
default: true,
|
|
112
|
+
helpText: 'Display line connecting timeline items',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'variant',
|
|
116
|
+
label: 'Visual Style',
|
|
117
|
+
type: 'select',
|
|
118
|
+
tab: 'design',
|
|
119
|
+
required: false,
|
|
120
|
+
default: 'default',
|
|
121
|
+
description: 'Timeline item appearance',
|
|
122
|
+
options: [
|
|
123
|
+
{ label: 'Default', value: 'default' },
|
|
124
|
+
{ label: 'Minimal', value: 'minimal' },
|
|
125
|
+
{ label: 'Cards', value: 'cards' },
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Complete field definitions organized by tab
|
|
132
|
+
*/
|
|
133
|
+
export const fieldDefinitions: FieldDefinition[] = [
|
|
134
|
+
// Content tab: base fields + timeline-specific
|
|
135
|
+
...baseContentFields,
|
|
136
|
+
...timelineContentFields,
|
|
137
|
+
|
|
138
|
+
// Design tab: base fields + timeline-specific
|
|
139
|
+
...baseDesignFields,
|
|
140
|
+
...timelineDesignFields,
|
|
141
|
+
|
|
142
|
+
// Advanced tab: base fields only
|
|
143
|
+
...baseAdvancedFields,
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
// Alias for compatibility
|
|
147
|
+
export const fields = fieldDefinitions
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { baseBlockSchema } from '@nextsparkjs/core/types/blocks'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Timeline Item Schema
|
|
6
|
+
* Individual event/step in the timeline
|
|
7
|
+
*/
|
|
8
|
+
const timelineItemSchema = z.object({
|
|
9
|
+
date: z.string().min(1, 'Date or step is required'),
|
|
10
|
+
title: z.string().min(1, 'Title is required').max(100),
|
|
11
|
+
description: z.string().optional().default(''),
|
|
12
|
+
icon: z.string().optional().default(''),
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export type TimelineItem = z.infer<typeof timelineItemSchema>
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Timeline Block Schema
|
|
19
|
+
*
|
|
20
|
+
* Extends base schema with:
|
|
21
|
+
* - subtitle: Section description (in addition to title)
|
|
22
|
+
* - items: Array of timeline items (date, title, description, icon)
|
|
23
|
+
* - layout: Vertical or horizontal direction
|
|
24
|
+
* - alternating: Alternate items left/right (vertical only)
|
|
25
|
+
* - showConnector: Show connecting line between items
|
|
26
|
+
* - variant: Visual style (default, minimal, cards)
|
|
27
|
+
*
|
|
28
|
+
* Note: Uses base schema title, backgroundColor, className, id
|
|
29
|
+
*/
|
|
30
|
+
export const timelineSpecificSchema = z.object({
|
|
31
|
+
// Content fields
|
|
32
|
+
subtitle: z.string().optional().default(''),
|
|
33
|
+
items: z.array(timelineItemSchema)
|
|
34
|
+
.min(1, 'At least one timeline item is required')
|
|
35
|
+
.max(20, 'Maximum 20 timeline items allowed'),
|
|
36
|
+
|
|
37
|
+
// Design fields
|
|
38
|
+
layout: z.enum(['vertical', 'horizontal']).default('vertical'),
|
|
39
|
+
alternating: z.boolean().default(true),
|
|
40
|
+
showConnector: z.boolean().default(true),
|
|
41
|
+
variant: z.enum(['default', 'minimal', 'cards']).default('default'),
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Complete Timeline Block Schema
|
|
46
|
+
*/
|
|
47
|
+
export const schema = baseBlockSchema.merge(timelineSpecificSchema)
|
|
48
|
+
|
|
49
|
+
export type TimelineBlockProps = z.infer<typeof schema>
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Button } from '@nextsparkjs/core/components/ui/button'
|
|
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 { VideoHeroBlockProps } from './schema'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Video Hero Block Component
|
|
10
|
+
*
|
|
11
|
+
* Props from 3-tab structure:
|
|
12
|
+
* - Content: title, content (subtitle), cta, videoUrl, videoThumbnail
|
|
13
|
+
* - Design: backgroundColor, layout, autoplay, overlayOpacity
|
|
14
|
+
* - Advanced: className, id
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Extract video ID from YouTube or Vimeo URL
|
|
19
|
+
*/
|
|
20
|
+
function parseVideoUrl(url: string): { platform: 'youtube' | 'vimeo' | null; videoId: string | null } {
|
|
21
|
+
// YouTube patterns
|
|
22
|
+
const youtubePatterns = [
|
|
23
|
+
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]+)/,
|
|
24
|
+
/youtube\.com\/watch\?.*v=([a-zA-Z0-9_-]+)/,
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
for (const pattern of youtubePatterns) {
|
|
28
|
+
const match = url.match(pattern)
|
|
29
|
+
if (match) {
|
|
30
|
+
return { platform: 'youtube', videoId: match[1] }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Vimeo pattern
|
|
35
|
+
const vimeoPattern = /vimeo\.com\/(\d+)/
|
|
36
|
+
const vimeoMatch = url.match(vimeoPattern)
|
|
37
|
+
if (vimeoMatch) {
|
|
38
|
+
return { platform: 'vimeo', videoId: vimeoMatch[1] }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { platform: null, videoId: null }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get embed URL for video
|
|
46
|
+
*/
|
|
47
|
+
function getEmbedUrl(videoUrl: string, autoplay: boolean): string | null {
|
|
48
|
+
const { platform, videoId } = parseVideoUrl(videoUrl)
|
|
49
|
+
|
|
50
|
+
if (!platform || !videoId) return null
|
|
51
|
+
|
|
52
|
+
if (platform === 'youtube') {
|
|
53
|
+
const params = new URLSearchParams({
|
|
54
|
+
autoplay: autoplay ? '1' : '0',
|
|
55
|
+
mute: autoplay ? '1' : '0', // Mute if autoplay (browser requirement)
|
|
56
|
+
rel: '0', // Don't show related videos
|
|
57
|
+
modestbranding: '1', // Minimal YouTube branding
|
|
58
|
+
})
|
|
59
|
+
return `https://www.youtube.com/embed/${videoId}?${params.toString()}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (platform === 'vimeo') {
|
|
63
|
+
const params = new URLSearchParams({
|
|
64
|
+
autoplay: autoplay ? '1' : '0',
|
|
65
|
+
muted: autoplay ? '1' : '0',
|
|
66
|
+
title: '0',
|
|
67
|
+
byline: '0',
|
|
68
|
+
portrait: '0',
|
|
69
|
+
})
|
|
70
|
+
return `https://player.vimeo.com/video/${videoId}?${params.toString()}`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function VideoHeroBlock({
|
|
77
|
+
// Base content props
|
|
78
|
+
title,
|
|
79
|
+
content,
|
|
80
|
+
cta,
|
|
81
|
+
// Video-specific content
|
|
82
|
+
videoUrl,
|
|
83
|
+
videoThumbnail,
|
|
84
|
+
// Base design props
|
|
85
|
+
backgroundColor,
|
|
86
|
+
// Video-specific design
|
|
87
|
+
layout = 'inline',
|
|
88
|
+
autoplay = false,
|
|
89
|
+
overlayOpacity = '40',
|
|
90
|
+
// Base advanced props
|
|
91
|
+
className,
|
|
92
|
+
id,
|
|
93
|
+
}: VideoHeroBlockProps) {
|
|
94
|
+
const embedUrl = getEmbedUrl(videoUrl, autoplay)
|
|
95
|
+
|
|
96
|
+
// Build base section classes
|
|
97
|
+
const baseSectionClasses = buildSectionClasses(
|
|
98
|
+
cn('relative overflow-hidden'),
|
|
99
|
+
{ backgroundColor, className }
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
// Overlay opacity class for background layout
|
|
103
|
+
const overlayClass = overlayOpacity === '0'
|
|
104
|
+
? 'bg-transparent'
|
|
105
|
+
: `bg-black/${overlayOpacity}`
|
|
106
|
+
|
|
107
|
+
// Video container with 16:9 aspect ratio
|
|
108
|
+
const VideoEmbed = () => {
|
|
109
|
+
if (!embedUrl) {
|
|
110
|
+
return (
|
|
111
|
+
<div className="flex items-center justify-center bg-gray-200 text-gray-600 p-8 rounded-lg">
|
|
112
|
+
<p>Invalid video URL. Please provide a valid YouTube or Vimeo link.</p>
|
|
113
|
+
</div>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<div className="relative w-full pb-[56.25%]">
|
|
119
|
+
<iframe
|
|
120
|
+
src={embedUrl}
|
|
121
|
+
className="absolute inset-0 w-full h-full"
|
|
122
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
123
|
+
allowFullScreen
|
|
124
|
+
title={title || 'Video'}
|
|
125
|
+
/>
|
|
126
|
+
</div>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// LAYOUT: Background (fullscreen video with overlay)
|
|
131
|
+
if (layout === 'background') {
|
|
132
|
+
return (
|
|
133
|
+
<section
|
|
134
|
+
id={id}
|
|
135
|
+
className={cn(baseSectionClasses, 'min-h-[600px] flex items-center justify-center')}
|
|
136
|
+
data-cy={sel('blocks.videoHero.container')}
|
|
137
|
+
>
|
|
138
|
+
{/* Background Video */}
|
|
139
|
+
<div className="absolute inset-0 z-0">
|
|
140
|
+
{embedUrl ? (
|
|
141
|
+
<iframe
|
|
142
|
+
src={embedUrl}
|
|
143
|
+
className="absolute inset-0 w-full h-full object-cover"
|
|
144
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
145
|
+
title={title || 'Background Video'}
|
|
146
|
+
/>
|
|
147
|
+
) : null}
|
|
148
|
+
<div className={cn('absolute inset-0', overlayClass)} />
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
{/* Content Overlay */}
|
|
152
|
+
<div className="container relative z-10 mx-auto max-w-4xl text-center px-4 py-20 text-white">
|
|
153
|
+
{title && (
|
|
154
|
+
<h1 className="mb-6 text-5xl font-bold leading-tight md:text-6xl lg:text-7xl">
|
|
155
|
+
{title}
|
|
156
|
+
</h1>
|
|
157
|
+
)}
|
|
158
|
+
|
|
159
|
+
{content && (
|
|
160
|
+
<p className="mb-8 text-xl md:text-2xl opacity-90">
|
|
161
|
+
{content}
|
|
162
|
+
</p>
|
|
163
|
+
)}
|
|
164
|
+
|
|
165
|
+
{cta && (
|
|
166
|
+
<Button asChild size="lg" className="text-lg px-8 py-6">
|
|
167
|
+
<a
|
|
168
|
+
href={cta.link}
|
|
169
|
+
target={cta.target}
|
|
170
|
+
rel={cta.target === '_blank' ? 'noopener noreferrer' : undefined}
|
|
171
|
+
>
|
|
172
|
+
{cta.text}
|
|
173
|
+
</a>
|
|
174
|
+
</Button>
|
|
175
|
+
)}
|
|
176
|
+
</div>
|
|
177
|
+
</section>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// LAYOUT: Side-by-side (text left, video right)
|
|
182
|
+
if (layout === 'side-by-side') {
|
|
183
|
+
return (
|
|
184
|
+
<section
|
|
185
|
+
id={id}
|
|
186
|
+
className={cn(baseSectionClasses, 'py-16 px-4')}
|
|
187
|
+
data-cy={sel('blocks.videoHero.container')}
|
|
188
|
+
>
|
|
189
|
+
<div className="container mx-auto">
|
|
190
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center">
|
|
191
|
+
{/* Text Content */}
|
|
192
|
+
<div className="space-y-6">
|
|
193
|
+
{title && (
|
|
194
|
+
<h1 className="text-4xl font-bold leading-tight md:text-5xl lg:text-6xl">
|
|
195
|
+
{title}
|
|
196
|
+
</h1>
|
|
197
|
+
)}
|
|
198
|
+
|
|
199
|
+
{content && (
|
|
200
|
+
<p className="text-lg md:text-xl text-gray-600">
|
|
201
|
+
{content}
|
|
202
|
+
</p>
|
|
203
|
+
)}
|
|
204
|
+
|
|
205
|
+
{cta && (
|
|
206
|
+
<Button asChild size="lg" className="text-lg px-8 py-6">
|
|
207
|
+
<a
|
|
208
|
+
href={cta.link}
|
|
209
|
+
target={cta.target}
|
|
210
|
+
rel={cta.target === '_blank' ? 'noopener noreferrer' : undefined}
|
|
211
|
+
>
|
|
212
|
+
{cta.text}
|
|
213
|
+
</a>
|
|
214
|
+
</Button>
|
|
215
|
+
)}
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
{/* Video */}
|
|
219
|
+
<div>
|
|
220
|
+
<VideoEmbed />
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
</section>
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// LAYOUT: Inline (default - text above, video below)
|
|
229
|
+
return (
|
|
230
|
+
<section
|
|
231
|
+
id={id}
|
|
232
|
+
className={cn(baseSectionClasses, 'py-16 px-4')}
|
|
233
|
+
data-cy="block-video-hero"
|
|
234
|
+
>
|
|
235
|
+
<div className="container mx-auto max-w-5xl">
|
|
236
|
+
{/* Text Content */}
|
|
237
|
+
<div className="text-center mb-10 space-y-6">
|
|
238
|
+
{title && (
|
|
239
|
+
<h1 className="text-4xl font-bold leading-tight md:text-5xl lg:text-6xl">
|
|
240
|
+
{title}
|
|
241
|
+
</h1>
|
|
242
|
+
)}
|
|
243
|
+
|
|
244
|
+
{content && (
|
|
245
|
+
<p className="text-lg md:text-xl text-gray-600 max-w-3xl mx-auto">
|
|
246
|
+
{content}
|
|
247
|
+
</p>
|
|
248
|
+
)}
|
|
249
|
+
|
|
250
|
+
{cta && (
|
|
251
|
+
<Button asChild size="lg" className="text-lg px-8 py-6">
|
|
252
|
+
<a
|
|
253
|
+
href={cta.link}
|
|
254
|
+
target={cta.target}
|
|
255
|
+
rel={cta.target === '_blank' ? 'noopener noreferrer' : undefined}
|
|
256
|
+
>
|
|
257
|
+
{cta.text}
|
|
258
|
+
</a>
|
|
259
|
+
</Button>
|
|
260
|
+
)}
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
{/* Video */}
|
|
264
|
+
<div className="max-w-4xl mx-auto">
|
|
265
|
+
<VideoEmbed />
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
</section>
|
|
269
|
+
)
|
|
270
|
+
}
|
|
@@ -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: 'video-hero',
|
|
5
|
+
name: 'Video Hero',
|
|
6
|
+
description: 'A hero section with embedded video (YouTube/Vimeo) either as background or inline, with title, subtitle, and optional CTA',
|
|
7
|
+
category: 'hero',
|
|
8
|
+
icon: 'Video',
|
|
9
|
+
thumbnail: '/theme/blocks/video-hero/thumbnail.png',
|
|
10
|
+
scope: ['pages', 'posts']
|
|
11
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BlockExample } from '@nextsparkjs/core/types/blocks'
|
|
2
|
+
|
|
3
|
+
export const examples: BlockExample[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'Default',
|
|
6
|
+
description: 'Video hero with poster image',
|
|
7
|
+
props: {
|
|
8
|
+
title: 'See Our Platform in Action',
|
|
9
|
+
content: 'Watch how leading teams are transforming their workflows with our powerful tools.',
|
|
10
|
+
videoUrl: 'https://storage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
|
|
11
|
+
posterImage: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=1920',
|
|
12
|
+
cta: {
|
|
13
|
+
text: 'Start Free Trial',
|
|
14
|
+
link: '/signup',
|
|
15
|
+
target: '_self',
|
|
16
|
+
},
|
|
17
|
+
backgroundColor: 'gray-900',
|
|
18
|
+
textColor: 'light',
|
|
19
|
+
autoplay: false,
|
|
20
|
+
muted: true,
|
|
21
|
+
loop: true,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
]
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
* Video Hero Block Field Definitions
|
|
10
|
+
*
|
|
11
|
+
* Organized into 3 tabs:
|
|
12
|
+
* - Content: title, content (subtitle), cta (from base) + videoUrl, videoThumbnail
|
|
13
|
+
* - Design: backgroundColor (from base) + layout, autoplay, overlayOpacity
|
|
14
|
+
* - Advanced: className, id (from base)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Video Hero-specific content fields
|
|
18
|
+
const videoHeroContentFields: FieldDefinition[] = [
|
|
19
|
+
{
|
|
20
|
+
name: 'videoUrl',
|
|
21
|
+
label: 'Video URL',
|
|
22
|
+
type: 'url',
|
|
23
|
+
tab: 'content',
|
|
24
|
+
required: true,
|
|
25
|
+
placeholder: 'https://www.youtube.com/watch?v=... or https://vimeo.com/...',
|
|
26
|
+
helpText: 'YouTube or Vimeo video URL',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'videoThumbnail',
|
|
30
|
+
label: 'Custom Thumbnail',
|
|
31
|
+
type: 'image',
|
|
32
|
+
tab: 'content',
|
|
33
|
+
required: false,
|
|
34
|
+
helpText: 'Optional custom thumbnail shown before video plays (recommended: 1920x1080px)',
|
|
35
|
+
},
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
// Video Hero-specific design fields
|
|
39
|
+
const videoHeroDesignFields: FieldDefinition[] = [
|
|
40
|
+
{
|
|
41
|
+
name: 'layout',
|
|
42
|
+
label: 'Video Layout',
|
|
43
|
+
type: 'select',
|
|
44
|
+
tab: 'design',
|
|
45
|
+
required: false,
|
|
46
|
+
default: 'inline',
|
|
47
|
+
helpText: 'How the video is displayed',
|
|
48
|
+
options: [
|
|
49
|
+
{ label: 'Inline (video centered with text above)', value: 'inline' },
|
|
50
|
+
{ label: 'Background (video as fullscreen background)', value: 'background' },
|
|
51
|
+
{ label: 'Side by Side (text left, video right)', value: 'side-by-side' },
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'autoplay',
|
|
56
|
+
label: 'Autoplay Video',
|
|
57
|
+
type: 'checkbox',
|
|
58
|
+
tab: 'design',
|
|
59
|
+
required: false,
|
|
60
|
+
default: false,
|
|
61
|
+
checkboxLabel: 'Automatically play video when loaded (muted)',
|
|
62
|
+
helpText: 'Note: Most browsers require videos to be muted for autoplay',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'overlayOpacity',
|
|
66
|
+
label: 'Overlay Opacity',
|
|
67
|
+
type: 'select',
|
|
68
|
+
tab: 'design',
|
|
69
|
+
required: false,
|
|
70
|
+
default: '40',
|
|
71
|
+
helpText: 'Darkness of overlay for background layout (helps text readability)',
|
|
72
|
+
options: [
|
|
73
|
+
{ label: 'None (0%)', value: '0' },
|
|
74
|
+
{ label: 'Light (20%)', value: '20' },
|
|
75
|
+
{ label: 'Medium (40%)', value: '40' },
|
|
76
|
+
{ label: 'Dark (60%)', value: '60' },
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Complete field definitions organized by tab
|
|
83
|
+
*/
|
|
84
|
+
export const fieldDefinitions: FieldDefinition[] = [
|
|
85
|
+
// Content tab: base fields + video-specific
|
|
86
|
+
...baseContentFields,
|
|
87
|
+
...videoHeroContentFields,
|
|
88
|
+
|
|
89
|
+
// Design tab: base fields + video-specific
|
|
90
|
+
...baseDesignFields,
|
|
91
|
+
...videoHeroDesignFields,
|
|
92
|
+
|
|
93
|
+
// Advanced tab: base fields only
|
|
94
|
+
...baseAdvancedFields,
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
// Alias for compatibility
|
|
98
|
+
export const fields = fieldDefinitions
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import {
|
|
3
|
+
baseBlockSchema,
|
|
4
|
+
type BaseBlockProps,
|
|
5
|
+
} from '@nextsparkjs/core/types/blocks'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Video Hero Block Schema
|
|
9
|
+
*
|
|
10
|
+
* Extends base schema with video-specific fields:
|
|
11
|
+
* - videoUrl: YouTube or Vimeo URL (required)
|
|
12
|
+
* - videoThumbnail: Custom thumbnail image
|
|
13
|
+
* - layout: How video is displayed (inline, background, side-by-side)
|
|
14
|
+
* - autoplay: Auto-play video when loaded (muted)
|
|
15
|
+
* - overlayOpacity: Opacity for background layout overlay
|
|
16
|
+
*
|
|
17
|
+
* Note: Uses base schema title, content (as subtitle), cta, backgroundColor, className, id
|
|
18
|
+
*/
|
|
19
|
+
export const videoHeroSpecificSchema = z.object({
|
|
20
|
+
// Content fields
|
|
21
|
+
videoUrl: z.string().url('Must be a valid YouTube or Vimeo URL'),
|
|
22
|
+
videoThumbnail: z.string().url('Must be a valid URL').optional(),
|
|
23
|
+
|
|
24
|
+
// Design fields
|
|
25
|
+
layout: z.enum(['inline', 'background', 'side-by-side']).default('inline'),
|
|
26
|
+
autoplay: z.boolean().default(false),
|
|
27
|
+
overlayOpacity: z.enum(['0', '20', '40', '60']).default('40'),
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Complete Video Hero Block Schema
|
|
32
|
+
* Combines base fields + video-hero-specific fields
|
|
33
|
+
*/
|
|
34
|
+
export const schema = baseBlockSchema.merge(videoHeroSpecificSchema)
|
|
35
|
+
|
|
36
|
+
export type VideoHeroBlockProps = z.infer<typeof schema>
|
|
37
|
+
|
|
38
|
+
// Also export for type-only imports
|
|
39
|
+
export type { BaseBlockProps }
|