diff --git a/README.md b/README.md index c6ec0b20a..3b1b6a943 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ The official documentation is built with Fumadocs and Next.js. ## ๐Ÿ› ๏ธ The Protocol Architecture -The ObjectStack Protocol (`@objectstack/spec`) is divided into five core modules: +The ObjectStack Protocol (`@objectstack/spec`) is divided into six core modules: ### 1. Data Protocol (ObjectQL) Defines the "Shape of Data" and business logic. @@ -75,7 +75,16 @@ Defines the "Shape of Interaction" for rendering interfaces. - **Theming:** Color palettes, typography, breakpoints, animations - **Widgets:** Custom field components -### 3. System Protocol (ObjectOS) +### 3. Website Protocol +Defines the "Shape of Marketing Websites" for landing pages and official sites. +- **Landing Pages:** Hero sections, features, testimonials, pricing, CTAs, FAQ +- **Navigation:** Website menus, dropdowns, mega menus, footers +- **SEO:** Meta tags, Open Graph, Twitter Cards, structured data +- **Analytics:** Google Analytics, GTM, Facebook Pixel integration +- **Theming:** Brand colors, typography, dark mode +- **Preview Release:** March 2026 + +### 4. System Protocol (ObjectOS) Defines the "Runtime Environment" and platform capabilities. - **Manifest:** Application packaging (`objectstack.config.ts`) - **Identity:** Authentication, Roles, Territories, Licenses, Organizations @@ -85,7 +94,7 @@ Defines the "Runtime Environment" and platform capabilities. - **I18n:** Translation and internationalization support - **Platform:** Events, Real-time sync, Audit logging, Background jobs, Multi-tenancy -### 4. AI Protocol +### 5. AI Protocol Defines AI agent integration capabilities. - **Agent:** AI agent definitions and configurations - **Model Registry:** LLM registry and selection @@ -96,7 +105,7 @@ Defines AI agent integration capabilities. - **Predictive:** Predictive analytics models - **Workflow Automation:** AI-powered workflow automation -### 5. API Protocol +### 6. API Protocol Defines standardized API contracts. - **Envelopes:** Response structures (BaseResponse, ListRecordResponse, etc.) - **Requests:** Request payloads (CreateRequest, UpdateRequest, BulkRequest, etc.) diff --git a/content/docs/references/meta.cn.json b/content/docs/references/meta.cn.json index 360bef3ad..a06b89a22 100644 --- a/content/docs/references/meta.cn.json +++ b/content/docs/references/meta.cn.json @@ -3,6 +3,7 @@ "pages": [ "data", "ui", + "website", "auth", "automation", "system", diff --git a/content/docs/references/website/content-block/ButtonConfig.mdx b/content/docs/references/website/content-block/ButtonConfig.mdx new file mode 100644 index 000000000..15cf96797 --- /dev/null +++ b/content/docs/references/website/content-block/ButtonConfig.mdx @@ -0,0 +1,15 @@ +--- +title: ButtonConfig +description: ButtonConfig Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **text** | `string` | โœ… | Button text | +| **href** | `string` | โœ… | Button target URL or action | +| **variant** | `Enum<'primary' \| 'secondary' \| 'outline' \| 'ghost' \| 'link'>` | optional | | +| **size** | `Enum<'sm' \| 'md' \| 'lg'>` | optional | | +| **target** | `Enum<'_self' \| '_blank'>` | optional | | +| **icon** | `string` | optional | Icon name | diff --git a/content/docs/references/website/content-block/ContentBlock.mdx b/content/docs/references/website/content-block/ContentBlock.mdx new file mode 100644 index 000000000..4aaff0c5a --- /dev/null +++ b/content/docs/references/website/content-block/ContentBlock.mdx @@ -0,0 +1,5 @@ +--- +title: ContentBlock +description: ContentBlock Schema Reference +--- + diff --git a/content/docs/references/website/content-block/ContentSection.mdx b/content/docs/references/website/content-block/ContentSection.mdx new file mode 100644 index 000000000..95ae76c90 --- /dev/null +++ b/content/docs/references/website/content-block/ContentSection.mdx @@ -0,0 +1,15 @@ +--- +title: ContentSection +description: ContentSection Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `string` | โœ… | | +| **id** | `string` | optional | Section ID for anchor links | +| **title** | `string` | optional | Section title | +| **content** | `string` | โœ… | Content in markdown or HTML format | +| **align** | `Enum<'left' \| 'center' \| 'right'>` | optional | | +| **maxWidth** | `Enum<'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>` | optional | | diff --git a/content/docs/references/website/content-block/CtaSection.mdx b/content/docs/references/website/content-block/CtaSection.mdx new file mode 100644 index 000000000..101ff3e2a --- /dev/null +++ b/content/docs/references/website/content-block/CtaSection.mdx @@ -0,0 +1,16 @@ +--- +title: CtaSection +description: CtaSection Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `string` | โœ… | | +| **id** | `string` | optional | Section ID for anchor links | +| **headline** | `string` | โœ… | CTA headline | +| **description** | `string` | optional | CTA description | +| **buttons** | `object[]` | โœ… | CTA buttons | +| **backgroundColor** | `string` | optional | Background color (hex, rgb, or preset) | +| **align** | `Enum<'left' \| 'center' \| 'right'>` | optional | | diff --git a/content/docs/references/website/content-block/CustomHtmlSection.mdx b/content/docs/references/website/content-block/CustomHtmlSection.mdx new file mode 100644 index 000000000..fc4df2087 --- /dev/null +++ b/content/docs/references/website/content-block/CustomHtmlSection.mdx @@ -0,0 +1,12 @@ +--- +title: CustomHtmlSection +description: CustomHtmlSection Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `string` | โœ… | | +| **id** | `string` | optional | Section ID for anchor links | +| **html** | `string` | โœ… | Custom HTML content | diff --git a/content/docs/references/website/content-block/FaqItem.mdx b/content/docs/references/website/content-block/FaqItem.mdx new file mode 100644 index 000000000..a74a3f974 --- /dev/null +++ b/content/docs/references/website/content-block/FaqItem.mdx @@ -0,0 +1,11 @@ +--- +title: FaqItem +description: FaqItem Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **question** | `string` | โœ… | FAQ question | +| **answer** | `string` | โœ… | FAQ answer (markdown or HTML) | diff --git a/content/docs/references/website/content-block/FaqSection.mdx b/content/docs/references/website/content-block/FaqSection.mdx new file mode 100644 index 000000000..e3911859c --- /dev/null +++ b/content/docs/references/website/content-block/FaqSection.mdx @@ -0,0 +1,15 @@ +--- +title: FaqSection +description: FaqSection Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `string` | โœ… | | +| **id** | `string` | optional | Section ID for anchor links | +| **title** | `string` | optional | Section title | +| **description** | `string` | optional | Section description | +| **items** | `object[]` | โœ… | FAQ items | +| **style** | `Enum<'accordion' \| 'grid'>` | optional | | diff --git a/content/docs/references/website/content-block/FeatureItem.mdx b/content/docs/references/website/content-block/FeatureItem.mdx new file mode 100644 index 000000000..a4b7bf2da --- /dev/null +++ b/content/docs/references/website/content-block/FeatureItem.mdx @@ -0,0 +1,13 @@ +--- +title: FeatureItem +description: FeatureItem Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **icon** | `string` | optional | Icon name or URL | +| **title** | `string` | โœ… | Feature title | +| **description** | `string` | โœ… | Feature description | +| **link** | `object` | optional | Optional link to learn more | diff --git a/content/docs/references/website/content-block/FeaturesSection.mdx b/content/docs/references/website/content-block/FeaturesSection.mdx new file mode 100644 index 000000000..7c4c7d70b --- /dev/null +++ b/content/docs/references/website/content-block/FeaturesSection.mdx @@ -0,0 +1,16 @@ +--- +title: FeaturesSection +description: FeaturesSection Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `string` | โœ… | | +| **id** | `string` | optional | Section ID for anchor links | +| **title** | `string` | optional | Section title | +| **description** | `string` | optional | Section description | +| **features** | `object[]` | โœ… | List of features | +| **columns** | `Enum<'2' \| '3' \| '4'>` | optional | Number of columns in grid | +| **align** | `Enum<'left' \| 'center' \| 'right'>` | optional | | diff --git a/content/docs/references/website/content-block/HeroSection.mdx b/content/docs/references/website/content-block/HeroSection.mdx new file mode 100644 index 000000000..9a148fcdc --- /dev/null +++ b/content/docs/references/website/content-block/HeroSection.mdx @@ -0,0 +1,19 @@ +--- +title: HeroSection +description: HeroSection Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `string` | โœ… | | +| **id** | `string` | optional | Section ID for anchor links | +| **headline** | `string` | โœ… | Main headline | +| **subheadline** | `string` | optional | Subheadline or tagline | +| **description** | `string` | optional | Description text | +| **buttons** | `object[]` | optional | CTA buttons | +| **backgroundImage** | `object` | optional | Hero background image | +| **backgroundVideo** | `object` | optional | Hero background video | +| **align** | `Enum<'left' \| 'center' \| 'right'>` | optional | | +| **overlayOpacity** | `number` | optional | | diff --git a/content/docs/references/website/content-block/ImageConfig.mdx b/content/docs/references/website/content-block/ImageConfig.mdx new file mode 100644 index 000000000..8d0fffde1 --- /dev/null +++ b/content/docs/references/website/content-block/ImageConfig.mdx @@ -0,0 +1,15 @@ +--- +title: ImageConfig +description: ImageConfig Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **src** | `string` | โœ… | Image source URL | +| **alt** | `string` | โœ… | Image alt text | +| **width** | `number` | optional | Image width in pixels | +| **height** | `number` | optional | Image height in pixels | +| **objectFit** | `Enum<'cover' \| 'contain' \| 'fill' \| 'none' \| 'scale-down'>` | optional | | +| **loading** | `Enum<'lazy' \| 'eager'>` | optional | | diff --git a/content/docs/references/website/content-block/LogoCloudSection.mdx b/content/docs/references/website/content-block/LogoCloudSection.mdx new file mode 100644 index 000000000..c28e32190 --- /dev/null +++ b/content/docs/references/website/content-block/LogoCloudSection.mdx @@ -0,0 +1,14 @@ +--- +title: LogoCloudSection +description: LogoCloudSection Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `string` | โœ… | | +| **id** | `string` | optional | Section ID for anchor links | +| **title** | `string` | optional | Section title (e.g., "Trusted by") | +| **logos** | `object[]` | โœ… | Logo images | +| **grayscale** | `boolean` | optional | Display logos in grayscale | diff --git a/content/docs/references/website/content-block/PricingPlan.mdx b/content/docs/references/website/content-block/PricingPlan.mdx new file mode 100644 index 000000000..583de0fe0 --- /dev/null +++ b/content/docs/references/website/content-block/PricingPlan.mdx @@ -0,0 +1,17 @@ +--- +title: PricingPlan +description: PricingPlan Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | โœ… | Plan name | +| **description** | `string` | optional | Plan description | +| **price** | `string` | โœ… | Price (e.g., "$29", "Free", "Custom") | +| **period** | `string` | optional | Billing period (e.g., "/month", "/year") | +| **features** | `string[]` | โœ… | List of features | +| **button** | `object` | โœ… | CTA button | +| **highlighted** | `boolean` | optional | Highlight as recommended plan | +| **badge** | `string` | optional | Badge text (e.g., "Popular", "Best Value") | diff --git a/content/docs/references/website/content-block/PricingSection.mdx b/content/docs/references/website/content-block/PricingSection.mdx new file mode 100644 index 000000000..c023ee097 --- /dev/null +++ b/content/docs/references/website/content-block/PricingSection.mdx @@ -0,0 +1,15 @@ +--- +title: PricingSection +description: PricingSection Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `string` | โœ… | | +| **id** | `string` | optional | Section ID for anchor links | +| **title** | `string` | optional | Section title | +| **description** | `string` | optional | Section description | +| **plans** | `object[]` | โœ… | Pricing plans | +| **billingToggle** | `object` | optional | Enable monthly/yearly toggle | diff --git a/content/docs/references/website/content-block/TestimonialItem.mdx b/content/docs/references/website/content-block/TestimonialItem.mdx new file mode 100644 index 000000000..ece6ef789 --- /dev/null +++ b/content/docs/references/website/content-block/TestimonialItem.mdx @@ -0,0 +1,15 @@ +--- +title: TestimonialItem +description: TestimonialItem Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **quote** | `string` | โœ… | Testimonial quote | +| **author** | `string` | โœ… | Author name | +| **title** | `string` | optional | Author title or role | +| **company** | `string` | optional | Author company | +| **avatar** | `string` | optional | Author avatar image URL | +| **rating** | `number` | optional | Rating out of 5 | diff --git a/content/docs/references/website/content-block/TestimonialsSection.mdx b/content/docs/references/website/content-block/TestimonialsSection.mdx new file mode 100644 index 000000000..3ff8bdd9a --- /dev/null +++ b/content/docs/references/website/content-block/TestimonialsSection.mdx @@ -0,0 +1,16 @@ +--- +title: TestimonialsSection +description: TestimonialsSection Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `string` | โœ… | | +| **id** | `string` | optional | Section ID for anchor links | +| **title** | `string` | optional | Section title | +| **description** | `string` | optional | Section description | +| **testimonials** | `object[]` | โœ… | List of testimonials | +| **style** | `Enum<'grid' \| 'carousel' \| 'masonry'>` | optional | | +| **columns** | `Enum<'1' \| '2' \| '3'>` | optional | | diff --git a/content/docs/references/website/content-block/meta.json b/content/docs/references/website/content-block/meta.json new file mode 100644 index 000000000..6fec53f8b --- /dev/null +++ b/content/docs/references/website/content-block/meta.json @@ -0,0 +1,3 @@ +{ + "title": "Content Block" +} \ No newline at end of file diff --git a/content/docs/references/website/landing-page/LandingPage.mdx b/content/docs/references/website/landing-page/LandingPage.mdx new file mode 100644 index 000000000..1f4f7f74d --- /dev/null +++ b/content/docs/references/website/landing-page/LandingPage.mdx @@ -0,0 +1,24 @@ +--- +title: LandingPage +description: LandingPage Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | โœ… | Page identifier (snake_case) | +| **title** | `string` | โœ… | Page title | +| **slug** | `string` | โœ… | URL slug (e.g., "/", "/pricing", "/about") | +| **description** | `string` | optional | Page description | +| **seo** | `object` | optional | SEO and meta tags configuration | +| **sections** | `object \| object \| object \| object \| object \| object \| object \| object \| object[]` | โœ… | Page content sections | +| **navigation** | `string` | optional | Navigation name to use | +| **footer** | `string` | optional | Footer name to use | +| **published** | `boolean` | optional | Whether page is published | +| **publishedAt** | `string` | optional | Publication timestamp | +| **updatedAt** | `string` | optional | Last update timestamp | +| **template** | `string` | optional | Custom template identifier | +| **scripts** | `object[]` | optional | Custom scripts to inject | +| **customCss** | `string` | optional | Custom CSS styles | +| **variant** | `string` | optional | A/B testing variant identifier | diff --git a/content/docs/references/website/landing-page/meta.json b/content/docs/references/website/landing-page/meta.json new file mode 100644 index 000000000..48cfb00ed --- /dev/null +++ b/content/docs/references/website/landing-page/meta.json @@ -0,0 +1,3 @@ +{ + "title": "Landing Page" +} \ No newline at end of file diff --git a/content/docs/references/website/meta.cn.json b/content/docs/references/website/meta.cn.json new file mode 100644 index 000000000..5af3fbdd0 --- /dev/null +++ b/content/docs/references/website/meta.cn.json @@ -0,0 +1,11 @@ +{ + "title": "็ฝ‘็ซ™่กจ่พพๅ่ฎฎ", + "root": true, + "pages": [ + "content-block", + "landing-page", + "navigation", + "seo", + "website" + ] +} diff --git a/content/docs/references/website/meta.json b/content/docs/references/website/meta.json new file mode 100644 index 000000000..706a92df6 --- /dev/null +++ b/content/docs/references/website/meta.json @@ -0,0 +1,11 @@ +{ + "title": "Website Protocol", + "root": true, + "pages": [ + "content-block", + "landing-page", + "navigation", + "seo", + "website" + ] +} \ No newline at end of file diff --git a/content/docs/references/website/navigation/ButtonMenuItem.mdx b/content/docs/references/website/navigation/ButtonMenuItem.mdx new file mode 100644 index 000000000..2751a9c0d --- /dev/null +++ b/content/docs/references/website/navigation/ButtonMenuItem.mdx @@ -0,0 +1,18 @@ +--- +title: ButtonMenuItem +description: ButtonMenuItem Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | โœ… | Unique identifier for menu item | +| **label** | `string` | โœ… | Display label | +| **icon** | `string` | optional | Icon name or URL | +| **badge** | `string` | optional | Badge text | +| **visible** | `string` | optional | Visibility formula condition | +| **type** | `string` | โœ… | | +| **href** | `string` | โœ… | Target URL | +| **variant** | `Enum<'primary' \| 'secondary' \| 'outline' \| 'ghost'>` | optional | | +| **target** | `Enum<'_self' \| '_blank'>` | optional | | diff --git a/content/docs/references/website/navigation/DropdownMenuItem.mdx b/content/docs/references/website/navigation/DropdownMenuItem.mdx new file mode 100644 index 000000000..810a66dee --- /dev/null +++ b/content/docs/references/website/navigation/DropdownMenuItem.mdx @@ -0,0 +1,15 @@ +--- +title: DropdownMenuItem +description: DropdownMenuItem Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | โœ… | Unique identifier for menu item | +| **label** | `string` | โœ… | Display label | +| **icon** | `string` | optional | Icon name or URL | +| **badge** | `string` | optional | Badge text | +| **visible** | `string` | optional | Visibility formula condition | +| **type** | `string` | โœ… | | diff --git a/content/docs/references/website/navigation/FooterLinkGroup.mdx b/content/docs/references/website/navigation/FooterLinkGroup.mdx new file mode 100644 index 000000000..04be78019 --- /dev/null +++ b/content/docs/references/website/navigation/FooterLinkGroup.mdx @@ -0,0 +1,11 @@ +--- +title: FooterLinkGroup +description: FooterLinkGroup Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **title** | `string` | โœ… | Link group title | +| **links** | `object[]` | โœ… | Links | diff --git a/content/docs/references/website/navigation/LinkMenuItem.mdx b/content/docs/references/website/navigation/LinkMenuItem.mdx new file mode 100644 index 000000000..0247e1a32 --- /dev/null +++ b/content/docs/references/website/navigation/LinkMenuItem.mdx @@ -0,0 +1,17 @@ +--- +title: LinkMenuItem +description: LinkMenuItem Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | โœ… | Unique identifier for menu item | +| **label** | `string` | โœ… | Display label | +| **icon** | `string` | optional | Icon name or URL | +| **badge** | `string` | optional | Badge text | +| **visible** | `string` | optional | Visibility formula condition | +| **type** | `string` | โœ… | | +| **href** | `string` | โœ… | Target URL (internal or external) | +| **target** | `Enum<'_self' \| '_blank'>` | optional | Link target | diff --git a/content/docs/references/website/navigation/MegaMenuColumn.mdx b/content/docs/references/website/navigation/MegaMenuColumn.mdx new file mode 100644 index 000000000..cbec90097 --- /dev/null +++ b/content/docs/references/website/navigation/MegaMenuColumn.mdx @@ -0,0 +1,11 @@ +--- +title: MegaMenuColumn +description: MegaMenuColumn Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **title** | `string` | optional | Column heading | +| **links** | `object[]` | โœ… | Links in this column | diff --git a/content/docs/references/website/navigation/MegaMenuItem.mdx b/content/docs/references/website/navigation/MegaMenuItem.mdx new file mode 100644 index 000000000..14a073a0b --- /dev/null +++ b/content/docs/references/website/navigation/MegaMenuItem.mdx @@ -0,0 +1,16 @@ +--- +title: MegaMenuItem +description: MegaMenuItem Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | โœ… | Unique identifier for menu item | +| **label** | `string` | โœ… | Display label | +| **icon** | `string` | optional | Icon name or URL | +| **badge** | `string` | optional | Badge text | +| **visible** | `string` | optional | Visibility formula condition | +| **type** | `string` | โœ… | | +| **columns** | `object[]` | โœ… | Mega menu columns | diff --git a/content/docs/references/website/navigation/NavigationMenuItem.mdx b/content/docs/references/website/navigation/NavigationMenuItem.mdx new file mode 100644 index 000000000..c77b4d69c --- /dev/null +++ b/content/docs/references/website/navigation/NavigationMenuItem.mdx @@ -0,0 +1,5 @@ +--- +title: NavigationMenuItem +description: NavigationMenuItem Schema Reference +--- + diff --git a/content/docs/references/website/navigation/SocialLink.mdx b/content/docs/references/website/navigation/SocialLink.mdx new file mode 100644 index 000000000..5ec18c0e8 --- /dev/null +++ b/content/docs/references/website/navigation/SocialLink.mdx @@ -0,0 +1,13 @@ +--- +title: SocialLink +description: SocialLink Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **platform** | `Enum<'facebook' \| 'twitter' \| 'linkedin' \| 'instagram' \| 'github' \| 'youtube' \| 'custom'>` | โœ… | | +| **url** | `string` | โœ… | Social profile URL | +| **icon** | `string` | optional | Custom icon name or URL | +| **label** | `string` | optional | Accessible label | diff --git a/content/docs/references/website/navigation/WebsiteFooter.mdx b/content/docs/references/website/navigation/WebsiteFooter.mdx new file mode 100644 index 000000000..fb06294b3 --- /dev/null +++ b/content/docs/references/website/navigation/WebsiteFooter.mdx @@ -0,0 +1,15 @@ +--- +title: WebsiteFooter +description: WebsiteFooter Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | โœ… | Footer identifier (snake_case) | +| **linkGroups** | `object[]` | optional | Footer link groups | +| **socialLinks** | `object[]` | optional | Social media links | +| **copyright** | `string` | optional | Copyright text | +| **description** | `string` | optional | Footer description or tagline | +| **newsletter** | `object` | optional | Newsletter signup configuration | diff --git a/content/docs/references/website/navigation/WebsiteNavigation.mdx b/content/docs/references/website/navigation/WebsiteNavigation.mdx new file mode 100644 index 000000000..9ba3e6f97 --- /dev/null +++ b/content/docs/references/website/navigation/WebsiteNavigation.mdx @@ -0,0 +1,16 @@ +--- +title: WebsiteNavigation +description: WebsiteNavigation Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | โœ… | Navigation identifier (snake_case) | +| **label** | `string` | โœ… | Navigation label | +| **position** | `Enum<'header' \| 'footer' \| 'sidebar'>` | optional | | +| **logo** | `object` | optional | Logo configuration | +| **items** | `object \| object \| object \| object[]` | โœ… | Navigation menu items | +| **mobileCollapsible** | `boolean` | optional | Enable mobile hamburger menu | +| **sticky** | `boolean` | optional | Enable sticky navigation on scroll | diff --git a/content/docs/references/website/navigation/meta.json b/content/docs/references/website/navigation/meta.json new file mode 100644 index 000000000..cfda4fc9e --- /dev/null +++ b/content/docs/references/website/navigation/meta.json @@ -0,0 +1,3 @@ +{ + "title": "Navigation" +} \ No newline at end of file diff --git a/content/docs/references/website/seo/OpenGraph.mdx b/content/docs/references/website/seo/OpenGraph.mdx new file mode 100644 index 000000000..2cfc212ec --- /dev/null +++ b/content/docs/references/website/seo/OpenGraph.mdx @@ -0,0 +1,17 @@ +--- +title: OpenGraph +description: OpenGraph Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **title** | `string` | โœ… | Open Graph title | +| **description** | `string` | โœ… | Open Graph description | +| **type** | `Enum<'website' \| 'article' \| 'product' \| 'profile'>` | optional | | +| **image** | `string` | โœ… | Open Graph image URL (1200x630px recommended) | +| **imageAlt** | `string` | optional | Open Graph image alt text | +| **siteName** | `string` | optional | Site name | +| **locale** | `string` | optional | Locale (e.g., "en_US", "zh_CN") | +| **url** | `string` | optional | Canonical URL | diff --git a/content/docs/references/website/seo/SeoConfig.mdx b/content/docs/references/website/seo/SeoConfig.mdx new file mode 100644 index 000000000..381a1a38e --- /dev/null +++ b/content/docs/references/website/seo/SeoConfig.mdx @@ -0,0 +1,14 @@ +--- +title: SeoConfig +description: SeoConfig Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **meta** | `object` | โœ… | Basic SEO meta tags | +| **openGraph** | `object` | optional | Open Graph protocol tags | +| **twitter** | `object` | optional | Twitter Card tags | +| **structuredData** | `object[]` | optional | Schema.org structured data | +| **customMeta** | `object[]` | optional | Custom meta tags | diff --git a/content/docs/references/website/seo/SeoMetaTags.mdx b/content/docs/references/website/seo/SeoMetaTags.mdx new file mode 100644 index 000000000..bc05066ac --- /dev/null +++ b/content/docs/references/website/seo/SeoMetaTags.mdx @@ -0,0 +1,16 @@ +--- +title: SeoMetaTags +description: SeoMetaTags Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **title** | `string` | โœ… | SEO title (50-60 characters recommended) | +| **description** | `string` | โœ… | Meta description (150-160 characters recommended) | +| **keywords** | `string[]` | optional | SEO keywords | +| **canonical** | `string` | optional | Canonical URL | +| **robots** | `string` | optional | Robots directive (e.g., "index, follow") | +| **author** | `string` | optional | Content author | +| **language** | `string` | optional | Language code (e.g., "en", "zh-CN") | diff --git a/content/docs/references/website/seo/StructuredData.mdx b/content/docs/references/website/seo/StructuredData.mdx new file mode 100644 index 000000000..3f90d1d21 --- /dev/null +++ b/content/docs/references/website/seo/StructuredData.mdx @@ -0,0 +1,11 @@ +--- +title: StructuredData +description: StructuredData Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `string` | โœ… | Schema.org type (e.g., "Organization", "Product", "Article") | +| **data** | `Record` | โœ… | Schema.org structured data object | diff --git a/content/docs/references/website/seo/TwitterCard.mdx b/content/docs/references/website/seo/TwitterCard.mdx new file mode 100644 index 000000000..6ba39bfc1 --- /dev/null +++ b/content/docs/references/website/seo/TwitterCard.mdx @@ -0,0 +1,16 @@ +--- +title: TwitterCard +description: TwitterCard Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **card** | `Enum<'summary' \| 'summary_large_image' \| 'app' \| 'player'>` | optional | | +| **title** | `string` | โœ… | Twitter card title | +| **description** | `string` | โœ… | Twitter card description | +| **image** | `string` | โœ… | Twitter card image URL | +| **imageAlt** | `string` | optional | Twitter card image alt text | +| **site** | `string` | optional | Twitter @username for site | +| **creator** | `string` | optional | Twitter @username for creator | diff --git a/content/docs/references/website/seo/meta.json b/content/docs/references/website/seo/meta.json new file mode 100644 index 000000000..ab274e131 --- /dev/null +++ b/content/docs/references/website/seo/meta.json @@ -0,0 +1,3 @@ +{ + "title": "Seo" +} \ No newline at end of file diff --git a/content/docs/references/website/website/AnalyticsConfig.mdx b/content/docs/references/website/website/AnalyticsConfig.mdx new file mode 100644 index 000000000..6515efade --- /dev/null +++ b/content/docs/references/website/website/AnalyticsConfig.mdx @@ -0,0 +1,13 @@ +--- +title: AnalyticsConfig +description: AnalyticsConfig Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **googleAnalytics** | `object` | optional | | +| **googleTagManager** | `object` | optional | | +| **facebookPixel** | `object` | optional | | +| **customScripts** | `object[]` | optional | | diff --git a/content/docs/references/website/website/WebsiteConfig.mdx b/content/docs/references/website/website/WebsiteConfig.mdx new file mode 100644 index 000000000..41fce7ccf --- /dev/null +++ b/content/docs/references/website/website/WebsiteConfig.mdx @@ -0,0 +1,24 @@ +--- +title: WebsiteConfig +description: WebsiteConfig Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | โœ… | Website identifier (snake_case) | +| **title** | `string` | โœ… | Website title | +| **description** | `string` | optional | Website description | +| **baseUrl** | `string` | โœ… | Base URL (e.g., https://example.com) | +| **locale** | `string` | optional | Default locale (e.g., "en", "zh-CN") | +| **locales** | `string[]` | optional | Supported locales for i18n | +| **theme** | `object` | optional | Visual theme settings | +| **navigations** | `object[]` | optional | Navigation menus | +| **footers** | `object[]` | optional | Footer configurations | +| **pages** | `object[]` | โœ… | Landing pages | +| **analytics** | `object` | optional | Analytics and tracking | +| **favicon** | `string` | optional | Favicon URL | +| **socialPreview** | `string` | optional | Default social preview image | +| **previewReleaseDate** | `string` | optional | Preview release date (ISO 8601 format, e.g., 2026-03-01T00:00:00Z) | +| **version** | `string` | optional | Website version | diff --git a/content/docs/references/website/website/WebsiteTheme.mdx b/content/docs/references/website/website/WebsiteTheme.mdx new file mode 100644 index 000000000..8feb9af96 --- /dev/null +++ b/content/docs/references/website/website/WebsiteTheme.mdx @@ -0,0 +1,15 @@ +--- +title: WebsiteTheme +description: WebsiteTheme Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | โœ… | Theme name | +| **primaryColor** | `string` | โœ… | Primary brand color (hex) | +| **secondaryColor** | `string` | optional | Secondary brand color (hex) | +| **fontFamily** | `object` | optional | | +| **borderRadius** | `Enum<'none' \| 'sm' \| 'md' \| 'lg' \| 'full'>` | optional | | +| **darkMode** | `boolean` | optional | Enable dark mode | diff --git a/content/docs/references/website/website/meta.json b/content/docs/references/website/website/meta.json new file mode 100644 index 000000000..111d32e31 --- /dev/null +++ b/content/docs/references/website/website/meta.json @@ -0,0 +1,3 @@ +{ + "title": "Website" +} \ No newline at end of file diff --git a/examples/website-demo/README.md b/examples/website-demo/README.md new file mode 100644 index 000000000..620710afb --- /dev/null +++ b/examples/website-demo/README.md @@ -0,0 +1,50 @@ +# Website Expression Protocol Example + +This example demonstrates how to use the ObjectStack Website Expression Protocol to define a complete marketing website. + +## Overview + +The Website Expression Protocol allows you to declaratively configure: +- Landing pages with content blocks +- Navigation menus and footers +- SEO metadata +- Analytics integration +- Theming + +## Preview Release + +**March 2026** - This protocol will be released as a preview version. + +## Example Configuration + +See `website.config.ts` for a complete example of: +- Hero section with CTA buttons +- Features grid +- Testimonials carousel +- Pricing table +- FAQ accordion +- SEO optimization with Open Graph and Twitter Cards +- Multi-language support (English and Chinese) + +## Usage + +```typescript +import type { Website } from '@objectstack/spec/website'; + +const websiteConfig: Website.WebsiteConfig = { + // See website.config.ts for full example +}; +``` + +## Features Demonstrated + +1. **Content Blocks**: Hero, features, testimonials, pricing, CTA, FAQ +2. **Navigation**: Header menu with dropdowns, footer with link groups +3. **SEO**: Meta tags, Open Graph, Twitter Cards, structured data +4. **Analytics**: Google Analytics integration +5. **Theming**: Custom colors and typography +6. **i18n**: Multi-language support + +## Documentation + +See [Website Protocol Reference](../../content/docs/references/website/) for complete API documentation. diff --git a/examples/website-demo/website.config.ts b/examples/website-demo/website.config.ts new file mode 100644 index 000000000..6b6e235f2 --- /dev/null +++ b/examples/website-demo/website.config.ts @@ -0,0 +1,395 @@ +import type { Website } from '@objectstack/spec/website'; + +/** + * ObjectStack Official Website Configuration + * Demonstrates the Website Expression Protocol + * Preview Release: March 2026 + */ +export const websiteConfig: Website.WebsiteConfig = { + name: 'objectstack_official', + title: 'ObjectStack - Post-SaaS Operating System', + description: 'A metadata-driven low-code platform for building enterprise software', + baseUrl: 'https://objectstack.ai', + locale: 'en', + locales: ['en', 'zh-CN'], + + // Theme Configuration + theme: { + name: 'ObjectStack Theme', + primaryColor: '#0070F3', + secondaryColor: '#10B981', + fontFamily: { + heading: 'Inter, sans-serif', + body: 'Inter, sans-serif', + }, + borderRadius: 'lg', + darkMode: true, + }, + + // Main Navigation + navigations: [ + { + name: 'main_header', + label: 'Main Navigation', + position: 'header', + logo: { + src: '/logo.svg', + alt: 'ObjectStack', + href: '/', + width: 150, + height: 40, + }, + items: [ + { + id: 'nav_home', + label: 'Home', + type: 'link', + href: '/', + }, + { + id: 'nav_products', + label: 'Products', + type: 'dropdown', + children: [ + { + id: 'nav_protocol', + label: 'Protocol', + type: 'link', + href: '/protocol', + }, + { + id: 'nav_runtime', + label: 'Runtime', + type: 'link', + href: '/runtime', + }, + ], + }, + { + id: 'nav_docs', + label: 'Documentation', + type: 'link', + href: '/docs', + }, + { + id: 'nav_pricing', + label: 'Pricing', + type: 'link', + href: '/pricing', + }, + { + id: 'nav_get_started', + label: 'Get Started', + type: 'button', + href: '/signup', + variant: 'primary', + }, + ], + sticky: true, + }, + ], + + // Footer + footers: [ + { + name: 'main_footer', + linkGroups: [ + { + title: 'Product', + links: [ + { label: 'Features', href: '/features' }, + { label: 'Pricing', href: '/pricing' }, + { label: 'Documentation', href: '/docs' }, + ], + }, + { + title: 'Company', + links: [ + { label: 'About', href: '/about' }, + { label: 'Blog', href: '/blog' }, + { label: 'Careers', href: '/careers' }, + ], + }, + { + title: 'Resources', + links: [ + { label: 'Community', href: '/community' }, + { label: 'Support', href: '/support' }, + { label: 'GitHub', href: 'https://github.com/objectstack-ai', target: '_blank' }, + ], + }, + ], + socialLinks: [ + { platform: 'github', url: 'https://github.com/objectstack-ai', label: 'GitHub' }, + { platform: 'twitter', url: 'https://twitter.com/objectstack', label: 'Twitter' }, + { platform: 'linkedin', url: 'https://linkedin.com/company/objectstack', label: 'LinkedIn' }, + ], + copyright: 'ยฉ 2026 ObjectStack. All rights reserved.', + newsletter: { + title: 'Stay Updated', + description: 'Get the latest news and updates from ObjectStack', + placeholder: 'Enter your email', + buttonText: 'Subscribe', + }, + }, + ], + + // Landing Pages + pages: [ + { + name: 'home', + title: 'ObjectStack - Post-SaaS Operating System', + slug: '/', + published: true, + + // SEO Configuration + seo: { + meta: { + title: 'ObjectStack - Post-SaaS Operating System', + description: 'Build enterprise software faster with metadata-driven low-code platform', + keywords: ['low-code', 'metadata', 'protocol', 'enterprise', 'objectstack'], + robots: 'index, follow', + language: 'en', + }, + openGraph: { + title: 'ObjectStack - Post-SaaS Operating System', + description: 'Build enterprise software faster with metadata', + type: 'website', + image: 'https://objectstack.ai/og-image.png', + siteName: 'ObjectStack', + locale: 'en_US', + url: 'https://objectstack.ai', + }, + twitter: { + card: 'summary_large_image', + title: 'ObjectStack', + description: 'Post-SaaS Operating System', + image: 'https://objectstack.ai/twitter-card.png', + site: '@objectstack', + }, + structuredData: [ + { + type: 'Organization', + data: { + '@context': 'https://schema.org', + '@type': 'Organization', + name: 'ObjectStack', + url: 'https://objectstack.ai', + logo: 'https://objectstack.ai/logo.png', + description: 'Post-SaaS Operating System for enterprise software', + }, + }, + ], + }, + + // Page Content Sections + sections: [ + // Hero Section + { + type: 'hero', + headline: 'Build Enterprise Software Faster', + subheadline: 'The Post-SaaS Operating System', + description: 'ObjectStack is a metadata-driven low-code platform that virtualizes data and unifies business logic.', + buttons: [ + { + text: 'Get Started', + href: '/docs/quick-start', + variant: 'primary', + size: 'lg', + }, + { + text: 'View Demo', + href: '/demo', + variant: 'outline', + size: 'lg', + }, + ], + backgroundImage: { + src: '/hero-bg.jpg', + alt: 'Hero background', + objectFit: 'cover', + }, + align: 'center', + }, + + // Features Section + { + type: 'features', + title: 'Why ObjectStack?', + description: 'Everything you need to build enterprise applications', + features: [ + { + icon: 'zap', + title: 'Metadata-Driven', + description: 'Define your application structure as code with Zod schemas and TypeScript types.', + }, + { + icon: 'database', + title: 'Data Virtualization', + description: 'Unified query interface across SQL, NoSQL, and SaaS connectors.', + }, + { + icon: 'layers', + title: 'Server-Driven UI', + description: 'Define UI layouts and components declaratively with ObjectUI protocol.', + }, + { + icon: 'shield', + title: 'Enterprise Security', + description: 'Built-in permissions, sharing rules, and audit logging.', + }, + { + icon: 'workflow', + title: 'Workflow Automation', + description: 'Visual flows, triggers, and validation rules without code.', + }, + { + icon: 'code', + title: 'Developer Friendly', + description: 'Clean TypeScript APIs with comprehensive documentation.', + }, + ], + columns: '3', + }, + + // Logo Cloud + { + type: 'logo_cloud', + title: 'Trusted by Leading Companies', + logos: [ + { src: '/logos/company1.svg', alt: 'Company 1' }, + { src: '/logos/company2.svg', alt: 'Company 2' }, + { src: '/logos/company3.svg', alt: 'Company 3' }, + { src: '/logos/company4.svg', alt: 'Company 4' }, + ], + }, + + // Pricing Section + { + type: 'pricing', + title: 'Simple, Transparent Pricing', + description: 'Choose the plan that fits your needs', + plans: [ + { + name: 'Open Source', + price: '$0', + period: '/forever', + description: 'Perfect for getting started', + features: [ + 'Core Protocol', + 'Community Support', + 'Self-Hosted', + 'Unlimited Objects', + ], + button: { + text: 'Get Started', + href: '/docs/quick-start', + variant: 'outline', + }, + }, + { + name: 'Professional', + price: '$99', + period: '/month', + description: 'For growing teams', + features: [ + 'Everything in Open Source', + 'Priority Support', + 'Advanced Features', + 'Cloud Hosting', + 'Custom Integrations', + ], + button: { + text: 'Start Free Trial', + href: '/signup?plan=pro', + variant: 'primary', + }, + highlighted: true, + badge: 'Popular', + }, + { + name: 'Enterprise', + price: 'Custom', + description: 'For large organizations', + features: [ + 'Everything in Professional', + 'Dedicated Support', + 'SLA Guarantee', + 'Custom Development', + 'On-Premise Deployment', + ], + button: { + text: 'Contact Sales', + href: '/contact', + variant: 'outline', + }, + }, + ], + }, + + // FAQ Section + { + type: 'faq', + title: 'Frequently Asked Questions', + items: [ + { + question: 'What is ObjectStack?', + answer: 'ObjectStack is a metadata-driven low-code platform that enables developers to build enterprise software faster by defining applications as code.', + }, + { + question: 'How is it different from other low-code platforms?', + answer: 'ObjectStack is protocol-first, allowing you to define your application structure using Zod schemas and TypeScript. It virtualizes data access and provides a server-driven UI.', + }, + { + question: 'Is it open source?', + answer: 'Yes! The core protocol and runtime are open source under the Apache 2.0 license.', + }, + { + question: 'When is the preview release?', + answer: 'The Website Expression Protocol preview is scheduled for March 2026.', + }, + ], + style: 'accordion', + }, + + // CTA Section + { + type: 'cta', + headline: 'Ready to Build Faster?', + description: 'Join thousands of developers building with ObjectStack', + buttons: [ + { + text: 'Start Free Trial', + href: '/signup', + variant: 'primary', + size: 'lg', + }, + { + text: 'View Documentation', + href: '/docs', + variant: 'outline', + size: 'lg', + }, + ], + backgroundColor: '#0070F3', + align: 'center', + }, + ], + + navigation: 'main_header', + footer: 'main_footer', + }, + ], + + // Analytics + analytics: { + googleAnalytics: { + measurementId: 'G-XXXXXXXXXX', + }, + }, + + favicon: '/favicon.ico', + socialPreview: 'https://objectstack.ai/og-image.png', + previewReleaseDate: '2026-03-01T00:00:00Z', + version: '1.0.0', +}; diff --git a/packages/spec/json-schema/website/AnalyticsConfig.json b/packages/spec/json-schema/website/AnalyticsConfig.json new file mode 100644 index 000000000..df2d43cf0 --- /dev/null +++ b/packages/spec/json-schema/website/AnalyticsConfig.json @@ -0,0 +1,75 @@ +{ + "$ref": "#/definitions/AnalyticsConfig", + "definitions": { + "AnalyticsConfig": { + "type": "object", + "properties": { + "googleAnalytics": { + "type": "object", + "properties": { + "measurementId": { + "type": "string", + "description": "GA4 Measurement ID (e.g., G-XXXXXXXXXX)" + } + }, + "required": [ + "measurementId" + ], + "additionalProperties": false + }, + "googleTagManager": { + "type": "object", + "properties": { + "containerId": { + "type": "string", + "description": "GTM Container ID (e.g., GTM-XXXXXXX)" + } + }, + "required": [ + "containerId" + ], + "additionalProperties": false + }, + "facebookPixel": { + "type": "object", + "properties": { + "pixelId": { + "type": "string", + "description": "Facebook Pixel ID" + } + }, + "required": [ + "pixelId" + ], + "additionalProperties": false + }, + "customScripts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Script name" + }, + "src": { + "type": "string", + "description": "Script source URL" + }, + "inline": { + "type": "string", + "description": "Inline script content" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/ButtonConfig.json b/packages/spec/json-schema/website/ButtonConfig.json new file mode 100644 index 000000000..eff80cf84 --- /dev/null +++ b/packages/spec/json-schema/website/ButtonConfig.json @@ -0,0 +1,56 @@ +{ + "$ref": "#/definitions/ButtonConfig", + "definitions": { + "ButtonConfig": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/ButtonMenuItem.json b/packages/spec/json-schema/website/ButtonMenuItem.json new file mode 100644 index 000000000..4f05de54c --- /dev/null +++ b/packages/spec/json-schema/website/ButtonMenuItem.json @@ -0,0 +1,64 @@ +{ + "$ref": "#/definitions/ButtonMenuItem", + "definitions": { + "ButtonMenuItem": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "button" + }, + "href": { + "type": "string", + "description": "Target URL" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost" + ], + "default": "primary" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/ContentBlock.json b/packages/spec/json-schema/website/ContentBlock.json new file mode 100644 index 000000000..090c75000 --- /dev/null +++ b/packages/spec/json-schema/website/ContentBlock.json @@ -0,0 +1,760 @@ +{ + "$ref": "#/definitions/ContentBlock", + "definitions": { + "ContentBlock": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "hero" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "headline": { + "type": "string", + "description": "Main headline" + }, + "subheadline": { + "type": "string", + "description": "Subheadline or tagline" + }, + "description": { + "type": "string", + "description": "Description text" + }, + "buttons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false + }, + "description": "CTA buttons" + }, + "backgroundImage": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Image source URL" + }, + "alt": { + "type": "string", + "description": "Image alt text" + }, + "width": { + "type": "number", + "description": "Image width in pixels" + }, + "height": { + "type": "number", + "description": "Image height in pixels" + }, + "objectFit": { + "type": "string", + "enum": [ + "cover", + "contain", + "fill", + "none", + "scale-down" + ], + "default": "cover" + }, + "loading": { + "type": "string", + "enum": [ + "lazy", + "eager" + ], + "default": "lazy" + } + }, + "required": [ + "src", + "alt" + ], + "additionalProperties": false, + "description": "Hero background image" + }, + "backgroundVideo": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Video source URL" + }, + "poster": { + "type": "string", + "description": "Video poster image" + } + }, + "required": [ + "src" + ], + "additionalProperties": false, + "description": "Hero background video" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + }, + "overlayOpacity": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.3 + } + }, + "required": [ + "type", + "headline" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "features" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "title": { + "type": "string", + "description": "Feature title" + }, + "description": { + "type": "string", + "description": "Feature description" + }, + "link": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Link text" + }, + "href": { + "type": "string", + "description": "Link URL" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false, + "description": "Optional link to learn more" + } + }, + "required": [ + "title", + "description" + ], + "additionalProperties": false + }, + "description": "List of features" + }, + "columns": { + "type": "string", + "enum": [ + "2", + "3", + "4" + ], + "default": "3", + "description": "Number of columns in grid" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + } + }, + "required": [ + "type", + "features" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "testimonials" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "testimonials": { + "type": "array", + "items": { + "type": "object", + "properties": { + "quote": { + "type": "string", + "description": "Testimonial quote" + }, + "author": { + "type": "string", + "description": "Author name" + }, + "title": { + "type": "string", + "description": "Author title or role" + }, + "company": { + "type": "string", + "description": "Author company" + }, + "avatar": { + "type": "string", + "description": "Author avatar image URL" + }, + "rating": { + "type": "number", + "minimum": 1, + "maximum": 5, + "description": "Rating out of 5" + } + }, + "required": [ + "quote", + "author" + ], + "additionalProperties": false + }, + "description": "List of testimonials" + }, + "style": { + "type": "string", + "enum": [ + "grid", + "carousel", + "masonry" + ], + "default": "grid" + }, + "columns": { + "type": "string", + "enum": [ + "1", + "2", + "3" + ], + "default": "3" + } + }, + "required": [ + "type", + "testimonials" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "cta" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "headline": { + "type": "string", + "description": "CTA headline" + }, + "description": { + "type": "string", + "description": "CTA description" + }, + "buttons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false + }, + "description": "CTA buttons" + }, + "backgroundColor": { + "type": "string", + "description": "Background color (hex, rgb, or preset)" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + } + }, + "required": [ + "type", + "headline", + "buttons" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "pricing" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "plans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Plan name" + }, + "description": { + "type": "string", + "description": "Plan description" + }, + "price": { + "type": "string", + "description": "Price (e.g., \"$29\", \"Free\", \"Custom\")" + }, + "period": { + "type": "string", + "description": "Billing period (e.g., \"/month\", \"/year\")" + }, + "features": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of features" + }, + "button": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false, + "description": "CTA button" + }, + "highlighted": { + "type": "boolean", + "default": false, + "description": "Highlight as recommended plan" + }, + "badge": { + "type": "string", + "description": "Badge text (e.g., \"Popular\", \"Best Value\")" + } + }, + "required": [ + "name", + "price", + "features", + "button" + ], + "additionalProperties": false + }, + "description": "Pricing plans" + }, + "billingToggle": { + "type": "object", + "properties": { + "monthly": { + "type": "string", + "default": "Monthly", + "description": "Monthly billing label" + }, + "yearly": { + "type": "string", + "default": "Yearly", + "description": "Yearly billing label" + } + }, + "additionalProperties": false, + "description": "Enable monthly/yearly toggle" + } + }, + "required": [ + "type", + "plans" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "content" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "content": { + "type": "string", + "description": "Content in markdown or HTML format" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "left" + }, + "maxWidth": { + "type": "string", + "enum": [ + "sm", + "md", + "lg", + "xl", + "full" + ], + "default": "lg" + } + }, + "required": [ + "type", + "content" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "faq" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "FAQ question" + }, + "answer": { + "type": "string", + "description": "FAQ answer (markdown or HTML)" + } + }, + "required": [ + "question", + "answer" + ], + "additionalProperties": false + }, + "description": "FAQ items" + }, + "style": { + "type": "string", + "enum": [ + "accordion", + "grid" + ], + "default": "accordion" + } + }, + "required": [ + "type", + "items" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "logo_cloud" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title (e.g., \"Trusted by\")" + }, + "logos": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Logo image URL" + }, + "alt": { + "type": "string", + "description": "Company/partner name" + }, + "href": { + "type": "string", + "description": "Optional link to company website" + } + }, + "required": [ + "src", + "alt" + ], + "additionalProperties": false + }, + "description": "Logo images" + }, + "grayscale": { + "type": "boolean", + "default": true, + "description": "Display logos in grayscale" + } + }, + "required": [ + "type", + "logos" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "custom_html" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "html": { + "type": "string", + "description": "Custom HTML content" + } + }, + "required": [ + "type", + "html" + ], + "additionalProperties": false + } + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/ContentSection.json b/packages/spec/json-schema/website/ContentSection.json new file mode 100644 index 000000000..482783370 --- /dev/null +++ b/packages/spec/json-schema/website/ContentSection.json @@ -0,0 +1,52 @@ +{ + "$ref": "#/definitions/ContentSection", + "definitions": { + "ContentSection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "content" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "content": { + "type": "string", + "description": "Content in markdown or HTML format" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "left" + }, + "maxWidth": { + "type": "string", + "enum": [ + "sm", + "md", + "lg", + "xl", + "full" + ], + "default": "lg" + } + }, + "required": [ + "type", + "content" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/CtaSection.json b/packages/spec/json-schema/website/CtaSection.json new file mode 100644 index 000000000..6387bee2b --- /dev/null +++ b/packages/spec/json-schema/website/CtaSection.json @@ -0,0 +1,100 @@ +{ + "$ref": "#/definitions/CtaSection", + "definitions": { + "CtaSection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "cta" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "headline": { + "type": "string", + "description": "CTA headline" + }, + "description": { + "type": "string", + "description": "CTA description" + }, + "buttons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false + }, + "description": "CTA buttons" + }, + "backgroundColor": { + "type": "string", + "description": "Background color (hex, rgb, or preset)" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + } + }, + "required": [ + "type", + "headline", + "buttons" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/CustomHtmlSection.json b/packages/spec/json-schema/website/CustomHtmlSection.json new file mode 100644 index 000000000..0b768380e --- /dev/null +++ b/packages/spec/json-schema/website/CustomHtmlSection.json @@ -0,0 +1,28 @@ +{ + "$ref": "#/definitions/CustomHtmlSection", + "definitions": { + "CustomHtmlSection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "custom_html" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "html": { + "type": "string", + "description": "Custom HTML content" + } + }, + "required": [ + "type", + "html" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/DropdownMenuItem.json b/packages/spec/json-schema/website/DropdownMenuItem.json new file mode 100644 index 000000000..9b1a509b6 --- /dev/null +++ b/packages/spec/json-schema/website/DropdownMenuItem.json @@ -0,0 +1,41 @@ +{ + "$ref": "#/definitions/DropdownMenuItem", + "definitions": { + "DropdownMenuItem": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "dropdown" + } + }, + "required": [ + "id", + "label", + "type" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/FaqItem.json b/packages/spec/json-schema/website/FaqItem.json new file mode 100644 index 000000000..aa515b8cb --- /dev/null +++ b/packages/spec/json-schema/website/FaqItem.json @@ -0,0 +1,24 @@ +{ + "$ref": "#/definitions/FaqItem", + "definitions": { + "FaqItem": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "FAQ question" + }, + "answer": { + "type": "string", + "description": "FAQ answer (markdown or HTML)" + } + }, + "required": [ + "question", + "answer" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/FaqSection.json b/packages/spec/json-schema/website/FaqSection.json new file mode 100644 index 000000000..48d2fabaa --- /dev/null +++ b/packages/spec/json-schema/website/FaqSection.json @@ -0,0 +1,62 @@ +{ + "$ref": "#/definitions/FaqSection", + "definitions": { + "FaqSection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "faq" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "FAQ question" + }, + "answer": { + "type": "string", + "description": "FAQ answer (markdown or HTML)" + } + }, + "required": [ + "question", + "answer" + ], + "additionalProperties": false + }, + "description": "FAQ items" + }, + "style": { + "type": "string", + "enum": [ + "accordion", + "grid" + ], + "default": "accordion" + } + }, + "required": [ + "type", + "items" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/FeatureItem.json b/packages/spec/json-schema/website/FeatureItem.json new file mode 100644 index 000000000..a3c5ed185 --- /dev/null +++ b/packages/spec/json-schema/website/FeatureItem.json @@ -0,0 +1,47 @@ +{ + "$ref": "#/definitions/FeatureItem", + "definitions": { + "FeatureItem": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "title": { + "type": "string", + "description": "Feature title" + }, + "description": { + "type": "string", + "description": "Feature description" + }, + "link": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Link text" + }, + "href": { + "type": "string", + "description": "Link URL" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false, + "description": "Optional link to learn more" + } + }, + "required": [ + "title", + "description" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/FeaturesSection.json b/packages/spec/json-schema/website/FeaturesSection.json new file mode 100644 index 000000000..0b8ff494b --- /dev/null +++ b/packages/spec/json-schema/website/FeaturesSection.json @@ -0,0 +1,96 @@ +{ + "$ref": "#/definitions/FeaturesSection", + "definitions": { + "FeaturesSection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "features" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "title": { + "type": "string", + "description": "Feature title" + }, + "description": { + "type": "string", + "description": "Feature description" + }, + "link": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Link text" + }, + "href": { + "type": "string", + "description": "Link URL" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false, + "description": "Optional link to learn more" + } + }, + "required": [ + "title", + "description" + ], + "additionalProperties": false + }, + "description": "List of features" + }, + "columns": { + "type": "string", + "enum": [ + "2", + "3", + "4" + ], + "default": "3", + "description": "Number of columns in grid" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + } + }, + "required": [ + "type", + "features" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/FooterLinkGroup.json b/packages/spec/json-schema/website/FooterLinkGroup.json new file mode 100644 index 000000000..078cb49b1 --- /dev/null +++ b/packages/spec/json-schema/website/FooterLinkGroup.json @@ -0,0 +1,50 @@ +{ + "$ref": "#/definitions/FooterLinkGroup", + "definitions": { + "FooterLinkGroup": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Link group title" + }, + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string", + "description": "Link label" + }, + "href": { + "type": "string", + "description": "Link URL" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + } + }, + "required": [ + "label", + "href" + ], + "additionalProperties": false + }, + "description": "Links" + } + }, + "required": [ + "title", + "links" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/HeroSection.json b/packages/spec/json-schema/website/HeroSection.json new file mode 100644 index 000000000..38e2c0fe7 --- /dev/null +++ b/packages/spec/json-schema/website/HeroSection.json @@ -0,0 +1,169 @@ +{ + "$ref": "#/definitions/HeroSection", + "definitions": { + "HeroSection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "hero" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "headline": { + "type": "string", + "description": "Main headline" + }, + "subheadline": { + "type": "string", + "description": "Subheadline or tagline" + }, + "description": { + "type": "string", + "description": "Description text" + }, + "buttons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false + }, + "description": "CTA buttons" + }, + "backgroundImage": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Image source URL" + }, + "alt": { + "type": "string", + "description": "Image alt text" + }, + "width": { + "type": "number", + "description": "Image width in pixels" + }, + "height": { + "type": "number", + "description": "Image height in pixels" + }, + "objectFit": { + "type": "string", + "enum": [ + "cover", + "contain", + "fill", + "none", + "scale-down" + ], + "default": "cover" + }, + "loading": { + "type": "string", + "enum": [ + "lazy", + "eager" + ], + "default": "lazy" + } + }, + "required": [ + "src", + "alt" + ], + "additionalProperties": false, + "description": "Hero background image" + }, + "backgroundVideo": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Video source URL" + }, + "poster": { + "type": "string", + "description": "Video poster image" + } + }, + "required": [ + "src" + ], + "additionalProperties": false, + "description": "Hero background video" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + }, + "overlayOpacity": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.3 + } + }, + "required": [ + "type", + "headline" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/ImageConfig.json b/packages/spec/json-schema/website/ImageConfig.json new file mode 100644 index 000000000..3e2f78ee7 --- /dev/null +++ b/packages/spec/json-schema/website/ImageConfig.json @@ -0,0 +1,51 @@ +{ + "$ref": "#/definitions/ImageConfig", + "definitions": { + "ImageConfig": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Image source URL" + }, + "alt": { + "type": "string", + "description": "Image alt text" + }, + "width": { + "type": "number", + "description": "Image width in pixels" + }, + "height": { + "type": "number", + "description": "Image height in pixels" + }, + "objectFit": { + "type": "string", + "enum": [ + "cover", + "contain", + "fill", + "none", + "scale-down" + ], + "default": "cover" + }, + "loading": { + "type": "string", + "enum": [ + "lazy", + "eager" + ], + "default": "lazy" + } + }, + "required": [ + "src", + "alt" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/LandingPage.json b/packages/spec/json-schema/website/LandingPage.json new file mode 100644 index 000000000..3da0c3c9f --- /dev/null +++ b/packages/spec/json-schema/website/LandingPage.json @@ -0,0 +1,1064 @@ +{ + "$ref": "#/definitions/LandingPage", + "definitions": { + "LandingPage": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Page identifier (snake_case)" + }, + "title": { + "type": "string", + "description": "Page title" + }, + "slug": { + "type": "string", + "description": "URL slug (e.g., \"/\", \"/pricing\", \"/about\")" + }, + "description": { + "type": "string", + "description": "Page description" + }, + "seo": { + "type": "object", + "properties": { + "meta": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "SEO title (50-60 characters recommended)" + }, + "description": { + "type": "string", + "description": "Meta description (150-160 characters recommended)" + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "SEO keywords" + }, + "canonical": { + "type": "string", + "format": "uri", + "description": "Canonical URL" + }, + "robots": { + "type": "string", + "description": "Robots directive (e.g., \"index, follow\")" + }, + "author": { + "type": "string", + "description": "Content author" + }, + "language": { + "type": "string", + "description": "Language code (e.g., \"en\", \"zh-CN\")" + } + }, + "required": [ + "title", + "description" + ], + "additionalProperties": false, + "description": "Basic SEO meta tags" + }, + "openGraph": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Open Graph title" + }, + "description": { + "type": "string", + "description": "Open Graph description" + }, + "type": { + "type": "string", + "enum": [ + "website", + "article", + "product", + "profile" + ], + "default": "website" + }, + "image": { + "type": "string", + "format": "uri", + "description": "Open Graph image URL (1200x630px recommended)" + }, + "imageAlt": { + "type": "string", + "description": "Open Graph image alt text" + }, + "siteName": { + "type": "string", + "description": "Site name" + }, + "locale": { + "type": "string", + "description": "Locale (e.g., \"en_US\", \"zh_CN\")" + }, + "url": { + "type": "string", + "format": "uri", + "description": "Canonical URL" + } + }, + "required": [ + "title", + "description", + "image" + ], + "additionalProperties": false, + "description": "Open Graph protocol tags" + }, + "twitter": { + "type": "object", + "properties": { + "card": { + "type": "string", + "enum": [ + "summary", + "summary_large_image", + "app", + "player" + ], + "default": "summary_large_image" + }, + "title": { + "type": "string", + "description": "Twitter card title" + }, + "description": { + "type": "string", + "description": "Twitter card description" + }, + "image": { + "type": "string", + "format": "uri", + "description": "Twitter card image URL" + }, + "imageAlt": { + "type": "string", + "description": "Twitter card image alt text" + }, + "site": { + "type": "string", + "description": "Twitter @username for site" + }, + "creator": { + "type": "string", + "description": "Twitter @username for creator" + } + }, + "required": [ + "title", + "description", + "image" + ], + "additionalProperties": false, + "description": "Twitter Card tags" + }, + "structuredData": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Schema.org type (e.g., \"Organization\", \"Product\", \"Article\")" + }, + "data": { + "type": "object", + "additionalProperties": {}, + "description": "Schema.org structured data object" + } + }, + "required": [ + "type", + "data" + ], + "additionalProperties": false + }, + "description": "Schema.org structured data" + }, + "customMeta": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Meta tag name" + }, + "content": { + "type": "string", + "description": "Meta tag content" + }, + "property": { + "type": "string", + "description": "Meta tag property (for og: tags)" + } + }, + "required": [ + "name", + "content" + ], + "additionalProperties": false + }, + "description": "Custom meta tags" + } + }, + "required": [ + "meta" + ], + "additionalProperties": false, + "description": "SEO and meta tags configuration" + }, + "sections": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "hero" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "headline": { + "type": "string", + "description": "Main headline" + }, + "subheadline": { + "type": "string", + "description": "Subheadline or tagline" + }, + "description": { + "type": "string", + "description": "Description text" + }, + "buttons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false + }, + "description": "CTA buttons" + }, + "backgroundImage": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Image source URL" + }, + "alt": { + "type": "string", + "description": "Image alt text" + }, + "width": { + "type": "number", + "description": "Image width in pixels" + }, + "height": { + "type": "number", + "description": "Image height in pixels" + }, + "objectFit": { + "type": "string", + "enum": [ + "cover", + "contain", + "fill", + "none", + "scale-down" + ], + "default": "cover" + }, + "loading": { + "type": "string", + "enum": [ + "lazy", + "eager" + ], + "default": "lazy" + } + }, + "required": [ + "src", + "alt" + ], + "additionalProperties": false, + "description": "Hero background image" + }, + "backgroundVideo": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Video source URL" + }, + "poster": { + "type": "string", + "description": "Video poster image" + } + }, + "required": [ + "src" + ], + "additionalProperties": false, + "description": "Hero background video" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + }, + "overlayOpacity": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.3 + } + }, + "required": [ + "type", + "headline" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "features" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "title": { + "type": "string", + "description": "Feature title" + }, + "description": { + "type": "string", + "description": "Feature description" + }, + "link": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Link text" + }, + "href": { + "type": "string", + "description": "Link URL" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false, + "description": "Optional link to learn more" + } + }, + "required": [ + "title", + "description" + ], + "additionalProperties": false + }, + "description": "List of features" + }, + "columns": { + "type": "string", + "enum": [ + "2", + "3", + "4" + ], + "default": "3", + "description": "Number of columns in grid" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + } + }, + "required": [ + "type", + "features" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "testimonials" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "testimonials": { + "type": "array", + "items": { + "type": "object", + "properties": { + "quote": { + "type": "string", + "description": "Testimonial quote" + }, + "author": { + "type": "string", + "description": "Author name" + }, + "title": { + "type": "string", + "description": "Author title or role" + }, + "company": { + "type": "string", + "description": "Author company" + }, + "avatar": { + "type": "string", + "description": "Author avatar image URL" + }, + "rating": { + "type": "number", + "minimum": 1, + "maximum": 5, + "description": "Rating out of 5" + } + }, + "required": [ + "quote", + "author" + ], + "additionalProperties": false + }, + "description": "List of testimonials" + }, + "style": { + "type": "string", + "enum": [ + "grid", + "carousel", + "masonry" + ], + "default": "grid" + }, + "columns": { + "type": "string", + "enum": [ + "1", + "2", + "3" + ], + "default": "3" + } + }, + "required": [ + "type", + "testimonials" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "cta" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "headline": { + "type": "string", + "description": "CTA headline" + }, + "description": { + "type": "string", + "description": "CTA description" + }, + "buttons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false + }, + "description": "CTA buttons" + }, + "backgroundColor": { + "type": "string", + "description": "Background color (hex, rgb, or preset)" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + } + }, + "required": [ + "type", + "headline", + "buttons" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "pricing" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "plans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Plan name" + }, + "description": { + "type": "string", + "description": "Plan description" + }, + "price": { + "type": "string", + "description": "Price (e.g., \"$29\", \"Free\", \"Custom\")" + }, + "period": { + "type": "string", + "description": "Billing period (e.g., \"/month\", \"/year\")" + }, + "features": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of features" + }, + "button": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false, + "description": "CTA button" + }, + "highlighted": { + "type": "boolean", + "default": false, + "description": "Highlight as recommended plan" + }, + "badge": { + "type": "string", + "description": "Badge text (e.g., \"Popular\", \"Best Value\")" + } + }, + "required": [ + "name", + "price", + "features", + "button" + ], + "additionalProperties": false + }, + "description": "Pricing plans" + }, + "billingToggle": { + "type": "object", + "properties": { + "monthly": { + "type": "string", + "default": "Monthly", + "description": "Monthly billing label" + }, + "yearly": { + "type": "string", + "default": "Yearly", + "description": "Yearly billing label" + } + }, + "additionalProperties": false, + "description": "Enable monthly/yearly toggle" + } + }, + "required": [ + "type", + "plans" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "content" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "content": { + "type": "string", + "description": "Content in markdown or HTML format" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "left" + }, + "maxWidth": { + "type": "string", + "enum": [ + "sm", + "md", + "lg", + "xl", + "full" + ], + "default": "lg" + } + }, + "required": [ + "type", + "content" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "faq" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "FAQ question" + }, + "answer": { + "type": "string", + "description": "FAQ answer (markdown or HTML)" + } + }, + "required": [ + "question", + "answer" + ], + "additionalProperties": false + }, + "description": "FAQ items" + }, + "style": { + "type": "string", + "enum": [ + "accordion", + "grid" + ], + "default": "accordion" + } + }, + "required": [ + "type", + "items" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "logo_cloud" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title (e.g., \"Trusted by\")" + }, + "logos": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Logo image URL" + }, + "alt": { + "type": "string", + "description": "Company/partner name" + }, + "href": { + "type": "string", + "description": "Optional link to company website" + } + }, + "required": [ + "src", + "alt" + ], + "additionalProperties": false + }, + "description": "Logo images" + }, + "grayscale": { + "type": "boolean", + "default": true, + "description": "Display logos in grayscale" + } + }, + "required": [ + "type", + "logos" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "custom_html" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "html": { + "type": "string", + "description": "Custom HTML content" + } + }, + "required": [ + "type", + "html" + ], + "additionalProperties": false + } + ] + }, + "description": "Page content sections" + }, + "navigation": { + "type": "string", + "description": "Navigation name to use" + }, + "footer": { + "type": "string", + "description": "Footer name to use" + }, + "published": { + "type": "boolean", + "default": false, + "description": "Whether page is published" + }, + "publishedAt": { + "type": "string", + "format": "date-time", + "description": "Publication timestamp" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "Last update timestamp" + }, + "template": { + "type": "string", + "description": "Custom template identifier" + }, + "scripts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Script source URL" + }, + "inline": { + "type": "string", + "description": "Inline script content" + }, + "position": { + "type": "string", + "enum": [ + "head", + "body_start", + "body_end" + ], + "default": "body_end" + }, + "async": { + "type": "boolean", + "default": true + }, + "defer": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "description": "Custom scripts to inject" + }, + "customCss": { + "type": "string", + "description": "Custom CSS styles" + }, + "variant": { + "type": "string", + "description": "A/B testing variant identifier" + } + }, + "required": [ + "name", + "title", + "slug", + "sections" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/LinkMenuItem.json b/packages/spec/json-schema/website/LinkMenuItem.json new file mode 100644 index 000000000..8d701d71a --- /dev/null +++ b/packages/spec/json-schema/website/LinkMenuItem.json @@ -0,0 +1,55 @@ +{ + "$ref": "#/definitions/LinkMenuItem", + "definitions": { + "LinkMenuItem": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "link" + }, + "href": { + "type": "string", + "description": "Target URL (internal or external)" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self", + "description": "Link target" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/LogoCloudSection.json b/packages/spec/json-schema/website/LogoCloudSection.json new file mode 100644 index 000000000..c21887005 --- /dev/null +++ b/packages/spec/json-schema/website/LogoCloudSection.json @@ -0,0 +1,59 @@ +{ + "$ref": "#/definitions/LogoCloudSection", + "definitions": { + "LogoCloudSection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "logo_cloud" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title (e.g., \"Trusted by\")" + }, + "logos": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Logo image URL" + }, + "alt": { + "type": "string", + "description": "Company/partner name" + }, + "href": { + "type": "string", + "description": "Optional link to company website" + } + }, + "required": [ + "src", + "alt" + ], + "additionalProperties": false + }, + "description": "Logo images" + }, + "grayscale": { + "type": "boolean", + "default": true, + "description": "Display logos in grayscale" + } + }, + "required": [ + "type", + "logos" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/MegaMenuColumn.json b/packages/spec/json-schema/website/MegaMenuColumn.json new file mode 100644 index 000000000..cf022544e --- /dev/null +++ b/packages/spec/json-schema/website/MegaMenuColumn.json @@ -0,0 +1,72 @@ +{ + "$ref": "#/definitions/MegaMenuColumn", + "definitions": { + "MegaMenuColumn": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Column heading" + }, + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "link" + }, + "href": { + "type": "string", + "description": "Target URL (internal or external)" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self", + "description": "Link target" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + }, + "description": "Links in this column" + } + }, + "required": [ + "links" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/MegaMenuItem.json b/packages/spec/json-schema/website/MegaMenuItem.json new file mode 100644 index 000000000..ab88a9097 --- /dev/null +++ b/packages/spec/json-schema/website/MegaMenuItem.json @@ -0,0 +1,112 @@ +{ + "$ref": "#/definitions/MegaMenuItem", + "definitions": { + "MegaMenuItem": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "megamenu" + }, + "columns": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Column heading" + }, + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "link" + }, + "href": { + "type": "string", + "description": "Target URL (internal or external)" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self", + "description": "Link target" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + }, + "description": "Links in this column" + } + }, + "required": [ + "links" + ], + "additionalProperties": false + }, + "description": "Mega menu columns" + } + }, + "required": [ + "id", + "label", + "type", + "columns" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/NavigationMenuItem.json b/packages/spec/json-schema/website/NavigationMenuItem.json new file mode 100644 index 000000000..f19d8d733 --- /dev/null +++ b/packages/spec/json-schema/website/NavigationMenuItem.json @@ -0,0 +1,264 @@ +{ + "$ref": "#/definitions/NavigationMenuItem", + "definitions": { + "NavigationMenuItem": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "link" + }, + "href": { + "type": "string", + "description": "Target URL (internal or external)" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self", + "description": "Link target" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "button" + }, + "href": { + "type": "string", + "description": "Target URL" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost" + ], + "default": "primary" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "megamenu" + }, + "columns": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Column heading" + }, + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "link" + }, + "href": { + "type": "string", + "description": "Target URL (internal or external)" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self", + "description": "Link target" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + }, + "description": "Links in this column" + } + }, + "required": [ + "links" + ], + "additionalProperties": false + }, + "description": "Mega menu columns" + } + }, + "required": [ + "id", + "label", + "type", + "columns" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "dropdown" + }, + "children": { + "type": "array", + "items": {}, + "description": "Child menu items" + } + }, + "required": [ + "id", + "label", + "type", + "children" + ], + "additionalProperties": false + } + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/OpenGraph.json b/packages/spec/json-schema/website/OpenGraph.json new file mode 100644 index 000000000..510f5d2d2 --- /dev/null +++ b/packages/spec/json-schema/website/OpenGraph.json @@ -0,0 +1,57 @@ +{ + "$ref": "#/definitions/OpenGraph", + "definitions": { + "OpenGraph": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Open Graph title" + }, + "description": { + "type": "string", + "description": "Open Graph description" + }, + "type": { + "type": "string", + "enum": [ + "website", + "article", + "product", + "profile" + ], + "default": "website" + }, + "image": { + "type": "string", + "format": "uri", + "description": "Open Graph image URL (1200x630px recommended)" + }, + "imageAlt": { + "type": "string", + "description": "Open Graph image alt text" + }, + "siteName": { + "type": "string", + "description": "Site name" + }, + "locale": { + "type": "string", + "description": "Locale (e.g., \"en_US\", \"zh_CN\")" + }, + "url": { + "type": "string", + "format": "uri", + "description": "Canonical URL" + } + }, + "required": [ + "title", + "description", + "image" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/PricingPlan.json b/packages/spec/json-schema/website/PricingPlan.json new file mode 100644 index 000000000..aaa6dfef1 --- /dev/null +++ b/packages/spec/json-schema/website/PricingPlan.json @@ -0,0 +1,101 @@ +{ + "$ref": "#/definitions/PricingPlan", + "definitions": { + "PricingPlan": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Plan name" + }, + "description": { + "type": "string", + "description": "Plan description" + }, + "price": { + "type": "string", + "description": "Price (e.g., \"$29\", \"Free\", \"Custom\")" + }, + "period": { + "type": "string", + "description": "Billing period (e.g., \"/month\", \"/year\")" + }, + "features": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of features" + }, + "button": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false, + "description": "CTA button" + }, + "highlighted": { + "type": "boolean", + "default": false, + "description": "Highlight as recommended plan" + }, + "badge": { + "type": "string", + "description": "Badge text (e.g., \"Popular\", \"Best Value\")" + } + }, + "required": [ + "name", + "price", + "features", + "button" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/PricingSection.json b/packages/spec/json-schema/website/PricingSection.json new file mode 100644 index 000000000..55934d019 --- /dev/null +++ b/packages/spec/json-schema/website/PricingSection.json @@ -0,0 +1,148 @@ +{ + "$ref": "#/definitions/PricingSection", + "definitions": { + "PricingSection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "pricing" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "plans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Plan name" + }, + "description": { + "type": "string", + "description": "Plan description" + }, + "price": { + "type": "string", + "description": "Price (e.g., \"$29\", \"Free\", \"Custom\")" + }, + "period": { + "type": "string", + "description": "Billing period (e.g., \"/month\", \"/year\")" + }, + "features": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of features" + }, + "button": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false, + "description": "CTA button" + }, + "highlighted": { + "type": "boolean", + "default": false, + "description": "Highlight as recommended plan" + }, + "badge": { + "type": "string", + "description": "Badge text (e.g., \"Popular\", \"Best Value\")" + } + }, + "required": [ + "name", + "price", + "features", + "button" + ], + "additionalProperties": false + }, + "description": "Pricing plans" + }, + "billingToggle": { + "type": "object", + "properties": { + "monthly": { + "type": "string", + "default": "Monthly", + "description": "Monthly billing label" + }, + "yearly": { + "type": "string", + "default": "Yearly", + "description": "Yearly billing label" + } + }, + "additionalProperties": false, + "description": "Enable monthly/yearly toggle" + } + }, + "required": [ + "type", + "plans" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/SeoConfig.json b/packages/spec/json-schema/website/SeoConfig.json new file mode 100644 index 000000000..51f784335 --- /dev/null +++ b/packages/spec/json-schema/website/SeoConfig.json @@ -0,0 +1,206 @@ +{ + "$ref": "#/definitions/SeoConfig", + "definitions": { + "SeoConfig": { + "type": "object", + "properties": { + "meta": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "SEO title (50-60 characters recommended)" + }, + "description": { + "type": "string", + "description": "Meta description (150-160 characters recommended)" + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "SEO keywords" + }, + "canonical": { + "type": "string", + "format": "uri", + "description": "Canonical URL" + }, + "robots": { + "type": "string", + "description": "Robots directive (e.g., \"index, follow\")" + }, + "author": { + "type": "string", + "description": "Content author" + }, + "language": { + "type": "string", + "description": "Language code (e.g., \"en\", \"zh-CN\")" + } + }, + "required": [ + "title", + "description" + ], + "additionalProperties": false, + "description": "Basic SEO meta tags" + }, + "openGraph": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Open Graph title" + }, + "description": { + "type": "string", + "description": "Open Graph description" + }, + "type": { + "type": "string", + "enum": [ + "website", + "article", + "product", + "profile" + ], + "default": "website" + }, + "image": { + "type": "string", + "format": "uri", + "description": "Open Graph image URL (1200x630px recommended)" + }, + "imageAlt": { + "type": "string", + "description": "Open Graph image alt text" + }, + "siteName": { + "type": "string", + "description": "Site name" + }, + "locale": { + "type": "string", + "description": "Locale (e.g., \"en_US\", \"zh_CN\")" + }, + "url": { + "type": "string", + "format": "uri", + "description": "Canonical URL" + } + }, + "required": [ + "title", + "description", + "image" + ], + "additionalProperties": false, + "description": "Open Graph protocol tags" + }, + "twitter": { + "type": "object", + "properties": { + "card": { + "type": "string", + "enum": [ + "summary", + "summary_large_image", + "app", + "player" + ], + "default": "summary_large_image" + }, + "title": { + "type": "string", + "description": "Twitter card title" + }, + "description": { + "type": "string", + "description": "Twitter card description" + }, + "image": { + "type": "string", + "format": "uri", + "description": "Twitter card image URL" + }, + "imageAlt": { + "type": "string", + "description": "Twitter card image alt text" + }, + "site": { + "type": "string", + "description": "Twitter @username for site" + }, + "creator": { + "type": "string", + "description": "Twitter @username for creator" + } + }, + "required": [ + "title", + "description", + "image" + ], + "additionalProperties": false, + "description": "Twitter Card tags" + }, + "structuredData": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Schema.org type (e.g., \"Organization\", \"Product\", \"Article\")" + }, + "data": { + "type": "object", + "additionalProperties": {}, + "description": "Schema.org structured data object" + } + }, + "required": [ + "type", + "data" + ], + "additionalProperties": false + }, + "description": "Schema.org structured data" + }, + "customMeta": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Meta tag name" + }, + "content": { + "type": "string", + "description": "Meta tag content" + }, + "property": { + "type": "string", + "description": "Meta tag property (for og: tags)" + } + }, + "required": [ + "name", + "content" + ], + "additionalProperties": false + }, + "description": "Custom meta tags" + } + }, + "required": [ + "meta" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/SeoMetaTags.json b/packages/spec/json-schema/website/SeoMetaTags.json new file mode 100644 index 000000000..bc66c63dc --- /dev/null +++ b/packages/spec/json-schema/website/SeoMetaTags.json @@ -0,0 +1,48 @@ +{ + "$ref": "#/definitions/SeoMetaTags", + "definitions": { + "SeoMetaTags": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "SEO title (50-60 characters recommended)" + }, + "description": { + "type": "string", + "description": "Meta description (150-160 characters recommended)" + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "SEO keywords" + }, + "canonical": { + "type": "string", + "format": "uri", + "description": "Canonical URL" + }, + "robots": { + "type": "string", + "description": "Robots directive (e.g., \"index, follow\")" + }, + "author": { + "type": "string", + "description": "Content author" + }, + "language": { + "type": "string", + "description": "Language code (e.g., \"en\", \"zh-CN\")" + } + }, + "required": [ + "title", + "description" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/SocialLink.json b/packages/spec/json-schema/website/SocialLink.json new file mode 100644 index 000000000..6138f1e2a --- /dev/null +++ b/packages/spec/json-schema/website/SocialLink.json @@ -0,0 +1,41 @@ +{ + "$ref": "#/definitions/SocialLink", + "definitions": { + "SocialLink": { + "type": "object", + "properties": { + "platform": { + "type": "string", + "enum": [ + "facebook", + "twitter", + "linkedin", + "instagram", + "github", + "youtube", + "custom" + ] + }, + "url": { + "type": "string", + "format": "uri", + "description": "Social profile URL" + }, + "icon": { + "type": "string", + "description": "Custom icon name or URL" + }, + "label": { + "type": "string", + "description": "Accessible label" + } + }, + "required": [ + "platform", + "url" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/StructuredData.json b/packages/spec/json-schema/website/StructuredData.json new file mode 100644 index 000000000..a302af105 --- /dev/null +++ b/packages/spec/json-schema/website/StructuredData.json @@ -0,0 +1,25 @@ +{ + "$ref": "#/definitions/StructuredData", + "definitions": { + "StructuredData": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Schema.org type (e.g., \"Organization\", \"Product\", \"Article\")" + }, + "data": { + "type": "object", + "additionalProperties": {}, + "description": "Schema.org structured data object" + } + }, + "required": [ + "type", + "data" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/TestimonialItem.json b/packages/spec/json-schema/website/TestimonialItem.json new file mode 100644 index 000000000..daf9c6d78 --- /dev/null +++ b/packages/spec/json-schema/website/TestimonialItem.json @@ -0,0 +1,42 @@ +{ + "$ref": "#/definitions/TestimonialItem", + "definitions": { + "TestimonialItem": { + "type": "object", + "properties": { + "quote": { + "type": "string", + "description": "Testimonial quote" + }, + "author": { + "type": "string", + "description": "Author name" + }, + "title": { + "type": "string", + "description": "Author title or role" + }, + "company": { + "type": "string", + "description": "Author company" + }, + "avatar": { + "type": "string", + "description": "Author avatar image URL" + }, + "rating": { + "type": "number", + "minimum": 1, + "maximum": 5, + "description": "Rating out of 5" + } + }, + "required": [ + "quote", + "author" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/TestimonialsSection.json b/packages/spec/json-schema/website/TestimonialsSection.json new file mode 100644 index 000000000..46d2f1a6a --- /dev/null +++ b/packages/spec/json-schema/website/TestimonialsSection.json @@ -0,0 +1,90 @@ +{ + "$ref": "#/definitions/TestimonialsSection", + "definitions": { + "TestimonialsSection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "testimonials" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "testimonials": { + "type": "array", + "items": { + "type": "object", + "properties": { + "quote": { + "type": "string", + "description": "Testimonial quote" + }, + "author": { + "type": "string", + "description": "Author name" + }, + "title": { + "type": "string", + "description": "Author title or role" + }, + "company": { + "type": "string", + "description": "Author company" + }, + "avatar": { + "type": "string", + "description": "Author avatar image URL" + }, + "rating": { + "type": "number", + "minimum": 1, + "maximum": 5, + "description": "Rating out of 5" + } + }, + "required": [ + "quote", + "author" + ], + "additionalProperties": false + }, + "description": "List of testimonials" + }, + "style": { + "type": "string", + "enum": [ + "grid", + "carousel", + "masonry" + ], + "default": "grid" + }, + "columns": { + "type": "string", + "enum": [ + "1", + "2", + "3" + ], + "default": "3" + } + }, + "required": [ + "type", + "testimonials" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/TwitterCard.json b/packages/spec/json-schema/website/TwitterCard.json new file mode 100644 index 000000000..9fd3714d5 --- /dev/null +++ b/packages/spec/json-schema/website/TwitterCard.json @@ -0,0 +1,52 @@ +{ + "$ref": "#/definitions/TwitterCard", + "definitions": { + "TwitterCard": { + "type": "object", + "properties": { + "card": { + "type": "string", + "enum": [ + "summary", + "summary_large_image", + "app", + "player" + ], + "default": "summary_large_image" + }, + "title": { + "type": "string", + "description": "Twitter card title" + }, + "description": { + "type": "string", + "description": "Twitter card description" + }, + "image": { + "type": "string", + "format": "uri", + "description": "Twitter card image URL" + }, + "imageAlt": { + "type": "string", + "description": "Twitter card image alt text" + }, + "site": { + "type": "string", + "description": "Twitter @username for site" + }, + "creator": { + "type": "string", + "description": "Twitter @username for creator" + } + }, + "required": [ + "title", + "description", + "image" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/WebsiteConfig.json b/packages/spec/json-schema/website/WebsiteConfig.json new file mode 100644 index 000000000..303a9bdde --- /dev/null +++ b/packages/spec/json-schema/website/WebsiteConfig.json @@ -0,0 +1,1729 @@ +{ + "$ref": "#/definitions/WebsiteConfig", + "definitions": { + "WebsiteConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Website identifier (snake_case)" + }, + "title": { + "type": "string", + "description": "Website title" + }, + "description": { + "type": "string", + "description": "Website description" + }, + "baseUrl": { + "type": "string", + "format": "uri", + "description": "Base URL (e.g., https://example.com)" + }, + "locale": { + "type": "string", + "default": "en", + "description": "Default locale (e.g., \"en\", \"zh-CN\")" + }, + "locales": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Supported locales for i18n" + }, + "theme": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Theme name" + }, + "primaryColor": { + "type": "string", + "description": "Primary brand color (hex)" + }, + "secondaryColor": { + "type": "string", + "description": "Secondary brand color (hex)" + }, + "fontFamily": { + "type": "object", + "properties": { + "heading": { + "type": "string", + "description": "Heading font family" + }, + "body": { + "type": "string", + "description": "Body font family" + } + }, + "additionalProperties": false + }, + "borderRadius": { + "type": "string", + "enum": [ + "none", + "sm", + "md", + "lg", + "full" + ], + "default": "md" + }, + "darkMode": { + "type": "boolean", + "default": false, + "description": "Enable dark mode" + } + }, + "required": [ + "name", + "primaryColor" + ], + "additionalProperties": false, + "description": "Visual theme settings" + }, + "navigations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Navigation identifier (snake_case)" + }, + "label": { + "type": "string", + "description": "Navigation label" + }, + "position": { + "type": "string", + "enum": [ + "header", + "footer", + "sidebar" + ], + "default": "header" + }, + "logo": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Logo image URL" + }, + "alt": { + "type": "string", + "description": "Logo alt text" + }, + "href": { + "type": "string", + "default": "/", + "description": "Logo link href" + }, + "width": { + "type": "number", + "description": "Logo width in pixels" + }, + "height": { + "type": "number", + "description": "Logo height in pixels" + } + }, + "required": [ + "src", + "alt" + ], + "additionalProperties": false, + "description": "Logo configuration" + }, + "items": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "link" + }, + "href": { + "type": "string", + "description": "Target URL (internal or external)" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self", + "description": "Link target" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "button" + }, + "href": { + "type": "string", + "description": "Target URL" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost" + ], + "default": "primary" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "megamenu" + }, + "columns": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Column heading" + }, + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "link" + }, + "href": { + "type": "string", + "description": "Target URL (internal or external)" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self", + "description": "Link target" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + }, + "description": "Links in this column" + } + }, + "required": [ + "links" + ], + "additionalProperties": false + }, + "description": "Mega menu columns" + } + }, + "required": [ + "id", + "label", + "type", + "columns" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "dropdown" + }, + "children": { + "type": "array", + "items": {}, + "description": "Child menu items" + } + }, + "required": [ + "id", + "label", + "type", + "children" + ], + "additionalProperties": false + } + ] + }, + "description": "Navigation menu items" + }, + "mobileCollapsible": { + "type": "boolean", + "default": true, + "description": "Enable mobile hamburger menu" + }, + "sticky": { + "type": "boolean", + "default": false, + "description": "Enable sticky navigation on scroll" + } + }, + "required": [ + "name", + "label", + "items" + ], + "additionalProperties": false + }, + "description": "Navigation menus" + }, + "footers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Footer identifier (snake_case)" + }, + "linkGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Link group title" + }, + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string", + "description": "Link label" + }, + "href": { + "type": "string", + "description": "Link URL" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + } + }, + "required": [ + "label", + "href" + ], + "additionalProperties": false + }, + "description": "Links" + } + }, + "required": [ + "title", + "links" + ], + "additionalProperties": false + }, + "description": "Footer link groups" + }, + "socialLinks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "platform": { + "type": "string", + "enum": [ + "facebook", + "twitter", + "linkedin", + "instagram", + "github", + "youtube", + "custom" + ] + }, + "url": { + "type": "string", + "format": "uri", + "description": "Social profile URL" + }, + "icon": { + "type": "string", + "description": "Custom icon name or URL" + }, + "label": { + "type": "string", + "description": "Accessible label" + } + }, + "required": [ + "platform", + "url" + ], + "additionalProperties": false + }, + "description": "Social media links" + }, + "copyright": { + "type": "string", + "description": "Copyright text" + }, + "description": { + "type": "string", + "description": "Footer description or tagline" + }, + "newsletter": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Newsletter section title" + }, + "description": { + "type": "string", + "description": "Newsletter description" + }, + "placeholder": { + "type": "string", + "default": "Enter your email", + "description": "Email input placeholder" + }, + "buttonText": { + "type": "string", + "default": "Subscribe", + "description": "Submit button text" + } + }, + "required": [ + "title" + ], + "additionalProperties": false, + "description": "Newsletter signup configuration" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "description": "Footer configurations" + }, + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Page identifier (snake_case)" + }, + "title": { + "type": "string", + "description": "Page title" + }, + "slug": { + "type": "string", + "description": "URL slug (e.g., \"/\", \"/pricing\", \"/about\")" + }, + "description": { + "type": "string", + "description": "Page description" + }, + "seo": { + "type": "object", + "properties": { + "meta": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "SEO title (50-60 characters recommended)" + }, + "description": { + "type": "string", + "description": "Meta description (150-160 characters recommended)" + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "SEO keywords" + }, + "canonical": { + "type": "string", + "format": "uri", + "description": "Canonical URL" + }, + "robots": { + "type": "string", + "description": "Robots directive (e.g., \"index, follow\")" + }, + "author": { + "type": "string", + "description": "Content author" + }, + "language": { + "type": "string", + "description": "Language code (e.g., \"en\", \"zh-CN\")" + } + }, + "required": [ + "title", + "description" + ], + "additionalProperties": false, + "description": "Basic SEO meta tags" + }, + "openGraph": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Open Graph title" + }, + "description": { + "type": "string", + "description": "Open Graph description" + }, + "type": { + "type": "string", + "enum": [ + "website", + "article", + "product", + "profile" + ], + "default": "website" + }, + "image": { + "type": "string", + "format": "uri", + "description": "Open Graph image URL (1200x630px recommended)" + }, + "imageAlt": { + "type": "string", + "description": "Open Graph image alt text" + }, + "siteName": { + "type": "string", + "description": "Site name" + }, + "locale": { + "type": "string", + "description": "Locale (e.g., \"en_US\", \"zh_CN\")" + }, + "url": { + "type": "string", + "format": "uri", + "description": "Canonical URL" + } + }, + "required": [ + "title", + "description", + "image" + ], + "additionalProperties": false, + "description": "Open Graph protocol tags" + }, + "twitter": { + "type": "object", + "properties": { + "card": { + "type": "string", + "enum": [ + "summary", + "summary_large_image", + "app", + "player" + ], + "default": "summary_large_image" + }, + "title": { + "type": "string", + "description": "Twitter card title" + }, + "description": { + "type": "string", + "description": "Twitter card description" + }, + "image": { + "type": "string", + "format": "uri", + "description": "Twitter card image URL" + }, + "imageAlt": { + "type": "string", + "description": "Twitter card image alt text" + }, + "site": { + "type": "string", + "description": "Twitter @username for site" + }, + "creator": { + "type": "string", + "description": "Twitter @username for creator" + } + }, + "required": [ + "title", + "description", + "image" + ], + "additionalProperties": false, + "description": "Twitter Card tags" + }, + "structuredData": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Schema.org type (e.g., \"Organization\", \"Product\", \"Article\")" + }, + "data": { + "type": "object", + "additionalProperties": {}, + "description": "Schema.org structured data object" + } + }, + "required": [ + "type", + "data" + ], + "additionalProperties": false + }, + "description": "Schema.org structured data" + }, + "customMeta": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Meta tag name" + }, + "content": { + "type": "string", + "description": "Meta tag content" + }, + "property": { + "type": "string", + "description": "Meta tag property (for og: tags)" + } + }, + "required": [ + "name", + "content" + ], + "additionalProperties": false + }, + "description": "Custom meta tags" + } + }, + "required": [ + "meta" + ], + "additionalProperties": false, + "description": "SEO and meta tags configuration" + }, + "sections": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "hero" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "headline": { + "type": "string", + "description": "Main headline" + }, + "subheadline": { + "type": "string", + "description": "Subheadline or tagline" + }, + "description": { + "type": "string", + "description": "Description text" + }, + "buttons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false + }, + "description": "CTA buttons" + }, + "backgroundImage": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Image source URL" + }, + "alt": { + "type": "string", + "description": "Image alt text" + }, + "width": { + "type": "number", + "description": "Image width in pixels" + }, + "height": { + "type": "number", + "description": "Image height in pixels" + }, + "objectFit": { + "type": "string", + "enum": [ + "cover", + "contain", + "fill", + "none", + "scale-down" + ], + "default": "cover" + }, + "loading": { + "type": "string", + "enum": [ + "lazy", + "eager" + ], + "default": "lazy" + } + }, + "required": [ + "src", + "alt" + ], + "additionalProperties": false, + "description": "Hero background image" + }, + "backgroundVideo": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Video source URL" + }, + "poster": { + "type": "string", + "description": "Video poster image" + } + }, + "required": [ + "src" + ], + "additionalProperties": false, + "description": "Hero background video" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + }, + "overlayOpacity": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.3 + } + }, + "required": [ + "type", + "headline" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "features" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "title": { + "type": "string", + "description": "Feature title" + }, + "description": { + "type": "string", + "description": "Feature description" + }, + "link": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Link text" + }, + "href": { + "type": "string", + "description": "Link URL" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false, + "description": "Optional link to learn more" + } + }, + "required": [ + "title", + "description" + ], + "additionalProperties": false + }, + "description": "List of features" + }, + "columns": { + "type": "string", + "enum": [ + "2", + "3", + "4" + ], + "default": "3", + "description": "Number of columns in grid" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + } + }, + "required": [ + "type", + "features" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "testimonials" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "testimonials": { + "type": "array", + "items": { + "type": "object", + "properties": { + "quote": { + "type": "string", + "description": "Testimonial quote" + }, + "author": { + "type": "string", + "description": "Author name" + }, + "title": { + "type": "string", + "description": "Author title or role" + }, + "company": { + "type": "string", + "description": "Author company" + }, + "avatar": { + "type": "string", + "description": "Author avatar image URL" + }, + "rating": { + "type": "number", + "minimum": 1, + "maximum": 5, + "description": "Rating out of 5" + } + }, + "required": [ + "quote", + "author" + ], + "additionalProperties": false + }, + "description": "List of testimonials" + }, + "style": { + "type": "string", + "enum": [ + "grid", + "carousel", + "masonry" + ], + "default": "grid" + }, + "columns": { + "type": "string", + "enum": [ + "1", + "2", + "3" + ], + "default": "3" + } + }, + "required": [ + "type", + "testimonials" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "cta" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "headline": { + "type": "string", + "description": "CTA headline" + }, + "description": { + "type": "string", + "description": "CTA description" + }, + "buttons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false + }, + "description": "CTA buttons" + }, + "backgroundColor": { + "type": "string", + "description": "Background color (hex, rgb, or preset)" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "center" + } + }, + "required": [ + "type", + "headline", + "buttons" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "pricing" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "plans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Plan name" + }, + "description": { + "type": "string", + "description": "Plan description" + }, + "price": { + "type": "string", + "description": "Price (e.g., \"$29\", \"Free\", \"Custom\")" + }, + "period": { + "type": "string", + "description": "Billing period (e.g., \"/month\", \"/year\")" + }, + "features": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of features" + }, + "button": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Button text" + }, + "href": { + "type": "string", + "description": "Button target URL or action" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost", + "link" + ], + "default": "primary" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ], + "default": "md" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + }, + "icon": { + "type": "string", + "description": "Icon name" + } + }, + "required": [ + "text", + "href" + ], + "additionalProperties": false, + "description": "CTA button" + }, + "highlighted": { + "type": "boolean", + "default": false, + "description": "Highlight as recommended plan" + }, + "badge": { + "type": "string", + "description": "Badge text (e.g., \"Popular\", \"Best Value\")" + } + }, + "required": [ + "name", + "price", + "features", + "button" + ], + "additionalProperties": false + }, + "description": "Pricing plans" + }, + "billingToggle": { + "type": "object", + "properties": { + "monthly": { + "type": "string", + "default": "Monthly", + "description": "Monthly billing label" + }, + "yearly": { + "type": "string", + "default": "Yearly", + "description": "Yearly billing label" + } + }, + "additionalProperties": false, + "description": "Enable monthly/yearly toggle" + } + }, + "required": [ + "type", + "plans" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "content" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "content": { + "type": "string", + "description": "Content in markdown or HTML format" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ], + "default": "left" + }, + "maxWidth": { + "type": "string", + "enum": [ + "sm", + "md", + "lg", + "xl", + "full" + ], + "default": "lg" + } + }, + "required": [ + "type", + "content" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "faq" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title" + }, + "description": { + "type": "string", + "description": "Section description" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "FAQ question" + }, + "answer": { + "type": "string", + "description": "FAQ answer (markdown or HTML)" + } + }, + "required": [ + "question", + "answer" + ], + "additionalProperties": false + }, + "description": "FAQ items" + }, + "style": { + "type": "string", + "enum": [ + "accordion", + "grid" + ], + "default": "accordion" + } + }, + "required": [ + "type", + "items" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "logo_cloud" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "title": { + "type": "string", + "description": "Section title (e.g., \"Trusted by\")" + }, + "logos": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Logo image URL" + }, + "alt": { + "type": "string", + "description": "Company/partner name" + }, + "href": { + "type": "string", + "description": "Optional link to company website" + } + }, + "required": [ + "src", + "alt" + ], + "additionalProperties": false + }, + "description": "Logo images" + }, + "grayscale": { + "type": "boolean", + "default": true, + "description": "Display logos in grayscale" + } + }, + "required": [ + "type", + "logos" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "custom_html" + }, + "id": { + "type": "string", + "description": "Section ID for anchor links" + }, + "html": { + "type": "string", + "description": "Custom HTML content" + } + }, + "required": [ + "type", + "html" + ], + "additionalProperties": false + } + ] + }, + "description": "Page content sections" + }, + "navigation": { + "type": "string", + "description": "Navigation name to use" + }, + "footer": { + "type": "string", + "description": "Footer name to use" + }, + "published": { + "type": "boolean", + "default": false, + "description": "Whether page is published" + }, + "publishedAt": { + "type": "string", + "format": "date-time", + "description": "Publication timestamp" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "Last update timestamp" + }, + "template": { + "type": "string", + "description": "Custom template identifier" + }, + "scripts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Script source URL" + }, + "inline": { + "type": "string", + "description": "Inline script content" + }, + "position": { + "type": "string", + "enum": [ + "head", + "body_start", + "body_end" + ], + "default": "body_end" + }, + "async": { + "type": "boolean", + "default": true + }, + "defer": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "description": "Custom scripts to inject" + }, + "customCss": { + "type": "string", + "description": "Custom CSS styles" + }, + "variant": { + "type": "string", + "description": "A/B testing variant identifier" + } + }, + "required": [ + "name", + "title", + "slug", + "sections" + ], + "additionalProperties": false + }, + "description": "Landing pages" + }, + "analytics": { + "type": "object", + "properties": { + "googleAnalytics": { + "type": "object", + "properties": { + "measurementId": { + "type": "string", + "description": "GA4 Measurement ID (e.g., G-XXXXXXXXXX)" + } + }, + "required": [ + "measurementId" + ], + "additionalProperties": false + }, + "googleTagManager": { + "type": "object", + "properties": { + "containerId": { + "type": "string", + "description": "GTM Container ID (e.g., GTM-XXXXXXX)" + } + }, + "required": [ + "containerId" + ], + "additionalProperties": false + }, + "facebookPixel": { + "type": "object", + "properties": { + "pixelId": { + "type": "string", + "description": "Facebook Pixel ID" + } + }, + "required": [ + "pixelId" + ], + "additionalProperties": false + }, + "customScripts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Script name" + }, + "src": { + "type": "string", + "description": "Script source URL" + }, + "inline": { + "type": "string", + "description": "Inline script content" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + } + }, + "additionalProperties": false, + "description": "Analytics and tracking" + }, + "favicon": { + "type": "string", + "description": "Favicon URL" + }, + "socialPreview": { + "type": "string", + "format": "uri", + "description": "Default social preview image" + }, + "previewReleaseDate": { + "type": "string", + "format": "date-time", + "description": "Preview release date (ISO 8601 format, e.g., 2026-03-01T00:00:00Z)" + }, + "version": { + "type": "string", + "description": "Website version" + } + }, + "required": [ + "name", + "title", + "baseUrl", + "pages" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/WebsiteFooter.json b/packages/spec/json-schema/website/WebsiteFooter.json new file mode 100644 index 000000000..c5eeeb6f3 --- /dev/null +++ b/packages/spec/json-schema/website/WebsiteFooter.json @@ -0,0 +1,143 @@ +{ + "$ref": "#/definitions/WebsiteFooter", + "definitions": { + "WebsiteFooter": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Footer identifier (snake_case)" + }, + "linkGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Link group title" + }, + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string", + "description": "Link label" + }, + "href": { + "type": "string", + "description": "Link URL" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + } + }, + "required": [ + "label", + "href" + ], + "additionalProperties": false + }, + "description": "Links" + } + }, + "required": [ + "title", + "links" + ], + "additionalProperties": false + }, + "description": "Footer link groups" + }, + "socialLinks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "platform": { + "type": "string", + "enum": [ + "facebook", + "twitter", + "linkedin", + "instagram", + "github", + "youtube", + "custom" + ] + }, + "url": { + "type": "string", + "format": "uri", + "description": "Social profile URL" + }, + "icon": { + "type": "string", + "description": "Custom icon name or URL" + }, + "label": { + "type": "string", + "description": "Accessible label" + } + }, + "required": [ + "platform", + "url" + ], + "additionalProperties": false + }, + "description": "Social media links" + }, + "copyright": { + "type": "string", + "description": "Copyright text" + }, + "description": { + "type": "string", + "description": "Footer description or tagline" + }, + "newsletter": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Newsletter section title" + }, + "description": { + "type": "string", + "description": "Newsletter description" + }, + "placeholder": { + "type": "string", + "default": "Enter your email", + "description": "Email input placeholder" + }, + "buttonText": { + "type": "string", + "default": "Subscribe", + "description": "Submit button text" + } + }, + "required": [ + "title" + ], + "additionalProperties": false, + "description": "Newsletter signup configuration" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/WebsiteNavigation.json b/packages/spec/json-schema/website/WebsiteNavigation.json new file mode 100644 index 000000000..c9ee313b5 --- /dev/null +++ b/packages/spec/json-schema/website/WebsiteNavigation.json @@ -0,0 +1,339 @@ +{ + "$ref": "#/definitions/WebsiteNavigation", + "definitions": { + "WebsiteNavigation": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Navigation identifier (snake_case)" + }, + "label": { + "type": "string", + "description": "Navigation label" + }, + "position": { + "type": "string", + "enum": [ + "header", + "footer", + "sidebar" + ], + "default": "header" + }, + "logo": { + "type": "object", + "properties": { + "src": { + "type": "string", + "description": "Logo image URL" + }, + "alt": { + "type": "string", + "description": "Logo alt text" + }, + "href": { + "type": "string", + "default": "/", + "description": "Logo link href" + }, + "width": { + "type": "number", + "description": "Logo width in pixels" + }, + "height": { + "type": "number", + "description": "Logo height in pixels" + } + }, + "required": [ + "src", + "alt" + ], + "additionalProperties": false, + "description": "Logo configuration" + }, + "items": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "link" + }, + "href": { + "type": "string", + "description": "Target URL (internal or external)" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self", + "description": "Link target" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "button" + }, + "href": { + "type": "string", + "description": "Target URL" + }, + "variant": { + "type": "string", + "enum": [ + "primary", + "secondary", + "outline", + "ghost" + ], + "default": "primary" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "megamenu" + }, + "columns": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Column heading" + }, + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "link" + }, + "href": { + "type": "string", + "description": "Target URL (internal or external)" + }, + "target": { + "type": "string", + "enum": [ + "_self", + "_blank" + ], + "default": "_self", + "description": "Link target" + } + }, + "required": [ + "id", + "label", + "type", + "href" + ], + "additionalProperties": false + }, + "description": "Links in this column" + } + }, + "required": [ + "links" + ], + "additionalProperties": false + }, + "description": "Mega menu columns" + } + }, + "required": [ + "id", + "label", + "type", + "columns" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for menu item" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "icon": { + "type": "string", + "description": "Icon name or URL" + }, + "badge": { + "type": "string", + "description": "Badge text" + }, + "visible": { + "type": "string", + "description": "Visibility formula condition" + }, + "type": { + "type": "string", + "const": "dropdown" + }, + "children": { + "type": "array", + "items": {}, + "description": "Child menu items" + } + }, + "required": [ + "id", + "label", + "type", + "children" + ], + "additionalProperties": false + } + ] + }, + "description": "Navigation menu items" + }, + "mobileCollapsible": { + "type": "boolean", + "default": true, + "description": "Enable mobile hamburger menu" + }, + "sticky": { + "type": "boolean", + "default": false, + "description": "Enable sticky navigation on scroll" + } + }, + "required": [ + "name", + "label", + "items" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/website/WebsiteTheme.json b/packages/spec/json-schema/website/WebsiteTheme.json new file mode 100644 index 000000000..42f9f1408 --- /dev/null +++ b/packages/spec/json-schema/website/WebsiteTheme.json @@ -0,0 +1,58 @@ +{ + "$ref": "#/definitions/WebsiteTheme", + "definitions": { + "WebsiteTheme": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Theme name" + }, + "primaryColor": { + "type": "string", + "description": "Primary brand color (hex)" + }, + "secondaryColor": { + "type": "string", + "description": "Secondary brand color (hex)" + }, + "fontFamily": { + "type": "object", + "properties": { + "heading": { + "type": "string", + "description": "Heading font family" + }, + "body": { + "type": "string", + "description": "Body font family" + } + }, + "additionalProperties": false + }, + "borderRadius": { + "type": "string", + "enum": [ + "none", + "sm", + "md", + "lg", + "full" + ], + "default": "md" + }, + "darkMode": { + "type": "boolean", + "default": false, + "description": "Enable dark mode" + } + }, + "required": [ + "name", + "primaryColor" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/package.json b/packages/spec/package.json index c6c09b019..31861dccc 100644 --- a/packages/spec/package.json +++ b/packages/spec/package.json @@ -48,6 +48,10 @@ "./ui": { "types": "./dist/ui/index.d.ts", "default": "./dist/ui/index.js" + }, + "./website": { + "types": "./dist/website/index.d.ts", + "default": "./dist/website/index.js" } }, "files": [ diff --git a/packages/spec/src/index.ts b/packages/spec/src/index.ts index d79b3a2cf..dea3d5e14 100644 --- a/packages/spec/src/index.ts +++ b/packages/spec/src/index.ts @@ -53,4 +53,5 @@ export * as Hub from './hub'; export * as AI from './ai'; export * as API from './api'; export * as Automation from './automation'; +export * as Website from './website'; diff --git a/packages/spec/src/website/content-block.test.ts b/packages/spec/src/website/content-block.test.ts new file mode 100644 index 000000000..3d137449e --- /dev/null +++ b/packages/spec/src/website/content-block.test.ts @@ -0,0 +1,310 @@ +import { describe, it, expect } from 'vitest'; +import { + HeroSectionSchema, + FeaturesSectionSchema, + TestimonialsSectionSchema, + CtaSectionSchema, + PricingSectionSchema, + ContentSectionSchema, + FaqSectionSchema, + LogoCloudSectionSchema, + CustomHtmlSectionSchema, + ContentBlockSchema, + type HeroSection, + type FeaturesSection, + type PricingSection, +} from './content-block.zod'; + +describe('HeroSectionSchema', () => { + it('should accept minimal hero section', () => { + const hero: HeroSection = { + type: 'hero', + headline: 'Welcome to ObjectStack', + }; + + const result = HeroSectionSchema.parse(hero); + expect(result.align).toBe('center'); + expect(result.overlayOpacity).toBe(0.3); + }); + + it('should accept full hero section', () => { + const hero: HeroSection = { + type: 'hero', + id: 'hero-home', + headline: 'Build Enterprise Software Faster', + subheadline: 'The Post-SaaS Operating System', + description: 'Metadata-driven low-code platform for enterprise applications', + buttons: [ + { + text: 'Get Started', + href: '/docs', + variant: 'primary', + size: 'lg', + }, + { + text: 'View Demo', + href: '/demo', + variant: 'outline', + size: 'lg', + }, + ], + backgroundImage: { + src: '/hero-bg.jpg', + alt: 'Hero background', + objectFit: 'cover', + }, + align: 'center', + overlayOpacity: 0.5, + }; + + expect(() => HeroSectionSchema.parse(hero)).not.toThrow(); + }); +}); + +describe('FeaturesSectionSchema', () => { + it('should accept features section', () => { + const features: FeaturesSection = { + type: 'features', + title: 'Why ObjectStack?', + description: 'Everything you need to build enterprise applications', + features: [ + { + icon: 'zap', + title: 'Lightning Fast', + description: 'Built for performance from the ground up', + }, + { + icon: 'shield', + title: 'Secure by Default', + description: 'Enterprise-grade security built in', + }, + { + icon: 'code', + title: 'Developer Friendly', + description: 'Clean APIs and comprehensive documentation', + }, + ], + }; + + const result = FeaturesSectionSchema.parse(features); + expect(result.columns).toBe('3'); + expect(result.align).toBe('center'); + }); + + it('should accept features with different columns', () => { + const features: FeaturesSection = { + type: 'features', + features: [ + { + title: 'Feature 1', + description: 'Description 1', + }, + { + title: 'Feature 2', + description: 'Description 2', + }, + ], + columns: '2', + }; + + expect(() => FeaturesSectionSchema.parse(features)).not.toThrow(); + }); +}); + +describe('TestimonialsSectionSchema', () => { + it('should accept testimonials section', () => { + const testimonials = { + type: 'testimonials' as const, + title: 'What Our Customers Say', + testimonials: [ + { + quote: 'ObjectStack transformed how we build software', + author: 'Jane Doe', + title: 'CTO', + company: 'Acme Corp', + avatar: '/avatars/jane.jpg', + rating: 5, + }, + { + quote: 'The best platform for enterprise development', + author: 'John Smith', + company: 'Tech Inc', + }, + ], + }; + + const result = TestimonialsSectionSchema.parse(testimonials); + expect(result.style).toBe('grid'); + expect(result.columns).toBe('3'); + }); +}); + +describe('CtaSectionSchema', () => { + it('should accept CTA section', () => { + const cta = { + type: 'cta' as const, + headline: 'Ready to Get Started?', + description: 'Join thousands of developers building with ObjectStack', + buttons: [ + { + text: 'Start Free Trial', + href: '/signup', + variant: 'primary' as const, + }, + ], + }; + + const result = CtaSectionSchema.parse(cta); + expect(result.align).toBe('center'); + }); +}); + +describe('PricingSectionSchema', () => { + it('should accept pricing section', () => { + const pricing: PricingSection = { + type: 'pricing', + title: 'Simple, Transparent Pricing', + plans: [ + { + name: 'Starter', + price: '$0', + period: '/month', + features: [ + 'Up to 10 users', + 'Basic features', + 'Community support', + ], + button: { + text: 'Get Started', + href: '/signup', + }, + }, + { + name: 'Professional', + price: '$99', + period: '/month', + features: [ + 'Unlimited users', + 'Advanced features', + 'Priority support', + 'Custom integrations', + ], + button: { + text: 'Start Trial', + href: '/signup?plan=pro', + variant: 'primary', + }, + highlighted: true, + badge: 'Popular', + }, + { + name: 'Enterprise', + price: 'Custom', + features: [ + 'Everything in Professional', + 'Dedicated support', + 'SLA guarantee', + 'Custom development', + ], + button: { + text: 'Contact Sales', + href: '/contact', + }, + }, + ], + }; + + expect(() => PricingSectionSchema.parse(pricing)).not.toThrow(); + }); +}); + +describe('ContentSectionSchema', () => { + it('should accept content section', () => { + const content = { + type: 'content' as const, + title: 'About ObjectStack', + content: '# Welcome\n\nThis is **markdown** content.', + }; + + const result = ContentSectionSchema.parse(content); + expect(result.align).toBe('left'); + expect(result.maxWidth).toBe('lg'); + }); +}); + +describe('FaqSectionSchema', () => { + it('should accept FAQ section', () => { + const faq = { + type: 'faq' as const, + title: 'Frequently Asked Questions', + items: [ + { + question: 'What is ObjectStack?', + answer: 'ObjectStack is a metadata-driven low-code platform.', + }, + { + question: 'How much does it cost?', + answer: 'We offer flexible pricing starting from $0.', + }, + ], + }; + + const result = FaqSectionSchema.parse(faq); + expect(result.style).toBe('accordion'); + }); +}); + +describe('LogoCloudSectionSchema', () => { + it('should accept logo cloud section', () => { + const logoCloud = { + type: 'logo_cloud' as const, + title: 'Trusted by Leading Companies', + logos: [ + { + src: '/logos/company1.png', + alt: 'Company 1', + href: 'https://company1.com', + }, + { + src: '/logos/company2.png', + alt: 'Company 2', + }, + ], + }; + + const result = LogoCloudSectionSchema.parse(logoCloud); + expect(result.grayscale).toBe(true); + }); +}); + +describe('CustomHtmlSectionSchema', () => { + it('should accept custom HTML section', () => { + const customHtml = { + type: 'custom_html' as const, + id: 'custom-widget', + html: '
Custom content
', + }; + + expect(() => CustomHtmlSectionSchema.parse(customHtml)).not.toThrow(); + }); +}); + +describe('ContentBlockSchema', () => { + it('should discriminate between different content block types', () => { + const blocks = [ + { type: 'hero', headline: 'Test' }, + { type: 'features', features: [] }, + { type: 'testimonials', testimonials: [] }, + { type: 'cta', headline: 'CTA', buttons: [] }, + { type: 'pricing', plans: [] }, + { type: 'content', content: 'Test' }, + { type: 'faq', items: [] }, + { type: 'logo_cloud', logos: [] }, + { type: 'custom_html', html: '
' }, + ]; + + blocks.forEach(block => { + expect(() => ContentBlockSchema.parse(block)).not.toThrow(); + }); + }); +}); diff --git a/packages/spec/src/website/content-block.zod.ts b/packages/spec/src/website/content-block.zod.ts new file mode 100644 index 000000000..6c810501c --- /dev/null +++ b/packages/spec/src/website/content-block.zod.ts @@ -0,0 +1,395 @@ +import { z } from 'zod'; + +/** + * Content Alignment Schema + */ +const ContentAlignmentSchema = z.enum(['left', 'center', 'right']); + +/** + * Button Configuration Schema + * Call-to-action button configuration. + */ +export const ButtonConfigSchema = z.object({ + /** Button text */ + text: z.string().describe('Button text'), + + /** Button URL/action */ + href: z.string().describe('Button target URL or action'), + + /** Button variant */ + variant: z.enum(['primary', 'secondary', 'outline', 'ghost', 'link']).default('primary'), + + /** Button size */ + size: z.enum(['sm', 'md', 'lg']).default('md'), + + /** Open in new tab */ + target: z.enum(['_self', '_blank']).default('_self'), + + /** Icon */ + icon: z.string().optional().describe('Icon name'), +}); + +/** + * Image Configuration Schema + */ +export const ImageConfigSchema = z.object({ + /** Image source URL */ + src: z.string().describe('Image source URL'), + + /** Alt text for accessibility */ + alt: z.string().describe('Image alt text'), + + /** Image width */ + width: z.number().optional().describe('Image width in pixels'), + + /** Image height */ + height: z.number().optional().describe('Image height in pixels'), + + /** Object fit */ + objectFit: z.enum(['cover', 'contain', 'fill', 'none', 'scale-down']).default('cover'), + + /** Loading strategy */ + loading: z.enum(['lazy', 'eager']).default('lazy'), +}); + +/** + * Hero Section Schema + * Full-width hero banner with headline, description, and CTA. + */ +export const HeroSectionSchema = z.object({ + type: z.literal('hero'), + + /** Section ID */ + id: z.string().optional().describe('Section ID for anchor links'), + + /** Headline */ + headline: z.string().describe('Main headline'), + + /** Subheadline or tagline */ + subheadline: z.string().optional().describe('Subheadline or tagline'), + + /** Description text */ + description: z.string().optional().describe('Description text'), + + /** Call-to-action buttons */ + buttons: z.array(ButtonConfigSchema).optional().describe('CTA buttons'), + + /** Background image */ + backgroundImage: ImageConfigSchema.optional().describe('Hero background image'), + + /** Background video */ + backgroundVideo: z.object({ + src: z.string().describe('Video source URL'), + poster: z.string().optional().describe('Video poster image'), + }).optional().describe('Hero background video'), + + /** Text alignment */ + align: ContentAlignmentSchema.default('center'), + + /** Overlay opacity (0-1) */ + overlayOpacity: z.number().min(0).max(1).default(0.3), +}); + +/** + * Feature Item Schema + */ +export const FeatureItemSchema = z.object({ + /** Feature icon */ + icon: z.string().optional().describe('Icon name or URL'), + + /** Feature title */ + title: z.string().describe('Feature title'), + + /** Feature description */ + description: z.string().describe('Feature description'), + + /** Feature link */ + link: z.object({ + text: z.string().describe('Link text'), + href: z.string().describe('Link URL'), + }).optional().describe('Optional link to learn more'), +}); + +/** + * Features Section Schema + * Grid of features with icons, titles, and descriptions. + */ +export const FeaturesSectionSchema = z.object({ + type: z.literal('features'), + + /** Section ID */ + id: z.string().optional().describe('Section ID for anchor links'), + + /** Section title */ + title: z.string().optional().describe('Section title'), + + /** Section description */ + description: z.string().optional().describe('Section description'), + + /** Features list */ + features: z.array(FeatureItemSchema).describe('List of features'), + + /** Grid columns */ + columns: z.enum(['2', '3', '4']).default('3').describe('Number of columns in grid'), + + /** Text alignment */ + align: ContentAlignmentSchema.default('center'), +}); + +/** + * Testimonial Item Schema + */ +export const TestimonialItemSchema = z.object({ + /** Testimonial quote */ + quote: z.string().describe('Testimonial quote'), + + /** Author name */ + author: z.string().describe('Author name'), + + /** Author title/role */ + title: z.string().optional().describe('Author title or role'), + + /** Author company */ + company: z.string().optional().describe('Author company'), + + /** Author avatar */ + avatar: z.string().optional().describe('Author avatar image URL'), + + /** Rating (1-5 stars) */ + rating: z.number().min(1).max(5).optional().describe('Rating out of 5'), +}); + +/** + * Testimonials Section Schema + * Customer testimonials and reviews. + */ +export const TestimonialsSectionSchema = z.object({ + type: z.literal('testimonials'), + + /** Section ID */ + id: z.string().optional().describe('Section ID for anchor links'), + + /** Section title */ + title: z.string().optional().describe('Section title'), + + /** Section description */ + description: z.string().optional().describe('Section description'), + + /** Testimonials list */ + testimonials: z.array(TestimonialItemSchema).describe('List of testimonials'), + + /** Display style */ + style: z.enum(['grid', 'carousel', 'masonry']).default('grid'), + + /** Grid columns (for grid style) */ + columns: z.enum(['1', '2', '3']).default('3'), +}); + +/** + * CTA Section Schema + * Call-to-action section with headline and buttons. + */ +export const CtaSectionSchema = z.object({ + type: z.literal('cta'), + + /** Section ID */ + id: z.string().optional().describe('Section ID for anchor links'), + + /** Headline */ + headline: z.string().describe('CTA headline'), + + /** Description */ + description: z.string().optional().describe('CTA description'), + + /** CTA buttons */ + buttons: z.array(ButtonConfigSchema).describe('CTA buttons'), + + /** Background color */ + backgroundColor: z.string().optional().describe('Background color (hex, rgb, or preset)'), + + /** Text alignment */ + align: ContentAlignmentSchema.default('center'), +}); + +/** + * Pricing Plan Schema + */ +export const PricingPlanSchema = z.object({ + /** Plan name */ + name: z.string().describe('Plan name'), + + /** Plan description */ + description: z.string().optional().describe('Plan description'), + + /** Price */ + price: z.string().describe('Price (e.g., "$29", "Free", "Custom")'), + + /** Billing period */ + period: z.string().optional().describe('Billing period (e.g., "/month", "/year")'), + + /** Features list */ + features: z.array(z.string()).describe('List of features'), + + /** CTA button */ + button: ButtonConfigSchema.describe('CTA button'), + + /** Highlight this plan */ + highlighted: z.boolean().default(false).describe('Highlight as recommended plan'), + + /** Badge text */ + badge: z.string().optional().describe('Badge text (e.g., "Popular", "Best Value")'), +}); + +/** + * Pricing Section Schema + * Pricing plans comparison. + */ +export const PricingSectionSchema = z.object({ + type: z.literal('pricing'), + + /** Section ID */ + id: z.string().optional().describe('Section ID for anchor links'), + + /** Section title */ + title: z.string().optional().describe('Section title'), + + /** Section description */ + description: z.string().optional().describe('Section description'), + + /** Pricing plans */ + plans: z.array(PricingPlanSchema).describe('Pricing plans'), + + /** Billing toggle */ + billingToggle: z.object({ + monthly: z.string().default('Monthly').describe('Monthly billing label'), + yearly: z.string().default('Yearly').describe('Yearly billing label'), + }).optional().describe('Enable monthly/yearly toggle'), +}); + +/** + * Rich Text Content Section Schema + * Flexible content section with markdown/HTML. + */ +export const ContentSectionSchema = z.object({ + type: z.literal('content'), + + /** Section ID */ + id: z.string().optional().describe('Section ID for anchor links'), + + /** Section title */ + title: z.string().optional().describe('Section title'), + + /** Content (markdown or HTML) */ + content: z.string().describe('Content in markdown or HTML format'), + + /** Text alignment */ + align: ContentAlignmentSchema.default('left'), + + /** Max width */ + maxWidth: z.enum(['sm', 'md', 'lg', 'xl', 'full']).default('lg'), +}); + +/** + * FAQ Item Schema + */ +export const FaqItemSchema = z.object({ + /** Question */ + question: z.string().describe('FAQ question'), + + /** Answer */ + answer: z.string().describe('FAQ answer (markdown or HTML)'), +}); + +/** + * FAQ Section Schema + * Frequently asked questions. + */ +export const FaqSectionSchema = z.object({ + type: z.literal('faq'), + + /** Section ID */ + id: z.string().optional().describe('Section ID for anchor links'), + + /** Section title */ + title: z.string().optional().describe('Section title'), + + /** Section description */ + description: z.string().optional().describe('Section description'), + + /** FAQ items */ + items: z.array(FaqItemSchema).describe('FAQ items'), + + /** Display style */ + style: z.enum(['accordion', 'grid']).default('accordion'), +}); + +/** + * Logo Cloud Schema + * Display logos of customers, partners, or certifications. + */ +export const LogoCloudSectionSchema = z.object({ + type: z.literal('logo_cloud'), + + /** Section ID */ + id: z.string().optional().describe('Section ID for anchor links'), + + /** Section title */ + title: z.string().optional().describe('Section title (e.g., "Trusted by")'), + + /** Logos */ + logos: z.array(z.object({ + src: z.string().describe('Logo image URL'), + alt: z.string().describe('Company/partner name'), + href: z.string().optional().describe('Optional link to company website'), + })).describe('Logo images'), + + /** Grayscale logos */ + grayscale: z.boolean().default(true).describe('Display logos in grayscale'), +}); + +/** + * Custom HTML Section Schema + * Embed custom HTML content. + */ +export const CustomHtmlSectionSchema = z.object({ + type: z.literal('custom_html'), + + /** Section ID */ + id: z.string().optional().describe('Section ID for anchor links'), + + /** Custom HTML content */ + html: z.string().describe('Custom HTML content'), +}); + +/** + * Content Block Union Schema + * Union of all possible content block types. + */ +export const ContentBlockSchema = z.discriminatedUnion('type', [ + HeroSectionSchema, + FeaturesSectionSchema, + TestimonialsSectionSchema, + CtaSectionSchema, + PricingSectionSchema, + ContentSectionSchema, + FaqSectionSchema, + LogoCloudSectionSchema, + CustomHtmlSectionSchema, +]); + +export type ButtonConfig = z.infer; +export type ImageConfig = z.infer; +export type HeroSection = z.infer; +export type FeatureItem = z.infer; +export type FeaturesSection = z.infer; +export type TestimonialItem = z.infer; +export type TestimonialsSection = z.infer; +export type CtaSection = z.infer; +export type PricingPlan = z.infer; +export type PricingSection = z.infer; +export type ContentSection = z.infer; +export type FaqItem = z.infer; +export type FaqSection = z.infer; +export type LogoCloudSection = z.infer; +export type CustomHtmlSection = z.infer; +export type ContentBlock = z.infer; diff --git a/packages/spec/src/website/index.ts b/packages/spec/src/website/index.ts new file mode 100644 index 000000000..9afcef4ac --- /dev/null +++ b/packages/spec/src/website/index.ts @@ -0,0 +1,23 @@ +/** + * Website Expression Protocol + * Defines schemas for website configuration, landing pages, navigation, and SEO. + * + * @module website + * @description Protocol for configuring marketing websites and landing pages. + * Preview Release: March 2026 + */ + +// Content Blocks +export * from './content-block.zod'; + +// Landing Pages +export * from './landing-page.zod'; + +// Navigation & Footer +export * from './navigation.zod'; + +// SEO +export * from './seo.zod'; + +// Website Configuration +export * from './website.zod'; diff --git a/packages/spec/src/website/landing-page.zod.ts b/packages/spec/src/website/landing-page.zod.ts new file mode 100644 index 000000000..74726e0d6 --- /dev/null +++ b/packages/spec/src/website/landing-page.zod.ts @@ -0,0 +1,62 @@ +import { z } from 'zod'; +import { ContentBlockSchema } from './content-block.zod'; +import { SeoConfigSchema } from './seo.zod'; + +/** + * Landing Page Schema + * Defines a complete landing page with sections, SEO, and metadata. + */ +export const LandingPageSchema = z.object({ + /** Page identifier (snake_case) */ + name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Page identifier (snake_case)'), + + /** Page title */ + title: z.string().describe('Page title'), + + /** Page slug/path */ + slug: z.string().describe('URL slug (e.g., "/", "/pricing", "/about")'), + + /** Page description */ + description: z.string().optional().describe('Page description'), + + /** SEO configuration */ + seo: SeoConfigSchema.optional().describe('SEO and meta tags configuration'), + + /** Page sections/content blocks */ + sections: z.array(ContentBlockSchema).describe('Page content sections'), + + /** Navigation to use (reference to WebsiteNavigation) */ + navigation: z.string().optional().describe('Navigation name to use'), + + /** Footer to use (reference to WebsiteFooter) */ + footer: z.string().optional().describe('Footer name to use'), + + /** Published status */ + published: z.boolean().default(false).describe('Whether page is published'), + + /** Publication date */ + publishedAt: z.string().datetime().optional().describe('Publication timestamp'), + + /** Last updated date */ + updatedAt: z.string().datetime().optional().describe('Last update timestamp'), + + /** Page template/layout */ + template: z.string().optional().describe('Custom template identifier'), + + /** Custom scripts (analytics, tracking, etc.) */ + scripts: z.array(z.object({ + src: z.string().optional().describe('Script source URL'), + inline: z.string().optional().describe('Inline script content'), + position: z.enum(['head', 'body_start', 'body_end']).default('body_end'), + async: z.boolean().default(true), + defer: z.boolean().default(false), + })).optional().describe('Custom scripts to inject'), + + /** Custom CSS */ + customCss: z.string().optional().describe('Custom CSS styles'), + + /** A/B testing variant */ + variant: z.string().optional().describe('A/B testing variant identifier'), +}); + +export type LandingPage = z.infer; diff --git a/packages/spec/src/website/navigation.zod.ts b/packages/spec/src/website/navigation.zod.ts new file mode 100644 index 000000000..ba93bce70 --- /dev/null +++ b/packages/spec/src/website/navigation.zod.ts @@ -0,0 +1,193 @@ +import { z } from 'zod'; + +/** + * Website Navigation Menu Item Schema + * Defines navigation menu structure for websites. + */ +const BaseMenuItemSchema = z.object({ + /** Unique identifier */ + id: z.string().describe('Unique identifier for menu item'), + + /** Display label */ + label: z.string().describe('Display label'), + + /** Icon (optional) */ + icon: z.string().optional().describe('Icon name or URL'), + + /** Badge text (e.g., "New", "Beta") */ + badge: z.string().optional().describe('Badge text'), + + /** Visibility condition */ + visible: z.string().optional().describe('Visibility formula condition'), +}); + +/** + * Link Menu Item + * A simple link to an internal or external URL. + */ +export const LinkMenuItemSchema = BaseMenuItemSchema.extend({ + type: z.literal('link'), + href: z.string().describe('Target URL (internal or external)'), + target: z.enum(['_self', '_blank']).default('_self').describe('Link target'), +}); + +/** + * Dropdown Menu Item + * A dropdown containing child menu items. + */ +export const DropdownMenuItemSchema = BaseMenuItemSchema.extend({ + type: z.literal('dropdown'), + // children property is added in the recursive definition +}); + +/** + * Mega Menu Column Schema + * A column in a mega menu layout. + */ +export const MegaMenuColumnSchema = z.object({ + /** Column title */ + title: z.string().optional().describe('Column heading'), + + /** Links in this column */ + links: z.array(LinkMenuItemSchema).describe('Links in this column'), +}); + +/** + * Mega Menu Item + * A mega menu with multi-column layout. + */ +export const MegaMenuItemSchema = BaseMenuItemSchema.extend({ + type: z.literal('megamenu'), + columns: z.array(MegaMenuColumnSchema).describe('Mega menu columns'), +}); + +/** + * Button Menu Item + * A call-to-action button in the navigation. + */ +export const ButtonMenuItemSchema = BaseMenuItemSchema.extend({ + type: z.literal('button'), + href: z.string().describe('Target URL'), + variant: z.enum(['primary', 'secondary', 'outline', 'ghost']).default('primary'), + target: z.enum(['_self', '_blank']).default('_self'), +}); + +/** + * Recursive Navigation Menu Item + */ +export const NavigationMenuItemSchema: z.ZodType = z.lazy(() => + z.union([ + LinkMenuItemSchema, + ButtonMenuItemSchema, + MegaMenuItemSchema, + DropdownMenuItemSchema.extend({ + children: z.array(NavigationMenuItemSchema).describe('Child menu items'), + }), + ]) +); + +/** + * Website Navigation Schema + * Top-level navigation configuration for a website. + */ +export const WebsiteNavigationSchema = z.object({ + /** Navigation name */ + name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Navigation identifier (snake_case)'), + + /** Navigation label */ + label: z.string().describe('Navigation label'), + + /** Navigation position */ + position: z.enum(['header', 'footer', 'sidebar']).default('header'), + + /** Logo configuration */ + logo: z.object({ + src: z.string().describe('Logo image URL'), + alt: z.string().describe('Logo alt text'), + href: z.string().default('/').describe('Logo link href'), + width: z.number().optional().describe('Logo width in pixels'), + height: z.number().optional().describe('Logo height in pixels'), + }).optional().describe('Logo configuration'), + + /** Menu items */ + items: z.array(NavigationMenuItemSchema).describe('Navigation menu items'), + + /** Mobile behavior */ + mobileCollapsible: z.boolean().default(true).describe('Enable mobile hamburger menu'), + + /** Sticky navigation */ + sticky: z.boolean().default(false).describe('Enable sticky navigation on scroll'), +}); + +/** + * Footer Link Group Schema + * A group of links in the footer. + */ +export const FooterLinkGroupSchema = z.object({ + /** Group title */ + title: z.string().describe('Link group title'), + + /** Links in this group */ + links: z.array(z.object({ + label: z.string().describe('Link label'), + href: z.string().describe('Link URL'), + target: z.enum(['_self', '_blank']).default('_self'), + })).describe('Links'), +}); + +/** + * Social Link Schema + * Social media links for footer/header. + */ +export const SocialLinkSchema = z.object({ + /** Platform name */ + platform: z.enum(['facebook', 'twitter', 'linkedin', 'instagram', 'github', 'youtube', 'custom']), + + /** Profile URL */ + url: z.string().url().describe('Social profile URL'), + + /** Icon (for custom platforms) */ + icon: z.string().optional().describe('Custom icon name or URL'), + + /** Label */ + label: z.string().optional().describe('Accessible label'), +}); + +/** + * Website Footer Schema + * Footer configuration with link groups and social links. + */ +export const WebsiteFooterSchema = z.object({ + /** Footer name */ + name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Footer identifier (snake_case)'), + + /** Footer sections/link groups */ + linkGroups: z.array(FooterLinkGroupSchema).optional().describe('Footer link groups'), + + /** Social media links */ + socialLinks: z.array(SocialLinkSchema).optional().describe('Social media links'), + + /** Copyright text */ + copyright: z.string().optional().describe('Copyright text'), + + /** Additional footer text */ + description: z.string().optional().describe('Footer description or tagline'), + + /** Newsletter signup form */ + newsletter: z.object({ + title: z.string().describe('Newsletter section title'), + description: z.string().optional().describe('Newsletter description'), + placeholder: z.string().default('Enter your email').describe('Email input placeholder'), + buttonText: z.string().default('Subscribe').describe('Submit button text'), + }).optional().describe('Newsletter signup configuration'), +}); + +export type NavigationMenuItem = z.infer; +export type LinkMenuItem = z.infer; +export type DropdownMenuItem = z.infer; +export type MegaMenuItem = z.infer; +export type ButtonMenuItem = z.infer; +export type WebsiteNavigation = z.infer; +export type FooterLinkGroup = z.infer; +export type SocialLink = z.infer; +export type WebsiteFooter = z.infer; diff --git a/packages/spec/src/website/seo.test.ts b/packages/spec/src/website/seo.test.ts new file mode 100644 index 000000000..96fa156e4 --- /dev/null +++ b/packages/spec/src/website/seo.test.ts @@ -0,0 +1,183 @@ +import { describe, it, expect } from 'vitest'; +import { + SeoMetaTagsSchema, + OpenGraphSchema, + TwitterCardSchema, + StructuredDataSchema, + SeoConfigSchema, + type SeoConfig, +} from './seo.zod'; + +describe('SeoMetaTagsSchema', () => { + it('should accept minimal SEO meta tags', () => { + const meta = { + title: 'ObjectStack - Post-SaaS Operating System', + description: 'A metadata-driven low-code platform for building enterprise software', + }; + + expect(() => SeoMetaTagsSchema.parse(meta)).not.toThrow(); + }); + + it('should accept full SEO meta tags', () => { + const meta = { + title: 'ObjectStack Protocol', + description: 'Complete metadata-driven protocol for enterprise applications', + keywords: ['low-code', 'metadata', 'protocol', 'enterprise'], + canonical: 'https://objectstack.ai', + robots: 'index, follow', + author: 'ObjectStack Team', + language: 'en', + }; + + expect(() => SeoMetaTagsSchema.parse(meta)).not.toThrow(); + }); +}); + +describe('OpenGraphSchema', () => { + it('should accept minimal Open Graph config', () => { + const og = { + title: 'ObjectStack', + description: 'Post-SaaS Operating System', + image: 'https://objectstack.ai/og-image.png', + }; + + const result = OpenGraphSchema.parse(og); + expect(result.type).toBe('website'); + }); + + it('should accept full Open Graph config', () => { + const og = { + title: 'ObjectStack Protocol', + description: 'Metadata-driven low-code platform', + type: 'article' as const, + image: 'https://objectstack.ai/og-image.png', + imageAlt: 'ObjectStack logo and tagline', + siteName: 'ObjectStack', + locale: 'en_US', + url: 'https://objectstack.ai', + }; + + expect(() => OpenGraphSchema.parse(og)).not.toThrow(); + }); +}); + +describe('TwitterCardSchema', () => { + it('should accept minimal Twitter Card config', () => { + const twitter = { + title: 'ObjectStack', + description: 'Post-SaaS Operating System', + image: 'https://objectstack.ai/twitter-card.png', + }; + + const result = TwitterCardSchema.parse(twitter); + expect(result.card).toBe('summary_large_image'); + }); + + it('should accept full Twitter Card config', () => { + const twitter = { + card: 'summary_large_image' as const, + title: 'ObjectStack Protocol', + description: 'Metadata-driven platform', + image: 'https://objectstack.ai/twitter-card.png', + imageAlt: 'ObjectStack preview', + site: '@objectstack', + creator: '@objectstack', + }; + + expect(() => TwitterCardSchema.parse(twitter)).not.toThrow(); + }); +}); + +describe('StructuredDataSchema', () => { + it('should accept Organization structured data', () => { + const structuredData = { + type: 'Organization', + data: { + '@context': 'https://schema.org', + '@type': 'Organization', + name: 'ObjectStack', + url: 'https://objectstack.ai', + logo: 'https://objectstack.ai/logo.png', + }, + }; + + expect(() => StructuredDataSchema.parse(structuredData)).not.toThrow(); + }); + + it('should accept Product structured data', () => { + const structuredData = { + type: 'Product', + data: { + '@context': 'https://schema.org', + '@type': 'Product', + name: 'ObjectStack Protocol', + description: 'Metadata-driven low-code platform', + brand: 'ObjectStack', + }, + }; + + expect(() => StructuredDataSchema.parse(structuredData)).not.toThrow(); + }); +}); + +describe('SeoConfigSchema', () => { + it('should accept minimal SEO config', () => { + const seo: SeoConfig = { + meta: { + title: 'ObjectStack', + description: 'Post-SaaS Operating System', + }, + }; + + expect(() => SeoConfigSchema.parse(seo)).not.toThrow(); + }); + + it('should accept complete SEO config', () => { + const seo: SeoConfig = { + meta: { + title: 'ObjectStack - Post-SaaS Operating System', + description: 'A metadata-driven low-code platform for building enterprise software', + keywords: ['low-code', 'metadata', 'protocol'], + canonical: 'https://objectstack.ai', + robots: 'index, follow', + author: 'ObjectStack', + language: 'en', + }, + openGraph: { + title: 'ObjectStack Protocol', + description: 'Build enterprise software with metadata', + type: 'website', + image: 'https://objectstack.ai/og-image.png', + siteName: 'ObjectStack', + locale: 'en_US', + url: 'https://objectstack.ai', + }, + twitter: { + card: 'summary_large_image', + title: 'ObjectStack', + description: 'Post-SaaS Operating System', + image: 'https://objectstack.ai/twitter-card.png', + site: '@objectstack', + }, + structuredData: [ + { + type: 'Organization', + data: { + '@context': 'https://schema.org', + '@type': 'Organization', + name: 'ObjectStack', + url: 'https://objectstack.ai', + }, + }, + ], + customMeta: [ + { + name: 'theme-color', + content: '#0070F3', + }, + ], + }; + + expect(() => SeoConfigSchema.parse(seo)).not.toThrow(); + }); +}); diff --git a/packages/spec/src/website/seo.zod.ts b/packages/spec/src/website/seo.zod.ts new file mode 100644 index 000000000..edd2136ea --- /dev/null +++ b/packages/spec/src/website/seo.zod.ts @@ -0,0 +1,128 @@ +import { z } from 'zod'; + +/** + * SEO Meta Tags Schema + * Defines metadata for search engine optimization. + */ +export const SeoMetaTagsSchema = z.object({ + /** Page title for SEO (appears in search results and browser tab) */ + title: z.string().describe('SEO title (50-60 characters recommended)'), + + /** Meta description for search results */ + description: z.string().describe('Meta description (150-160 characters recommended)'), + + /** Keywords for search engines */ + keywords: z.array(z.string()).optional().describe('SEO keywords'), + + /** Canonical URL to avoid duplicate content */ + canonical: z.string().url().optional().describe('Canonical URL'), + + /** Robots meta tag (index/noindex, follow/nofollow) */ + robots: z.string().optional().describe('Robots directive (e.g., "index, follow")'), + + /** Author metadata */ + author: z.string().optional().describe('Content author'), + + /** Language code */ + language: z.string().optional().describe('Language code (e.g., "en", "zh-CN")'), +}); + +/** + * Open Graph Protocol Schema + * Social media sharing metadata (Facebook, LinkedIn, etc.). + */ +export const OpenGraphSchema = z.object({ + /** OG title */ + title: z.string().describe('Open Graph title'), + + /** OG description */ + description: z.string().describe('Open Graph description'), + + /** OG type (website, article, product, etc.) */ + type: z.enum(['website', 'article', 'product', 'profile']).default('website'), + + /** OG image URL */ + image: z.string().url().describe('Open Graph image URL (1200x630px recommended)'), + + /** OG image alt text */ + imageAlt: z.string().optional().describe('Open Graph image alt text'), + + /** Site name */ + siteName: z.string().optional().describe('Site name'), + + /** Locale */ + locale: z.string().optional().describe('Locale (e.g., "en_US", "zh_CN")'), + + /** URL */ + url: z.string().url().optional().describe('Canonical URL'), +}); + +/** + * Twitter Card Schema + * Twitter-specific sharing metadata. + */ +export const TwitterCardSchema = z.object({ + /** Card type */ + card: z.enum(['summary', 'summary_large_image', 'app', 'player']).default('summary_large_image'), + + /** Twitter title */ + title: z.string().describe('Twitter card title'), + + /** Twitter description */ + description: z.string().describe('Twitter card description'), + + /** Twitter image */ + image: z.string().url().describe('Twitter card image URL'), + + /** Twitter image alt text */ + imageAlt: z.string().optional().describe('Twitter card image alt text'), + + /** Twitter site account */ + site: z.string().optional().describe('Twitter @username for site'), + + /** Twitter creator account */ + creator: z.string().optional().describe('Twitter @username for creator'), +}); + +/** + * JSON-LD Structured Data Schema + * Schema.org structured data for rich snippets. + */ +export const StructuredDataSchema = z.object({ + /** Schema.org type */ + type: z.string().describe('Schema.org type (e.g., "Organization", "Product", "Article")'), + + /** Structured data object */ + data: z.record(z.any()).describe('Schema.org structured data object'), +}); + +/** + * SEO Configuration Schema + * Complete SEO configuration including meta tags, social sharing, and structured data. + */ +export const SeoConfigSchema = z.object({ + /** Basic SEO meta tags */ + meta: SeoMetaTagsSchema.describe('Basic SEO meta tags'), + + /** Open Graph tags for social sharing */ + openGraph: OpenGraphSchema.optional().describe('Open Graph protocol tags'), + + /** Twitter Card tags */ + twitter: TwitterCardSchema.optional().describe('Twitter Card tags'), + + /** JSON-LD structured data */ + structuredData: z.array(StructuredDataSchema).optional().describe('Schema.org structured data'), + + /** Additional custom meta tags */ + customMeta: z.array(z.object({ + name: z.string().describe('Meta tag name'), + content: z.string().describe('Meta tag content'), + property: z.string().optional().describe('Meta tag property (for og: tags)'), + })).optional().describe('Custom meta tags'), +}); + +export type SeoMetaTags = z.infer; +export type OpenGraph = z.infer; +export type TwitterCard = z.infer; +export type StructuredData = z.infer; +export type SeoConfig = z.infer; diff --git a/packages/spec/src/website/website.test.ts b/packages/spec/src/website/website.test.ts new file mode 100644 index 000000000..3152b7838 --- /dev/null +++ b/packages/spec/src/website/website.test.ts @@ -0,0 +1,200 @@ +import { describe, it, expect } from 'vitest'; +import { + WebsiteConfigSchema, + WebsiteThemeSchema, + AnalyticsConfigSchema, + type WebsiteConfig, +} from './website.zod'; + +describe('WebsiteThemeSchema', () => { + it('should accept minimal theme', () => { + const theme = { + name: 'Default', + primaryColor: '#0070F3', + }; + + const result = WebsiteThemeSchema.parse(theme); + expect(result.borderRadius).toBe('md'); + expect(result.darkMode).toBe(false); + }); + + it('should accept full theme config', () => { + const theme = { + name: 'Brand Theme', + primaryColor: '#0070F3', + secondaryColor: '#10B981', + fontFamily: { + heading: 'Inter, sans-serif', + body: 'Inter, sans-serif', + }, + borderRadius: 'lg' as const, + darkMode: true, + }; + + expect(() => WebsiteThemeSchema.parse(theme)).not.toThrow(); + }); +}); + +describe('AnalyticsConfigSchema', () => { + it('should accept Google Analytics config', () => { + const analytics = { + googleAnalytics: { + measurementId: 'G-XXXXXXXXXX', + }, + }; + + expect(() => AnalyticsConfigSchema.parse(analytics)).not.toThrow(); + }); + + it('should accept multiple analytics providers', () => { + const analytics = { + googleAnalytics: { + measurementId: 'G-XXXXXXXXXX', + }, + googleTagManager: { + containerId: 'GTM-XXXXXXX', + }, + facebookPixel: { + pixelId: '123456789', + }, + customScripts: [ + { + name: 'Custom Tracker', + src: 'https://analytics.example.com/tracker.js', + }, + ], + }; + + expect(() => AnalyticsConfigSchema.parse(analytics)).not.toThrow(); + }); +}); + +describe('WebsiteConfigSchema', () => { + it('should accept minimal website config', () => { + const website: WebsiteConfig = { + name: 'objectstack_site', + title: 'ObjectStack', + baseUrl: 'https://objectstack.ai', + pages: [], + }; + + const result = WebsiteConfigSchema.parse(website); + expect(result.locale).toBe('en'); + }); + + it('should enforce snake_case for website name', () => { + const validNames = ['main_site', 'landing_page', '_test']; + validNames.forEach(name => { + expect(() => + WebsiteConfigSchema.parse({ + name, + title: 'Test', + baseUrl: 'https://example.com', + pages: [], + }) + ).not.toThrow(); + }); + + const invalidNames = ['MainSite', 'landing-page', '123site']; + invalidNames.forEach(name => { + expect(() => + WebsiteConfigSchema.parse({ + name, + title: 'Test', + baseUrl: 'https://example.com', + pages: [], + }) + ).toThrow(); + }); + }); + + it('should accept complete website config with preview release date', () => { + const website: WebsiteConfig = { + name: 'objectstack_official', + title: 'ObjectStack - Post-SaaS Operating System', + description: 'Build enterprise software with metadata', + baseUrl: 'https://objectstack.ai', + locale: 'en', + locales: ['en', 'zh-CN'], + theme: { + name: 'ObjectStack Theme', + primaryColor: '#0070F3', + secondaryColor: '#10B981', + borderRadius: 'lg', + darkMode: true, + }, + navigations: [ + { + name: 'main_nav', + label: 'Main Navigation', + position: 'header', + items: [ + { + id: 'home', + label: 'Home', + type: 'link', + href: '/', + }, + { + id: 'docs', + label: 'Documentation', + type: 'link', + href: '/docs', + }, + ], + }, + ], + footers: [ + { + name: 'main_footer', + linkGroups: [ + { + title: 'Product', + links: [ + { label: 'Features', href: '/features' }, + { label: 'Pricing', href: '/pricing' }, + ], + }, + ], + copyright: 'ยฉ 2026 ObjectStack. All rights reserved.', + }, + ], + pages: [ + { + name: 'home', + title: 'Home', + slug: '/', + sections: [ + { + type: 'hero', + headline: 'Welcome to ObjectStack', + }, + ], + }, + ], + analytics: { + googleAnalytics: { + measurementId: 'G-XXXXXXXXXX', + }, + }, + favicon: '/favicon.ico', + socialPreview: 'https://objectstack.ai/og-image.png', + previewReleaseDate: '2026-03-01T00:00:00Z', + version: '1.0.0', + }; + + expect(() => WebsiteConfigSchema.parse(website)).not.toThrow(); + }); + + it('should validate preview release date format', () => { + const website: WebsiteConfig = { + name: 'test_site', + title: 'Test', + baseUrl: 'https://example.com', + pages: [], + previewReleaseDate: '2026-03-01T00:00:00Z', + }; + + expect(() => WebsiteConfigSchema.parse(website)).not.toThrow(); + }); +}); diff --git a/packages/spec/src/website/website.zod.ts b/packages/spec/src/website/website.zod.ts new file mode 100644 index 000000000..e1fd37f7d --- /dev/null +++ b/packages/spec/src/website/website.zod.ts @@ -0,0 +1,113 @@ +import { z } from 'zod'; +import { LandingPageSchema } from './landing-page.zod'; +import { WebsiteNavigationSchema, WebsiteFooterSchema } from './navigation.zod'; + +/** + * Website Theme Configuration + * Visual theme settings for the website. + */ +export const WebsiteThemeSchema = z.object({ + /** Theme name */ + name: z.string().describe('Theme name'), + + /** Primary color */ + primaryColor: z.string().describe('Primary brand color (hex)'), + + /** Secondary color */ + secondaryColor: z.string().optional().describe('Secondary brand color (hex)'), + + /** Font family */ + fontFamily: z.object({ + heading: z.string().optional().describe('Heading font family'), + body: z.string().optional().describe('Body font family'), + }).optional(), + + /** Border radius */ + borderRadius: z.enum(['none', 'sm', 'md', 'lg', 'full']).default('md'), + + /** Dark mode support */ + darkMode: z.boolean().default(false).describe('Enable dark mode'), +}); + +/** + * Analytics Configuration + * Configure analytics and tracking tools. + */ +export const AnalyticsConfigSchema = z.object({ + /** Google Analytics */ + googleAnalytics: z.object({ + measurementId: z.string().describe('GA4 Measurement ID (e.g., G-XXXXXXXXXX)'), + }).optional(), + + /** Google Tag Manager */ + googleTagManager: z.object({ + containerId: z.string().describe('GTM Container ID (e.g., GTM-XXXXXXX)'), + }).optional(), + + /** Facebook Pixel */ + facebookPixel: z.object({ + pixelId: z.string().describe('Facebook Pixel ID'), + }).optional(), + + /** Custom tracking scripts */ + customScripts: z.array(z.object({ + name: z.string().describe('Script name'), + src: z.string().optional().describe('Script source URL'), + inline: z.string().optional().describe('Inline script content'), + })).optional(), +}); + +/** + * Website Configuration Schema + * Complete website configuration including pages, navigation, theme, and settings. + */ +export const WebsiteConfigSchema = z.object({ + /** Website name */ + name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Website identifier (snake_case)'), + + /** Website title */ + title: z.string().describe('Website title'), + + /** Website description */ + description: z.string().optional().describe('Website description'), + + /** Base URL */ + baseUrl: z.string().url().describe('Base URL (e.g., https://example.com)'), + + /** Default locale */ + locale: z.string().default('en').describe('Default locale (e.g., "en", "zh-CN")'), + + /** Supported locales */ + locales: z.array(z.string()).optional().describe('Supported locales for i18n'), + + /** Theme configuration */ + theme: WebsiteThemeSchema.optional().describe('Visual theme settings'), + + /** Navigation configurations */ + navigations: z.array(WebsiteNavigationSchema).optional().describe('Navigation menus'), + + /** Footer configurations */ + footers: z.array(WebsiteFooterSchema).optional().describe('Footer configurations'), + + /** Landing pages */ + pages: z.array(LandingPageSchema).describe('Landing pages'), + + /** Analytics configuration */ + analytics: AnalyticsConfigSchema.optional().describe('Analytics and tracking'), + + /** Favicon */ + favicon: z.string().optional().describe('Favicon URL'), + + /** Social preview image (default OG image) */ + socialPreview: z.string().url().optional().describe('Default social preview image'), + + /** Preview release date */ + previewReleaseDate: z.string().datetime().optional().describe('Preview release date (ISO 8601 format, e.g., 2026-03-01T00:00:00Z)'), + + /** Version */ + version: z.string().optional().describe('Website version'), +}); + +export type WebsiteTheme = z.infer; +export type AnalyticsConfig = z.infer; +export type WebsiteConfig = z.infer;