From 3efd23a258a937f411803b8de9a93d7e3852d2be Mon Sep 17 00:00:00 2001
From: tristantr
Date: Tue, 27 Jan 2026 10:26:26 +0100
Subject: [PATCH 01/20] Start public apps section
---
src/landing/ExampleCard.tsx | 91 ++++++++++++
src/landing/ExampleLayout.tsx | 169 +++++++++++++++++++++++
src/landing/LandingHeader.jsx | 2 +-
src/pages/examples/ai-agent.jsx | 42 ++++++
src/pages/examples/customer-support.jsx | 42 ++++++
src/pages/examples/data-pipeline.jsx | 42 ++++++
src/pages/examples/index.jsx | 116 ++++++++++++++++
src/pages/examples/payment-dashboard.jsx | 42 ++++++
8 files changed, 545 insertions(+), 1 deletion(-)
create mode 100644 src/landing/ExampleCard.tsx
create mode 100644 src/landing/ExampleLayout.tsx
create mode 100644 src/pages/examples/ai-agent.jsx
create mode 100644 src/pages/examples/customer-support.jsx
create mode 100644 src/pages/examples/data-pipeline.jsx
create mode 100644 src/pages/examples/index.jsx
create mode 100644 src/pages/examples/payment-dashboard.jsx
diff --git a/src/landing/ExampleCard.tsx b/src/landing/ExampleCard.tsx
new file mode 100644
index 000000000..a1b1dc579
--- /dev/null
+++ b/src/landing/ExampleCard.tsx
@@ -0,0 +1,91 @@
+import React from 'react';
+import { motion } from 'framer-motion';
+import Link from '@docusaurus/Link';
+
+interface ExampleCardProps {
+ title: string;
+ description: string;
+ href: string;
+ thumbnail?: string;
+ color?: 'blue' | 'teal' | 'orange' | 'purple';
+}
+
+export default function ExampleCard({
+ title,
+ description,
+ href,
+ thumbnail,
+ color = 'blue'
+}: ExampleCardProps) {
+ const gradients = {
+ blue: 'from-blue-500/20 to-blue-600/20',
+ teal: 'from-teal-500/20 to-teal-600/20',
+ orange: 'from-orange-500/20 to-orange-600/20',
+ purple: 'from-purple-500/20 to-purple-600/20'
+ };
+
+ const borders = {
+ blue: 'hover:border-blue-500',
+ teal: 'hover:border-teal-500',
+ orange: 'hover:border-orange-500',
+ purple: 'hover:border-purple-500'
+ };
+
+ const textGradients = {
+ blue: 'from-blue-600 to-blue-800 dark:from-blue-300 dark:to-blue-500',
+ teal: 'from-teal-600 to-teal-800 dark:from-teal-300 dark:to-teal-500',
+ orange: 'from-orange-600 to-orange-800 dark:from-orange-300 dark:to-orange-500',
+ purple: 'from-purple-600 to-purple-800 dark:from-purple-300 dark:to-purple-500'
+ };
+
+ return (
+
+
+ {/* Thumbnail */}
+
+ {thumbnail ? (
+

+ ) : (
+
+ Preview
+
+ )}
+
+
+ {/* Content */}
+
+
+ {title}
+
+
+ {description}
+
+
+
+
+ );
+}
diff --git a/src/landing/ExampleLayout.tsx b/src/landing/ExampleLayout.tsx
new file mode 100644
index 000000000..d926fc11f
--- /dev/null
+++ b/src/landing/ExampleLayout.tsx
@@ -0,0 +1,169 @@
+import React from 'react';
+import Head from '@docusaurus/Head';
+import Link from '@docusaurus/Link';
+import LayoutProvider from '@theme/Layout/Provider';
+import LandingHeader from './LandingHeader';
+import Footer from './Footer';
+import { motion } from 'framer-motion';
+import { ArrowLeftIcon } from '@heroicons/react/24/outline';
+
+interface ExampleLayoutProps {
+ title: string;
+ description: string;
+ iframeUrl: string;
+ children?: React.ReactNode;
+}
+
+export default function ExampleLayout({
+ title,
+ description,
+ iframeUrl,
+ children
+}: ExampleLayoutProps) {
+ return (
+
+
+
+ {title} | Examples | Windmill
+
+
+
+
+
+
+
+
+ {/* Back link */}
+
+
+
+ Back to examples
+
+
+
+ {/* Main content */}
+
+ {/* Sidebar */}
+
+
+
+ {title}
+
+
+ {description}
+
+
+ {children}
+
+
+
+ Try Windmill
+
+
+
+ {/* Social share */}
+
+
+ Share this example
+
+
+
+
+
+
+ {/* Iframe container */}
+
+
+ {/* App header bar */}
+
+
+ {/* Iframe */}
+
+ {iframeUrl ? (
+
+ ) : (
+
+
+
+ App preview placeholder
+
+
+ Iframe URL will be configured here
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/landing/LandingHeader.jsx b/src/landing/LandingHeader.jsx
index 1f5df77e4..2397a384b 100644
--- a/src/landing/LandingHeader.jsx
+++ b/src/landing/LandingHeader.jsx
@@ -342,7 +342,7 @@ export default function LandingHeader() {
Case studies
diff --git a/src/pages/examples/ai-agent.jsx b/src/pages/examples/ai-agent.jsx
new file mode 100644
index 000000000..59be285b7
--- /dev/null
+++ b/src/pages/examples/ai-agent.jsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import ExampleLayout from '../../landing/ExampleLayout';
+
+export default function AIAgentExample() {
+ return (
+
+
+
+
+ Features
+
+
+ - Natural language processing
+ - Context-aware responses
+ - Integration with external APIs
+ - Conversation history
+
+
+
+
+ Built with
+
+
+
+ React
+
+
+ OpenAI
+
+
+ Windmill
+
+
+
+
+
+ );
+}
diff --git a/src/pages/examples/customer-support.jsx b/src/pages/examples/customer-support.jsx
new file mode 100644
index 000000000..4ec00bc46
--- /dev/null
+++ b/src/pages/examples/customer-support.jsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import ExampleLayout from '../../landing/ExampleLayout';
+
+export default function CustomerSupportExample() {
+ return (
+
+
+
+
+ Features
+
+
+ - Ticket management & routing
+ - Response time tracking
+ - Customer satisfaction scores
+ - Team performance metrics
+
+
+
+
+ Built with
+
+
+
+ Svelte
+
+
+ Zendesk
+
+
+ Windmill
+
+
+
+
+
+ );
+}
diff --git a/src/pages/examples/data-pipeline.jsx b/src/pages/examples/data-pipeline.jsx
new file mode 100644
index 000000000..e86da7e1d
--- /dev/null
+++ b/src/pages/examples/data-pipeline.jsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import ExampleLayout from '../../landing/ExampleLayout';
+
+export default function DataPipelineExample() {
+ return (
+
+
+
+
+ Features
+
+
+ - Multi-source data extraction
+ - Scheduled pipeline runs
+ - Data transformation & validation
+ - Error handling & retries
+
+
+
+
+ Built with
+
+
+
+ Python
+
+
+ PostgreSQL
+
+
+ Windmill
+
+
+
+
+
+ );
+}
diff --git a/src/pages/examples/index.jsx b/src/pages/examples/index.jsx
new file mode 100644
index 000000000..7b43d1f5e
--- /dev/null
+++ b/src/pages/examples/index.jsx
@@ -0,0 +1,116 @@
+import React from 'react';
+import Head from '@docusaurus/Head';
+import LayoutProvider from '@theme/Layout/Provider';
+import LandingHeader from '../../landing/LandingHeader';
+import Footer from '../../landing/Footer';
+import CallToAction from '../../landing/CallToAction';
+import RadialBlur from '../../landing/RadialBlur';
+import LandingSection from '../../landing/LandingSection';
+import ExampleCard from '../../landing/ExampleCard';
+import { motion } from 'framer-motion';
+
+const examples = [
+ {
+ title: 'AI Agent',
+ description:
+ 'Build an AI-powered agent that can interact with your data, answer questions, and automate tasks using LLMs.',
+ href: '/examples/ai-agent',
+ color: 'purple'
+ },
+ {
+ title: 'Data Pipeline',
+ description:
+ 'Create ETL workflows that extract, transform, and load data across multiple sources with scheduling and monitoring.',
+ href: '/examples/data-pipeline',
+ color: 'teal'
+ },
+ {
+ title: 'Payment Dashboard',
+ description:
+ 'Track payments, revenue metrics, and financial data with an interactive dashboard connected to your payment provider.',
+ href: '/examples/payment-dashboard',
+ color: 'blue'
+ },
+ {
+ title: 'Customer Support',
+ description:
+ 'Manage support tickets, track response times, and monitor customer satisfaction with a unified support interface.',
+ href: '/examples/customer-support',
+ color: 'orange'
+ }
+];
+
+export default function ExamplesPage() {
+ return (
+
+
+
+ Examples | Windmill
+
+
+
+
+
+
+
+ {/* Hero Section */}
+
+
+
+ Examples
+
+
+ Interactive applications built with Windmill's Raw App Builder.
+ Explore what you can create with code-first frontends powered by Windmill.
+
+
+
+
+ {/* Examples Grid */}
+
+
+
+ {examples.map((example, index) => (
+
+
+
+ ))}
+
+
+
+
+ {/* Call to Action */}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/examples/payment-dashboard.jsx b/src/pages/examples/payment-dashboard.jsx
new file mode 100644
index 000000000..2c8d37522
--- /dev/null
+++ b/src/pages/examples/payment-dashboard.jsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import ExampleLayout from '../../landing/ExampleLayout';
+
+export default function PaymentDashboardExample() {
+ return (
+
+
+
+
+ Features
+
+
+ - Real-time revenue metrics
+ - Transaction history & search
+ - Subscription analytics
+ - Export & reporting
+
+
+
+
+ Built with
+
+
+
+ React
+
+
+ Stripe
+
+
+ Windmill
+
+
+
+
+
+ );
+}
From 0b966ecb57812922a71b80235dd229c6bcca1d65 Mon Sep 17 00:00:00 2001
From: tristantr
Date: Tue, 27 Jan 2026 11:21:49 +0100
Subject: [PATCH 02/20] Refactor
---
src/landing/ExampleLayout.tsx | 51 +++++++++++---
src/landing/examplesData.ts | 85 ++++++++++++++++++++++++
src/pages/examples/ai-agent.jsx | 40 +----------
src/pages/examples/customer-support.jsx | 40 +----------
src/pages/examples/data-pipeline.jsx | 40 +----------
src/pages/examples/index.jsx | 38 ++---------
src/pages/examples/payment-dashboard.jsx | 40 +----------
7 files changed, 141 insertions(+), 193 deletions(-)
create mode 100644 src/landing/examplesData.ts
diff --git a/src/landing/ExampleLayout.tsx b/src/landing/ExampleLayout.tsx
index d926fc11f..c38244a6f 100644
--- a/src/landing/ExampleLayout.tsx
+++ b/src/landing/ExampleLayout.tsx
@@ -6,20 +6,46 @@ import LandingHeader from './LandingHeader';
import Footer from './Footer';
import { motion } from 'framer-motion';
import { ArrowLeftIcon } from '@heroicons/react/24/outline';
+import { Example } from './examplesData';
interface ExampleLayoutProps {
- title: string;
- description: string;
- iframeUrl: string;
- children?: React.ReactNode;
+ example: Example;
}
-export default function ExampleLayout({
- title,
- description,
- iframeUrl,
- children
-}: ExampleLayoutProps) {
+function FeaturesList({ features }: { features: string[] }) {
+ return (
+
+
Features
+
+ {features.map((feature) => (
+ - {feature}
+ ))}
+
+
+ );
+}
+
+function BuiltWithTags({ tags }: { tags: string[] }) {
+ return (
+
+
Built with
+
+ {tags.map((tag) => (
+
+ {tag}
+
+ ))}
+
+
+ );
+}
+
+export default function ExampleLayout({ example }: ExampleLayoutProps) {
+ const { title, description, iframeUrl, features, builtWith } = example;
+
return (
@@ -66,7 +92,10 @@ export default function ExampleLayout({
{description}
- {children}
+
+
+
+
example.slug === slug);
+}
diff --git a/src/pages/examples/ai-agent.jsx b/src/pages/examples/ai-agent.jsx
index 59be285b7..a7b8475ba 100644
--- a/src/pages/examples/ai-agent.jsx
+++ b/src/pages/examples/ai-agent.jsx
@@ -1,42 +1,8 @@
import React from 'react';
import ExampleLayout from '../../landing/ExampleLayout';
+import { getExampleBySlug } from '../../landing/examplesData';
export default function AIAgentExample() {
- return (
-
-
-
-
- Features
-
-
- - Natural language processing
- - Context-aware responses
- - Integration with external APIs
- - Conversation history
-
-
-
-
- Built with
-
-
-
- React
-
-
- OpenAI
-
-
- Windmill
-
-
-
-
-
- );
+ const example = getExampleBySlug('ai-agent');
+ return
;
}
diff --git a/src/pages/examples/customer-support.jsx b/src/pages/examples/customer-support.jsx
index 4ec00bc46..dc2466a0d 100644
--- a/src/pages/examples/customer-support.jsx
+++ b/src/pages/examples/customer-support.jsx
@@ -1,42 +1,8 @@
import React from 'react';
import ExampleLayout from '../../landing/ExampleLayout';
+import { getExampleBySlug } from '../../landing/examplesData';
export default function CustomerSupportExample() {
- return (
-
-
-
-
- Features
-
-
- - Ticket management & routing
- - Response time tracking
- - Customer satisfaction scores
- - Team performance metrics
-
-
-
-
- Built with
-
-
-
- Svelte
-
-
- Zendesk
-
-
- Windmill
-
-
-
-
-
- );
+ const example = getExampleBySlug('customer-support');
+ return
;
}
diff --git a/src/pages/examples/data-pipeline.jsx b/src/pages/examples/data-pipeline.jsx
index e86da7e1d..1343762aa 100644
--- a/src/pages/examples/data-pipeline.jsx
+++ b/src/pages/examples/data-pipeline.jsx
@@ -1,42 +1,8 @@
import React from 'react';
import ExampleLayout from '../../landing/ExampleLayout';
+import { getExampleBySlug } from '../../landing/examplesData';
export default function DataPipelineExample() {
- return (
-
-
-
-
- Features
-
-
- - Multi-source data extraction
- - Scheduled pipeline runs
- - Data transformation & validation
- - Error handling & retries
-
-
-
-
- Built with
-
-
-
- Python
-
-
- PostgreSQL
-
-
- Windmill
-
-
-
-
-
- );
+ const example = getExampleBySlug('data-pipeline');
+ return
;
}
diff --git a/src/pages/examples/index.jsx b/src/pages/examples/index.jsx
index 7b43d1f5e..bf81aad06 100644
--- a/src/pages/examples/index.jsx
+++ b/src/pages/examples/index.jsx
@@ -7,39 +7,9 @@ import CallToAction from '../../landing/CallToAction';
import RadialBlur from '../../landing/RadialBlur';
import LandingSection from '../../landing/LandingSection';
import ExampleCard from '../../landing/ExampleCard';
+import { examples } from '../../landing/examplesData';
import { motion } from 'framer-motion';
-const examples = [
- {
- title: 'AI Agent',
- description:
- 'Build an AI-powered agent that can interact with your data, answer questions, and automate tasks using LLMs.',
- href: '/examples/ai-agent',
- color: 'purple'
- },
- {
- title: 'Data Pipeline',
- description:
- 'Create ETL workflows that extract, transform, and load data across multiple sources with scheduling and monitoring.',
- href: '/examples/data-pipeline',
- color: 'teal'
- },
- {
- title: 'Payment Dashboard',
- description:
- 'Track payments, revenue metrics, and financial data with an interactive dashboard connected to your payment provider.',
- href: '/examples/payment-dashboard',
- color: 'blue'
- },
- {
- title: 'Customer Support',
- description:
- 'Manage support tickets, track response times, and monitor customer satisfaction with a unified support interface.',
- href: '/examples/customer-support',
- color: 'orange'
- }
-];
-
export default function ExamplesPage() {
return (
@@ -86,7 +56,7 @@ export default function ExamplesPage() {
>
{examples.map((example, index) => (
diff --git a/src/pages/examples/payment-dashboard.jsx b/src/pages/examples/payment-dashboard.jsx
index 2c8d37522..728caf793 100644
--- a/src/pages/examples/payment-dashboard.jsx
+++ b/src/pages/examples/payment-dashboard.jsx
@@ -1,42 +1,8 @@
import React from 'react';
import ExampleLayout from '../../landing/ExampleLayout';
+import { getExampleBySlug } from '../../landing/examplesData';
export default function PaymentDashboardExample() {
- return (
-
-
-
-
- Features
-
-
- - Real-time revenue metrics
- - Transaction history & search
- - Subscription analytics
- - Export & reporting
-
-
-
-
- Built with
-
-
-
- React
-
-
- Stripe
-
-
- Windmill
-
-
-
-
-
- );
+ const example = getExampleBySlug('payment-dashboard');
+ return ;
}
From 2920e32cd2b9d9d4c8a433af729d3c32c19ab23c Mon Sep 17 00:00:00 2001
From: tristantr
Date: Tue, 27 Jan 2026 11:30:41 +0100
Subject: [PATCH 03/20] Improve background of example page
---
src/landing/ExampleLayout.tsx | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/landing/ExampleLayout.tsx b/src/landing/ExampleLayout.tsx
index c38244a6f..9dfc42f73 100644
--- a/src/landing/ExampleLayout.tsx
+++ b/src/landing/ExampleLayout.tsx
@@ -4,6 +4,7 @@ import Link from '@docusaurus/Link';
import LayoutProvider from '@theme/Layout/Provider';
import LandingHeader from './LandingHeader';
import Footer from './Footer';
+import RadialBlur from './RadialBlur';
import { motion } from 'framer-motion';
import { ArrowLeftIcon } from '@heroicons/react/24/outline';
import { Example } from './examplesData';
@@ -48,7 +49,7 @@ export default function ExampleLayout({ example }: ExampleLayoutProps) {
return (
-
+
{title} | Examples | Windmill
@@ -56,8 +57,9 @@ export default function ExampleLayout({ example }: ExampleLayoutProps) {
+
-
+
{/* Back link */}
Date: Tue, 27 Jan 2026 11:34:06 +0100
Subject: [PATCH 04/20] Align page layouts
---
src/landing/ExampleLayout.tsx | 25 +++++++++----------------
1 file changed, 9 insertions(+), 16 deletions(-)
diff --git a/src/landing/ExampleLayout.tsx b/src/landing/ExampleLayout.tsx
index 9dfc42f73..1999105cc 100644
--- a/src/landing/ExampleLayout.tsx
+++ b/src/landing/ExampleLayout.tsx
@@ -6,7 +6,7 @@ import LandingHeader from './LandingHeader';
import Footer from './Footer';
import RadialBlur from './RadialBlur';
import { motion } from 'framer-motion';
-import { ArrowLeftIcon } from '@heroicons/react/24/outline';
+import { ArrowLeft } from 'lucide-react';
import { Example } from './examplesData';
interface ExampleLayoutProps {
@@ -59,23 +59,16 @@ export default function ExampleLayout({ example }: ExampleLayoutProps) {
-
-
+
+
{/* Back link */}
-
-
-
- Back to examples
-
-
+
+ Back to examples
+
{/* Main content */}
From 8732e9736ad845fc71abd521568526e6220ecfc4 Mon Sep 17 00:00:00 2001
From: tristantr
Date: Tue, 27 Jan 2026 12:06:39 +0100
Subject: [PATCH 05/20] Add Open in new tab CTA
---
src/landing/ExampleLayout.tsx | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/landing/ExampleLayout.tsx b/src/landing/ExampleLayout.tsx
index 1999105cc..f6989ce3f 100644
--- a/src/landing/ExampleLayout.tsx
+++ b/src/landing/ExampleLayout.tsx
@@ -6,7 +6,7 @@ import LandingHeader from './LandingHeader';
import Footer from './Footer';
import RadialBlur from './RadialBlur';
import { motion } from 'framer-motion';
-import { ArrowLeft } from 'lucide-react';
+import { ArrowLeft, ExternalLink } from 'lucide-react';
import { Example } from './examplesData';
interface ExampleLayoutProps {
@@ -156,6 +156,18 @@ export default function ExampleLayout({ example }: ExampleLayoutProps) {
{title}
+ !iframeUrl && e.preventDefault()}
+ >
+ Open in new tab
+
+
{/* Iframe */}
From 2d1e67a0380d9e6aeef8d61d7a2e248300d75a33 Mon Sep 17 00:00:00 2001
From: tristantr
Date: Tue, 27 Jan 2026 12:17:12 +0100
Subject: [PATCH 06/20] Align Examples section text layout
---
src/pages/examples/index.jsx | 92 ++++++++++++++++--------------------
1 file changed, 42 insertions(+), 50 deletions(-)
diff --git a/src/pages/examples/index.jsx b/src/pages/examples/index.jsx
index bf81aad06..9f88b5c74 100644
--- a/src/pages/examples/index.jsx
+++ b/src/pages/examples/index.jsx
@@ -5,7 +5,6 @@ import LandingHeader from '../../landing/LandingHeader';
import Footer from '../../landing/Footer';
import CallToAction from '../../landing/CallToAction';
import RadialBlur from '../../landing/RadialBlur';
-import LandingSection from '../../landing/LandingSection';
import ExampleCard from '../../landing/ExampleCard';
import { examples } from '../../landing/examplesData';
import { motion } from 'framer-motion';
@@ -13,7 +12,8 @@ import { motion } from 'framer-motion';
export default function ExamplesPage() {
return (
-
+
+
Examples | Windmill
@@ -23,56 +23,48 @@ export default function ExamplesPage() {
/>
-
-
-
- {/* Hero Section */}
-
-
-
- Examples
-
-
- Interactive applications built with Windmill's Raw App Builder.
- Explore what you can create with code-first frontends powered by Windmill.
-
-
-
+ <>
+
+
+
+ {/* Header section */}
+
+
+ Examples
+
+
+ Interactive applications built with Windmill's Raw App Builder.
+ Explore what you can create with code-first frontends powered by Windmill.
+
+
- {/* Examples Grid */}
-
-
-
- {examples.map((example, index) => (
-
-
-
- ))}
-
+ {/* Examples Grid */}
+
+ {examples.map((example, index) => (
+
+
+
+ ))}
+
+
-
+ >
{/* Call to Action */}
From 48940a741d912be3b9809e4a04dbec5db86e84c7 Mon Sep 17 00:00:00 2001
From: tristantr
Date: Tue, 27 Jan 2026 13:07:39 +0100
Subject: [PATCH 07/20] Add posters to videos when not loaded yet
---
src/landing/components/ProductionTabs.tsx | 15 +++++++++++----
static/videos/posters/landingapps-local.jpg | Bin 0 -> 153485 bytes
static/videos/posters/landingapps-ui.jpg | Bin 0 -> 290067 bytes
static/videos/posters/landingflows-local.jpg | Bin 0 -> 161418 bytes
static/videos/posters/landingflows-ui.jpg | Bin 0 -> 211091 bytes
static/videos/posters/landingscripts-local.jpg | Bin 0 -> 78452 bytes
static/videos/posters/landingscripts-ui.jpg | Bin 0 -> 413013 bytes
7 files changed, 11 insertions(+), 4 deletions(-)
create mode 100644 static/videos/posters/landingapps-local.jpg
create mode 100644 static/videos/posters/landingapps-ui.jpg
create mode 100644 static/videos/posters/landingflows-local.jpg
create mode 100644 static/videos/posters/landingflows-ui.jpg
create mode 100644 static/videos/posters/landingscripts-local.jpg
create mode 100644 static/videos/posters/landingscripts-ui.jpg
diff --git a/src/landing/components/ProductionTabs.tsx b/src/landing/components/ProductionTabs.tsx
index 6c8fd37fe..74f7cf10f 100644
--- a/src/landing/components/ProductionTabs.tsx
+++ b/src/landing/components/ProductionTabs.tsx
@@ -15,6 +15,8 @@ export interface TabConfig {
description: string;
video: string;
localDevVideo: string;
+ poster?: string;
+ localDevPoster?: string;
youtubeUrl?: string;
}
@@ -34,9 +36,9 @@ export interface ProductionTabsProps {
// Default tabs configuration
export const defaultTabs: TabConfig[] = [
- { id: 'scripts', label: 'Scripts', icon: 'Server', description: 'Write scripts in 20+ languages (Python, TS, Go...) with full LSP support, auto-generated UI, managed dependencies and turn them into instant endpoints or hooks for pubsub events.', video: '/videos/landingscripts-ui.webm', localDevVideo: '/videos/landingscripts-local.webm', youtubeUrl: 'https://www.youtube.com/watch?v=QRf8C8qF7CY' },
- { id: 'flows', label: 'Flows', icon: 'Server', description: 'Orchestrate your scripts into high-performance flows with full code flexibility, AI assistance, and sub-20ms overhead.', video: '/videos/landingflows-ui.webm', localDevVideo: '/videos/landingflows-local.webm', youtubeUrl: 'https://www.youtube.com/watch?v=yE-eDNWTj3g' },
- { id: 'apps', label: 'Apps', icon: 'Monitor', description: 'Build powerful full-stack apps using Windmill as a backend and any framework as frontend.', video: '/videos/landingapps-ui.webm', localDevVideo: '/videos/landingapps-local.webm', youtubeUrl: 'https://www.youtube.com/watch?v=CNtRLDXbfOE' },
+ { id: 'scripts', label: 'Scripts', icon: 'Server', description: 'Write scripts in 20+ languages (Python, TS, Go...) with full LSP support, auto-generated UI, managed dependencies and turn them into instant endpoints or hooks for pubsub events.', video: '/videos/landingscripts-ui.webm', localDevVideo: '/videos/landingscripts-local.webm', poster: '/videos/posters/landingscripts-ui.jpg', localDevPoster: '/videos/posters/landingscripts-local.jpg', youtubeUrl: 'https://www.youtube.com/watch?v=QRf8C8qF7CY' },
+ { id: 'flows', label: 'Flows', icon: 'Server', description: 'Orchestrate your scripts into high-performance flows with full code flexibility, AI assistance, and sub-20ms overhead.', video: '/videos/landingflows-ui.webm', localDevVideo: '/videos/landingflows-local.webm', poster: '/videos/posters/landingflows-ui.jpg', localDevPoster: '/videos/posters/landingflows-local.jpg', youtubeUrl: 'https://www.youtube.com/watch?v=yE-eDNWTj3g' },
+ { id: 'apps', label: 'Apps', icon: 'Monitor', description: 'Build powerful full-stack apps using Windmill as a backend and any framework as frontend.', video: '/videos/landingapps-ui.webm', localDevVideo: '/videos/landingapps-local.webm', poster: '/videos/posters/landingapps-ui.jpg', localDevPoster: '/videos/posters/landingapps-local.jpg', youtubeUrl: 'https://www.youtube.com/watch?v=CNtRLDXbfOE' },
];
// Default subtitles configuration for Windmill UI videos
@@ -448,6 +450,10 @@ export default function ProductionTabs({
return videoMode === 'ui' ? tab.video : tab.localDevVideo;
};
+ const getPosterSrc = (tab: TabConfig) => {
+ return videoMode === 'ui' ? tab.poster : tab.localDevPoster;
+ };
+
const getCurrentSubtitles = () => {
return videoMode === 'ui' ? subtitles : localDevSubtitles;
};
@@ -512,11 +518,12 @@ export default function ProductionTabs({